Update Emojibase and switch to IamCal (Slack-style) shortcodes

for consistency with shortcodes commonly used by other platforms, as
was decided in https://github.com/vector-im/element-web/issues/13857.

One thing to be aware of is that the currently used version of Twemoji
does not support a few of the newer emoji present in Emojibase, so these
look a little out of place in the emoji picker. Optimally Twemoji would
be updated at the same time, though I don't know how to do that.

Signed-off-by: Robin Townsend <robin@robin.town>
This commit is contained in:
Robin Townsend 2021-07-10 22:40:30 -04:00
parent 829169ec87
commit 718887dd27
7 changed files with 72 additions and 79 deletions

View file

@ -25,8 +25,7 @@ import { PillCompletion } from './Components';
import { ICompletion, ISelectionRange } from './Autocompleter';
import { uniq, sortBy } from 'lodash';
import SettingsStore from "../settings/SettingsStore";
import { shortcodeToUnicode } from '../HtmlUtils';
import { EMOJI, IEmoji } from '../emoji';
import { EMOJI, IEmoji, getShortcodes } from '../emoji';
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
@ -38,21 +37,26 @@ const EMOJI_REGEX = new RegExp('(' + EMOTICON_REGEX.source + '|(?:^|\\s):[+-\\w]
interface IEmojiShort {
emoji: IEmoji;
shortname: string;
shortcode: string;
altShortcodes: string[];
_orderBy: number;
}
const EMOJI_SHORTNAMES: IEmojiShort[] = EMOJI.sort((a, b) => {
const EMOJI_SHORTCODES: IEmojiShort[] = EMOJI.sort((a, b) => {
if (a.group === b.group) {
return a.order - b.order;
}
return a.group - b.group;
}).map((emoji, index) => ({
emoji,
shortname: `:${emoji.shortcodes[0]}:`,
// Include the index so that we can preserve the original order
_orderBy: index,
}));
}).map((emoji, index) => {
const [shortcode, ...altShortcodes] = getShortcodes(emoji);
return {
emoji,
shortcode: shortcode ? `:${shortcode}:` : undefined,
altShortcodes: altShortcodes.map(s => `:${s}:`),
// Include the index so that we can preserve the original order
_orderBy: index,
};
}).filter(emoji => emoji.shortcode);
function score(query, space) {
const index = space.indexOf(query);
@ -69,15 +73,15 @@ export default class EmojiProvider extends AutocompleteProvider {
constructor() {
super(EMOJI_REGEX);
this.matcher = new QueryMatcher<IEmojiShort>(EMOJI_SHORTNAMES, {
keys: ['emoji.emoticon', 'shortname'],
this.matcher = new QueryMatcher<IEmojiShort>(EMOJI_SHORTCODES, {
keys: ['emoji.emoticon', 'shortcode'],
funcs: [
(o) => o.emoji.shortcodes.length > 1 ? o.emoji.shortcodes.slice(1).map(s => `:${s}:`).join(" ") : "", // aliases
o => o.altShortcodes.join(" "), // aliases
],
// For matching against ascii equivalents
shouldMatchWordsOnly: false,
});
this.nameMatcher = new QueryMatcher(EMOJI_SHORTNAMES, {
this.nameMatcher = new QueryMatcher(EMOJI_SHORTCODES, {
keys: ['emoji.annotation'],
// For removing punctuation
shouldMatchWordsOnly: true,
@ -105,34 +109,33 @@ export default class EmojiProvider extends AutocompleteProvider {
const sorters = [];
// make sure that emoticons come first
sorters.push((c) => score(matchedString, c.emoji.emoticon || ""));
sorters.push(c => score(matchedString, c.emoji.emoticon || ""));
// then sort by score (Infinity if matchedString not in shortname)
sorters.push((c) => score(matchedString, c.shortname));
// then sort by score (Infinity if matchedString not in shortcode)
sorters.push(c => score(matchedString, c.shortcode));
// then sort by max score of all shortcodes, trim off the `:`
sorters.push((c) => Math.min(...c.emoji.shortcodes.map(s => score(matchedString.substring(1), s))));
// If the matchedString is not empty, sort by length of shortname. Example:
sorters.push(c => Math.min(
...[c.shortcode, ...c.altShortcodes].map(s => score(matchedString.substring(1), s)),
));
// If the matchedString is not empty, sort by length of shortcode. Example:
// matchedString = ":bookmark"
// completions = [":bookmark:", ":bookmark_tabs:", ...]
if (matchedString.length > 1) {
sorters.push((c) => c.shortname.length);
sorters.push(c => c.shortcode.length);
}
// Finally, sort by original ordering
sorters.push((c) => c._orderBy);
sorters.push(c => c._orderBy);
completions = sortBy(uniq(completions), sorters);
completions = completions.map(({ shortname }) => {
const unicode = shortcodeToUnicode(shortname);
return {
completion: unicode,
component: (
<PillCompletion title={shortname} aria-label={unicode}>
<span>{ unicode }</span>
</PillCompletion>
),
range,
};
}).slice(0, LIMIT);
completions = completions.map(c => ({
completion: c.emoji.unicode,
component: (
<PillCompletion title={c.shortcode} aria-label={c.emoji.unicode}>
<span>{ c.emoji.unicode }</span>
</PillCompletion>
),
range,
})).slice(0, LIMIT);
}
return completions;
}