From 07bfc8632795bda70cbf0e11cf9249f34c3532a0 Mon Sep 17 00:00:00 2001 From: osdnk Date: Thu, 24 Oct 2019 18:50:16 +0200 Subject: [PATCH] fix: keyboard manager in stack for fast swipe --- packages/stack/src/views/KeyboardManager.tsx | 37 ++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/stack/src/views/KeyboardManager.tsx b/packages/stack/src/views/KeyboardManager.tsx index 4e57be4d..0fb122cd 100644 --- a/packages/stack/src/views/KeyboardManager.tsx +++ b/packages/stack/src/views/KeyboardManager.tsx @@ -11,14 +11,27 @@ type Props = { }; export default class KeyboardManager extends React.Component { + 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: number | null = null; + private startTimestamp: number = 0; + private keyboardTimeout: NodeJS.Timeout | undefined; + + clearKeyboardTimeout = () => { + if (this.keyboardTimeout !== undefined) { + clearTimeout(this.keyboardTimeout); + this.keyboardTimeout = undefined; + } + }; private handlePageChangeStart = () => { if (!this.props.enabled) { return; } + this.clearKeyboardTimeout(); const input = TextInput.State.currentlyFocusedField(); @@ -27,12 +40,16 @@ export default class KeyboardManager extends React.Component { // Store the id of this input so we can refocus it if change was cancelled this.previouslyFocusedTextInput = input; + + // Store timestamp for touch start + this.startTimestamp = Date.now(); }; private handlePageChangeConfirm = () => { if (!this.props.enabled) { return; } + this.clearKeyboardTimeout(); Keyboard.dismiss(); @@ -44,15 +61,29 @@ export default class KeyboardManager extends React.Component { if (!this.props.enabled) { return; } + this.clearKeyboardTimeout(); // The page didn't change, we should restore the focus of text input const input = this.previouslyFocusedTextInput; if (input) { - TextInput.State.focusTextInput(input); - } + // If the interaction was super short we should make sure keyboard won't hide again. - this.previouslyFocusedTextInput = null; + // Too fast input refocus will result only in keyboard flashing on screen and hiding right away. + // During first ~100ms keyboard will be dismissed no matter what, + // so we have to make sure it won't interrupt input refocus logic. + // That's why when the interaction is shorter than 100ms we add delay so it won't hide once again. + // Subtracting timestamps makes us sure the delay is executed only when needed. + if (Date.now() - this.startTimestamp < 100) { + this.keyboardTimeout = setTimeout(() => { + TextInput.State.focusTextInput(input); + this.previouslyFocusedTextInput = null; + }, 100); + } else { + TextInput.State.focusTextInput(input); + this.previouslyFocusedTextInput = null; + } + } }; render() {