Fix/simplify onlyEmoji
This commit is contained in:
parent
d1738273ed
commit
1738f4365a
|
@ -102,10 +102,6 @@ body.underline-links [data-markup] a {
|
|||
@apply underline;
|
||||
}
|
||||
|
||||
[data-markup].big-emoji img.emojione {
|
||||
@apply inline w-9 h-9 p-1;
|
||||
}
|
||||
|
||||
[data-markup] .status-link {
|
||||
@apply hover:underline text-primary-600 dark:text-accent-blue hover:text-primary-800 dark:hover:text-accent-blue;
|
||||
}
|
||||
|
|
|
@ -1,11 +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 { useState, useRef, useLayoutEffect, useMemo, memo } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import { onlyEmoji as isOnlyEmoji } from 'soapbox/utils/rich-content.ts';
|
||||
|
||||
import { getTextDirection } from '../utils/rtl.ts';
|
||||
|
||||
|
@ -49,10 +49,14 @@ const StatusContent: React.FC<IStatusContent> = ({
|
|||
textSize = 'md',
|
||||
}) => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [onlyEmoji, setOnlyEmoji] = useState(false);
|
||||
|
||||
const node = useRef<HTMLDivElement>(null);
|
||||
|
||||
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 maybeSetCollapsed = (): void => {
|
||||
if (!node.current) return;
|
||||
|
||||
|
@ -63,18 +67,8 @@ const StatusContent: React.FC<IStatusContent> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const maybeSetOnlyEmoji = (): void => {
|
||||
if (!node.current) return;
|
||||
const only = isOnlyEmoji(node.current, BIG_EMOJI_LIMIT, true);
|
||||
|
||||
if (only !== onlyEmoji) {
|
||||
setOnlyEmoji(only);
|
||||
}
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
maybeSetCollapsed();
|
||||
maybeSetOnlyEmoji();
|
||||
});
|
||||
|
||||
const parsedHtml = useMemo((): string => {
|
||||
|
@ -149,7 +143,7 @@ const StatusContent: React.FC<IStatusContent> = ({
|
|||
'cursor-pointer': onClick,
|
||||
'whitespace-normal': withSpoiler,
|
||||
'max-h-[300px]': collapsed,
|
||||
'leading-normal big-emoji': onlyEmoji,
|
||||
'leading-normal !text-4xl': isOnlyEmoji,
|
||||
});
|
||||
|
||||
if (onClick) {
|
||||
|
@ -184,7 +178,7 @@ const StatusContent: React.FC<IStatusContent> = ({
|
|||
tabIndex={0}
|
||||
key='content'
|
||||
className={clsx(baseClassName, {
|
||||
'leading-normal big-emoji': onlyEmoji,
|
||||
'leading-normal !text-4xl': isOnlyEmoji,
|
||||
})}
|
||||
direction={direction}
|
||||
lang={status.language || undefined}
|
||||
|
|
|
@ -6,12 +6,12 @@ import moodSmileIcon from '@tabler/icons/outline/mood-smile.svg';
|
|||
import trashIcon from '@tabler/icons/outline/trash.svg';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import clsx from 'clsx';
|
||||
import graphemesplit from 'graphemesplit';
|
||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
import escape from 'lodash/escape';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
|
||||
import { openModal } from 'soapbox/actions/modals.ts';
|
||||
import { initReport, ReportableEntities } from 'soapbox/actions/reports.ts';
|
||||
import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts';
|
||||
|
@ -27,7 +27,6 @@ import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
|||
import { ChatKeys, IChat, useChatActions } from 'soapbox/queries/chats.ts';
|
||||
import { queryClient } from 'soapbox/queries/client.ts';
|
||||
import { stripHTML } from 'soapbox/utils/html.ts';
|
||||
import { onlyEmoji } from 'soapbox/utils/rich-content.ts';
|
||||
|
||||
import ChatMessageReactionWrapper from './chat-message-reaction-wrapper/chat-message-reaction-wrapper.tsx';
|
||||
import ChatMessageReaction from './chat-message-reaction.tsx';
|
||||
|
@ -100,10 +99,9 @@ const ChatMessage = (props: IChatMessage) => {
|
|||
&& lastReadMessageTimestamp >= new Date(chatMessage.created_at);
|
||||
|
||||
const isOnlyEmoji = useMemo(() => {
|
||||
const hiddenEl = document.createElement('div');
|
||||
hiddenEl.innerHTML = content;
|
||||
return onlyEmoji(hiddenEl, BIG_EMOJI_LIMIT, false);
|
||||
}, []);
|
||||
const textContent = new DOMParser().parseFromString(content, 'text/html').body.firstChild?.textContent;
|
||||
return Boolean(textContent && /^\p{Extended_Pictographic}+$/u.test(textContent) && (graphemesplit(textContent).length <= BIG_EMOJI_LIMIT));
|
||||
}, [content]);
|
||||
|
||||
const emojiReactionRows = useMemo(() => {
|
||||
if (!chatMessage.emoji_reactions) {
|
||||
|
@ -302,7 +300,7 @@ const ChatMessage = (props: IChatMessage) => {
|
|||
'[&_.mention]:text-white dark:[&_.mention]:white': isMyMessage,
|
||||
'bg-primary-500 text-white': isMyMessage,
|
||||
'bg-gray-200 dark:bg-gray-800 text-gray-900 dark:text-gray-100': !isMyMessage,
|
||||
'!bg-transparent !p-0 emoji-lg': isOnlyEmoji,
|
||||
'!bg-transparent !p-0 text-4xl': isOnlyEmoji,
|
||||
})
|
||||
}
|
||||
ref={setBubbleRef}
|
||||
|
|
|
@ -130,15 +130,6 @@
|
|||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.emoji-lg img.emojione {
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
}
|
||||
|
||||
.emojione {
|
||||
@apply w-4 h-4 -mt-[0.2ex] mb-[0.2ex] inline-block align-middle object-contain;
|
||||
}
|
||||
|
||||
.compose-form-warning {
|
||||
strong {
|
||||
@apply font-medium;
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/** Returns `true` if the node contains only emojis, up to a limit */
|
||||
export const onlyEmoji = (node: HTMLElement, limit = 1, ignoreMentions = true): boolean => {
|
||||
if (!node) return false;
|
||||
|
||||
try {
|
||||
// Remove mentions before checking content
|
||||
if (ignoreMentions) {
|
||||
node = node.cloneNode(true) as HTMLElement;
|
||||
node.querySelectorAll('a.mention').forEach(m => m.parentNode?.removeChild(m));
|
||||
}
|
||||
|
||||
if (node.textContent?.replace(new RegExp(' ', 'g'), '') !== '') return false;
|
||||
const emojis = Array.from(node.querySelectorAll('img.emojione'));
|
||||
if (emojis.length === 0) return false;
|
||||
if (emojis.length > limit) return false;
|
||||
const images = Array.from(node.querySelectorAll('img'));
|
||||
if (images.length > emojis.length) return false;
|
||||
return true;
|
||||
} catch (e) {
|
||||
// If anything in here crashes, skipping it is inconsequential.
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue