mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-13 15:58:50 +00:00
95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
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<string | null>(null);
|
|
|
|
const appState = useEmojiAppState();
|
|
const extra: ExtraCustomEmojiMap = useMemo(() => {
|
|
if (!extraEmojis) {
|
|
return {};
|
|
}
|
|
if (isList(extraEmojis)) {
|
|
return (
|
|
extraEmojis.toJS() as ApiCustomEmojiJSON[]
|
|
).reduce<ExtraCustomEmojiMap>(
|
|
(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'),
|
|
};
|
|
}
|