import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { isList } from 'immutable'; import type { ApiCustomEmojiJSON } from '@/flavours/glitch/api_types/custom_emoji'; import { useAppSelector } from '@/flavours/glitch/store'; import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment'; import { toSupportedLocale } from './locale'; import { determineEmojiMode } from './mode'; import { emojifyElement, emojifyText } from './render'; import type { CustomEmojiMapArg, EmojiAppState, ExtraCustomEmojiMap, } from './types'; import { stringHasAnyEmoji } from './utils'; interface UseEmojifyOptions { text: string; extraEmojis?: CustomEmojiMapArg; deep?: boolean; } export function useEmojify({ text, extraEmojis, deep = true, }: UseEmojifyOptions) { const [emojifiedText, setEmojifiedText] = useState(null); const appState = useEmojiAppState(); const extra: ExtraCustomEmojiMap = useMemo(() => { if (!extraEmojis) { return {}; } if (isList(extraEmojis)) { return ( extraEmojis.toJS() as ApiCustomEmojiJSON[] ).reduce( (acc, emoji) => ({ ...acc, [emoji.shortcode]: emoji }), {}, ); } return extraEmojis; }, [extraEmojis]); const emojify = useCallback( async (input: string) => { let result: string | null = null; if (deep) { const wrapper = document.createElement('div'); wrapper.innerHTML = input; if (await emojifyElement(wrapper, appState, extra)) { result = wrapper.innerHTML; } } else { result = await emojifyText(text, appState, extra); } if (result) { setEmojifiedText(result); } else { setEmojifiedText(input); } }, [appState, deep, extra, text], ); useLayoutEffect(() => { if (isModernEmojiEnabled() && !!text.trim() && stringHasAnyEmoji(text)) { void emojify(text); } else { // If no emoji or we don't want to render, fall back. setEmojifiedText(text); } }, [emojify, text]); return emojifiedText; } export function useEmojiAppState(): EmojiAppState { const locale = useAppSelector((state) => toSupportedLocale(state.meta.get('locale') as string), ); const mode = useAppSelector((state) => determineEmojiMode(state.meta.get('emoji_style') as string), ); return { currentLocale: locale, locales: [locale], mode, darkTheme: document.body.classList.contains('theme-default'), }; }