Skip to content

Commit

Permalink
fix: force dismiss keyboard if there was no gesture
Browse files Browse the repository at this point in the history
closes #9078
  • Loading branch information
satya164 committed Nov 21, 2020
1 parent b82a912 commit 14ac256
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 35 deletions.
36 changes: 22 additions & 14 deletions packages/stack/src/views/KeyboardManager.tsx
@@ -1,23 +1,25 @@
import * as React from 'react';
import { TextInput, Platform, Keyboard } from 'react-native';
import { TextInput, Keyboard, HostComponent } from 'react-native';

type Props = {
enabled: boolean;
children: (props: {
onPageChangeStart: () => void;
onPageChangeConfirm: () => void;
onPageChangeConfirm: (force: boolean) => void;
onPageChangeCancel: () => void;
}) => React.ReactNode;
};

type InputRef = React.ElementRef<HostComponent<unknown>> | undefined;

export default class KeyboardManager extends React.Component<Props> {
componentWillUnmount() {
this.clearKeyboardTimeout();
}

// Numeric id of the previously focused text input
// When a gesture didn't change the tab, we can restore the focused input with this
private previouslyFocusedTextInput: any | null = null;
private previouslyFocusedTextInput: InputRef = undefined;
private startTimestamp: number = 0;
private keyboardTimeout: any;

Expand All @@ -35,7 +37,8 @@ export default class KeyboardManager extends React.Component<Props> {

this.clearKeyboardTimeout();

const input: any = TextInput.State.currentlyFocusedInput
// @ts-expect-error: blurTextInput accepts both number and ref, but types say only ref
const input: InputRef = TextInput.State.currentlyFocusedInput
? TextInput.State.currentlyFocusedInput()
: TextInput.State.currentlyFocusedField();

Expand All @@ -49,25 +52,30 @@ export default class KeyboardManager extends React.Component<Props> {
this.startTimestamp = Date.now();
};

private handlePageChangeConfirm = () => {
private handlePageChangeConfirm = (force: boolean) => {
if (!this.props.enabled) {
return;
}

this.clearKeyboardTimeout();

const input = this.previouslyFocusedTextInput;

if (input) {
if (Platform.OS === 'android') {
Keyboard.dismiss();
} else {
if (force) {
// Always dismiss input, even if we don't have a ref to it
// We might not have the ref if onPageChangeStart was never called
// This can happen if page change was not from a gesture
Keyboard.dismiss();
} else {
const input = this.previouslyFocusedTextInput;

if (input) {
// Dismiss the keyboard only if an input was a focused before
// This makes sure we don't dismiss input on going back and focusing an input
TextInput.State.blurTextInput(input);
}
}

// Cleanup the ID on successful page change
this.previouslyFocusedTextInput = null;
this.previouslyFocusedTextInput = undefined;
};

private handlePageChangeCancel = () => {
Expand All @@ -91,11 +99,11 @@ export default class KeyboardManager extends React.Component<Props> {
if (Date.now() - this.startTimestamp < 100) {
this.keyboardTimeout = setTimeout(() => {
TextInput.State.focusTextInput(input);
this.previouslyFocusedTextInput = null;
this.previouslyFocusedTextInput = undefined;
}, 100);
} else {
TextInput.State.focusTextInput(input);
this.previouslyFocusedTextInput = null;
this.previouslyFocusedTextInput = undefined;
}
}
};
Expand Down
6 changes: 3 additions & 3 deletions packages/stack/src/views/Stack/Card.tsx
Expand Up @@ -41,7 +41,7 @@ type Props = ViewProps & {
gestureDirection: GestureDirection;
onOpen: () => void;
onClose: () => void;
onTransitionStart?: (props: { closing: boolean }) => void;
onTransition?: (props: { closing: boolean; gesture: boolean }) => void;
onGestureBegin?: () => void;
onGestureCanceled?: () => void;
onGestureEnd?: () => void;
Expand Down Expand Up @@ -177,7 +177,7 @@ export default class Card extends React.Component<Props> {
transitionSpec,
onOpen,
onClose,
onTransitionStart,
onTransition,
} = this.props;

const toValue = this.getAnimateToValue({
Expand All @@ -197,7 +197,7 @@ export default class Card extends React.Component<Props> {

clearTimeout(this.pendingGestureCallback);

onTransitionStart?.({ closing });
onTransition?.({ closing, gesture: velocity !== undefined });
animation(gesture, {
...spec.config,
velocity,
Expand Down
50 changes: 33 additions & 17 deletions packages/stack/src/views/Stack/CardContainer.tsx
Expand Up @@ -46,7 +46,7 @@ type Props = TransitionPreset & {
) => void;
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
onPageChangeStart?: () => void;
onPageChangeConfirm?: () => void;
onPageChangeConfirm?: (force: boolean) => void;
onPageChangeCancel?: () => void;
onGestureStart?: (props: { route: Route<string> }) => void;
onGestureEnd?: (props: { route: Route<string> }) => void;
Expand Down Expand Up @@ -116,42 +116,58 @@ function CardContainer({
scene,
transitionSpec,
}: Props) {
React.useEffect(() => {
onPageChangeConfirm?.();
}, [active, onPageChangeConfirm]);

const handleOpen = () => {
onTransitionEnd?.({ route: scene.descriptor.route }, false);
onOpenRoute({ route: scene.descriptor.route });
const { route } = scene.descriptor;

onTransitionEnd?.({ route }, false);
onOpenRoute({ route });
};

const handleClose = () => {
onTransitionEnd?.({ route: scene.descriptor.route }, true);
onCloseRoute({ route: scene.descriptor.route });
const { route } = scene.descriptor;

onTransitionEnd?.({ route }, true);
onCloseRoute({ route });
};

const handleGestureBegin = () => {
const { route } = scene.descriptor;

onPageChangeStart?.();
onGestureStart?.({ route: scene.descriptor.route });
onGestureStart?.({ route });
};

const handleGestureCanceled = () => {
const { route } = scene.descriptor;

onPageChangeCancel?.();
onGestureCancel?.({ route: scene.descriptor.route });
onGestureCancel?.({ route });
};

const handleGestureEnd = () => {
onGestureEnd?.({ route: scene.descriptor.route });
const { route } = scene.descriptor;

onGestureEnd?.({ route });
};

const handleTransitionStart = ({ closing }: { closing: boolean }) => {
if (active && closing) {
onPageChangeConfirm?.();
const handleTransition = ({
closing,
gesture,
}: {
closing: boolean;
gesture: boolean;
}) => {
const { route } = scene.descriptor;

if (!gesture) {
onPageChangeConfirm?.(true);
} else if (active && closing) {
onPageChangeConfirm?.(false);
} else {
onPageChangeCancel?.();
}

onTransitionStart?.({ route: scene.descriptor.route }, closing);
onTransitionStart?.({ route }, closing);
};

const insets = {
Expand Down Expand Up @@ -201,7 +217,7 @@ function CardContainer({
overlay={cardOverlay}
overlayEnabled={cardOverlayEnabled}
shadowEnabled={cardShadowEnabled}
onTransitionStart={handleTransitionStart}
onTransition={handleTransition}
onGestureBegin={handleGestureBegin}
onGestureCanceled={handleGestureCanceled}
onGestureEnd={handleGestureEnd}
Expand Down
2 changes: 1 addition & 1 deletion packages/stack/src/views/Stack/CardStack.tsx
Expand Up @@ -66,7 +66,7 @@ type Props = {
) => void;
onTransitionEnd: (props: { route: Route<string> }, closing: boolean) => void;
onPageChangeStart?: () => void;
onPageChangeConfirm?: () => void;
onPageChangeConfirm?: (force: boolean) => void;
onPageChangeCancel?: () => void;
onGestureStart?: (props: { route: Route<string> }) => void;
onGestureEnd?: (props: { route: Route<string> }) => void;
Expand Down

0 comments on commit 14ac256

Please sign in to comment.