Emoji: Statuses (#36393)
This commit is contained in:
@@ -1,15 +1,38 @@
|
|||||||
|
import type { List } from 'immutable';
|
||||||
|
|
||||||
|
import type { CustomEmoji } from '../models/custom_emoji';
|
||||||
|
import type { Status } from '../models/status';
|
||||||
|
|
||||||
|
import { EmojiHTML } from './emoji/html';
|
||||||
import { StatusBanner, BannerVariant } from './status_banner';
|
import { StatusBanner, BannerVariant } from './status_banner';
|
||||||
|
|
||||||
export const ContentWarning: React.FC<{
|
export const ContentWarning: React.FC<{
|
||||||
text: string;
|
status: Status;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}> = ({ text, expanded, onClick }) => (
|
}> = ({ status, expanded, onClick }) => {
|
||||||
<StatusBanner
|
const hasSpoiler = !!status.get('spoiler_text');
|
||||||
expanded={expanded}
|
if (!hasSpoiler) {
|
||||||
onClick={onClick}
|
return null;
|
||||||
variant={BannerVariant.Warning}
|
}
|
||||||
>
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: text }} />
|
const text =
|
||||||
</StatusBanner>
|
status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml');
|
||||||
);
|
if (typeof text !== 'string' || text.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StatusBanner
|
||||||
|
expanded={expanded}
|
||||||
|
onClick={onClick}
|
||||||
|
variant={BannerVariant.Warning}
|
||||||
|
>
|
||||||
|
<EmojiHTML
|
||||||
|
as='span'
|
||||||
|
htmlString={text}
|
||||||
|
extraEmojis={status.get('emoji') as List<CustomEmoji>}
|
||||||
|
/>
|
||||||
|
</StatusBanner>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import classNames from 'classnames';
|
|||||||
import { animated, useSpring } from '@react-spring/web';
|
import { animated, useSpring } from '@react-spring/web';
|
||||||
import escapeTextContentForBrowser from 'escape-html';
|
import escapeTextContentForBrowser from 'escape-html';
|
||||||
|
|
||||||
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { fetchPoll, vote } from 'mastodon/actions/polls';
|
import { fetchPoll, vote } from 'mastodon/actions/polls';
|
||||||
@@ -305,10 +306,11 @@ const PollOption: React.FC<PollOptionProps> = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<span
|
<EmojiHTML
|
||||||
className='poll__option__text translate'
|
className='poll__option__text translate'
|
||||||
lang={lang}
|
lang={lang}
|
||||||
dangerouslySetInnerHTML={{ __html: titleHtml }}
|
htmlString={titleHtml}
|
||||||
|
extraEmojis={poll.emojis}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!!voted && (
|
{!!voted && (
|
||||||
|
|||||||
@@ -600,7 +600,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
|
|
||||||
{matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
|
{matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
|
||||||
|
|
||||||
{(status.get('spoiler_text').length > 0 && (!matchedFilters || this.state.showDespiteFilter)) && <ContentWarning text={status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml')} expanded={expanded} onClick={this.handleExpandedToggle} />}
|
{(!matchedFilters || this.state.showDespiteFilter) && <ContentWarning status={status} expanded={expanded} onClick={this.handleExpandedToggle} />}
|
||||||
|
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -83,14 +83,15 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
|||||||
|
|
||||||
export const useElementHandledLink = ({
|
export const useElementHandledLink = ({
|
||||||
hashtagAccountId,
|
hashtagAccountId,
|
||||||
mentionAccountId,
|
hrefToMentionAccountId,
|
||||||
}: {
|
}: {
|
||||||
hashtagAccountId?: string;
|
hashtagAccountId?: string;
|
||||||
mentionAccountId?: string;
|
hrefToMentionAccountId?: (href: string) => string | undefined;
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const onElement = useCallback<OnElementHandler>(
|
const onElement = useCallback<OnElementHandler>(
|
||||||
(element, { key, ...props }) => {
|
(element, { key, ...props }) => {
|
||||||
if (element instanceof HTMLAnchorElement) {
|
if (element instanceof HTMLAnchorElement) {
|
||||||
|
const mentionId = hrefToMentionAccountId?.(element.href);
|
||||||
return (
|
return (
|
||||||
<HandledLink
|
<HandledLink
|
||||||
{...props}
|
{...props}
|
||||||
@@ -98,13 +99,13 @@ export const useElementHandledLink = ({
|
|||||||
href={element.href}
|
href={element.href}
|
||||||
text={element.innerText}
|
text={element.innerText}
|
||||||
hashtagAccountId={hashtagAccountId}
|
hashtagAccountId={hashtagAccountId}
|
||||||
mentionAccountId={mentionAccountId}
|
mentionAccountId={mentionId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
[hashtagAccountId, mentionAccountId],
|
[hashtagAccountId, hrefToMentionAccountId],
|
||||||
);
|
);
|
||||||
return { onElement };
|
return { onElement };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { useCallback, useRef, useId } from 'react';
|
|||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { AnimateEmojiProvider } from './emoji/context';
|
||||||
|
|
||||||
export enum BannerVariant {
|
export enum BannerVariant {
|
||||||
Warning = 'warning',
|
Warning = 'warning',
|
||||||
Filter = 'filter',
|
Filter = 'filter',
|
||||||
@@ -34,8 +36,7 @@ export const StatusBanner: React.FC<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
// Element clicks are passed on to button
|
// Element clicks are passed on to button
|
||||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
<AnimateEmojiProvider
|
||||||
<div
|
|
||||||
className={
|
className={
|
||||||
variant === BannerVariant.Warning
|
variant === BannerVariant.Warning
|
||||||
? 'content-warning'
|
? 'content-warning'
|
||||||
@@ -69,6 +70,6 @@ export const StatusBanner: React.FC<{
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</AnimateEmojiProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ export const EditIndicator = () => {
|
|||||||
|
|
||||||
<EmbeddedStatusContent
|
<EmbeddedStatusContent
|
||||||
className='edit-indicator__content translate'
|
className='edit-indicator__content translate'
|
||||||
content={status.get('contentHtml')}
|
status={status}
|
||||||
language={status.get('language')}
|
|
||||||
mentions={status.get('mentions')}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(status.get('poll') || status.get('media_attachments').size > 0) && (
|
{(status.get('poll') || status.get('media_attachments').size > 0) && (
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ export const ReplyIndicator = () => {
|
|||||||
|
|
||||||
<EmbeddedStatusContent
|
<EmbeddedStatusContent
|
||||||
className='reply-indicator__content translate'
|
className='reply-indicator__content translate'
|
||||||
content={status.get('contentHtml')}
|
status={status}
|
||||||
language={status.get('language')}
|
|
||||||
mentions={status.get('mentions')}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(status.get('poll') || status.get('media_attachments').size > 0) && (
|
{(status.get('poll') || status.get('media_attachments').size > 0) && (
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { DisplayName } from 'mastodon/components/display_name';
|
import { DisplayName } from 'mastodon/components/display_name';
|
||||||
import { FollowButton } from 'mastodon/components/follow_button';
|
import { FollowButton } from 'mastodon/components/follow_button';
|
||||||
@@ -39,9 +40,10 @@ export const AccountCard: React.FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{account.get('note').length > 0 && (
|
{account.get('note').length > 0 && (
|
||||||
<div
|
<EmojiHTML
|
||||||
className='account-card__bio translate animate-parent'
|
className='account-card__bio translate'
|
||||||
dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
|
htmlString={account.get('note_emojified')}
|
||||||
|
extraEmojis={account.get('emojis')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,12 @@ export function cleanExtraEmojis(extraEmojis?: CustomEmojiMapArg) {
|
|||||||
if (!extraEmojis) {
|
if (!extraEmojis) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(extraEmojis)) {
|
||||||
|
return extraEmojis.reduce<ExtraCustomEmojiMap>(
|
||||||
|
(acc, emoji) => ({ ...acc, [emoji.shortcode]: emoji }),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!isList(extraEmojis)) {
|
if (!isList(extraEmojis)) {
|
||||||
return extraEmojis;
|
return extraEmojis;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ export type EmojiStateMap = LimitedCache<string, EmojiState>;
|
|||||||
|
|
||||||
export type CustomEmojiMapArg =
|
export type CustomEmojiMapArg =
|
||||||
| ExtraCustomEmojiMap
|
| ExtraCustomEmojiMap
|
||||||
| ImmutableList<CustomEmoji>;
|
| ImmutableList<CustomEmoji>
|
||||||
|
| CustomEmoji[];
|
||||||
|
|
||||||
export type ExtraCustomEmojiMap = Record<
|
export type ExtraCustomEmojiMap = Record<
|
||||||
string,
|
string,
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
|
||||||
import { Avatar } from '../../../components/avatar';
|
import { Avatar } from '@/mastodon/components/avatar';
|
||||||
import { DisplayName } from '../../../components/display_name';
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '@/mastodon/components/icon_button';
|
||||||
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
||||||
@@ -30,7 +31,6 @@ class AccountAuthorize extends ImmutablePureComponent {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, account, onAuthorize, onReject } = this.props;
|
const { intl, account, onAuthorize, onReject } = this.props;
|
||||||
const content = { __html: account.get('note_emojified') };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='account-authorize__wrapper'>
|
<div className='account-authorize__wrapper'>
|
||||||
@@ -40,7 +40,11 @@ class AccountAuthorize extends ImmutablePureComponent {
|
|||||||
<DisplayName account={account} />
|
<DisplayName account={account} />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className='account__header__content translate' dangerouslySetInnerHTML={content} />
|
<EmojiHTML
|
||||||
|
className='account__header__content translate'
|
||||||
|
htmlString={account.get('note_emojified')}
|
||||||
|
extraEmojis={account.get('emojis')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account--panel'>
|
<div className='account--panel'>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
|
|
||||||
import type { List as ImmutableList, RecordOf } from 'immutable';
|
import type { List as ImmutableList, RecordOf } from 'immutable';
|
||||||
|
|
||||||
|
import type { ApiMentionJSON } from '@/mastodon/api_types/statuses';
|
||||||
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
|
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
|
||||||
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
|
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
|
||||||
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
|
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
|
||||||
@@ -18,7 +19,7 @@ import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
|||||||
|
|
||||||
import { EmbeddedStatusContent } from './embedded_status_content';
|
import { EmbeddedStatusContent } from './embedded_status_content';
|
||||||
|
|
||||||
export type Mention = RecordOf<{ url: string; acct: string }>;
|
export type Mention = RecordOf<ApiMentionJSON>;
|
||||||
|
|
||||||
export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
||||||
statusId,
|
statusId,
|
||||||
@@ -86,12 +87,9 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assign status attributes to variables with a forced type, as status is not yet properly typed
|
// Assign status attributes to variables with a forced type, as status is not yet properly typed
|
||||||
const contentHtml = status.get('contentHtml') as string;
|
const hasContentWarning = !!status.get('spoiler_text');
|
||||||
const contentWarning = status.get('spoilerHtml') as string;
|
|
||||||
const poll = status.get('poll');
|
const poll = status.get('poll');
|
||||||
const language = status.get('language') as string;
|
const expanded = !status.get('hidden') || !hasContentWarning;
|
||||||
const mentions = status.get('mentions') as ImmutableList<Mention>;
|
|
||||||
const expanded = !status.get('hidden') || !contentWarning;
|
|
||||||
const mediaAttachmentsSize = (
|
const mediaAttachmentsSize = (
|
||||||
status.get('media_attachments') as ImmutableList<unknown>
|
status.get('media_attachments') as ImmutableList<unknown>
|
||||||
).size;
|
).size;
|
||||||
@@ -109,20 +107,16 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
|||||||
<DisplayName account={account} />
|
<DisplayName account={account} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{contentWarning && (
|
<ContentWarning
|
||||||
<ContentWarning
|
status={status}
|
||||||
text={contentWarning}
|
onClick={handleContentWarningClick}
|
||||||
onClick={handleContentWarningClick}
|
expanded={expanded}
|
||||||
expanded={expanded}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(!contentWarning || expanded) && (
|
{(!hasContentWarning || expanded) && (
|
||||||
<EmbeddedStatusContent
|
<EmbeddedStatusContent
|
||||||
className='notification-group__embedded-status__content reply-indicator__content translate'
|
className='notification-group__embedded-status__content reply-indicator__content translate'
|
||||||
content={contentHtml}
|
status={status}
|
||||||
language={language}
|
|
||||||
mentions={mentions}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -6,16 +6,22 @@ import type { List } from 'immutable';
|
|||||||
|
|
||||||
import type { History } from 'history';
|
import type { History } from 'history';
|
||||||
|
|
||||||
|
import type { ApiMentionJSON } from '@/mastodon/api_types/statuses';
|
||||||
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
|
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
|
||||||
|
import type { Status } from '@/mastodon/models/status';
|
||||||
|
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
||||||
|
|
||||||
import type { Mention } from './embedded_status';
|
import type { Mention } from './embedded_status';
|
||||||
|
|
||||||
const handleMentionClick = (
|
const handleMentionClick = (
|
||||||
history: History,
|
history: History,
|
||||||
mention: Mention,
|
mention: ApiMentionJSON,
|
||||||
e: MouseEvent,
|
e: MouseEvent,
|
||||||
) => {
|
) => {
|
||||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
history.push(`/@${mention.get('acct')}`);
|
history.push(`/@${mention.acct}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,16 +37,26 @@ const handleHashtagClick = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const EmbeddedStatusContent: React.FC<{
|
export const EmbeddedStatusContent: React.FC<{
|
||||||
content: string;
|
status: Status;
|
||||||
mentions: List<Mention>;
|
|
||||||
language: string;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
}> = ({ content, mentions, language, className }) => {
|
}> = ({ status, className }) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const mentions = useMemo(
|
||||||
|
() => (status.get('mentions') as List<Mention>).toJS(),
|
||||||
|
[status],
|
||||||
|
);
|
||||||
|
const htmlHandlers = useElementHandledLink({
|
||||||
|
hashtagAccountId: status.get('account') as string | undefined,
|
||||||
|
hrefToMentionAccountId(href) {
|
||||||
|
const mention = mentions.find((item) => item.url === href);
|
||||||
|
return mention?.id;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleContentRef = useCallback(
|
const handleContentRef = useCallback(
|
||||||
(node: HTMLDivElement | null) => {
|
(node: HTMLDivElement | null) => {
|
||||||
if (!node) {
|
if (!node || isModernEmojiEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +69,7 @@ export const EmbeddedStatusContent: React.FC<{
|
|||||||
|
|
||||||
link.classList.add('status-link');
|
link.classList.add('status-link');
|
||||||
|
|
||||||
const mention = mentions.find((item) => link.href === item.get('url'));
|
const mention = mentions.find((item) => link.href === item.url);
|
||||||
|
|
||||||
if (mention) {
|
if (mention) {
|
||||||
link.addEventListener(
|
link.addEventListener(
|
||||||
@@ -61,8 +77,8 @@ export const EmbeddedStatusContent: React.FC<{
|
|||||||
handleMentionClick.bind(null, history, mention),
|
handleMentionClick.bind(null, history, mention),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
link.setAttribute('title', `@${mention.get('acct')}`);
|
link.setAttribute('title', `@${mention.acct}`);
|
||||||
link.setAttribute('href', `/@${mention.get('acct')}`);
|
link.setAttribute('href', `/@${mention.acct}`);
|
||||||
} else if (
|
} else if (
|
||||||
link.textContent.startsWith('#') ||
|
link.textContent.startsWith('#') ||
|
||||||
link.previousSibling?.textContent?.endsWith('#')
|
link.previousSibling?.textContent?.endsWith('#')
|
||||||
@@ -83,11 +99,12 @@ export const EmbeddedStatusContent: React.FC<{
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<EmojiHTML
|
||||||
|
{...htmlHandlers}
|
||||||
className={className}
|
className={className}
|
||||||
ref={handleContentRef}
|
ref={handleContentRef}
|
||||||
lang={language}
|
lang={status.get('language') as string}
|
||||||
dangerouslySetInnerHTML={{ __html: content }}
|
htmlString={status.get('contentHtml') as string}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -394,17 +394,13 @@ export const DetailedStatus: React.FC<{
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{status.get('spoiler_text').length > 0 &&
|
{(!matchedFilters || showDespiteFilter) && (
|
||||||
(!matchedFilters || showDespiteFilter) && (
|
<ContentWarning
|
||||||
<ContentWarning
|
status={status}
|
||||||
text={
|
expanded={expanded}
|
||||||
status.getIn(['translation', 'spoilerHtml']) ||
|
onClick={handleExpandedToggle}
|
||||||
status.get('spoilerHtml')
|
/>
|
||||||
}
|
)}
|
||||||
expanded={expanded}
|
|
||||||
onClick={handleExpandedToggle}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user