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%;
|
height: 100%;
|
||||||
|
|
||||||
.ql-editor {
|
.ql-editor {
|
||||||
|
caret-color: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
&--loaded {
|
||||||
|
caret-color: auto;
|
||||||
|
}
|
||||||
|
|
||||||
&.ql-blank::before {
|
&.ql-blank::before {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
|
@ -24,7 +24,11 @@ import {
|
||||||
matchReactEmoji,
|
matchReactEmoji,
|
||||||
} from '../quill/emoji/matchers';
|
} from '../quill/emoji/matchers';
|
||||||
import { matchMention } from '../quill/mentions/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/emoji', EmojiBlot);
|
||||||
Quill.register('formats/mention', MentionBlot);
|
Quill.register('formats/mention', MentionBlot);
|
||||||
|
@ -221,32 +225,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
||||||
return ['', []];
|
return ['', []];
|
||||||
}
|
}
|
||||||
|
|
||||||
const mentions: Array<BodyRangeType> = [];
|
return getTextAndMentionsFromOps(ops);
|
||||||
|
|
||||||
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];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const focus = () => {
|
const focus = () => {
|
||||||
|
@ -368,7 +347,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (large) {
|
if (propsRef.current.large) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,6 +414,26 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
||||||
return true;
|
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 onChange = () => {
|
||||||
const quill = quillRef.current;
|
const quill = quillRef.current;
|
||||||
|
|
||||||
|
@ -564,6 +563,8 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
||||||
handler: onShortKeyEnter,
|
handler: onShortKeyEnter,
|
||||||
},
|
},
|
||||||
onEscape: { key: 27, handler: onEscape }, // 27 = Escape
|
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: {
|
emojiCompletion: {
|
||||||
|
@ -603,6 +604,7 @@ export const CompositionInput: React.ComponentType<Props> = props => {
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
quill.setSelection(quill.getLength(), 0);
|
quill.setSelection(quill.getLength(), 0);
|
||||||
|
quill.root.classList.add('ql-editor--loaded');
|
||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Delta from 'quill-delta';
|
||||||
import { DeltaOperation } from 'quill';
|
import { DeltaOperation } from 'quill';
|
||||||
|
|
||||||
import { ConversationType } from '../state/ducks/conversations';
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
import { BodyRangeType } from '../types/Util';
|
||||||
|
|
||||||
const FUSE_OPTIONS = {
|
const FUSE_OPTIONS = {
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
|
@ -15,6 +16,52 @@ const FUSE_OPTIONS = {
|
||||||
keys: ['name', 'firstName', 'profileName', 'title'],
|
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 = (
|
export const getDeltaToRemoveStaleMentions = (
|
||||||
ops: Array<DeltaOperation>,
|
ops: Array<DeltaOperation>,
|
||||||
memberUuids: Array<string>
|
memberUuids: Array<string>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { assert } from 'chai';
|
||||||
import {
|
import {
|
||||||
MemberRepository,
|
MemberRepository,
|
||||||
getDeltaToRemoveStaleMentions,
|
getDeltaToRemoveStaleMentions,
|
||||||
|
getTextAndMentionsFromOps,
|
||||||
} from '../../quill/util';
|
} from '../../quill/util';
|
||||||
import { ConversationType } from '../../state/ducks/conversations';
|
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…
Add table
Add a link
Reference in a new issue