Fix several composer bugs
This commit is contained in:
parent
e9642ae66f
commit
158ed4e455
4 changed files with 152 additions and 28 deletions
|
@ -8655,8 +8655,13 @@ button.module-image__border-overlay:focus {
|
|||
height: 100%;
|
||||
|
||||
.ql-editor {
|
||||
caret-color: transparent;
|
||||
padding: 0;
|
||||
|
||||
&--loaded {
|
||||
caret-color: auto;
|
||||
}
|
||||
|
||||
&.ql-blank::before {
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
|
|
@ -24,7 +24,11 @@ import {
|
|||
matchReactEmoji,
|
||||
} from '../quill/emoji/matchers';
|
||||
import { matchMention } from '../quill/mentions/matchers';
|
||||
import { MemberRepository, getDeltaToRemoveStaleMentions } from '../quill/util';
|
||||
import {
|
||||
MemberRepository,
|
||||
getDeltaToRemoveStaleMentions,
|
||||
getTextAndMentionsFromOps,
|
||||
} from '../quill/util';
|
||||
|
||||
Quill.register('formats/emoji', EmojiBlot);
|
||||
Quill.register('formats/mention', MentionBlot);
|
||||
|
@ -221,32 +225,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
|||
return ['', []];
|
||||
}
|
||||
|
||||
const mentions: Array<BodyRangeType> = [];
|
||||
|
||||
const text = ops.reduce((acc, { insert }) => {
|
||||
if (typeof insert === 'string') {
|
||||
return acc + insert;
|
||||
}
|
||||
|
||||
if (insert.emoji) {
|
||||
return acc + insert.emoji;
|
||||
}
|
||||
|
||||
if (insert.mention) {
|
||||
mentions.push({
|
||||
length: 1, // The length of `\uFFFC`
|
||||
mentionUuid: insert.mention.uuid,
|
||||
replacementText: insert.mention.title,
|
||||
start: acc.length,
|
||||
});
|
||||
|
||||
return `${acc}\uFFFC`;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, '');
|
||||
|
||||
return [text.trim(), mentions];
|
||||
return getTextAndMentionsFromOps(ops);
|
||||
};
|
||||
|
||||
const focus = () => {
|
||||
|
@ -368,7 +347,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (large) {
|
||||
if (propsRef.current.large) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -435,6 +414,26 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
|||
return true;
|
||||
};
|
||||
|
||||
const onCtrlA = () => {
|
||||
const quill = quillRef.current;
|
||||
|
||||
if (quill === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
quill.setSelection(0, 0);
|
||||
};
|
||||
|
||||
const onCtrlE = () => {
|
||||
const quill = quillRef.current;
|
||||
|
||||
if (quill === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
quill.setSelection(quill.getLength(), 0);
|
||||
};
|
||||
|
||||
const onChange = () => {
|
||||
const quill = quillRef.current;
|
||||
|
||||
|
@ -564,6 +563,8 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
|||
handler: onShortKeyEnter,
|
||||
},
|
||||
onEscape: { key: 27, handler: onEscape }, // 27 = Escape
|
||||
onCtrlA: { key: 65, ctrlKey: true, handler: onCtrlA }, // 65 = a
|
||||
onCtrlE: { key: 69, ctrlKey: true, handler: onCtrlE }, // 69 = e
|
||||
},
|
||||
},
|
||||
emojiCompletion: {
|
||||
|
@ -603,6 +604,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
|||
|
||||
setTimeout(() => {
|
||||
quill.setSelection(quill.getLength(), 0);
|
||||
quill.root.classList.add('ql-editor--loaded');
|
||||
}, 0);
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import Delta from 'quill-delta';
|
|||
import { DeltaOperation } from 'quill';
|
||||
|
||||
import { ConversationType } from '../state/ducks/conversations';
|
||||
import { BodyRangeType } from '../types/Util';
|
||||
|
||||
const FUSE_OPTIONS = {
|
||||
shouldSort: true,
|
||||
|
@ -15,6 +16,52 @@ const FUSE_OPTIONS = {
|
|||
keys: ['name', 'firstName', 'profileName', 'title'],
|
||||
};
|
||||
|
||||
export const getTextAndMentionsFromOps = (
|
||||
ops: Array<DeltaOperation>
|
||||
): [string, Array<BodyRangeType>] => {
|
||||
const mentions: Array<BodyRangeType> = [];
|
||||
|
||||
const text = ops.reduce((acc, { insert }, index) => {
|
||||
if (typeof insert === 'string') {
|
||||
let textToAdd;
|
||||
switch (index) {
|
||||
case 0: {
|
||||
textToAdd = insert.trimLeft();
|
||||
break;
|
||||
}
|
||||
case ops.length - 1: {
|
||||
textToAdd = insert.trimRight();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
textToAdd = insert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return acc + textToAdd;
|
||||
}
|
||||
|
||||
if (insert.emoji) {
|
||||
return acc + insert.emoji;
|
||||
}
|
||||
|
||||
if (insert.mention) {
|
||||
mentions.push({
|
||||
length: 1, // The length of `\uFFFC`
|
||||
mentionUuid: insert.mention.uuid,
|
||||
replacementText: insert.mention.title,
|
||||
start: acc.length,
|
||||
});
|
||||
|
||||
return `${acc}\uFFFC`;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, '');
|
||||
|
||||
return [text, mentions];
|
||||
};
|
||||
|
||||
export const getDeltaToRemoveStaleMentions = (
|
||||
ops: Array<DeltaOperation>,
|
||||
memberUuids: Array<string>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { assert } from 'chai';
|
|||
import {
|
||||
MemberRepository,
|
||||
getDeltaToRemoveStaleMentions,
|
||||
getTextAndMentionsFromOps,
|
||||
} from '../../quill/util';
|
||||
import { ConversationType } from '../../state/ducks/conversations';
|
||||
|
||||
|
@ -162,3 +163,72 @@ describe('getDeltaToRemoveStaleMentions', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTextAndMentionsFromOps', () => {
|
||||
describe('given only text', () => {
|
||||
it('returns only text trimmed', () => {
|
||||
const ops = [{ insert: ' The ' }, { insert: ' text ' }];
|
||||
const [resultText, resultMentions] = getTextAndMentionsFromOps(ops);
|
||||
assert.equal(resultText, 'The text');
|
||||
assert.equal(resultMentions.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given text, emoji, and mentions', () => {
|
||||
it('returns the trimmed text with placeholders and mentions', () => {
|
||||
const ops = [
|
||||
{
|
||||
insert: {
|
||||
emoji: '😂',
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: ' wow, funny, ',
|
||||
},
|
||||
{
|
||||
insert: {
|
||||
mention: {
|
||||
uuid: 'abcdef',
|
||||
title: '@fred',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const [resultText, resultMentions] = getTextAndMentionsFromOps(ops);
|
||||
assert.equal(resultText, '😂 wow, funny, \uFFFC');
|
||||
assert.deepEqual(resultMentions, [
|
||||
{
|
||||
length: 1,
|
||||
mentionUuid: 'abcdef',
|
||||
replacementText: '@fred',
|
||||
start: 15,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given only mentions', () => {
|
||||
it('returns the trimmed text with placeholders and mentions', () => {
|
||||
const ops = [
|
||||
{
|
||||
insert: {
|
||||
mention: {
|
||||
uuid: 'abcdef',
|
||||
title: '@fred',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const [resultText, resultMentions] = getTextAndMentionsFromOps(ops);
|
||||
assert.equal(resultText, '\uFFFC');
|
||||
assert.deepEqual(resultMentions, [
|
||||
{
|
||||
length: 1,
|
||||
mentionUuid: 'abcdef',
|
||||
replacementText: '@fred',
|
||||
start: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue