Fix localized emoji auto-completions
This commit is contained in:
parent
dd3ab66593
commit
074fa8af4b
11 changed files with 66 additions and 49 deletions
|
|
@ -1271,7 +1271,7 @@ function useReactionsToast(props: UseReactionsToastType): void {
|
|||
<span className="CallingReactionsToasts__reaction">
|
||||
<FunStaticEmoji
|
||||
role="img"
|
||||
aria-label={emojiLocalizer(emojiVariantKey)}
|
||||
aria-label={emojiLocalizer.getLocaleShortName(emojiVariantKey)}
|
||||
size={28}
|
||||
emoji={emojiVariant}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ function BioEmoji(props: { emoji: EmojiVariantKey }) {
|
|||
return (
|
||||
<FunStaticEmoji
|
||||
role="img"
|
||||
aria-label={emojiLocalizer(props.emoji)}
|
||||
aria-label={emojiLocalizer.getLocaleShortName(props.emoji)}
|
||||
emoji={emojiVariant}
|
||||
size={24}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export function Emojify({
|
|||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={index}
|
||||
role="img"
|
||||
aria-label={emojiLocalizer(variantKey)}
|
||||
aria-label={emojiLocalizer.getLocaleShortName(variantKey)}
|
||||
emoji={variant}
|
||||
size={fontSizeOverride}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ function ReactionViewerEmoji(props: {
|
|||
return (
|
||||
<FunStaticEmoji
|
||||
role="img"
|
||||
aria-label={emojiLocalizer(emojiVariantKey)}
|
||||
aria-label={emojiLocalizer.getLocaleShortName(emojiVariantKey)}
|
||||
size={18}
|
||||
emoji={emojiVariant}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,7 @@
|
|||
|
||||
// Camelcase disabled due to emoji-datasource using snake_case
|
||||
/* eslint-disable camelcase */
|
||||
import {
|
||||
compact,
|
||||
flatMap,
|
||||
groupBy,
|
||||
keyBy,
|
||||
map,
|
||||
mapValues,
|
||||
sortBy,
|
||||
} from 'lodash';
|
||||
import { groupBy, keyBy, mapValues, sortBy } from 'lodash';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
import {
|
||||
EMOJI_SKIN_TONE_TO_KEY,
|
||||
|
|
@ -152,15 +144,6 @@ export function getEmojiData(
|
|||
return base;
|
||||
}
|
||||
|
||||
const shortNames = new Set([
|
||||
...map(data, 'short_name'),
|
||||
...compact<string>(flatMap(data, 'short_names')),
|
||||
]);
|
||||
|
||||
export function isShortName(name: string): boolean {
|
||||
return shortNames.has(name);
|
||||
}
|
||||
|
||||
export function unifiedToEmoji(unified: string): string {
|
||||
return unified
|
||||
.split('-')
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export function FunEmojiPickerButton(
|
|||
<FunStaticEmoji
|
||||
role="img"
|
||||
size={20}
|
||||
aria-label={emojiLocalizer(emojiVarant.key)}
|
||||
aria-label={emojiLocalizer.getLocaleShortName(emojiVarant.key)}
|
||||
emoji={emojiVarant}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -252,7 +252,10 @@ type EmojiIndex = Readonly<{
|
|||
pickerCategories: Record<EmojiPickerCategory, Array<EmojiParentKey>>;
|
||||
|
||||
defaultEnglishSearchIndex: Array<FunEmojiSearchIndexEntry>;
|
||||
defaultEnglishLocalizerIndex: Map<EmojiParentKey, string>;
|
||||
defaultEnglishLocalizerIndex: {
|
||||
parentKeyToLocaleShortName: Map<EmojiParentKey, string>;
|
||||
localeShortNameToParentKey: Map<string, EmojiParentKey>;
|
||||
};
|
||||
}>;
|
||||
|
||||
/** @internal */
|
||||
|
|
@ -288,7 +291,10 @@ const EMOJI_INDEX: EmojiIndex = {
|
|||
[EmojiPickerCategory.Flags]: [],
|
||||
},
|
||||
defaultEnglishSearchIndex: [],
|
||||
defaultEnglishLocalizerIndex: new Map(),
|
||||
defaultEnglishLocalizerIndex: {
|
||||
parentKeyToLocaleShortName: new Map(),
|
||||
localeShortNameToParentKey: new Map(),
|
||||
},
|
||||
};
|
||||
|
||||
function addParent(parent: EmojiParentData, rank: number) {
|
||||
|
|
@ -320,10 +326,14 @@ function addParent(parent: EmojiParentData, rank: number) {
|
|||
emoticons: parent.emoticons,
|
||||
});
|
||||
|
||||
EMOJI_INDEX.defaultEnglishLocalizerIndex.set(
|
||||
EMOJI_INDEX.defaultEnglishLocalizerIndex.parentKeyToLocaleShortName.set(
|
||||
parent.key,
|
||||
parent.englishShortNameDefault
|
||||
);
|
||||
EMOJI_INDEX.defaultEnglishLocalizerIndex.localeShortNameToParentKey.set(
|
||||
parent.englishShortNameDefault,
|
||||
parent.key
|
||||
);
|
||||
}
|
||||
|
||||
function addVariant(parentKey: EmojiParentKey, variant: EmojiVariantData) {
|
||||
|
|
|
|||
|
|
@ -641,7 +641,7 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
|
|||
);
|
||||
|
||||
const emojiName = useMemo(() => {
|
||||
return emojiLocalizer(emojiVariant.key);
|
||||
return emojiLocalizer.getLocaleShortName(emojiVariant.key);
|
||||
}, [emojiVariant.key, emojiLocalizer]);
|
||||
|
||||
const emojiShortNameDisplay = useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,21 @@ import type { LocaleEmojiListType } from '../../types/emoji';
|
|||
import { strictAssert } from '../../util/assert';
|
||||
import { useFunEmojiLocalization } from './FunEmojiLocalizationProvider';
|
||||
|
||||
export type FunEmojiLocalizerIndex = ReadonlyMap<EmojiParentKey, string>;
|
||||
export type FunEmojiLocalizer = (key: EmojiVariantKey) => string;
|
||||
export type FunEmojiLocalizerIndex = Readonly<{
|
||||
parentKeyToLocaleShortName: ReadonlyMap<EmojiParentKey, string>;
|
||||
localeShortNameToParentKey: ReadonlyMap<string, EmojiParentKey>;
|
||||
}>;
|
||||
|
||||
export type FunEmojiLocalizer = Readonly<{
|
||||
getLocaleShortName: (key: EmojiVariantKey) => string;
|
||||
getParentKeyForText: (text: string) => EmojiParentKey | null;
|
||||
}>;
|
||||
|
||||
export function createFunEmojiLocalizerIndex(
|
||||
localeEmojiList: LocaleEmojiListType
|
||||
): FunEmojiLocalizerIndex {
|
||||
const index = new Map<EmojiParentKey, string>();
|
||||
const parentKeyToLocaleShortName = new Map<EmojiParentKey, string>();
|
||||
const localeShortNameToParentKey = new Map<string, EmojiParentKey>();
|
||||
|
||||
for (const entry of localeEmojiList) {
|
||||
strictAssert(
|
||||
|
|
@ -29,26 +37,35 @@ export function createFunEmojiLocalizerIndex(
|
|||
const variantKey = getEmojiVariantKeyByValue(entry.emoji);
|
||||
const parentKey = getEmojiParentKeyByVariantKey(variantKey);
|
||||
const localizedShortName = entry.tags.at(0) ?? entry.shortName;
|
||||
index.set(parentKey, localizedShortName);
|
||||
parentKeyToLocaleShortName.set(parentKey, localizedShortName);
|
||||
localeShortNameToParentKey.set(localizedShortName, parentKey);
|
||||
}
|
||||
|
||||
return index;
|
||||
return { parentKeyToLocaleShortName, localeShortNameToParentKey };
|
||||
}
|
||||
|
||||
/** @internal exported for tests */
|
||||
export function _createFunEmojiLocalizer(
|
||||
emojiLocalizerIndex: FunEmojiLocalizerIndex
|
||||
): FunEmojiLocalizer {
|
||||
return variantKey => {
|
||||
function getLocaleShortName(variantKey: EmojiVariantKey) {
|
||||
const parentKey = getEmojiParentKeyByVariantKey(variantKey);
|
||||
const localeShortName = emojiLocalizerIndex.get(parentKey);
|
||||
const localeShortName =
|
||||
emojiLocalizerIndex.parentKeyToLocaleShortName.get(parentKey);
|
||||
if (localeShortName != null) {
|
||||
return localeShortName;
|
||||
}
|
||||
// Fallback to english short name
|
||||
const parent = getEmojiParentByKey(parentKey);
|
||||
return parent.englishShortNameDefault;
|
||||
};
|
||||
}
|
||||
|
||||
function getParentKeyForText(text: string): EmojiParentKey | null {
|
||||
const parentKey = emojiLocalizerIndex.localeShortNameToParentKey.get(text);
|
||||
return parentKey ?? null;
|
||||
}
|
||||
|
||||
return { getLocaleShortName, getParentKeyForText };
|
||||
}
|
||||
|
||||
export function useFunEmojiLocalizer(): FunEmojiLocalizer {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ import Emitter from '@signalapp/quill-cjs/core/emitter';
|
|||
import React from 'react';
|
||||
import _, { isNumber } from 'lodash';
|
||||
import type Quill from '@signalapp/quill-cjs';
|
||||
|
||||
import { Popper } from 'react-popper';
|
||||
import classNames from 'classnames';
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { VirtualElement } from '@popperjs/core';
|
||||
import { convertShortName, isShortName } from '../../components/emoji/lib';
|
||||
import { convertShortName } from '../../components/emoji/lib';
|
||||
import type { EmojiPickDataType } from '../../components/emoji/EmojiPicker';
|
||||
import { getBlotTextPartitions, matchBlotTextPartitions } from '../util';
|
||||
import { handleOutsideClick } from '../../util/handleOutsideClick';
|
||||
|
|
@ -23,8 +22,10 @@ import {
|
|||
getEmojiVariantByParentKeyAndSkinTone,
|
||||
normalizeShortNameCompletionDisplay,
|
||||
} from '../../components/fun/data/emojis';
|
||||
import type { FunEmojiSearchResult } from '../../components/fun/useFunEmojiSearch';
|
||||
import { type FunEmojiSearch } from '../../components/fun/useFunEmojiSearch';
|
||||
import type {
|
||||
FunEmojiSearchResult,
|
||||
FunEmojiSearch,
|
||||
} from '../../components/fun/useFunEmojiSearch';
|
||||
import { type FunEmojiLocalizer } from '../../components/fun/useFunEmojiLocalizer';
|
||||
|
||||
export type EmojiCompletionOptions = {
|
||||
|
|
@ -164,11 +165,14 @@ export class EmojiCompletion {
|
|||
const [, leftTokenText, isSelfClosing] = leftTokenTextMatch;
|
||||
|
||||
if (isSelfClosing || justPressedColon) {
|
||||
if (isShortName(leftTokenText)) {
|
||||
const parentKey =
|
||||
this.options.emojiLocalizer.getParentKeyForText(leftTokenText);
|
||||
if (parentKey != null) {
|
||||
const numberOfColons = isSelfClosing ? 2 : 1;
|
||||
const emoji = getEmojiParentByKey(parentKey);
|
||||
|
||||
this.insertEmoji({
|
||||
shortName: leftTokenText,
|
||||
shortName: emoji.englishShortNameDefault,
|
||||
index: range.index - leftTokenText.length - numberOfColons,
|
||||
range: leftTokenText.length + numberOfColons,
|
||||
justPressedColon,
|
||||
|
|
@ -182,10 +186,13 @@ export class EmojiCompletion {
|
|||
if (rightTokenTextMatch) {
|
||||
const [, rightTokenText] = rightTokenTextMatch;
|
||||
const tokenText = leftTokenText + rightTokenText;
|
||||
const parentKey =
|
||||
this.options.emojiLocalizer.getParentKeyForText(tokenText);
|
||||
|
||||
if (isShortName(tokenText)) {
|
||||
if (parentKey != null) {
|
||||
const emoji = getEmojiParentByKey(parentKey);
|
||||
this.insertEmoji({
|
||||
shortName: tokenText,
|
||||
shortName: emoji.englishShortNameDefault,
|
||||
index: range.index - leftTokenText.length - 1,
|
||||
range: tokenText.length + 2,
|
||||
justPressedColon,
|
||||
|
|
@ -377,9 +384,10 @@ export class EmojiCompletion {
|
|||
this.options.emojiSkinToneDefault ?? EmojiSkinTone.None
|
||||
);
|
||||
|
||||
const localeShortName = this.options.emojiLocalizer(
|
||||
emojiVariant.key
|
||||
);
|
||||
const localeShortName =
|
||||
this.options.emojiLocalizer.getLocaleShortName(
|
||||
emojiVariant.key
|
||||
);
|
||||
|
||||
const normalized =
|
||||
normalizeShortNameCompletionDisplay(localeShortName);
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@
|
|||
|
||||
import { createSelector } from 'reselect';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import type { StateType } from '../reducer';
|
||||
import { isShortName } from '../../components/emoji/lib';
|
||||
import { isEmojiEnglishShortName } from '../../components/fun/data/emojis';
|
||||
|
||||
export const selectRecentEmojis = createSelector(
|
||||
({ emojis }: StateType) => emojis.recents,
|
||||
recents => recents.filter(isShortName)
|
||||
recents => recents.filter(isEmojiEnglishShortName)
|
||||
);
|
||||
|
||||
export const useRecentEmojis = (): Array<string> =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue