Skip to content

Commit

Permalink
Request #7668255: Editor breaks on some Android 7+ phones
Browse files Browse the repository at this point in the history
Also replace patch facebookarchive#572
in favor of facebookarchive#907
  • Loading branch information
robbertbrak committed Apr 26, 2018
1 parent a90a693 commit 621c66a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 22 deletions.
18 changes: 5 additions & 13 deletions src/component/base/DraftEditor.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ const handlerMap = {
};

type State = {
containerKey: number,
contentsKey: number,
contentsKey: number
};

/**
Expand Down Expand Up @@ -118,7 +117,7 @@ class DraftEditor extends React.Component {
blur: () => void;
setMode: (mode: DraftEditorModes) => void;
exitCurrentMode: () => void;
restoreEditorDOM: (scrollPosition: DraftScrollPosition, keyToUse: string) => void;
restoreEditorDOM: (scrollPosition: DraftScrollPosition) => void;
setRenderGuard: () => void;
removeRenderGuard: () => void;
setClipboard: (clipboard?: BlockMap) => void;
Expand Down Expand Up @@ -177,7 +176,7 @@ class DraftEditor extends React.Component {
this.onDragLeave = this._onDragLeave.bind(this);

// See `_restoreEditorDOM()`.
this.state = {containerKey: 0, contentsKey: 0};
this.state = {contentsKey: 0};
}

/**
Expand Down Expand Up @@ -235,7 +234,6 @@ class DraftEditor extends React.Component {
<div className={rootClass}>
{this._renderPlaceholder()}
<div
key={'editor' + this.state.containerKey}
className={cx('DraftEditor/editorContainer')}
ref="editorContainer">
<div
Expand Down Expand Up @@ -395,14 +393,8 @@ class DraftEditor extends React.Component {
* state (cut command, IME) and we want to make sure that reconciliation
* occurs on a version of the DOM that is synchronized with our EditorState.
*/
_restoreEditorDOM(scrollPosition?: DraftScrollPosition, keyToUse?: string): void {
let newState = {};
if (typeof(keyToUse) === "undefined") {
keyToUse = "containerKey";
}
newState[keyToUse] = this.state[keyToUse] + 1;

this.setState(newState, () => {
_restoreEditorDOM(scrollPosition?: DraftScrollPosition): void {
this.setState({contentsKey: this.state.contentsKey + 1}, () => {
this._focus(scrollPosition);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const RESOLVE_DELAY = 20;
* and it simplifies state management with respect to the DraftEditor component.
*/
let resolved = false;
let initial = true;
let stillComposing = false;
let textInputData = '';
let formerTextInputData = '';
Expand Down Expand Up @@ -193,9 +192,16 @@ var DraftEditorCompositionHandler = {
}

// In Android 7+ there is (unlike Android 6-) no textInput event just before ending composition.
// Rely on the data in the composition end event instead.
if (isAndroid7 && e.data && !textInputData) {
textInputData = e.data;
// Rely on native handling instead.
if (isAndroid7) {
if (isSelectionAtLeafStart(this.props.editorState)) {
this.restoreEditorDOM(undefined);
}
this.exitCurrentMode();
this.removeRenderGuard();
this._onInput(e);
textInputData = ''; // Reset the composition handler.
return;
}

charInCompStart = '';
Expand Down Expand Up @@ -268,7 +274,6 @@ var DraftEditorCompositionHandler = {
}

resolved = true;
initial = true;

const wasKoreanOnIE = isKoreanOnIE;
isKoreanOnIE = false;
Expand Down Expand Up @@ -301,7 +306,7 @@ var DraftEditorCompositionHandler = {
);

if (mustReset) {
this.restoreEditorDOM(undefined, ((composedChars && composedChars.length > 0) ? "contentsKey" : "containerKey"));
this.restoreEditorDOM(undefined);
}

this.exitCurrentMode();
Expand Down
38 changes: 35 additions & 3 deletions src/component/handlers/edit/editOnInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ var EditorState = require('EditorState');
var Entity = require('DraftEntity');
var UserAgent = require('UserAgent');

var keyCommandPlainBackspace = require('keyCommandPlainBackspace');

var findAncestorOffsetKey = require('findAncestorOffsetKey');
var nullthrows = require('nullthrows');

Expand All @@ -37,9 +39,14 @@ var DOUBLE_NEWLINE = '\n\n';
* when an `input` change leads to a DOM/model mismatch, the change should be
* due to a spellcheck change, and we can incorporate it into our model.
*/
function editOnInput(): void {
function editOnInput(e): void {
var {editorState} = this.props;
var domSelection = global.getSelection();

if (editorState.isInCompositionMode()) {
editorState = EditorState.set(editorState, {inCompositionMode: false});
}

var {anchorNode, isCollapsed} = domSelection;
if (anchorNode.nodeType !== Node.TEXT_NODE && anchorNode.nodeType !== Node.ELEMENT_NODE) {
return;
Expand Down Expand Up @@ -67,7 +74,7 @@ function editOnInput(): void {
}

var domText = anchorNode.textContent;
var {editorState} = this.props;

var offsetKey = nullthrows(findAncestorOffsetKey(anchorNode));
var {blockKey, decoratorKey, leafKey} = DraftOffsetKey.decode(offsetKey);

Expand All @@ -77,6 +84,32 @@ function editOnInput(): void {

var content = editorState.getCurrentContent();
var block = content.getBlockForKey(blockKey);
var selection = editorState.getSelection();

// Special case for backwards deletion on Android.
// Pressing the backspace key on the soft keyboard does not always trigger a 'backspace' keydown event,

This comment has been minimized.

Copy link
@robbertbrak

robbertbrak Apr 26, 2018

Author Owner
// so we try to detect it by comparing the editor selection (as it was before the input event)
// with the actual cursor position.
// If the cursor is in a smaller block than before, or in the same block but in a smaller leaf than before,
// we perform a backspace command to sync the editorstate with the DOM
if (isAndroid && e && e.nativeEvent.type === 'input') {
var blocksArray = content.getBlocksAsArray();
var isDeletion = blocksArray.indexOf(block) < blocksArray.indexOf(content.getBlockForKey(selection.getEndKey()))
|| end < selection.getEndOffset();
if (isDeletion) {
this.restoreEditorDOM(undefined);
editorState = EditorState.set(editorState, {
selection: selection.merge({
anchorKey: blockKey, anchorOffset: start + domSelection.anchorOffset,
focusKey: selection.getEndKey(), focusOffset: selection.getEndOffset(),
isBackward: false
})
});
this.update(keyCommandPlainBackspace(editorState));
return;
}
}

var modelText = block.getText().slice(start, end);

// Special-case soft newlines here. If the DOM text ends in a soft newline,
Expand All @@ -92,7 +125,6 @@ function editOnInput(): void {
return;
}

var selection = editorState.getSelection();

// We'll replace the entire leaf with the text content of the target.
var targetRange = selection.merge({
Expand Down

0 comments on commit 621c66a

Please sign in to comment.