Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] Rich Text loop when autocompleting text on iOS #1702

Merged
merged 13 commits into from
Jan 7, 2020

Conversation

dratwas
Copy link
Contributor

@dratwas dratwas commented Dec 19, 2019

Fixes #
Gutenberg PR: WordPress/gutenberg#19240
Fixes: #1696

In this PR I created a local variable for focus state instead of using TextInputState, thanks to that we do not call focus() in onFocus which was a bit weird. I also pass the latest native value to onBlur event to update JS value if that is not correct.

PR submission checklist:

  • I have considered adding unit tests where possible.
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

@mchowning
Copy link
Contributor

Do we have two PRs fixing the same issue? cc: @koke

#1698

@pinarol
Copy link
Contributor

pinarol commented Dec 19, 2019

Yes we do @mchowning, we are trying both approaches :)

@pinarol
Copy link
Contributor

pinarol commented Dec 19, 2019

I realized that this PR is breaking auto-scroll on iOS, because keyboard-aware-scroll-view trusts TextInputState to find out the currently focused TextInput and then it adjusts the scroll position accordingly.

This is PR is making the global TextInputState not trustable anymore so it can have lots of other side effects as well.

So how about we just add onBlur fix onto @koke’s PR?

@dratwas
Copy link
Contributor Author

dratwas commented Dec 20, 2019

Yeah, right we need the TextInputState. However, I still think we should change the way we use it.

The paint point is that there is no method setCurrentlyFocusedID and we need to call TextInputState.focusTextInput in _onFocus -> _onPress instead.
https://github.com/wordpress-mobile/gutenberg-mobile/blob/develop/react-native-aztec/src/AztecView.js#L144
The TextInputState.focusTextInput sets currentlyFocusedID for us but also calls UIManager.focus which tries to focus our input.
https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInputState.js#L52
We do the same for _onBlur. We call TextInputState.blurTextInput which clears the currentlyFocusedID but also tries to blur the input.
https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInputState.js#L73

My point is I think there is no reason to call focus in _onFocus callback and blur in _onBlur callback. We should only set the correct ID since we already know that the input is focused/blurred.

Let's imagine this kind of scenario:

  • Focus input and write something as we did with autocorrection (with different length)
  • Click on "Move up" button - blur() is called
  • onSelectionChange() is called because suggestion has a different length than original text - focus() is called
  • _onBlur() is called - remember that in the meantime the focus() was called and input is focused and in the current implementation we call blur() in _onBlur()
  • _onFocus() is called - like above - the blur() was called in _onBlur() so input is blurred and we call focus() in _onFocus()
  • _onBlur() is called - input is focused - we call one more time blur()
    -_onFocus() is called - input is blurred - we call focus()

And the loop starts. It's because we call blur() in _onBlur() and focus() in onFocus() instead of setting the correct currentlyFocusedID w/o focusing/bluring in TextInputState.

I added this function to TextInputState and used it just to check if it will fix these issues and seems like yes.

function setCurrentlyFocusedFieldID(textFieldID: number) {
  if (textFieldID != currentlyFocusedID) {
    currentlyFocusedID = textFieldID;
  }
}

Good news is that there are actually functions blurField and focusField on the master branch of react-native and they do what we need 🙂 https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInputState.js#L31-L41

@koke
Copy link
Member

koke commented Dec 20, 2019

Those new methods in react-native are nice and will likely make this more robust, but I think that today's problem lies in here:

onSelectionChange() is called because suggestion has a different length than original text - focus() is called

A change on selection shouldn't trigger a change in focus. This kind of makes sense on the web since selection is global across the whole document, but on native, selection is specific to each view, so that's making things confusing:

  • The selection changes in the text view because of the autocorrection
  • onSelectionChange() is called on RichText because the selection has changed
  • Normally, RichText would propagate that to the store with this.props.onSelectionChange, which is the other kind of selection global to the document. The store then sees the new selection and understands that the selection is back on the text view, which ends up in it requesting focus again.

We still want to receive the first onSelectionChange() to keep the internal state of RichText updated, but we don't want to update the global selection if that's not the currently selected view.

@dratwas
Copy link
Contributor Author

dratwas commented Dec 20, 2019

Make sense. I reverted those changes related to the TextInputState and added registerInput and unregisterInput. I also cherry-picked @koke's changes in gutenberg PR WordPress/gutenberg#19240

@@ -34,6 +34,14 @@ class AztecView extends React.Component {
...TextViewPropTypes, // include the default view properties
}

componentDidMount() {
TextInputState.registerInput(ReactNative.findNodeHandle(this))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain a bit how this registerInput/unregisterInput works and how do they contribute to the solution?

Copy link
Contributor Author

@dratwas dratwas Dec 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I don't think that they are needed in that solution but I added it because I realized that we don't do that at this moment. They add or remove fieldID to/from Set of inputs in TextInputState
https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInputState.js#L85-L95

W/o this the false will be always returned when isTextInput is called with Aztec field ID
https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInputState.js#L93-L95

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds really nice but I am a bit afraid about consequences. We don't know who is calling isTextInput and what could be done differently if it turns true this time. So I'd avoid adding it unless we have enough context. I am just trying to be extra cautious since this has been a pretty fragile component so far.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I understand your worries so I will remove it then :)

@pinarol
Copy link
Contributor

pinarol commented Dec 20, 2019

the new solution looks a lot better but I come across with a keyboard close button issue. It is not working in certain scenarios:

  • create a new post
  • write something
  • tap keyboard close button

keyboard-close3

  • open a post with media text
  • type something on text area
  • delete the text
  • tap delete button once again so the paragraph block is also deleted
  • focus on text area again
  • tap keyboard close button

keyboard-close

@pinarol
Copy link
Contributor

pinarol commented Dec 20, 2019

But the issues related to focus loop on auto correct is fixed.

I tested both cases:

text from autocorrection has the same length as written text:

  • type something that auto-correct wants to correct
  • make sure suggested text has same length
  • tap move down
  • see that auto-correct made the change
  • switch to html mode
  • verify text is matching ✅

text from autocorrection has a different length as written text

  • type something that auto-correct wants to correct
  • make sure suggested text has different length
  • tap the image
  • see that auto-correct made the change
  • verify there's no focus loop ✅

@pinarol
Copy link
Contributor

pinarol commented Dec 20, 2019

The issue about keyboard close button is fixed! 🎉

Copy link
Contributor

@pinarol pinarol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!
Tested with steps here on both WPAndroid and WPiOS.

@dratwas dratwas changed the base branch from develop to release/1.20.0 January 7, 2020 09:24
@dratwas dratwas force-pushed the fix/rich-text-loop branch from a717073 to 3d3fb92 Compare January 7, 2020 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants