fix(app): convert pasted and edited unicode emoji in the composer (#2005)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-25 13:44:26 +02:00
1 parent c101610c46
commit 78f7db9250
1 file changed
+29 -2
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
import UnicodeEmojis from '@app/features/expressions/utils/UnicodeEmojis';
import { import {
$captureSelectionOffsets, $captureSelectionOffsets,
$getComposerNodeDisplayStart, $getComposerNodeDisplayStart,
@@ -12,7 +13,7 @@ import {
$isComposerStandardEmojiNode, $isComposerStandardEmojiNode,
} from '@app/features/lexical/composer/nodes/ComposerStandardEmojiNode'; } from '@app/features/lexical/composer/nodes/ComposerStandardEmojiNode';
import {$isSyntaxMarkerNode} from '@app/features/lexical/composer/nodes/SyntaxMarkerNode'; import {$isSyntaxMarkerNode} from '@app/features/lexical/composer/nodes/SyntaxMarkerNode';
import {findTypedEmojiShortcode} from '@app/features/messaging/utils/markdown/TypedEmojiMatch'; import {findTypedEmojiShortcode, type TypedEmojiMatch} from '@app/features/messaging/utils/markdown/TypedEmojiMatch';
import type {ResolvedTypedEmoji} from '@app/features/messaging/utils/TypedEmojiShortcodeUtils'; import type {ResolvedTypedEmoji} from '@app/features/messaging/utils/TypedEmojiShortcodeUtils';
import {type LexicalEditor, TextNode} from 'lexical'; import {type LexicalEditor, TextNode} from 'lexical';
@@ -26,6 +27,32 @@ export function registerComposerEmojiShortcode(editor: LexicalEditor, resolve: C
}); });
} }
function findUnicodeEmoji(text: string, startIndex: number): TypedEmojiMatch | null {
const pattern = new RegExp(UnicodeEmojis.EMOJI_SURROGATE_RE.source, 'g');
pattern.lastIndex = startIndex;
const match = pattern.exec(text);
if (match == null) {
return null;
}
const name = UnicodeEmojis.nameForSurrogate(match[0], false);
if (!name) {
return null;
}
return {start: match.index, end: match.index + match[0].length, name};
}
function findNextEmojiToken(text: string, startIndex: number): TypedEmojiMatch | null {
const shortcode = findTypedEmojiShortcode(text, startIndex);
const surrogate = findUnicodeEmoji(text, startIndex);
if (shortcode == null) {
return surrogate;
}
if (surrogate == null) {
return shortcode;
}
return surrogate.start < shortcode.start ? surrogate : shortcode;
}
export function $convertEmojiShortcode(node: TextNode, resolve: ComposerEmojiResolver): void { export function $convertEmojiShortcode(node: TextNode, resolve: ComposerEmojiResolver): void {
if ($isSyntaxMarkerNode(node) || node.hasFormat('code')) { if ($isSyntaxMarkerNode(node) || node.hasFormat('code')) {
return; return;
@@ -37,7 +64,7 @@ export function $convertEmojiShortcode(node: TextNode, resolve: ComposerEmojiRes
const text = node.getTextContent(); const text = node.getTextContent();
let searchFrom = 0; let searchFrom = 0;
while (true) { while (true) {
const match = findTypedEmojiShortcode(text, searchFrom); const match = findNextEmojiToken(text, searchFrom);
if (match == null) { if (match == null) {
return; return;
} }