Skip to content

Commit

Permalink
Do not eat taps/clicks in ScrollView when soft-keyboard is detached f…
Browse files Browse the repository at this point in the history
…rom viewport

Summary:
If currently focused on a TextInput, clicking an item in a ScrollView takes two clicks.

This is because of `keyboardShouldPersistTaps`, which will fire despite a lack of keyboard events on Android due to special-casing.

This behavior is jarring in scenarios like VR where the soft keyboard is detached from the application. This change avoids eating taps, in this case, where a soft keyboard is open but not inset.

Reviewed By: genkikondo

Differential Revision: D38529237

fbshipit-source-id: a10c5dbf04e6288e0e9e0c805215054bc883339f
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Aug 23, 2022
1 parent f5f6896 commit fd1e82a
Showing 1 changed file with 29 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Libraries/Components/ScrollView/ScrollView.js
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,11 @@ class ScrollView extends React.Component<Props, State> {
return false;
}

// Let presses through if the soft keyboard is detached from the viewport
if (this._softKeyboardIsDetached()) {
return false;
}

if (
keyboardNeverPersistTaps &&
this._keyboardIsDismissible() &&
Expand Down Expand Up @@ -1548,6 +1553,15 @@ class ScrollView extends React.Component<Props, State> {
return hasFocusedTextInput && softKeyboardMayBeOpen;
};

/**
* Whether an open soft keyboard is present which does not overlap the
* viewport. E.g. for a VR soft-keyboard which is detached from the app
* viewport.
*/
_softKeyboardIsDetached: () => boolean = () => {
return this._keyboardMetrics != null && this._keyboardMetrics.height === 0;
};

/**
* Invoke this from an `onTouchEnd` event.
*
Expand All @@ -1556,6 +1570,21 @@ class ScrollView extends React.Component<Props, State> {
_handleTouchEnd: (e: PressEvent) => void = (e: PressEvent) => {
const nativeEvent = e.nativeEvent;
this._isTouching = nativeEvent.touches.length !== 0;

const {keyboardShouldPersistTaps} = this.props;
const keyboardNeverPersistsTaps =
!keyboardShouldPersistTaps || keyboardShouldPersistTaps === 'never';

// Dismiss the keyboard now if we didn't become responder in capture phase
// to eat presses, but still want to dismiss on interaction.
if (
this._softKeyboardIsDetached() &&
this._keyboardIsDismissible() &&
keyboardNeverPersistsTaps
) {
TextInputState.blurTextInput(TextInputState.currentlyFocusedInput());
}

this.props.onTouchEnd && this.props.onTouchEnd(e);
};

Expand Down

0 comments on commit fd1e82a

Please sign in to comment.