signal-desktop/ts/components/conversation/Emojify.tsx

75 lines
2.4 KiB
TypeScript
Raw Normal View History

2023-01-03 11:55:46 -08:00
// Copyright 2018 Signal Messenger, LLC
2020-10-30 15:34:04 -05:00
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { RenderTextCallbackType } from '../../types/Util';
import { splitByEmoji } from '../../util/emoji';
import { missingCaseError } from '../../util/missingCaseError';
2025-03-26 12:35:32 -07:00
import { FunInlineEmoji } from '../fun/FunEmoji';
import {
getEmojiVariantByKey,
getEmojiVariantKeyByValue,
isEmojiVariantValue,
isEmojiVariantValueNonQualified,
2025-03-26 12:35:32 -07:00
} from '../fun/data/emojis';
2025-06-16 11:59:31 -07:00
import { createLogger } from '../../logging/log';
2025-04-09 11:10:54 -07:00
import { useFunEmojiLocalizer } from '../fun/useFunEmojiLocalizer';
2025-06-16 11:59:31 -07:00
const log = createLogger('Emojify');
export type Props = {
2025-03-26 12:35:32 -07:00
fontSizeOverride?: number | null;
text: string;
/** When behind a spoiler, this emoji needs to be visibility: hidden */
isInvisible?: boolean;
/** Allows you to customize now non-newlines are rendered. Simplest is just a <span>. */
2019-01-14 13:49:58 -08:00
renderNonEmoji?: RenderTextCallbackType;
};
2022-11-09 20:59:36 -08:00
const defaultRenderNonEmoji: RenderTextCallbackType = ({ text }) => text;
export function Emojify({
2025-03-26 12:35:32 -07:00
fontSizeOverride,
text,
2025-03-26 12:35:32 -07:00
renderNonEmoji = defaultRenderNonEmoji,
}: Props): JSX.Element {
2025-04-09 11:10:54 -07:00
const emojiLocalizer = useFunEmojiLocalizer();
return (
<>
{splitByEmoji(text).map(({ type, value: match }, index) => {
if (type === 'emoji') {
// If we don't recognize the emoji, render it as text.
if (!isEmojiVariantValue(match)) {
log.error(`Found emoji that we did not recognize: ${match}`);
return renderNonEmoji({ text: match, key: index });
}
// Render emoji as text if they are a non-qualified emoji value.
if (isEmojiVariantValueNonQualified(match)) {
return renderNonEmoji({ text: match, key: index });
}
2025-03-26 12:35:32 -07:00
const variantKey = getEmojiVariantKeyByValue(match);
const variant = getEmojiVariantByKey(variantKey);
return (
<FunInlineEmoji
// eslint-disable-next-line react/no-array-index-key
key={index}
role="img"
2025-04-29 16:24:14 -07:00
aria-label={emojiLocalizer.getLocaleShortName(variantKey)}
2025-03-26 12:35:32 -07:00
emoji={variant}
size={fontSizeOverride}
/>
);
}
if (type === 'text') {
return renderNonEmoji({ text: match, key: index });
}
throw missingCaseError(type);
})}
</>
);
}