From ea07161698120fc2f4f47ccc73eaba5336854753 Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 6 Jul 2026 15:00:27 +0200 Subject: [PATCH] Use status ID instead of passing status objects (#39727) --- .../__tests__/avatar_overlay-test.jsx | 10 +-- .../components/__tests__/hashtag_bar.tsx | 89 ++++++++----------- app/javascript/mastodon/components/avatar.tsx | 9 +- .../mastodon/components/avatar_overlay.tsx | 23 ++--- .../mastodon/components/content_warning.tsx | 27 ++---- .../components/display_name/default.tsx | 2 +- .../components/display_name/index.tsx | 4 +- .../components/display_name/no-domain.tsx | 4 +- .../components/display_name/simple.tsx | 4 +- .../mastodon/components/hashtag_bar.tsx | 56 ++++++++---- app/javascript/mastodon/components/status.jsx | 8 +- .../mastodon/components/status/content.tsx | 7 +- .../mastodon/components/status/header.tsx | 51 +++++------ .../components/embedded_status.tsx | 2 +- .../status/components/detailed_status.tsx | 2 +- app/javascript/mastodon/selectors/accounts.ts | 4 +- 16 files changed, 145 insertions(+), 157 deletions(-) diff --git a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx index 4b91e980786..96ab5b824f9 100644 --- a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx +++ b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx @@ -1,25 +1,23 @@ -import { fromJS } from 'immutable'; - import { render } from '@/testing/rendering'; import { AvatarOverlay } from '../avatar_overlay'; describe(' { - const account = fromJS({ + const account = { username: 'alice', acct: 'alice', display_name: 'Alice', avatar: '/animated/alice.gif', avatar_static: '/static/alice.jpg', - }); + }; - const friend = fromJS({ + const friend = { username: 'eve', acct: 'eve@blackhat.lair', display_name: 'Evelyn', avatar: '/animated/eve.gif', avatar_static: '/static/eve.jpg', - }); + }; it('renders a overlay avatar', () => { const { container } = render(); diff --git a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx index f86c1a2a6bc..d0efa03c063 100644 --- a/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/__tests__/hashtag_bar.tsx @@ -1,6 +1,5 @@ -import { fromJS } from 'immutable'; +import { mediaAttachmentFactoryAPI } from '@/testing/factories'; -import type { StatusLike } from '../hashtag_bar'; import { computeHashtagBarForStatus } from '../hashtag_bar'; function createStatus( @@ -9,25 +8,27 @@ function createStatus( hasMedia = false, spoilerText?: string, ) { - return fromJS({ - tags: hashtags.map((name) => ({ name })), + return { + tags: hashtags.map((name) => ({ + name, + url: `https://example.com/tag/${name}`, + })), contentHtml: content, - media_attachments: hasMedia ? ['fakeMedia'] : [], + media_attachments: hasMedia + ? [{ ...mediaAttachmentFactoryAPI(), remote_url: null }] + : [], spoiler_text: spoilerText, - }) as unknown as StatusLike; // need to force the type here, as it is not properly defined + }; } describe('computeHashtagBarForStatus', () => { it('does nothing when there are no tags', () => { const status = createStatus('

Simple text

', []); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( - `"

Simple text

"`, - ); + expect(statusContent).toMatchInlineSnapshot(`"

Simple text

"`); }); it('displays out of band hashtags in the bar', () => { @@ -36,11 +37,10 @@ describe('computeHashtagBarForStatus', () => { ['hashtag', 'test'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual(['test']); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

Simple text #hashtag

"`, ); }); @@ -51,11 +51,10 @@ describe('computeHashtagBarForStatus', () => { ['test'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"this is a #test. Some more text"`, ); }); @@ -66,13 +65,10 @@ describe('computeHashtagBarForStatus', () => { ['hashtag'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual(['hashtag']); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( - `"

Simple text

"`, - ); + expect(statusContent).toMatchInlineSnapshot(`"

Simple text

"`); }); it('does not include tags from content', () => { @@ -81,11 +77,10 @@ describe('computeHashtagBarForStatus', () => { ['hashtag'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

Simple text with a #hashtag

"`, ); }); @@ -96,11 +91,10 @@ describe('computeHashtagBarForStatus', () => { ['hashtag', 'test'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

#test. And another #hashtag

"`, ); }); @@ -111,13 +105,10 @@ describe('computeHashtagBarForStatus', () => { ['éaa'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual(['Éaa']); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( - `"

Text

"`, - ); + expect(statusContent).toMatchInlineSnapshot(`"

Text

"`); }); it('handles server-side normalized tags with accentuated characters', () => { @@ -126,13 +117,10 @@ describe('computeHashtagBarForStatus', () => { ['eaa'], // The server may normalize the hashtags in the `tags` attribute ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual(['Éaa']); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( - `"

Text

"`, - ); + expect(statusContent).toMatchInlineSnapshot(`"

Text

"`); }); it('does not display in bar a hashtag in content with a case difference', () => { @@ -141,11 +129,10 @@ describe('computeHashtagBarForStatus', () => { ['éaa'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

Text #Éaa

"`, ); }); @@ -156,11 +143,10 @@ describe('computeHashtagBarForStatus', () => { ['test', 'hashtag'], ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

#test #hashtag

"`, ); }); @@ -172,11 +158,10 @@ describe('computeHashtagBarForStatus', () => { true, ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

This is my content! #hashtag

"`, ); }); @@ -188,11 +173,10 @@ describe('computeHashtagBarForStatus', () => { true, ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual(['test', 'hashtag']); - expect(statusContentProps.statusContent).toMatchInlineSnapshot(`""`); + expect(statusContent).toMatchInlineSnapshot(`""`); }); it('does not use the hashtag bar if the status content is only hashtags, has a CW and a media', () => { @@ -203,11 +187,10 @@ describe('computeHashtagBarForStatus', () => { 'My CW text', ); - const { hashtagsInBar, statusContentProps } = - computeHashtagBarForStatus(status); + const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status); expect(hashtagsInBar).toEqual([]); - expect(statusContentProps.statusContent).toMatchInlineSnapshot( + expect(statusContent).toMatchInlineSnapshot( `"

#test #hashtag

"`, ); }); diff --git a/app/javascript/mastodon/components/avatar.tsx b/app/javascript/mastodon/components/avatar.tsx index e2da16703e0..eeea446d0c6 100644 --- a/app/javascript/mastodon/components/avatar.tsx +++ b/app/javascript/mastodon/components/avatar.tsx @@ -5,14 +5,15 @@ import { Link } from 'react-router-dom'; import { useHovering } from 'mastodon/hooks/useHovering'; import { autoPlayGif } from 'mastodon/initial_state'; -import type { Account } from 'mastodon/models/account'; +import type { Account, AccountShapeFull } from 'mastodon/models/account'; import { useAccount } from '../hooks/useAccount'; interface Props { - account: - | Pick - | undefined; // FIXME: remove `undefined` once we know for sure its always there + account?: Pick< + Account | AccountShapeFull, + 'id' | 'acct' | 'avatar' | 'avatar_static' + >; alt?: string; size?: number; style?: React.CSSProperties; diff --git a/app/javascript/mastodon/components/avatar_overlay.tsx b/app/javascript/mastodon/components/avatar_overlay.tsx index e7fc1252c1b..ff5e9655ba5 100644 --- a/app/javascript/mastodon/components/avatar_overlay.tsx +++ b/app/javascript/mastodon/components/avatar_overlay.tsx @@ -1,10 +1,15 @@ import { useHovering } from 'mastodon/hooks/useHovering'; import { autoPlayGif } from 'mastodon/initial_state'; -import type { Account } from 'mastodon/models/account'; +import type { Account, AccountShapeFull } from 'mastodon/models/account'; + +type AvatarAccount = Pick< + Account | AccountShapeFull, + 'acct' | 'avatar' | 'avatar_static' +>; interface Props { - account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there - friend: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there + account?: AvatarAccount; + friend?: AvatarAccount; size?: number; baseSize?: number; overlaySize?: number; @@ -27,12 +32,8 @@ export const AvatarOverlay: React.FC = ({ }) => { const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(autoPlayGif); - const accountSrc = hovering - ? account?.get('avatar') - : account?.get('avatar_static'); - const friendSrc = hovering - ? friend?.get('avatar') - : friend?.get('avatar_static'); + const accountSrc = hovering ? account?.avatar : account?.avatar_static; + const friendSrc = hovering ? friend?.avatar : friend?.avatar_static; return (
= ({ {accountSrc && ( {account?.get('acct')} )} @@ -63,7 +64,7 @@ export const AvatarOverlay: React.FC = ({ {friendSrc && ( {friend?.get('acct')} )} diff --git a/app/javascript/mastodon/components/content_warning.tsx b/app/javascript/mastodon/components/content_warning.tsx index 2d3222f479a..7f9e96cbd1e 100644 --- a/app/javascript/mastodon/components/content_warning.tsx +++ b/app/javascript/mastodon/components/content_warning.tsx @@ -1,24 +1,17 @@ -import type { List } from 'immutable'; - -import type { CustomEmoji } from '../models/custom_emoji'; -import type { Status } from '../models/status'; +import { useStatus } from '../hooks/useStatus'; import { EmojiHTML } from './emoji/html'; import { StatusBanner, BannerVariant } from './status_banner'; export const ContentWarning: React.FC<{ - status: Status; + statusId: string; expanded?: boolean; onClick?: () => void; -}> = ({ status, expanded, onClick }) => { - const hasSpoiler = !!status.get('spoiler_text'); - if (!hasSpoiler) { - return null; - } - - const text = - status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml'); - if (typeof text !== 'string' || text.length === 0) { +}> = ({ statusId, expanded, onClick }) => { + const status = useStatus(statusId); + const hasSpoiler = !!status?.spoiler_text; + const text = status?.translation?.spoilerHtml ?? status?.spoilerHtml; + if (!hasSpoiler || !text) { return null; } @@ -28,11 +21,7 @@ export const ContentWarning: React.FC<{ onClick={onClick} variant={BannerVariant.Warning} > - } - /> + ); }; diff --git a/app/javascript/mastodon/components/display_name/default.tsx b/app/javascript/mastodon/components/display_name/default.tsx index ec42c9ada97..0f93884d802 100644 --- a/app/javascript/mastodon/components/display_name/default.tsx +++ b/app/javascript/mastodon/components/display_name/default.tsx @@ -14,7 +14,7 @@ export function useAccountHandle( if (!account) { return null; } - let acct = account.get('acct'); + let acct = account.acct; if (!acct.includes('@') && localDomain) { acct = `${acct}@${localDomain}`; diff --git a/app/javascript/mastodon/components/display_name/index.tsx b/app/javascript/mastodon/components/display_name/index.tsx index 06bc380a10b..28bd9c17ff3 100644 --- a/app/javascript/mastodon/components/display_name/index.tsx +++ b/app/javascript/mastodon/components/display_name/index.tsx @@ -3,14 +3,14 @@ import type { ComponentPropsWithoutRef, FC } from 'react'; import type { LinkProps } from 'react-router-dom'; import { Link } from 'react-router-dom'; -import type { Account } from '@/mastodon/models/account'; +import type { Account, AccountShapeFull } from '@/mastodon/models/account'; import { DisplayNameDefault } from './default'; import { DisplayNameWithoutDomain } from './no-domain'; import { DisplayNameSimple } from './simple'; export interface DisplayNameProps { - account?: Account; + account?: Account | AccountShapeFull; localDomain?: string; variant?: 'default' | 'simple' | 'noDomain'; } diff --git a/app/javascript/mastodon/components/display_name/no-domain.tsx b/app/javascript/mastodon/components/display_name/no-domain.tsx index 530e0a08e0c..7c970aa5bd3 100644 --- a/app/javascript/mastodon/components/display_name/no-domain.tsx +++ b/app/javascript/mastodon/components/display_name/no-domain.tsx @@ -21,9 +21,9 @@ export const DisplayNameWithoutDomain: FC< {account ? ( ) : ( diff --git a/app/javascript/mastodon/components/display_name/simple.tsx b/app/javascript/mastodon/components/display_name/simple.tsx index 2bdddfeb82f..baad3f4affe 100644 --- a/app/javascript/mastodon/components/display_name/simple.tsx +++ b/app/javascript/mastodon/components/display_name/simple.tsx @@ -16,8 +16,8 @@ export const DisplayNameSimple: FC< ); diff --git a/app/javascript/mastodon/components/hashtag_bar.tsx b/app/javascript/mastodon/components/hashtag_bar.tsx index a9dd6941a47..9dacc2c76e7 100644 --- a/app/javascript/mastodon/components/hashtag_bar.tsx +++ b/app/javascript/mastodon/components/hashtag_bar.tsx @@ -8,7 +8,12 @@ import type { List, Record } from 'immutable'; import { groupBy, minBy } from 'lodash'; -import { getStatusContent } from './status_content'; +import type { ApiTagJSON } from '../api_types/statuses'; +import type { + MediaAttachmentShape, + StatusShape, + StatusTranslation, +} from '../models/status'; // Fit on a single line on desktop const VISIBLE_HASHTAGS = 3; @@ -17,10 +22,11 @@ const VISIBLE_HASHTAGS = 3; export type TagLike = Record<{ name: string }>; export type StatusLike = Record<{ tags: List; - contentHTML: string; + contentHtml: string; media_attachments: List; spoiler_text?: string; account: Record<{ id: string }>; + translation?: Record<{ contentHtml: string }>; }>; function normalizeHashtag(hashtag: string) { @@ -88,20 +94,23 @@ function localeAwareInclude(collection: string[], value: string) { } // We use an intermediate function here to make it easier to test -export function computeHashtagBarForStatus(status: StatusLike): { - statusContentProps: { statusContent: string }; - hashtagsInBar: string[]; -} { - let statusContent = getStatusContent(status); +export function computeHashtagBarForStatus( + status: Pick< + StatusShape, + | 'tags' + | 'media_attachments' + | 'spoiler_text' + | 'translation' + | 'contentHtml' + >, +) { + let statusContent = status.translation?.contentHtml ?? status.contentHtml; - const tagNames = status - .get('tags') - .map((tag) => tag.get('name')) - .toJS(); + const tagNames = status.tags.map((tag) => tag.name); // this is returned if we stop the processing early, it does not change what is displayed const defaultResult = { - statusContentProps: { statusContent }, + statusContent, hashtagsInBar: [], }; @@ -164,8 +173,8 @@ export function computeHashtagBarForStatus(status: StatusLike): { }); const isOnlyOneLine = contentWithoutLastLine.content.childElementCount === 0; - const hasMedia = status.get('media_attachments').size > 0; - const hasSpoiler = !!status.get('spoiler_text'); + const hasMedia = status.media_attachments.length > 0; + const hasSpoiler = !!status.spoiler_text; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- due to https://github.com/microsoft/TypeScript/issues/9998 if (onlyHashtags && ((hasMedia && !hasSpoiler) || !isOnlyOneLine)) { @@ -178,7 +187,7 @@ export function computeHashtagBarForStatus(status: StatusLike): { } return { - statusContentProps: { statusContent }, + statusContent, hashtagsInBar: uniqueHashtagsWithCaseHandling(hashtagsInBar), }; } @@ -191,11 +200,20 @@ export function computeHashtagBarForStatus(status: StatusLike): { * @returns Props to be passed to the component, and the hashtagBar to render */ export function getHashtagBarForStatus(status: StatusLike) { - const { statusContentProps, hashtagsInBar } = - computeHashtagBarForStatus(status); + const { statusContent, hashtagsInBar } = computeHashtagBarForStatus({ + tags: status.get('tags').toJS() as ApiTagJSON[], + media_attachments: status + .get('media_attachments') + .toJS() as MediaAttachmentShape[], + spoiler_text: status.get('spoiler_text'), + translation: status + .get('translation') + ?.toJSON() as unknown as StatusTranslation, + contentHtml: status.get('contentHtml'), + }); return { - statusContentProps, + statusContentProps: { statusContent }, hashtagBar: ( = ({ hashtags, accountId }) => { diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index a409f3fd59c..93a70c18d06 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -555,7 +555,7 @@ class Status extends ImmutablePureComponent { const taggedCollection = ( status.get('tagged_collections') ).find((item) => compareUrls(item.get('url'), cardUrl)); - + if (taggedCollection) { media = ; } else { @@ -579,10 +579,10 @@ class Status extends ImmutablePureComponent { const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); const header = this.props.headerRenderFn - ? this.props.headerRenderFn({ status, account, avatarSize, messages, onHeaderClick: this.handleHeaderClick, featured }) + ? this.props.headerRenderFn({ statusId: status.get('id'), account, avatarSize, messages, onHeaderClick: this.handleHeaderClick, featured }) : ( } - {(!matchedFilters || this.state.showDespiteFilter) && } + {(!matchedFilters || this.state.showDespiteFilter) && } {expanded && ( <> diff --git a/app/javascript/mastodon/components/status/content.tsx b/app/javascript/mastodon/components/status/content.tsx index 5ddec7b4073..29389c75bc7 100644 --- a/app/javascript/mastodon/components/status/content.tsx +++ b/app/javascript/mastodon/components/status/content.tsx @@ -23,10 +23,11 @@ const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top) export const StatusContent: React.FC<{ statusId: string; + statusContent?: string; onClick?: React.MouseEventHandler; onTranslate?: React.MouseEventHandler; collapsible?: boolean; -}> = ({ statusId, onClick, onTranslate, collapsible }) => { +}> = ({ statusId, statusContent, onClick, onTranslate, collapsible }) => { const status = useStatus(statusId); const { signedIn } = useIdentity(); const targetLanguages = useAppSelector( @@ -129,7 +130,9 @@ export const StatusContent: React.FC<{ diff --git a/app/javascript/mastodon/components/status/header.tsx b/app/javascript/mastodon/components/status/header.tsx index 1ce5c4a36c6..15fca480e9f 100644 --- a/app/javascript/mastodon/components/status/header.tsx +++ b/app/javascript/mastodon/components/status/header.tsx @@ -5,9 +5,9 @@ import { defineMessage, useIntl } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; -import { isStatusVisibility } from '@/mastodon/api_types/statuses'; -import type { Account } from '@/mastodon/models/account'; -import type { Status } from '@/mastodon/models/status'; +import type { Account, AccountShapeFull } from '@/mastodon/models/account'; +import { selectAccountStatus } from '@/mastodon/selectors/statuses'; +import { useAppSelector } from '@/mastodon/store'; import { Avatar } from '../avatar'; import { AvatarOverlay } from '../avatar_overlay'; @@ -17,8 +17,8 @@ import { RelativeTimestamp } from '../relative_timestamp'; import { VisibilityIcon } from '../visibility_icon'; export interface StatusHeaderProps { - status: Status; - account?: Account; + statusId: string; + account?: Account | AccountShapeFull; avatarSize?: number; contentBeforeDate?: ReactNode; contentAfterDate?: ReactNode; @@ -32,7 +32,7 @@ export interface StatusHeaderProps { export type StatusHeaderRenderFn = (args: StatusHeaderProps) => ReactNode; export const StatusHeader: FC = ({ - status, + statusId, account, className, avatarSize = 48, @@ -41,8 +41,14 @@ export const StatusHeader: FC = ({ contentAfterDate, onHeaderClick, }) => { - const statusAccount = status.get('account') as Account | undefined; - const editedAt = status.get('edited_at') as string; + const status = useAppSelector((state) => + selectAccountStatus(state, statusId), + ); + if (!status) { + return null; + } + const statusAccount = status.account; + const editedAt = status.edited_at; return ( /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ @@ -62,11 +68,13 @@ export const StatusHeader: FC = ({ {contentBeforeDate} - - + + + + {editedAt && } @@ -75,25 +83,12 @@ export const StatusHeader: FC = ({ ); }; -export const StatusVisibility: FC<{ visibility: unknown }> = ({ - visibility, -}) => { - if (typeof visibility !== 'string' || !isStatusVisibility(visibility)) { - return null; - } - return ( - - - - ); -}; - const editMessage = defineMessage({ id: 'status.edited', defaultMessage: 'Edited {date}', }); -export const StatusEditedAt: FC<{ editedAt: string }> = ({ editedAt }) => { +const StatusEditedAt: FC<{ editedAt: string }> = ({ editedAt }) => { const intl = useIntl(); return ( = ({ editedAt }) => { ); }; -export const StatusDisplayName: FC<{ - statusAccount?: Account; - friendAccount?: Account; +const StatusDisplayName: FC<{ + statusAccount?: AccountShapeFull; + friendAccount?: Account | AccountShapeFull; avatarSize: number; }> = ({ statusAccount, friendAccount, avatarSize }) => { const AccountComponent = friendAccount ? AvatarOverlay : Avatar; diff --git a/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx index 5a783ecc38c..e53434ba2e2 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx @@ -109,7 +109,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.tsx b/app/javascript/mastodon/features/status/components/detailed_status.tsx index 0463643c7a6..53365940910 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.tsx +++ b/app/javascript/mastodon/features/status/components/detailed_status.tsx @@ -471,7 +471,7 @@ export const DetailedStatus: React.FC<{ {(!matchedFilters || showDespiteFilter) && ( diff --git a/app/javascript/mastodon/selectors/accounts.ts b/app/javascript/mastodon/selectors/accounts.ts index d8cb0bb319c..48330073d5e 100644 --- a/app/javascript/mastodon/selectors/accounts.ts +++ b/app/javascript/mastodon/selectors/accounts.ts @@ -55,8 +55,8 @@ export function makeGetAccount() { } export const selectPlainAccount = createAppSelector( - [(state, accountId: string) => state.accounts.get(accountId)], - (account) => (account ? (account.toJS() as AccountShapeFull) : null), + [(state, accountId?: string | null) => state.accounts.get(accountId ?? '')], + (account) => (account?.toJS() as AccountShapeFull | undefined) ?? null, ); export const selectIsAccountLocal = createAppSelector(