diff --git a/src/libs/focusWithDelay/focusWithDelay.js b/src/libs/focusWithDelay/focusWithDelay.js new file mode 100644 index 000000000000..143d5dd12430 --- /dev/null +++ b/src/libs/focusWithDelay/focusWithDelay.js @@ -0,0 +1,40 @@ +import {InteractionManager} from 'react-native'; + +/** + * Creates a function that can be used to focus a text input + * @param {Boolean} disableDelay whether to force focus without a delay (on web and desktop) + * @returns {Function} a focusWithDelay function + */ +function focusWithDelay(disableDelay = false) { + /** + * Create a function that focuses a text input. + * @param {Object} textInput the text input to focus + * @returns {Function} a function that focuses the text input with a configurable delay + */ + return (textInput) => + /** + * Focus the text input + * @param {Boolean} [shouldDelay=false] Impose delay before focusing the text input + */ + (shouldDelay = false) => { + // There could be other animations running while we trigger manual focus. + // This prevents focus from making those animations janky. + InteractionManager.runAfterInteractions(() => { + if (!textInput) { + return; + } + + if (disableDelay || !shouldDelay) { + textInput.focus(); + } else { + // Keyboard is not opened after Emoji Picker is closed + // SetTimeout is used as a workaround + // https://github.com/react-native-modal/react-native-modal/issues/114 + // We carefully choose a delay. 100ms is found enough for keyboard to open. + setTimeout(() => textInput.focus(), 100); + } + }); + }; +} + +export default focusWithDelay; diff --git a/src/libs/focusWithDelay/index.js b/src/libs/focusWithDelay/index.js new file mode 100644 index 000000000000..faeb43147c5c --- /dev/null +++ b/src/libs/focusWithDelay/index.js @@ -0,0 +1,7 @@ +import focusWithDelay from './focusWithDelay'; + +/** + * We pass true to disable the delay on the web because it doesn't require + * using the workaround (explained in the focusWithDelay.js file). + */ +export default focusWithDelay(true); diff --git a/src/libs/focusWithDelay/index.native.js b/src/libs/focusWithDelay/index.native.js new file mode 100644 index 000000000000..27fb19fe1570 --- /dev/null +++ b/src/libs/focusWithDelay/index.native.js @@ -0,0 +1,6 @@ +import focusWithDelay from './focusWithDelay'; + +/** + * We enable the delay on native to display the keyboard correctly + */ +export default focusWithDelay(false); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 907eba01e641..90887ea01285 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -1,11 +1,12 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; -import {View, InteractionManager, LayoutAnimation, NativeModules, findNodeHandle} from 'react-native'; +import {View, LayoutAnimation, NativeModules, findNodeHandle} from 'react-native'; import {runOnJS} from 'react-native-reanimated'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; +import focusWithDelay from '../../../libs/focusWithDelay'; import styles from '../../../styles/styles'; import themeColors from '../../../styles/themes/default'; import Composer from '../../../components/Composer'; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 54c5fec4533e..cb03d12b4caf 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -36,6 +36,7 @@ import useKeyboardState from '../../../hooks/useKeyboardState'; import useWindowDimensions from '../../../hooks/useWindowDimensions'; import useReportScrollManager from '../../../hooks/useReportScrollManager'; import * as EmojiPickerAction from '../../../libs/actions/EmojiPickerAction'; +import focusWithDelay from '../../../libs/focusWithDelay'; const propTypes = { /** All the data of the action */ @@ -271,6 +272,11 @@ function ReportActionItemMessageEdit(props) { [deleteDraft, isKeyboardShown, isSmallScreenWidth, publishDraft], ); + /** + * Focus the composer text input + */ + const focus = focusWithDelay(textInputRef.current); + return ( <> @@ -346,7 +352,10 @@ function ReportActionItemMessageEdit(props) { InteractionManager.runAfterInteractions(() => textInputRef.current.focus())} + onModalHide={() => { + setIsFocused(true); + focus(true); + }} onEmojiSelected={addEmojiToTextBox} nativeID={emojiButtonID} reportAction={props.action}