From 22fb468481db77de49461978f370b825efbed1d7 Mon Sep 17 00:00:00 2001
From: Jamie Kyle <113370520+jamiebuilds-signal@users.noreply.github.com>
Date: Fri, 28 Mar 2025 16:01:27 -0700
Subject: [PATCH] Fix rendering non/overly-qualified emoji in emojify
---
ts/components/conversation/Emojify.stories.tsx | 16 ++++++++++++++++
ts/components/conversation/Emojify.tsx | 17 ++++++++++++-----
ts/components/fun/data/emojis.ts | 14 ++++++++++++++
3 files changed, 42 insertions(+), 5 deletions(-)
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