mirror of
https://github.com/mastodon/mastodon.git
synced 2025-12-07 23:03:38 +00:00
Some checks failed
Check i18n / check-i18n (push) Waiting to run
Chromatic / Run Chromatic (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.2) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Bundler Audit / security (push) Has been cancelled
Haml Linting / lint (push) Has been cancelled
100 lines
2.5 KiB
TypeScript
100 lines
2.5 KiB
TypeScript
import type { FC } from 'react';
|
|
import { useContext, useEffect, useState } from 'react';
|
|
|
|
import { EMOJI_TYPE_CUSTOM } from '@/mastodon/features/emoji/constants';
|
|
import { useEmojiAppState } from '@/mastodon/features/emoji/hooks';
|
|
import { unicodeHexToUrl } from '@/mastodon/features/emoji/normalize';
|
|
import {
|
|
isStateLoaded,
|
|
loadEmojiDataToState,
|
|
shouldRenderImage,
|
|
stringToEmojiState,
|
|
tokenizeText,
|
|
} from '@/mastodon/features/emoji/render';
|
|
|
|
import { AnimateEmojiContext, CustomEmojiContext } from './context';
|
|
|
|
interface EmojiProps {
|
|
code: string;
|
|
showFallback?: boolean;
|
|
showLoading?: boolean;
|
|
}
|
|
|
|
export const Emoji: FC<EmojiProps> = ({
|
|
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 <span className='emojione emoji-loading' title={code} />;
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
if (state.type === EMOJI_TYPE_CUSTOM) {
|
|
const shortcode = `:${state.code}:`;
|
|
return (
|
|
<img
|
|
src={animate ? state.data.url : state.data.static_url}
|
|
alt={shortcode}
|
|
title={shortcode}
|
|
className='emojione custom-emoji'
|
|
loading='lazy'
|
|
/>
|
|
);
|
|
}
|
|
|
|
const src = unicodeHexToUrl(state.code, appState.darkTheme);
|
|
|
|
return (
|
|
<img
|
|
src={src}
|
|
alt={state.data.unicode}
|
|
title={state.data.label}
|
|
className='emojione'
|
|
loading='lazy'
|
|
/>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 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 <Emoji code={token.code} key={`emoji-${token.code}-${index}`} />;
|
|
});
|
|
}
|