Support backspace after emoji completion

Co-authored-by: Jamie Kyle <jamie@signal.org>
This commit is contained in:
Fedor Indutny 2024-05-28 19:49:49 -07:00 committed by GitHub
parent 85a75cb28f
commit 4ec69ee3a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 62 additions and 46 deletions

View file

@ -66,7 +66,7 @@ export class AutoSubstituteAsciiEmojis {
}
onTextChange(): void {
if (!window.storage.get('autoConvertEmoji', false)) {
if (!window.storage.get('autoConvertEmoji', true)) {
return;
}

View file

@ -27,6 +27,14 @@ type EmojiPickerOptions = {
search: SearchFnType;
};
export type InsertEmojiOptionsType = Readonly<{
shortName: string;
index: number;
range: number;
withTrailingSpace?: boolean;
justPressedColon?: boolean;
}>;
export class EmojiCompletion {
results: Array<string>;
@ -84,10 +92,13 @@ export class EmojiCompletion {
() => this.onTextChange(true)
);
this.quill.on(
'text-change',
_.debounce(() => this.onTextChange(), 100)
);
const debouncedOnTextChange = _.debounce(() => this.onTextChange(), 100);
this.quill.on('text-change', (_now, _before, source) => {
if (source === 'user') {
debouncedOnTextChange();
}
});
this.quill.on('selection-change', this.onSelectionChange.bind(this));
}
@ -139,11 +150,12 @@ export class EmojiCompletion {
if (isShortName(leftTokenText)) {
const numberOfColons = isSelfClosing ? 2 : 1;
this.insertEmoji(
leftTokenText,
range.index - leftTokenText.length - numberOfColons,
leftTokenText.length + numberOfColons
);
this.insertEmoji({
shortName: leftTokenText,
index: range.index - leftTokenText.length - numberOfColons,
range: leftTokenText.length + numberOfColons,
justPressedColon,
});
return INTERCEPT;
}
this.reset();
@ -155,11 +167,12 @@ export class EmojiCompletion {
const tokenText = leftTokenText + rightTokenText;
if (isShortName(tokenText)) {
this.insertEmoji(
tokenText,
range.index - leftTokenText.length - 1,
tokenText.length + 2
);
this.insertEmoji({
shortName: tokenText,
index: range.index - leftTokenText.length - 1,
range: tokenText.length + 2,
justPressedColon,
});
return INTERCEPT;
}
}
@ -212,27 +225,33 @@ export class EmojiCompletion {
const [, tokenText] = tokenTextMatch;
this.insertEmoji(
emoji,
range.index - tokenText.length - 1,
tokenText.length + 1,
true
);
this.insertEmoji({
shortName: emoji,
index: range.index - tokenText.length - 1,
range: tokenText.length + 1,
withTrailingSpace: true,
});
}
insertEmoji(
shortName: string,
index: number,
range: number,
withTrailingSpace = false
): void {
insertEmoji({
shortName,
index,
range,
withTrailingSpace = false,
justPressedColon = false,
}: InsertEmojiOptionsType): void {
const emoji = convertShortName(shortName, this.options.skinTone);
let source = this.quill.getText(index, range);
if (justPressedColon) {
source += ':';
}
const delta = new Delta()
.retain(index)
.delete(range)
.insert({
emoji: { value: emoji },
emoji: { value: emoji, source },
});
if (withTrailingSpace) {

View file

@ -5,6 +5,7 @@ import { assert } from 'chai';
import sinon from 'sinon';
import { EmojiCompletion } from '../../../quill/emoji/completion';
import type { InsertEmojiOptionsType } from '../../../quill/emoji/completion';
import { createSearch } from '../../../components/emoji/lib';
describe('emojiCompletion', () => {
@ -15,6 +16,7 @@ describe('emojiCompletion', () => {
beforeEach(function (this: Mocha.Context) {
mockQuill = {
getLeaf: sinon.stub(),
getText: sinon.stub(),
getSelection: sinon.stub(),
keyboard: {
addBinding: sinon.stub(),
@ -55,10 +57,7 @@ describe('emojiCompletion', () => {
});
describe('onTextChange', () => {
let insertEmojiStub: sinon.SinonStub<
[string, number, number, (boolean | undefined)?],
void
>;
let insertEmojiStub: sinon.SinonStub<[InsertEmojiOptionsType], void>;
beforeEach(() => {
emojiCompletion.results = ['joy'];
@ -194,9 +193,9 @@ describe('emojiCompletion', () => {
});
it('inserts the emoji at the current cursor position', () => {
const [emoji, index, range] = insertEmojiStub.args[0];
const [{ shortName, index, range }] = insertEmojiStub.args[0];
assert.equal(emoji, 'smile');
assert.equal(shortName, 'smile');
assert.equal(index, 0);
assert.equal(range, 7);
});
@ -223,9 +222,9 @@ describe('emojiCompletion', () => {
});
it('inserts the emoji at the current cursor position', () => {
const [emoji, index, range] = insertEmojiStub.args[0];
const [{ shortName, index, range }] = insertEmojiStub.args[0];
assert.equal(emoji, 'smile');
assert.equal(shortName, 'smile');
assert.equal(index, 7);
assert.equal(range, 7);
});
@ -283,9 +282,9 @@ describe('emojiCompletion', () => {
});
it('inserts the emoji at the current cursor position', () => {
const [emoji, index, range] = insertEmojiStub.args[0];
const [{ shortName, index, range }] = insertEmojiStub.args[0];
assert.equal(emoji, 'smile');
assert.equal(shortName, 'smile');
assert.equal(index, 0);
assert.equal(range, validEmoji.length);
});
@ -332,9 +331,9 @@ describe('emojiCompletion', () => {
});
it('inserts the emoji at the current cursor position', () => {
const [emoji, index, range] = insertEmojiStub.args[0];
const [{ shortName, index, range }] = insertEmojiStub.args[0];
assert.equal(emoji, 'smile');
assert.equal(shortName, 'smile');
assert.equal(index, 0);
assert.equal(range, 6);
});
@ -347,10 +346,7 @@ describe('emojiCompletion', () => {
});
describe('completeEmoji', () => {
let insertEmojiStub: sinon.SinonStub<
[string, number, number, (boolean | undefined)?],
void
>;
let insertEmojiStub: sinon.SinonStub<[InsertEmojiOptionsType], void>;
beforeEach(() => {
emojiCompletion.results = ['smile', 'smile_cat'];
@ -377,9 +373,10 @@ describe('emojiCompletion', () => {
});
it('inserts the currently selected emoji at the current cursor position', () => {
const [emoji, insertIndex, range] = insertEmojiStub.args[0];
const [{ shortName, index: insertIndex, range }] =
insertEmojiStub.args[0];
assert.equal(emoji, 'smile_cat');
assert.equal(shortName, 'smile_cat');
assert.equal(insertIndex, 0);
assert.equal(range, text.length);
});