diff --git a/ts/components/conversation/Emojify.stories.tsx b/ts/components/conversation/Emojify.stories.tsx index 712affd3a0..82a0bc4716 100644 --- a/ts/components/conversation/Emojify.stories.tsx +++ b/ts/components/conversation/Emojify.stories.tsx @@ -121,3 +121,19 @@ export function TensOfThousandsOfEmojiInterspersedWithText(): JSX.Element { return ; } + +export function NonQualifiedEmoji(): JSX.Element { + const props = createProps({ + text: '\u{00AE}', + }); + + return ; +} + +export function OverlyQualifiedEmoji(): JSX.Element { + const props = createProps({ + text: '\u{26AA}\u{FE0F}', + }); + + return ; +} diff --git a/ts/components/conversation/Emojify.tsx b/ts/components/conversation/Emojify.tsx index 642337dbc7..090a954fa8 100644 --- a/ts/components/conversation/Emojify.tsx +++ b/ts/components/conversation/Emojify.tsx @@ -11,8 +11,9 @@ import { getEmojiVariantByKey, getEmojiVariantKeyByValue, isEmojiVariantValue, + isEmojiVariantValueNonQualified, } from '../fun/data/emojis'; -import { strictAssert } from '../../util/assert'; +import * as log from '../../logging/log'; export type Props = { fontSizeOverride?: number | null; @@ -34,10 +35,16 @@ export function Emojify({ <> {splitByEmoji(text).map(({ type, value: match }, index) => { if (type === 'emoji') { - strictAssert( - isEmojiVariantValue(match), - `Must be emoji variant value: ${match}` - ); + // 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 }); + } const variantKey = getEmojiVariantKeyByValue(match); const variant = getEmojiVariantByKey(variantKey); diff --git a/ts/components/fun/data/emojis.ts b/ts/components/fun/data/emojis.ts index 3e51342311..865460a36f 100644 --- a/ts/components/fun/data/emojis.ts +++ b/ts/components/fun/data/emojis.ts @@ -252,10 +252,12 @@ type EmojiIndex = Readonly<{ parentByKey: Record; parentKeysByName: Record; parentKeysByValue: Record; + parentKeysByValueNonQualified: Record; parentKeysByVariantKeys: Record; variantByKey: Record; variantKeysByValue: Record; + variantKeysByValueNonQualified: Record; unicodeCategories: Record>; pickerCategories: Record>; @@ -266,10 +268,12 @@ type EmojiIndex = Readonly<{ const EMOJI_INDEX: EmojiIndex = { parentByKey: {}, parentKeysByValue: {}, + parentKeysByValueNonQualified: {}, parentKeysByName: {}, parentKeysByVariantKeys: {}, variantByKey: {}, variantKeysByValue: {}, + variantKeysByValueNonQualified: {}, unicodeCategories: { [EmojiUnicodeCategory.SmileysAndEmotion]: [], [EmojiUnicodeCategory.PeopleAndBody]: [], @@ -300,6 +304,8 @@ function addParent(parent: EmojiParentData, rank: number) { EMOJI_INDEX.parentKeysByValue[parent.value] = parent.key; if (parent.valueNonqualified != null) { EMOJI_INDEX.parentKeysByValue[parent.valueNonqualified] = parent.key; + EMOJI_INDEX.parentKeysByValueNonQualified[parent.valueNonqualified] = + parent.key; } EMOJI_INDEX.parentKeysByName[parent.englishShortNameDefault] = parent.key; EMOJI_INDEX.unicodeCategories[parent.unicodeCategory].push(parent.key); @@ -327,6 +333,8 @@ function addVariant(parentKey: EmojiParentKey, variant: EmojiVariantData) { EMOJI_INDEX.variantKeysByValue[variant.value] = variant.key; if (variant.valueNonqualified) { EMOJI_INDEX.variantKeysByValue[variant.valueNonqualified] = variant.key; + EMOJI_INDEX.variantKeysByValueNonQualified[variant.valueNonqualified] = + variant.key; } } @@ -419,6 +427,12 @@ export function isEmojiVariantValue(input: string): input is EmojiVariantValue { return Object.hasOwn(EMOJI_INDEX.variantKeysByValue, input); } +export function isEmojiVariantValueNonQualified( + input: EmojiVariantValue +): boolean { + return Object.hasOwn(EMOJI_INDEX.variantKeysByValueNonQualified, input); +} + /** @deprecated Prefer EmojiKey for refs, load short names from translations */ export function isEmojiEnglishShortName( input: string