feat(input): added ascii to emoji conversion
This commit is contained in:
parent
68dfc46185
commit
e4ab71eb78
2 changed files with 99 additions and 0 deletions
|
@ -61,12 +61,14 @@ import {
|
||||||
matchStrikethrough,
|
matchStrikethrough,
|
||||||
} from '../quill/formatting/matchers';
|
} from '../quill/formatting/matchers';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
|
import { AutoSubstituteAsciiEmojis } from '../quill/auto-substitute-ascii-emojis';
|
||||||
|
|
||||||
Quill.register('formats/emoji', EmojiBlot);
|
Quill.register('formats/emoji', EmojiBlot);
|
||||||
Quill.register('formats/mention', MentionBlot);
|
Quill.register('formats/mention', MentionBlot);
|
||||||
Quill.register('formats/block', DirectionalBlot);
|
Quill.register('formats/block', DirectionalBlot);
|
||||||
Quill.register('formats/monospace', MonospaceBlot);
|
Quill.register('formats/monospace', MonospaceBlot);
|
||||||
Quill.register('formats/spoiler', SpoilerBlot);
|
Quill.register('formats/spoiler', SpoilerBlot);
|
||||||
|
Quill.register('modules/autoSubstituteAsciiEmojis', AutoSubstituteAsciiEmojis);
|
||||||
Quill.register('modules/emojiCompletion', EmojiCompletion);
|
Quill.register('modules/emojiCompletion', EmojiCompletion);
|
||||||
Quill.register('modules/mentionCompletion', MentionCompletion);
|
Quill.register('modules/mentionCompletion', MentionCompletion);
|
||||||
Quill.register('modules/formattingMenu', FormattingMenu);
|
Quill.register('modules/formattingMenu', FormattingMenu);
|
||||||
|
@ -767,6 +769,7 @@ export function CompositionInput(props: Props): React.ReactElement {
|
||||||
callbacksRef.current.onPickEmoji(emoji),
|
callbacksRef.current.onPickEmoji(emoji),
|
||||||
skinTone,
|
skinTone,
|
||||||
},
|
},
|
||||||
|
autoSubstituteAsciiEmojis: true,
|
||||||
formattingMenu: {
|
formattingMenu: {
|
||||||
i18n,
|
i18n,
|
||||||
isMenuEnabled: isFormattingEnabled,
|
isMenuEnabled: isFormattingEnabled,
|
||||||
|
|
96
ts/quill/auto-substitute-ascii-emojis/index.tsx
Normal file
96
ts/quill/auto-substitute-ascii-emojis/index.tsx
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type Quill from 'quill';
|
||||||
|
import Delta from 'quill-delta';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import type { EmojiData } from '../../components/emoji/lib';
|
||||||
|
import {
|
||||||
|
convertShortName,
|
||||||
|
convertShortNameToData,
|
||||||
|
} from '../../components/emoji/lib';
|
||||||
|
|
||||||
|
type AutoSubstituteAsciiEmojisOptions = {
|
||||||
|
skinTone: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const emojiMap: Record<string, string> = {
|
||||||
|
':)': 'slightly_smiling_face',
|
||||||
|
':-)': 'slightly_smiling_face',
|
||||||
|
':(': 'slightly_frowning_face',
|
||||||
|
':-(': 'slightly_frowning_face',
|
||||||
|
':D': 'smiley',
|
||||||
|
':-D': 'smiley',
|
||||||
|
':*': 'kissing',
|
||||||
|
':-*': 'kissing',
|
||||||
|
':P': 'stuck_out_tongue',
|
||||||
|
':-P': 'stuck_out_tongue',
|
||||||
|
';P': 'stuck_out_tongue_winking_eye',
|
||||||
|
';-P': 'stuck_out_tongue_winking_eye',
|
||||||
|
'D:': 'anguished',
|
||||||
|
"D-':": 'anguished',
|
||||||
|
':O': 'open_mouth',
|
||||||
|
':-O': 'open_mouth',
|
||||||
|
":'(": 'cry',
|
||||||
|
":'-(": 'cry',
|
||||||
|
':/': 'confused',
|
||||||
|
':-/': 'confused',
|
||||||
|
';)': 'wink',
|
||||||
|
';-)': 'wink',
|
||||||
|
'(Y)': '+1',
|
||||||
|
'(N)': '-1',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class AutoSubstituteAsciiEmojis {
|
||||||
|
options: AutoSubstituteAsciiEmojisOptions;
|
||||||
|
|
||||||
|
quill: Quill;
|
||||||
|
|
||||||
|
constructor(quill: Quill, options: AutoSubstituteAsciiEmojisOptions) {
|
||||||
|
this.options = options;
|
||||||
|
this.quill = quill;
|
||||||
|
|
||||||
|
this.quill.on(
|
||||||
|
'text-change',
|
||||||
|
_.debounce(() => this.onTextChange(), 100)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChange(): void {
|
||||||
|
const range = this.quill.getSelection();
|
||||||
|
|
||||||
|
if (!range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [blot, index] = this.quill.getLeaf(range.index);
|
||||||
|
|
||||||
|
if (blot !== undefined && blot.text !== undefined) {
|
||||||
|
const blotText: string = blot.text;
|
||||||
|
Object.entries(emojiMap).some(([textEmoji, emojiName]) => {
|
||||||
|
if (blotText.substring(0, index).endsWith(textEmoji)) {
|
||||||
|
const emojiData = convertShortNameToData(
|
||||||
|
emojiName,
|
||||||
|
this.options.skinTone
|
||||||
|
);
|
||||||
|
if (emojiData) {
|
||||||
|
this.insertEmoji(
|
||||||
|
emojiData,
|
||||||
|
range.index - textEmoji.length,
|
||||||
|
textEmoji.length
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertEmoji(emojiData: EmojiData, index: number, range: number): void {
|
||||||
|
const emoji = convertShortName(emojiData.short_name, this.options.skinTone);
|
||||||
|
const delta = new Delta().retain(index).delete(range).insert({ emoji });
|
||||||
|
this.quill.updateContents(delta, 'user');
|
||||||
|
this.quill.setSelection(index + 1, 0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue