Prevent >64k text in composition box; truncate too-large drafts

This commit is contained in:
Scott Nonnenberg 2019-09-13 14:54:19 -07:00
parent 87ae65c852
commit 095cd884a2
5 changed files with 116 additions and 7 deletions

View file

@ -29,6 +29,7 @@ import {
} from './emoji/lib';
import { LocalizerType } from '../types/Util';
const MAX_LENGTH = 64 * 1024;
const colonsRegex = /(?:^|\s):[a-z0-9-_+]+:?/gi;
const triggerEmojiRegex = /^(?:[-+]\d|[a-z]{2})/i;
@ -43,6 +44,7 @@ export type Props = {
onDirtyChange?(dirty: boolean): unknown;
onEditorStateChange?(messageText: string, caretLocation: number): unknown;
onEditorSizeChange?(rect: ContentRect): unknown;
onTextTooLong(): unknown;
onPickEmoji(o: EmojiPickDataType): unknown;
onSubmit(message: string): unknown;
};
@ -78,6 +80,43 @@ function getTrimmedMatchAtIndex(str: string, index: number, pattern: RegExp) {
return null;
}
function getLengthOfSelectedText(state: EditorState): number {
const currentSelection = state.getSelection();
let length = 0;
const currentContent = state.getCurrentContent();
const startKey = currentSelection.getStartKey();
const endKey = currentSelection.getEndKey();
const startBlock = currentContent.getBlockForKey(startKey);
const isStartAndEndBlockAreTheSame = startKey === endKey;
const startBlockTextLength = startBlock.getLength();
const startSelectedTextLength =
startBlockTextLength - currentSelection.getStartOffset();
const endSelectedTextLength = currentSelection.getEndOffset();
const keyAfterEnd = currentContent.getKeyAfter(endKey);
if (isStartAndEndBlockAreTheSame) {
length +=
currentSelection.getEndOffset() - currentSelection.getStartOffset();
} else {
let currentKey = startKey;
while (currentKey && currentKey !== keyAfterEnd) {
if (currentKey === startKey) {
length += startSelectedTextLength + 1;
} else if (currentKey === endKey) {
length += endSelectedTextLength;
} else {
length += currentContent.getBlockForKey(currentKey).getLength() + 1;
}
currentKey = currentContent.getKeyAfter(currentKey);
}
}
return length;
}
function getWordAtIndex(str: string, index: number) {
const start = str
.slice(0, index + 1)
@ -172,6 +211,7 @@ export const CompositionInput = ({
onDirtyChange,
onEditorStateChange,
onEditorSizeChange,
onTextTooLong,
onPickEmoji,
onSubmit,
skinTone,
@ -298,6 +338,51 @@ export const CompositionInput = ({
]
);
const handleBeforeInput = React.useCallback(
(): DraftHandleValue => {
if (!editorStateRef.current) {
return 'not-handled';
}
const editorState = editorStateRef.current;
const plainText = editorState.getCurrentContent().getPlainText();
const selectedTextLength = getLengthOfSelectedText(editorState);
if (plainText.length - selectedTextLength > MAX_LENGTH - 1) {
onTextTooLong();
return 'handled';
}
return 'not-handled';
},
[onTextTooLong, editorStateRef]
);
const handlePastedText = React.useCallback(
(pastedText: string): DraftHandleValue => {
if (!editorStateRef.current) {
return 'not-handled';
}
const editorState = editorStateRef.current;
const plainText = editorState.getCurrentContent().getPlainText();
const selectedTextLength = getLengthOfSelectedText(editorState);
if (
plainText.length + pastedText.length - selectedTextLength >
MAX_LENGTH
) {
onTextTooLong();
return 'handled';
}
return 'not-handled';
},
[onTextTooLong, editorStateRef]
);
const resetEditorState = React.useCallback(
() => {
const newEmptyState = EditorState.createEmpty(compositeDecorator);
@ -694,6 +779,8 @@ export const CompositionInput = ({
onEscape={handleEscapeKey}
onTab={onTab}
handleKeyCommand={handleEditorCommand}
handleBeforeInput={handleBeforeInput}
handlePastedText={handlePastedText}
keyBindingFn={editorKeybindingFn}
spellCheck={true}
stripPastedStyles={true}