StatusContent: replace custom emojis on render

fix-error-messages
Alex Gleason 2024-11-17 22:53:17 -06:00
rodzic afac59dc3d
commit f335249104
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
1 zmienionych plików z 24 dodań i 6 usunięć

Wyświetl plik

@ -1,10 +1,11 @@
import chevronRightIcon from '@tabler/icons/outline/chevron-right.svg';
import clsx from 'clsx';
import graphemesplit from 'graphemesplit';
import parse, { Element, type HTMLReactParserOptions, domToReact, type DOMNode } from 'html-react-parser';
import parse, { Element, type HTMLReactParserOptions, domToReact, type DOMNode, Text as DOMText } from 'html-react-parser';
import { useState, useRef, useLayoutEffect, useMemo, memo } from 'react';
import { FormattedMessage } from 'react-intl';
import { useCustomEmojis } from 'soapbox/api/hooks/useCustomEmojis.ts';
import Icon from 'soapbox/components/icon.tsx';
import { getTextDirection } from '../utils/rtl.ts';
@ -51,11 +52,12 @@ const StatusContent: React.FC<IStatusContent> = ({
const [collapsed, setCollapsed] = useState(false);
const node = useRef<HTMLDivElement>(null);
const { customEmojis } = useCustomEmojis();
const isOnlyEmoji = useMemo(() => {
const textContent = new DOMParser().parseFromString(status.contentHtml, 'text/html').body.firstChild?.textContent;
return Boolean(textContent && /^\p{Extended_Pictographic}+$/u.test(textContent) && (graphemesplit(textContent).length <= BIG_EMOJI_LIMIT));
}, [status.contentHtml]);
const textContent = new DOMParser().parseFromString(status.content, 'text/html').body.firstChild?.textContent ?? '';
return Boolean(/^\p{Extended_Pictographic}+$/u.test(textContent) && (graphemesplit(textContent).length <= BIG_EMOJI_LIMIT));
}, [status.content]);
const maybeSetCollapsed = (): void => {
if (!node.current) return;
@ -72,8 +74,8 @@ const StatusContent: React.FC<IStatusContent> = ({
});
const parsedHtml = useMemo((): string => {
return translatable && status.translation ? status.translation.get('content')! : status.contentHtml;
}, [status.contentHtml, status.translation]);
return translatable && status.translation ? status.translation.get('content')! : status.content;
}, [status.content, status.translation]);
if (status.content.length === 0) {
return null;
@ -89,6 +91,22 @@ const StatusContent: React.FC<IStatusContent> = ({
return null;
}
if (domNode instanceof DOMText) {
const parts = domNode.data.split(' ').map((part) => {
const match = part.match(/^:(\w+):$/);
if (!match) return part;
const [, shortcode] = match;
const customEmoji = customEmojis.find((e) => e.shortcode === shortcode);
if (!customEmoji) return part;
return <img key={part} src={customEmoji.url} alt={part} className='inline-block h-[1em]' />;
});
return <>{parts}</>;
}
if (domNode instanceof Element && domNode.name === 'a') {
const classes = domNode.attribs.class?.split(' ');