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.
pull/21833/head
Bruno Windels 2019-08-29 13:56:21 +02:00
parent 599fccd9ce
commit 891ccf0f4c
1 changed files with 31 additions and 4 deletions

View File

@ -75,6 +75,7 @@ export default class BasicMessageEditor extends React.Component {
this._editorRef = null; this._editorRef = null;
this._autocompleteRef = null; this._autocompleteRef = null;
this._modifiedFlag = false; this._modifiedFlag = false;
this._isIMEComposing = false;
} }
_replaceEmoticon = (caretPosition, inputType, diff) => { _replaceEmoticon = (caretPosition, inputType, diff) => {
@ -119,11 +120,9 @@ export default class BasicMessageEditor extends React.Component {
if (this.props.placeholder) { if (this.props.placeholder) {
const {isEmpty} = this.props.model; const {isEmpty} = this.props.model;
if (isEmpty) { if (isEmpty) {
this._editorRef.style.setProperty("--placeholder", `'${this.props.placeholder}'`); this._showPlaceholder();
this._editorRef.classList.add("mx_BasicMessageComposer_inputEmpty");
} else { } else {
this._editorRef.classList.remove("mx_BasicMessageComposer_inputEmpty"); this._hidePlaceholder();
this._editorRef.style.removeProperty("--placeholder");
} }
} }
this.setState({autoComplete: this.props.model.autoComplete}); 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) => { _onInput = (event) => {
// ignore any input while doing IME compositions
if (this._isIMEComposing) {
return;
}
this._modifiedFlag = true; this._modifiedFlag = true;
const sel = document.getSelection(); const sel = document.getSelection();
const {caret, text} = getCaretOffsetAndText(this._editorRef, sel); const {caret, text} = getCaretOffsetAndText(this._editorRef, sel);
@ -323,6 +346,8 @@ export default class BasicMessageEditor extends React.Component {
componentWillUnmount() { componentWillUnmount() {
this._editorRef.removeEventListener("input", this._onInput, true); this._editorRef.removeEventListener("input", this._onInput, true);
this._editorRef.removeEventListener("compositionstart", this._onCompositionStart, true);
this._editorRef.removeEventListener("compositionend", this._onCompositionEnd, true);
} }
componentDidMount() { componentDidMount() {
@ -344,6 +369,8 @@ export default class BasicMessageEditor extends React.Component {
// attach input listener by hand so React doesn't proxy the events, // attach input listener by hand so React doesn't proxy the events,
// as the proxied event doesn't support inputType, which we need. // as the proxied event doesn't support inputType, which we need.
this._editorRef.addEventListener("input", this._onInput, true); this._editorRef.addEventListener("input", this._onInput, true);
this._editorRef.addEventListener("compositionstart", this._onCompositionStart, true);
this._editorRef.addEventListener("compositionend", this._onCompositionEnd, true);
this._editorRef.focus(); this._editorRef.focus();
} }