diff --git a/src/autocomplete/EmojiProvider.tsx b/src/autocomplete/EmojiProvider.tsx index 8a81acd498..7cd784d71c 100644 --- a/src/autocomplete/EmojiProvider.tsx +++ b/src/autocomplete/EmojiProvider.tsx @@ -49,7 +49,7 @@ const EMOJI_SHORTCODES: IEmojiShort[] = EMOJI.sort((a, b) => { emoji, // Include the index so that we can preserve the original order _orderBy: index, -})).filter(o => o.emoji.shortcodes[0]); +})); function score(query, space) { const index = space.indexOf(query); @@ -68,7 +68,7 @@ export default class EmojiProvider extends AutocompleteProvider { super(EMOJI_REGEX); this.matcher = new QueryMatcher(EMOJI_SHORTCODES, { keys: ['emoji.emoticon'], - funcs: [o => o.emoji.shortcodes.map(s => `:${s}:`).join(" ")], + funcs: [o => o.emoji.shortcodes.map(s => `:${s}:`)], // For matching against ascii equivalents shouldMatchWordsOnly: false, }); diff --git a/src/components/views/emojipicker/EmojiPicker.tsx b/src/components/views/emojipicker/EmojiPicker.tsx index 9b2e771e64..22dbfe89b8 100644 --- a/src/components/views/emojipicker/EmojiPicker.tsx +++ b/src/components/views/emojipicker/EmojiPicker.tsx @@ -32,6 +32,8 @@ export const CATEGORY_HEADER_HEIGHT = 22; export const EMOJI_HEIGHT = 37; export const EMOJIS_PER_ROW = 8; +const ZERO_WIDTH_JOINER = "\u200D"; + interface IProps { selectedEmojis?: Set; showQuickReactions?: boolean; @@ -180,7 +182,7 @@ class EmojiPicker extends React.Component { } else { emojis = cat.id === "recent" ? this.recentlyUsed : DATA_BY_CATEGORY[cat.id]; } - emojis = emojis.filter(emoji => emoji.filterString.includes(filter)); + emojis = emojis.filter(emoji => this.emojiMatchesFilter(emoji, filter)); this.memoizedDataByCategory[cat.id] = emojis; cat.enabled = emojis.length > 0; // The setState below doesn't re-render the header and we already have the refs for updateVisibility, so... @@ -192,6 +194,10 @@ class EmojiPicker extends React.Component { setTimeout(this.updateVisibility, 0); }; + private emojiMatchesFilter = (emoji: IEmoji, filter: string): boolean => + [emoji.annotation, ...emoji.shortcodes, emoji.emoticon, ...emoji.unicode.split(ZERO_WIDTH_JOINER)] + .some(x => x?.includes(filter)); + private onEnterFilter = () => { const btn = this.bodyRef.current.querySelector(".mx_EmojiPicker_item"); if (btn) { diff --git a/src/components/views/emojipicker/Preview.tsx b/src/components/views/emojipicker/Preview.tsx index da2f8dcd89..a0203bec85 100644 --- a/src/components/views/emojipicker/Preview.tsx +++ b/src/components/views/emojipicker/Preview.tsx @@ -38,9 +38,9 @@ class Preview extends React.PureComponent {
{annotation}
- { shortcode ? -
{shortcode}
: - null } +
+ {shortcode} +
); diff --git a/src/components/views/emojipicker/QuickReactions.tsx b/src/components/views/emojipicker/QuickReactions.tsx index 9321450fc1..ffd3ce9760 100644 --- a/src/components/views/emojipicker/QuickReactions.tsx +++ b/src/components/views/emojipicker/QuickReactions.tsx @@ -62,7 +62,6 @@ class QuickReactions extends React.Component { }; render() { - const shortcode = this.state.hover?.shortcodes?.[0]; return (

@@ -70,9 +69,7 @@ class QuickReactions extends React.Component { ? _t("Quick Reactions") : {this.state.hover.annotation} - { shortcode ? - {shortcode} : - null } + {this.state.hover.shortcodes[0]} }

diff --git a/src/emoji.ts b/src/emoji.ts index fe9e52d35f..ec6a6654c4 100644 --- a/src/emoji.ts +++ b/src/emoji.ts @@ -25,8 +25,8 @@ export interface IEmoji { shortcodes: string[]; tags?: string[]; unicode: string; + skins?: any[]; // Currently unused emoticon?: string; - filterString: string; } // The unicode is stored without the variant selector @@ -59,20 +59,17 @@ export const DATA_BY_CATEGORY = { "flags": [], }; -const ZERO_WIDTH_JOINER = "\u200D"; - // Store various mappings from unicode/emoticon/shortcode to the Emoji objects -export const EMOJI: IEmoji[] = EMOJIBASE.map(emojiData => { +export const EMOJI: IEmoji[] = EMOJIBASE.map((emojiData: Omit) => { const shortcodeData = SHORTCODES[emojiData.hexcode]; - // Homogenize shortcodes by ensuring that everything is an array - const shortcodes = typeof shortcodeData === "string" ? [shortcodeData] : (shortcodeData ?? []); - const emoji: IEmoji = { ...emojiData, - shortcodes, - // This is used as the string to match the query against when filtering emojis - filterString: (`${emojiData.annotation}\n${shortcodes.join('\n')}}\n${emojiData.emoticon || ''}\n` + - `${emojiData.unicode.split(ZERO_WIDTH_JOINER).join("\n")}`).toLowerCase(), + // Homogenize shortcodes by ensuring that everything is an array + shortcodes: typeof shortcodeData === "string" ? + [shortcodeData] : + // If there's ever a gap in shortcode coverage, we fudge it by + // filling it in with the emoji's CLDR annotation + (shortcodeData ?? [emojiData.annotation.toLowerCase().replace(/ /g, "_")]), }; const categoryId = EMOJIBASE_GROUP_ID_TO_CATEGORY[emoji.group];