diff --git a/package.json b/package.json index e09b5dd94..1aefdc9d2 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,6 @@ "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", "@tanstack/react-query": "^5.59.13", - "@twemoji/svg": "^15.0.0", "@types/escape-html": "^1.0.1", "@types/http-link-header": "^1.0.3", "@types/leaflet": "^1.8.0", @@ -97,7 +96,6 @@ "comlink": "^4.4.1", "cssnano": "^6.0.0", "detect-passive-events": "^2.0.0", - "emoji-datasource": "15.0.1", "emoji-mart": "^5.6.0", "escape-html": "^1.0.3", "eslint-plugin-formatjs": "^5.2.2", diff --git a/src/actions/soapbox.ts b/src/actions/soapbox.ts index cee62ab4f..cd0170c90 100644 --- a/src/actions/soapbox.ts +++ b/src/actions/soapbox.ts @@ -3,7 +3,6 @@ import { createSelector } from 'reselect'; import { getHost } from 'soapbox/actions/instance.ts'; import { normalizeSoapboxConfig } from 'soapbox/normalizers/index.ts'; import KVStore from 'soapbox/storage/kv-store.ts'; -import { removeVS16s } from 'soapbox/utils/emoji.ts'; import { getFeatures } from 'soapbox/utils/features.ts'; import api from '../api/index.ts'; @@ -29,12 +28,6 @@ const getSoapboxConfig = createSelector([ if (soapbox.get('displayFqn') === undefined) { soapboxConfig.set('displayFqn', features.federating); } - - // If RGI reacts aren't supported, strip VS16s - // https://git.pleroma.social/pleroma/pleroma/-/issues/2355 - if (features.emojiReactsNonRGI) { - soapboxConfig.set('allowedEmoji', soapboxConfig.allowedEmoji.map(removeVS16s)); - } }); }); diff --git a/src/components/account.tsx b/src/components/account.tsx index 4dba320de..f9ffc41cf 100644 --- a/src/components/account.tsx +++ b/src/components/account.tsx @@ -214,11 +214,13 @@ const Account = ({ {emoji && ( - +
+ {emojiUrl ? ( + {emoji} + ) : ( + + )} +
)}
diff --git a/src/components/autosuggest-emoji.tsx b/src/components/autosuggest-emoji.tsx index fb4a4b4fc..411526b9c 100644 --- a/src/components/autosuggest-emoji.tsx +++ b/src/components/autosuggest-emoji.tsx @@ -1,3 +1,5 @@ +import EmojiComponent from 'soapbox/components/ui/emoji.tsx'; +import HStack from 'soapbox/components/ui/hstack.tsx'; import { isCustomEmoji } from 'soapbox/features/emoji/index.ts'; import unicodeMapping from 'soapbox/features/emoji/mapping.ts'; @@ -8,11 +10,10 @@ interface IAutosuggestEmoji { } const AutosuggestEmoji: React.FC = ({ emoji }) => { - let url, alt; + let elem: React.ReactNode; if (isCustomEmoji(emoji)) { - url = emoji.imageUrl; - alt = emoji.colons; + elem = {emoji.colons}; } else { const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')]; @@ -20,19 +21,14 @@ const AutosuggestEmoji: React.FC = ({ emoji }) => { return null; } - url = `/packs/emoji/${mapping.unified}.svg`; - alt = emoji.native; + elem = ; } return ( -
- {alt} - {emoji.colons} -
+ + {elem} + {emoji.colons} + ); }; diff --git a/src/components/emoji-graphic.tsx b/src/components/emoji-graphic.tsx index 7facaa4ad..207fb409d 100644 --- a/src/components/emoji-graphic.tsx +++ b/src/components/emoji-graphic.tsx @@ -9,7 +9,7 @@ const EmojiGraphic: React.FC = ({ emoji }) => { return (
- +
); diff --git a/src/components/markup.css b/src/components/markup.css index 482b37237..4c7d6fb30 100644 --- a/src/components/markup.css +++ b/src/components/markup.css @@ -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; } diff --git a/src/components/status-action-button.tsx b/src/components/status-action-button.tsx index b72218b4d..2819adaa3 100644 --- a/src/components/status-action-button.tsx +++ b/src/components/status-action-button.tsx @@ -46,8 +46,12 @@ const StatusActionButton = forwardRef((p const renderIcon = () => { if (emoji) { return ( - - + + {emoji.url ? ( + {emoji.name} + ) : ( + + )} ); } else { diff --git a/src/components/status-content.tsx b/src/components/status-content.tsx index 45ff497c8..3ee565f0e 100644 --- a/src/components/status-content.tsx +++ b/src/components/status-content.tsx @@ -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 = ({ textSize = 'md', }) => { const [collapsed, setCollapsed] = useState(false); - const [onlyEmoji, setOnlyEmoji] = useState(false); const node = useRef(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 = ({ } }; - 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 = ({ '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 = ({ tabIndex={0} key='content' className={clsx(baseClassName, { - 'leading-normal big-emoji': onlyEmoji, + 'leading-normal !text-4xl': isOnlyEmoji, })} direction={direction} lang={status.language || undefined} diff --git a/src/components/ui/emoji-selector.tsx b/src/components/ui/emoji-selector.tsx index 2487bb127..6d4b716e0 100644 --- a/src/components/ui/emoji-selector.tsx +++ b/src/components/ui/emoji-selector.tsx @@ -41,7 +41,9 @@ const EmojiButton: React.FC = ({ emoji, className, onClick, tabInd return ( ); }; diff --git a/src/components/ui/emoji.tsx b/src/components/ui/emoji.tsx index da3449ccb..75cc4d111 100644 --- a/src/components/ui/emoji.tsx +++ b/src/components/ui/emoji.tsx @@ -1,30 +1,19 @@ -import { removeVS16s, toCodePoints } from 'soapbox/utils/emoji.ts'; - -interface IEmoji extends React.ImgHTMLAttributes { +interface IEmoji { /** Unicode emoji character. */ - emoji?: string; + emoji: string; + /** Size to render the emoji. */ + size?: number; } /** A single emoji image. */ const Emoji: React.FC = (props): JSX.Element | null => { - const { emoji, alt, src, ...rest } = props; - - let filename; - - if (emoji) { - const codepoints = toCodePoints(removeVS16s(emoji)); - filename = codepoints.join('-'); - } - - if (!filename && !src) return null; + const { emoji, size = 16 } = props; + const px = `${size}px`; return ( - {alt +
+ {emoji} +
); }; diff --git a/src/features/bookmark-folders/index.tsx b/src/features/bookmark-folders/index.tsx index c48ae5925..79128daa7 100644 --- a/src/features/bookmark-folders/index.tsx +++ b/src/features/bookmark-folders/index.tsx @@ -51,24 +51,28 @@ const BookmarkFolders: React.FC = () => { } /> - {bookmarkFolders?.map((folder) => ( - - {folder.emoji ? ( - - ) : } - {folder.name} - - } - /> - ))} + {bookmarkFolders?.map((folder) => { + let icon = ; + + if (folder.emoji_url) { + icon = {folder.emoji}; + } else if (folder.emoji) { + icon = ; + } + + return ( + +
{icon}
+ {folder.name} + + } + /> + ); + })} diff --git a/src/features/chats/components/chat-message.tsx b/src/features/chats/components/chat-message.tsx index 8e1f7e091..ad2b7694a 100644 --- a/src/features/chats/components/chat-message.tsx +++ b/src/features/chats/components/chat-message.tsx @@ -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} diff --git a/src/features/compose/editor/nodes/emoji-node.tsx b/src/features/compose/editor/nodes/emoji-node.tsx index 4c7155d00..d29f24af2 100644 --- a/src/features/compose/editor/nodes/emoji-node.tsx +++ b/src/features/compose/editor/nodes/emoji-node.tsx @@ -1,6 +1,5 @@ import { $applyNodeReplacement, DecoratorNode } from 'lexical'; -import Component from 'soapbox/components/ui/emoji.tsx'; import { isNativeEmoji, type Emoji } from 'soapbox/features/emoji/index.ts'; import type { @@ -17,7 +16,7 @@ type SerializedEmojiNode = Spread<{ version: 1; }, SerializedLexicalNode>; -class EmojiNode extends DecoratorNode { +class EmojiNode extends DecoratorNode { __emoji: Emoji; @@ -77,12 +76,12 @@ class EmojiNode extends DecoratorNode { } } - decorate(): JSX.Element { + decorate(): React.ReactNode { const emoji = this.__emoji; if (isNativeEmoji(emoji)) { - return ; + return emoji.native; } else { - return ; + return {emoji.colons}; } } diff --git a/src/features/compose/editor/plugins/autosuggest-plugin.tsx b/src/features/compose/editor/plugins/autosuggest-plugin.tsx index 6efc85369..af5eb3e66 100644 --- a/src/features/compose/editor/plugins/autosuggest-plugin.tsx +++ b/src/features/compose/editor/plugins/autosuggest-plugin.tsx @@ -36,6 +36,7 @@ import ReactDOM from 'react-dom'; import { clearComposeSuggestions, fetchComposeSuggestions } from 'soapbox/actions/compose.ts'; import { chooseEmoji } from 'soapbox/actions/emojis.ts'; import AutosuggestEmoji from 'soapbox/components/autosuggest-emoji.tsx'; +import { isNativeEmoji } from 'soapbox/features/emoji/index.ts'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useCompose } from 'soapbox/hooks/useCompose.ts'; import { selectAccount } from 'soapbox/selectors/index.ts'; @@ -316,7 +317,11 @@ const AutosuggestPlugin = ({ if (typeof suggestion === 'object') { if (!suggestion.id) return; dispatch(chooseEmoji(suggestion)); - replaceMatch($createEmojiNode(suggestion)); + if (isNativeEmoji(suggestion)) { + replaceMatch(new TextNode(suggestion.native)); + } else { + replaceMatch($createEmojiNode(suggestion)); + } } else if (suggestion[0] === '#') { (node as TextNode).setTextContent(`${suggestion} `); node.select(); diff --git a/src/features/crypto-donate/components/lightning-address.tsx b/src/features/crypto-donate/components/lightning-address.tsx index f0223bd61..725b7e8ce 100644 --- a/src/features/crypto-donate/components/lightning-address.tsx +++ b/src/features/crypto-donate/components/lightning-address.tsx @@ -15,11 +15,8 @@ const LightningAddress: React.FC = (props): JSX.Element => { return ( - - + + diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx index e96e77d51..17a4ddbd0 100644 --- a/src/features/edit-identity/index.tsx +++ b/src/features/edit-identity/index.tsx @@ -129,7 +129,7 @@ const EditIdentity: React.FC = () => { {(account.source?.nostr?.nip05 === identifier && account.acct !== identifier) && (
- +
)} diff --git a/src/features/emoji/components/emoji-picker-dropdown.tsx b/src/features/emoji/components/emoji-picker-dropdown.tsx index bdae364b3..3a528c267 100644 --- a/src/features/emoji/components/emoji-picker-dropdown.tsx +++ b/src/features/emoji/components/emoji-picker-dropdown.tsx @@ -234,7 +234,6 @@ const EmojiPickerDropdown: React.FC = ({ skin={handleSkinTone} emojiSize={22} emojiButtonSize={34} - set='twitter' theme={theme} i18n={getI18n()} skinTonePosition='search' diff --git a/src/features/emoji/components/emoji-picker.tsx b/src/features/emoji/components/emoji-picker.tsx index a6dc860ff..3407c76c8 100644 --- a/src/features/emoji/components/emoji-picker.tsx +++ b/src/features/emoji/components/emoji-picker.tsx @@ -1,20 +1,13 @@ -import spriteSheet from 'emoji-datasource/img/twitter/sheets/32.png'; import { Picker as EmojiPicker } from 'emoji-mart'; import { useRef, useEffect } from 'react'; import data from '../data.ts'; -const getSpritesheetURL = () => spriteSheet; - -const getImageURL = (_set: string, name: string) => { - return `/packs/emoji/${name}.svg`; -}; - const Picker: React.FC = (props) => { const ref = useRef(null); useEffect(() => { - const input = { ...props, data, ref, getImageURL, getSpritesheetURL }; + const input = { ...props, data, ref }; new EmojiPicker(input); }, []); diff --git a/src/features/emoji/index.ts b/src/features/emoji/index.ts index 2c4995dae..1efed4fcb 100644 --- a/src/features/emoji/index.ts +++ b/src/features/emoji/index.ts @@ -63,13 +63,11 @@ const validEmojiChar = (c: string) => { }; const convertCustom = (shortname: string, filename: string) => { - return `${shortname}`; + return `${shortname}`; }; const convertUnicode = (c: string) => { - const { unified, shortcode } = unicodeMapping[c]; - - return `${c}`; + return c; }; const convertEmoji = (str: string, customEmojis: any) => { diff --git a/src/features/notifications/components/notification.tsx b/src/features/notifications/components/notification.tsx index 24d48b7ca..69bc27bc1 100644 --- a/src/features/notifications/components/notification.tsx +++ b/src/features/notifications/components/notification.tsx @@ -315,13 +315,11 @@ const Notification: React.FC = (props) => { const renderIcon = (): React.ReactNode => { if (type === 'pleroma:emoji_reaction' && notification.emoji) { - return ( - - ); + if (notification.emoji_url) { + return {notification.emoji}; + } else { + return ; + } } else if (validType(type)) { return ( { >
- +
diff --git a/src/features/status/components/status-interaction-bar.tsx b/src/features/status/components/status-interaction-bar.tsx index 39a9e1c64..62ee17cf7 100644 --- a/src/features/status/components/status-interaction-bar.tsx +++ b/src/features/status/components/status-interaction-bar.tsx @@ -182,15 +182,12 @@ const StatusInteractionBar: React.FC = ({ status }): JSX. return ( - {emojiReacts.take(3).map((e, i) => { - return ( - - ); + {emojiReacts.take(3).map((emoji, i) => { + if (emoji.url) { + return {emoji.name}; + } else { + return
; + } })}
diff --git a/src/features/ui/components/modals/edit-bookmark-folder-modal.tsx b/src/features/ui/components/modals/edit-bookmark-folder-modal.tsx index 6329e5104..8b122edd3 100644 --- a/src/features/ui/components/modals/edit-bookmark-folder-modal.tsx +++ b/src/features/ui/components/modals/edit-bookmark-folder-modal.tsx @@ -63,7 +63,7 @@ const EmojiPicker: React.FC = ({ emoji, emojiUrl, ...props }) => { tabIndex={0} > {emoji - ? + ? : } diff --git a/src/features/ui/components/modals/reactions-modal.tsx b/src/features/ui/components/modals/reactions-modal.tsx index e399d4176..d3334f6ab 100644 --- a/src/features/ui/components/modals/reactions-modal.tsx +++ b/src/features/ui/components/modals/reactions-modal.tsx @@ -62,10 +62,16 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction reactions!.forEach(reaction => items.push( { - text:
- - {reaction.count} -
, + text: ( +
+ {reaction.url ? ( + + ) : ( + + )} + {reaction.count} +
+ ), action: () => setReaction(reaction.name), name: reaction.name, }, diff --git a/src/features/ui/components/modals/select-bookmark-folder-modal.tsx b/src/features/ui/components/modals/select-bookmark-folder-modal.tsx index 672cd1a3c..6c6708981 100644 --- a/src/features/ui/components/modals/select-bookmark-folder-modal.tsx +++ b/src/features/ui/components/modals/select-bookmark-folder-modal.tsx @@ -60,25 +60,29 @@ const SelectBookmarkFolderModal: React.FC = ({ statu ]; if (!isFetching) { - items.push(...(bookmarkFolders.map((folder) => ( - - {folder.emoji ? ( - - ) : } - {folder.name} -
- } - checked={selectedFolder === folder.id} - value={folder.id} - /> - )))); + items.push(...(bookmarkFolders.map((folder) => { + let icon = ; + + if (folder.emoji_url) { + icon = {folder.emoji}; + } else if (folder.emoji) { + icon = ; + } + + return ( + +
{icon}
+ {folder.name} +
+ } + checked={selectedFolder === folder.id} + value={folder.id} + /> + ); + }))); } const body = isFetching ? : ( diff --git a/src/styles/tailwind.css b/src/styles/tailwind.css index 280099460..5981ddc9e 100644 --- a/src/styles/tailwind.css +++ b/src/styles/tailwind.css @@ -131,15 +131,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; diff --git a/src/utils/emoji.test.ts b/src/utils/emoji.test.ts deleted file mode 100644 index 7587e57a5..000000000 --- a/src/utils/emoji.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { - removeVS16s, - toCodePoints, -} from './emoji.ts'; - -const ASCII_HEART = '❤'; // '\u2764\uFE0F' -const RED_HEART_RGI = '❤️'; // '\u2764' -const JOY = '😂'; - -describe('removeVS16s()', () => { - it('removes Variation Selector-16 characters from emoji', () => { - // Sanity check - expect(ASCII_HEART).not.toBe(RED_HEART_RGI); - - // It normalizes an emoji with VS16s - expect(removeVS16s(RED_HEART_RGI)).toBe(ASCII_HEART); - - // Leaves a regular emoji alone - expect(removeVS16s(JOY)).toBe(JOY); - }); -}); - -describe('toCodePoints()', () => { - it('converts a plain emoji', () => { - expect(toCodePoints('😂')).toEqual(['1f602']); - }); - - it('converts a VS16 emoji', () => { - expect(toCodePoints(RED_HEART_RGI)).toEqual(['2764', 'fe0f']); - }); - - it('converts an ASCII character', () => { - expect(toCodePoints(ASCII_HEART)).toEqual(['2764']); - }); - - it('converts a sequence emoji', () => { - expect(toCodePoints('🇺🇸')).toEqual(['1f1fa', '1f1f8']); - }); -}); diff --git a/src/utils/emoji.ts b/src/utils/emoji.ts deleted file mode 100644 index 1d6da69d1..000000000 --- a/src/utils/emoji.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Taken from twemoji-parser -// https://github.com/twitter/twemoji-parser/blob/a97ef3994e4b88316812926844d51c296e889f76/src/index.js - -/** Remove Variation Selector-16 characters from emoji */ -// https://emojipedia.org/variation-selector-16/ -const removeVS16s = (rawEmoji: string): string => { - const vs16RegExp = /\uFE0F/g; - const zeroWidthJoiner = String.fromCharCode(0x200d); - return rawEmoji.indexOf(zeroWidthJoiner) < 0 ? rawEmoji.replace(vs16RegExp, '') : rawEmoji; -}; - -/** Convert emoji into an array of Unicode codepoints */ -const toCodePoints = (unicodeSurrogates: string): string[] => { - const points = []; - let char = 0; - let previous = 0; - let i = 0; - while (i < unicodeSurrogates.length) { - char = unicodeSurrogates.charCodeAt(i++); - if (previous) { - points.push((0x10000 + ((previous - 0xd800) << 10) + (char - 0xdc00)).toString(16)); - previous = 0; - } else if (char > 0xd800 && char <= 0xdbff) { - previous = char; - } else { - points.push(char.toString(16)); - } - } - return points; -}; - -export { - removeVS16s, - toCodePoints, -}; diff --git a/src/utils/rich-content.ts b/src/utils/rich-content.ts deleted file mode 100644 index 1fe3f5c79..000000000 --- a/src/utils/rich-content.ts +++ /dev/null @@ -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; - } -}; diff --git a/tailwind.config.ts b/tailwind.config.ts index 2781645b7..57bb3709d 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -27,7 +27,7 @@ const config: Config = { base: '0.9375rem', }, fontFamily: { - 'sans': [ + sans: [ 'Soapbox i18n', 'Inter', 'ui-sans-serif', @@ -45,11 +45,20 @@ const config: Config = { 'Segoe UI Symbol', 'Noto Color Emoji', ], - 'mono': [ + mono: [ 'Roboto Mono', 'ui-monospace', 'mono', ], + emoji: [ + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Segoe UI', + 'Apple Color Emoji', + 'Twemoji Mozilla', + 'Noto Color Emoji', + 'Android Emoji', + ], }, spacing: { '4.5': '1.125rem', diff --git a/vite.config.ts b/vite.config.ts index c056a0a4d..7ca8ca508 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -69,9 +69,6 @@ export default defineConfig(() => { }), viteStaticCopy({ targets: [{ - src: './node_modules/@twemoji/svg/*', - dest: 'packs/emoji/', - }, { src: './src/instance', dest: '.', }, { diff --git a/yarn.lock b/yarn.lock index e1add8d3e..9a05530f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2525,11 +2525,6 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== -"@twemoji/svg@^15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@twemoji/svg/-/svg-15.0.0.tgz#0e3828c654726f1848fe11f31ef4e8a75854cc7f" - integrity sha512-ZSPef2B6nBaYnfgdTbAy4jgW95o7pi2xPGwGCU+WMTxo7J6B1lMPTWwSq/wTuiMq+N0khQ90CcvYp1wFoQpo/w== - "@types/aria-query@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" @@ -4132,11 +4127,6 @@ electron-to-chromium@^1.5.28: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.28.tgz#aee074e202c6ee8a0030a9c2ef0b3fe9f967d576" integrity sha512-VufdJl+rzaKZoYVUijN13QcXVF5dWPZANeFTLNy+OSpHdDL5ynXTF35+60RSBbaQYB1ae723lQXHCrf4pyLsMw== -emoji-datasource@15.0.1: - version "15.0.1" - resolved "https://registry.yarnpkg.com/emoji-datasource/-/emoji-datasource-15.0.1.tgz#6cc7676e4d48d7559c2e068ffcacf84ec653584c" - integrity sha512-aF5Q6LCKXzJzpG4K0ETiItuzz0xLYxNexR9qWw45/shuuEDWZkOIbeGHA23uopOSYA/LmeZIXIFsySCx+YKg2g== - emoji-mart@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.6.0.tgz#71b3ed0091d3e8c68487b240d9d6d9a73c27f023"