Files
mastodon/app/javascript/flavours/glitch/features/emoji/hooks.ts
2025-09-27 22:05:42 +02:00

76 lines
2.1 KiB
TypeScript

import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { useAppSelector } from '@/flavours/glitch/store';
import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment';
import { toSupportedLocale } from './locale';
import { determineEmojiMode } from './mode';
import { cleanExtraEmojis } from './normalize';
import { emojifyElement, emojifyText } from './render';
import type { CustomEmojiMapArg, EmojiAppState } 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<string | null>(null);
const appState = useEmojiAppState();
const extra = useMemo(() => cleanExtraEmojis(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'),
};
}