From 7f50fcdb54b2cd8fd52102a954525e6a738125c8 Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Mon, 30 Aug 2021 11:39:03 -0500 Subject: [PATCH] Improve performance when rendering many emoji --- ts/components/conversation/Emojify.stories.tsx | 16 ++++++++++++++++ ts/test-both/util/emoji_test.ts | 18 ++++-------------- ts/util/emoji.ts | 15 ++++++++++++--- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/ts/components/conversation/Emojify.stories.tsx b/ts/components/conversation/Emojify.stories.tsx index 547b03da9e..edb62cea67 100644 --- a/ts/components/conversation/Emojify.stories.tsx +++ b/ts/components/conversation/Emojify.stories.tsx @@ -96,3 +96,19 @@ story.add('Custom Text Render', () => { return ; }); + +story.add('Tens of thousands of emoji', () => { + const props = createProps({ + text: 'πŸ’…'.repeat(40000), + }); + + return ; +}); + +story.add('Tens of thousands of emoji, interspersed with text', () => { + const props = createProps({ + text: 'πŸ’… hi '.repeat(40000), + }); + + return ; +}); diff --git a/ts/test-both/util/emoji_test.ts b/ts/test-both/util/emoji_test.ts index 0eb24968d0..c86081df67 100644 --- a/ts/test-both/util/emoji_test.ts +++ b/ts/test-both/util/emoji_test.ts @@ -28,25 +28,15 @@ describe('emoji', () => { { type: 'emoji', value: 'πŸ˜›' }, { type: 'text', value: 'world' }, { type: 'emoji', value: '😎' }, - { type: 'text', value: '' }, { type: 'emoji', value: 'πŸ˜›' }, { type: 'text', value: '!' }, ]); }); - it('should return empty string after split at the end', () => { - assert.deepStrictEqual(splitByEmoji('helloπŸ˜›'), [ - { type: 'text', value: 'hello' }, - { type: 'emoji', value: 'πŸ˜›' }, - { type: 'text', value: '' }, - ]); - }); - - it('should return empty string before the split at the start', () => { - assert.deepStrictEqual(splitByEmoji('πŸ˜›hello'), [ - { type: 'text', value: '' }, - { type: 'emoji', value: 'πŸ˜›' }, - { type: 'text', value: 'hello' }, + it('returns emojis as text after 5,000 emojis are found', () => { + assert.deepStrictEqual(splitByEmoji('πŸ’¬'.repeat(5002)), [ + ...Array(5000).fill({ type: 'emoji', value: 'πŸ’¬' }), + { type: 'text', value: 'πŸ’¬πŸ’¬' }, ]); }); }); diff --git a/ts/util/emoji.ts b/ts/util/emoji.ts index bf0d044370..b26c3ac349 100644 --- a/ts/util/emoji.ts +++ b/ts/util/emoji.ts @@ -4,8 +4,10 @@ import emojiRegex from 'emoji-regex/es2015/RGI_Emoji'; import { assert } from './assert'; +import { take } from './iterables'; const REGEXP = emojiRegex(); +const MAX_EMOJI_TO_MATCH = 5000; export function replaceEmojiWithSpaces(value: string): string { return value.replace(REGEXP, ' '); @@ -17,19 +19,26 @@ export type SplitElement = Readonly<{ }>; export function splitByEmoji(value: string): ReadonlyArray { - const emojis = value.matchAll(REGEXP); + const emojis = take(value.matchAll(REGEXP), MAX_EMOJI_TO_MATCH); const result: Array = []; let lastIndex = 0; for (const match of emojis) { - result.push({ type: 'text', value: value.slice(lastIndex, match.index) }); + const nonEmojiText = value.slice(lastIndex, match.index); + if (nonEmojiText) { + result.push({ type: 'text', value: nonEmojiText }); + } + result.push({ type: 'emoji', value: match[0] }); assert(match.index !== undefined, '`matchAll` should provide indices'); lastIndex = match.index + match[0].length; } - result.push({ type: 'text', value: value.slice(lastIndex) }); + const finalNonEmojiText = value.slice(lastIndex); + if (finalNonEmojiText) { + result.push({ type: 'text', value: finalNonEmojiText }); + } return result; }