Use status ID instead of passing status objects (#39727)

This commit is contained in:
Echo
2026-07-06 15:00:27 +02:00
committed by GitHub
parent dff66fe890
commit ea07161698
16 changed files with 145 additions and 157 deletions

View File

@@ -1,25 +1,23 @@
import { fromJS } from 'immutable';
import { render } from '@/testing/rendering';
import { AvatarOverlay } from '../avatar_overlay';
describe('<AvatarOverlay', () => {
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(<AvatarOverlay account={account} friend={friend} />);

View File

@@ -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('<p>Simple text</p>', []);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
const { hashtagsInBar, statusContent } = computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Simple text</p>"`,
);
expect(statusContent).toMatchInlineSnapshot(`"<p>Simple text</p>"`);
});
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(
`"<p>Simple text <a href="test">#hashtag</a></p>"`,
);
});
@@ -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 #<a class="zrl" href="https://example.com/search?tag=test">test</a>. 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(
`"<p>Simple text</p>"`,
);
expect(statusContent).toMatchInlineSnapshot(`"<p>Simple text</p>"`);
});
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(
`"<p>Simple text with a <a href="test">#hashtag</a></p>"`,
);
});
@@ -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(
`"<p><a href="test">#test</a>. And another <a href="test">#hashtag</a></p>"`,
);
});
@@ -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(
`"<p>Text</p>"`,
);
expect(statusContent).toMatchInlineSnapshot(`"<p>Text</p>"`);
});
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(
`"<p>Text</p>"`,
);
expect(statusContent).toMatchInlineSnapshot(`"<p>Text</p>"`);
});
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(
`"<p>Text <a href="test">#Éaa</a></p>"`,
);
});
@@ -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(
`"<p><a href="test">#test</a> <a href="test">#hashtag</a></p>"`,
);
});
@@ -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(
`"<p>This is my content! <a href="test">#hashtag</a></p>"`,
);
});
@@ -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(
`"<p><a href="test">#test</a> <a href="test">#hashtag</a></p>"`,
);
});

View File

@@ -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<Account, 'id' | 'acct' | 'avatar' | 'avatar_static'>
| 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;

View File

@@ -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<Props> = ({
}) => {
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 (
<div
@@ -49,7 +50,7 @@ export const AvatarOverlay: React.FC<Props> = ({
{accountSrc && (
<img
src={accountSrc}
alt={account?.get('acct')}
alt={account?.acct}
onError={handleImgLoadError}
/>
)}
@@ -63,7 +64,7 @@ export const AvatarOverlay: React.FC<Props> = ({
{friendSrc && (
<img
src={friendSrc}
alt={friend?.get('acct')}
alt={friend?.acct}
onError={handleImgLoadError}
/>
)}

View File

@@ -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}
>
<EmojiHTML
as='span'
htmlString={text}
extraEmojis={status.get('emojis') as List<CustomEmoji>}
/>
<EmojiHTML as='span' htmlString={text} extraEmojis={status.emojis} />
</StatusBanner>
);
};

View File

@@ -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}`;

View File

@@ -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';
}

View File

@@ -21,9 +21,9 @@ export const DisplayNameWithoutDomain: FC<
{account ? (
<EmojiHTML
className='display-name__html'
htmlString={account.get('display_name_html')}
htmlString={account.display_name_html}
as='strong'
extraEmojis={account.get('emojis')}
extraEmojis={account.emojis}
/>
) : (
<strong className='display-name__html'>

View File

@@ -16,8 +16,8 @@ export const DisplayNameSimple: FC<
<EmojiHTML
{...props}
as='span'
htmlString={account.get('display_name_html')}
extraEmojis={account.get('emojis')}
htmlString={account.display_name_html}
extraEmojis={account.emojis}
/>
</bdi>
);

View File

@@ -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<TagLike>;
contentHTML: string;
contentHtml: string;
media_attachments: List<unknown>;
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 <StatusContent> 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: (
<HashtagBar
hashtags={hashtagsInBar}
@@ -205,7 +223,7 @@ export function getHashtagBarForStatus(status: StatusLike) {
};
}
const HashtagBar: React.FC<{
export const HashtagBar: React.FC<{
hashtags: string[];
accountId: string;
}> = ({ hashtags, accountId }) => {

View File

@@ -555,7 +555,7 @@ class Status extends ImmutablePureComponent {
const taggedCollection = (
status.get('tagged_collections')
).find((item) => compareUrls(item.get('url'), cardUrl));
if (taggedCollection) {
media = <CollectionPreviewCard collection={taggedCollection.toJS()} headingLevel='h2' />;
} 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 })
: (
<StatusHeader
status={status}
statusId={status.get('id')}
account={account}
avatarSize={avatarSize}
onHeaderClick={this.handleHeaderClick}
@@ -614,7 +614,7 @@ class Status extends ImmutablePureComponent {
{matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
{(!matchedFilters || this.state.showDespiteFilter) && <ContentWarning status={status} expanded={expanded} onClick={this.handleExpandedToggle} />}
{(!matchedFilters || this.state.showDespiteFilter) && <ContentWarning statusId={status.get('id')} expanded={expanded} onClick={this.handleExpandedToggle} />}
{expanded && (
<>

View File

@@ -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<HTMLButtonElement>;
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<{
<EmojiHTML
className='status__content__text status__content__text--visible translate'
lang={language}
htmlString={status.translation?.contentHtml ?? status.contentHtml}
htmlString={
statusContent ?? status.translation?.contentHtml ?? status.contentHtml
}
extraEmojis={status.emojis}
{...htmlHandlers}
/>

View File

@@ -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<StatusHeaderProps> = ({
status,
statusId,
account,
className,
avatarSize = 48,
@@ -41,8 +41,14 @@ export const StatusHeader: FC<StatusHeaderProps> = ({
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<StatusHeaderProps> = ({
{contentBeforeDate}
<Link
to={`/@${statusAccount?.acct}/${status.get('id') as string}`}
to={`/@${statusAccount.acct}/${status.id}`}
className='status__relative-time'
>
<StatusVisibility visibility={status.get('visibility')} />
<RelativeTimestamp timestamp={status.get('created_at') as string} />
<span className='status__visibility-icon'>
<VisibilityIcon visibility={status.visibility} />
</span>
<RelativeTimestamp timestamp={status.created_at} />
{editedAt && <StatusEditedAt editedAt={editedAt} />}
</Link>
@@ -75,25 +83,12 @@ export const StatusHeader: FC<StatusHeaderProps> = ({
);
};
export const StatusVisibility: FC<{ visibility: unknown }> = ({
visibility,
}) => {
if (typeof visibility !== 'string' || !isStatusVisibility(visibility)) {
return null;
}
return (
<span className='status__visibility-icon'>
<VisibilityIcon visibility={visibility} />
</span>
);
};
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 (
<abbr
@@ -113,9 +108,9 @@ export const StatusEditedAt: FC<{ editedAt: string }> = ({ 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;

View File

@@ -109,7 +109,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
</div>
<ContentWarning
status={status}
statusId={status.get('id') as string}
onClick={handleContentWarningClick}
expanded={expanded}
/>

View File

@@ -471,7 +471,7 @@ export const DetailedStatus: React.FC<{
{(!matchedFilters || showDespiteFilter) && (
<ContentWarning
status={status}
statusId={status.get('id')}
expanded={expanded}
onClick={handleExpandedToggle}
/>

View File

@@ -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(