From 891ccf0f4ce0850ffa596bfa50fa380263072be4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 29 Aug 2019 13:56:21 +0200 Subject: [PATCH] don't update model while doing IME composition this prevents the composition from being disrupted because the DOM is modified, and also complete compositions are added to the undo history like this. --- .../views/rooms/BasicMessageComposer.js | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 49815c6f23..a72f00c28f 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -75,6 +75,7 @@ export default class BasicMessageEditor extends React.Component { this._editorRef = null; this._autocompleteRef = null; this._modifiedFlag = false; + this._isIMEComposing = false; } _replaceEmoticon = (caretPosition, inputType, diff) => { @@ -119,11 +120,9 @@ export default class BasicMessageEditor extends React.Component { if (this.props.placeholder) { const {isEmpty} = this.props.model; if (isEmpty) { - this._editorRef.style.setProperty("--placeholder", `'${this.props.placeholder}'`); - this._editorRef.classList.add("mx_BasicMessageComposer_inputEmpty"); + this._showPlaceholder(); } else { - this._editorRef.classList.remove("mx_BasicMessageComposer_inputEmpty"); - this._editorRef.style.removeProperty("--placeholder"); + this._hidePlaceholder(); } } this.setState({autoComplete: this.props.model.autoComplete}); @@ -135,7 +134,31 @@ export default class BasicMessageEditor extends React.Component { } } + _showPlaceholder() { + this._editorRef.style.setProperty("--placeholder", `'${this.props.placeholder}'`); + this._editorRef.classList.add("mx_BasicMessageComposer_inputEmpty"); + } + + _hidePlaceholder() { + this._editorRef.classList.remove("mx_BasicMessageComposer_inputEmpty"); + this._editorRef.style.removeProperty("--placeholder"); + } + + _onCompositionStart = (event) => { + this._isIMEComposing = true; + // even if the model is empty, the composition text shouldn't be mixed with the placeholder + this._hidePlaceholder(); + } + + _onCompositionEnd = (event) => { + this._isIMEComposing = false; + } + _onInput = (event) => { + // ignore any input while doing IME compositions + if (this._isIMEComposing) { + return; + } this._modifiedFlag = true; const sel = document.getSelection(); const {caret, text} = getCaretOffsetAndText(this._editorRef, sel); @@ -323,6 +346,8 @@ export default class BasicMessageEditor extends React.Component { componentWillUnmount() { this._editorRef.removeEventListener("input", this._onInput, true); + this._editorRef.removeEventListener("compositionstart", this._onCompositionStart, true); + this._editorRef.removeEventListener("compositionend", this._onCompositionEnd, true); } componentDidMount() { @@ -344,6 +369,8 @@ export default class BasicMessageEditor extends React.Component { // attach input listener by hand so React doesn't proxy the events, // as the proxied event doesn't support inputType, which we need. this._editorRef.addEventListener("input", this._onInput, true); + this._editorRef.addEventListener("compositionstart", this._onCompositionStart, true); + this._editorRef.addEventListener("compositionend", this._onCompositionEnd, true); this._editorRef.focus(); }