Add localized emoji search

This commit is contained in:
Fedor Indutny 2024-03-21 09:35:54 -07:00 committed by GitHub
parent ce0fb22041
commit e90553b3b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 878 additions and 97 deletions

View file

@ -23,6 +23,7 @@ import { getOwn } from '../../util/getOwn';
import * as log from '../../logging/log';
import { MINUTE } from '../../util/durations';
import { drop } from '../../util/drop';
import type { LocaleEmojiType } from '../../types/emoji';
export const skinTones = ['1F3FB', '1F3FC', '1F3FD', '1F3FE', '1F3FF'];
@ -218,34 +219,69 @@ export function getImagePath(
return makeImagePath(emojiData.image);
}
const fuse = new Fuse(data, {
shouldSort: true,
threshold: 0.2,
minMatchCharLength: 1,
keys: ['short_name', 'short_names'],
});
export type SearchFnType = (query: string, count?: number) => Array<string>;
const fuseExactPrefix = new Fuse(data, {
shouldSort: true,
threshold: 0, // effectively a prefix search
minMatchCharLength: 2,
keys: ['short_name', 'short_names'],
});
export type SearchEmojiListType = ReadonlyArray<
Pick<LocaleEmojiType, 'shortName' | 'rank' | 'tags'>
>;
export function search(query: string, count = 0): Array<EmojiData> {
// when we only have 2 characters, do an exact prefix match
// to avoid matching on emoticon, like :-P
const fuseIndex = query.length === 2 ? fuseExactPrefix : fuse;
type CachedSearchFnType = Readonly<{
localeEmoji: SearchEmojiListType;
fn: SearchFnType;
}>;
const results = fuseIndex
.search(query.substr(0, 32))
.map(result => result.item);
let cachedSearchFn: CachedSearchFnType | undefined;
if (count) {
return take(results, count);
export function createSearch(localeEmoji: SearchEmojiListType): SearchFnType {
if (cachedSearchFn && cachedSearchFn.localeEmoji === localeEmoji) {
return cachedSearchFn.fn;
}
return results;
const fuse = new Fuse(localeEmoji, {
shouldSort: true,
threshold: 0.2,
minMatchCharLength: 1,
keys: ['shortName', 'tags'],
includeScore: true,
});
const fuseExactPrefix = new Fuse(localeEmoji, {
shouldSort: true,
threshold: 0, // effectively a prefix search
minMatchCharLength: 2,
keys: ['shortName', 'tags'],
includeScore: true,
});
const fn = (query: string, count = 0): Array<string> => {
// when we only have 2 characters, do an exact prefix match
// to avoid matching on emoticon, like :-P
const fuseIndex = query.length === 2 ? fuseExactPrefix : fuse;
const rawResults = fuseIndex.search(query.substr(0, 32));
const rankedResults = rawResults.map(entry => {
const rank = entry.item.rank || 1e9;
return {
score: (entry.score ?? 0) + rank / localeEmoji.length,
item: entry.item,
};
});
const results = rankedResults
.sort((a, b) => a.score - b.score)
.map(result => result.item.shortName);
if (count) {
return take(results, count);
}
return results;
};
cachedSearchFn = { localeEmoji, fn };
return fn;
}
const shortNames = new Set([