import type { FC } from 'react'; import { useContext, useEffect, useState } from 'react'; import { EMOJI_TYPE_CUSTOM } from '@/flavours/glitch/features/emoji/constants'; import { useEmojiAppState } from '@/flavours/glitch/features/emoji/mode'; import { unicodeHexToUrl } from '@/flavours/glitch/features/emoji/normalize'; import { isStateLoaded, loadEmojiDataToState, shouldRenderImage, stringToEmojiState, tokenizeText, } from '@/flavours/glitch/features/emoji/render'; import { AnimateEmojiContext, CustomEmojiContext } from './context'; interface EmojiProps { code: string; showFallback?: boolean; showLoading?: boolean; } export const Emoji: FC = ({ code, showFallback = true, showLoading = true, }) => { const customEmoji = useContext(CustomEmojiContext); // First, set the emoji state based on the input code. const [state, setState] = useState(() => stringToEmojiState(code, customEmoji), ); // If we don't have data, then load emoji data asynchronously. const appState = useEmojiAppState(); useEffect(() => { if (state !== null) { void loadEmojiDataToState(state, appState.currentLocale).then(setState); } }, [appState.currentLocale, state]); const animate = useContext(AnimateEmojiContext); const fallback = showFallback ? code : null; // If the code is invalid or we otherwise know it's not valid, show the fallback. if (!state) { return fallback; } if (!shouldRenderImage(state, appState.mode)) { return code; } if (!isStateLoaded(state)) { if (showLoading) { return ; } return fallback; } if (state.type === EMOJI_TYPE_CUSTOM) { const shortcode = `:${state.code}:`; return ( {shortcode} ); } const src = unicodeHexToUrl(state.code, appState.darkTheme); return ( {state.data.unicode} ); }; /** * Takes a text string and converts it to an array of React nodes. * @param text The text to be tokenized and converted. */ export function textToEmojis(text: string) { return tokenizeText(text).map((token, index) => { if (typeof token === 'string') { return token; } return ; }); }