Handled non-string preferredUsername values (#21598)

ref https://linear.app/ghost/issue/AP-585

If preferredUsername is not a string, and we attempt to render it, we
will crash the entire React application!
This commit is contained in:
Fabien 'egg' O'Carroll 2024-11-12 14:22:45 +00:00 committed by GitHub
parent e7a80d79f3
commit d555cc6612
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 24 additions and 5 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@tryghost/admin-x-activitypub",
"version": "0.3.14",
"version": "0.3.15",
"license": "MIT",
"repository": {
"type": "git",

View File

@ -6,6 +6,7 @@ import MainNavigation from './navigation/MainNavigation';
import NiceModal from '@ebay/nice-modal-react';
import React, {useEffect, useRef} from 'react';
import ViewProfileModal from './global/ViewProfileModal';
import getName from '../utils/get-name';
import getUsername from '../utils/get-username';
import {Button, Heading, LoadingIndicator} from '@tryghost/admin-x-design-system';
import {handleViewContent} from '../utils/content-handlers';
@ -123,7 +124,7 @@ const Inbox: React.FC<InboxProps> = ({}) => {
<APAvatar author={actor} />
<div>
<div className='text-grey-600'>
<span className='mr-1 truncate font-bold text-black'>{actor.name || actor.preferredUsername || 'Unknown'}</span>
<span className='mr-1 truncate font-bold text-black'>{getName(actor)}</span>
<div className='truncate text-sm'>{getUsername(actor)}</div>
</div>
</div>

View File

@ -4,6 +4,7 @@ import FeedItem from './feed/FeedItem';
import MainNavigation from './navigation/MainNavigation';
import NiceModal from '@ebay/nice-modal-react';
import React, {useEffect, useRef, useState} from 'react';
import getName from '../utils/get-name';
import getUsername from '../utils/get-username';
import {ActorProperties} from '@tryghost/admin-x-framework/api/activitypub';
@ -187,7 +188,7 @@ const Profile: React.FC<ProfileProps> = ({}) => {
<APAvatar author={item} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{item.name || item.preferredUsername || 'Unknown'}</span>
<span className='mr-1 font-bold text-black'>{getName(item)}</span>
<div className='text-sm'>{getUsername(item)}</div>
</div>
</div>
@ -235,7 +236,7 @@ const Profile: React.FC<ProfileProps> = ({}) => {
<APAvatar author={item} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{item.name || item.preferredUsername || 'Unknown'}</span>
<span className='mr-1 font-bold text-black'>{item.name || getName(item) || 'Unknown'}</span>
<div className='text-sm'>{getUsername(item)}</div>
</div>
</div>

View File

@ -13,6 +13,7 @@ import APAvatar from '../global/APAvatar';
import ActivityItem from '../activities/ActivityItem';
import FeedItem from '../feed/FeedItem';
import FollowButton from '../global/FollowButton';
import getName from '../../utils/get-name';
import getUsername from '../../utils/get-username';
const noop = () => {};
@ -86,7 +87,7 @@ const ActorList: React.FC<ActorListProps> = ({
<APAvatar author={actor} />
<div>
<div className='text-grey-600'>
<span className='mr-1 font-bold text-black'>{actor.name || actor.preferredUsername || 'Unknown'}</span>
<span className='mr-1 font-bold text-black'>{getName(actor)}</span>
<div className='text-sm'>{getUsername(actor)}</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
export default function getName(actor: {name: unknown, preferredUsername: unknown}): string {
if (typeof actor.name === 'string') {
return actor.name;
}
if (typeof actor.preferredUsername === 'string') {
return actor.preferredUsername;
}
if (typeof actor.preferredUsername === 'object' && actor.preferredUsername !== null) {
if ('@value' in actor.preferredUsername) {
if (typeof actor.preferredUsername['@value'] === 'string') {
return actor.preferredUsername['@value'];
}
}
}
return 'Unknown';
}