diff --git a/src/Expensify.js b/src/Expensify.js index b7e3f0f60567..1b692f86a197 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -90,6 +90,8 @@ const defaultProps = { isCheckingPublicRoom: true, }; +const SplashScreenHiddenContext = React.createContext({}); + function Expensify(props) { const appStateChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); @@ -105,6 +107,14 @@ function Expensify(props) { }, [props.isCheckingPublicRoom]); const isAuthenticated = useMemo(() => Boolean(lodashGet(props.session, 'authToken', null)), [props.session]); + + const contextValue = useMemo( + () => ({ + isSplashHidden, + }), + [isSplashHidden], + ); + const shouldInit = isNavigationReady && (!isAuthenticated || props.isSidebarLoaded) && hasAttemptedToOpenPublicRoom; const shouldHideSplash = shouldInit && !isSplashHidden; @@ -216,10 +226,12 @@ function Expensify(props) { {hasAttemptedToOpenPublicRoom && ( - + + + )} {shouldHideSplash && } @@ -251,3 +263,5 @@ export default compose( }, }), )(Expensify); + +export {SplashScreenHiddenContext}; diff --git a/src/hooks/useAutoFocusInput.js b/src/hooks/useAutoFocusInput.js index 181df9359fe8..d4d43c8bf144 100644 --- a/src/hooks/useAutoFocusInput.js +++ b/src/hooks/useAutoFocusInput.js @@ -1,21 +1,24 @@ import {useFocusEffect} from '@react-navigation/native'; -import {useCallback, useEffect, useRef, useState} from 'react'; +import {useCallback, useContext, useEffect, useRef, useState} from 'react'; import CONST from '@src/CONST'; +import * as Expensify from '@src/Expensify'; export default function useAutoFocusInput() { const [isInputInitialized, setIsInputInitialized] = useState(false); const [isScreenTransitionEnded, setIsScreenTransitionEnded] = useState(false); + const {isSplashHidden} = useContext(Expensify.SplashScreenHiddenContext); + const inputRef = useRef(null); const focusTimeoutRef = useRef(null); useEffect(() => { - if (!isScreenTransitionEnded || !isInputInitialized || !inputRef.current) { + if (!isScreenTransitionEnded || !isInputInitialized || !inputRef.current || !isSplashHidden) { return; } inputRef.current.focus(); setIsScreenTransitionEnded(false); - }, [isScreenTransitionEnded, isInputInitialized]); + }, [isScreenTransitionEnded, isInputInitialized, isSplashHidden]); useFocusEffect( useCallback(() => { diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index f8b041951016..22af82041f7b 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -9,7 +9,7 @@ import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import useDelayedInputFocus from '@hooks/useDelayedInputFocus'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useNetwork from '@hooks/useNetwork'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; @@ -53,7 +53,6 @@ const defaultProps = { const excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, isSearchingForReports}) { - const optionSelectorRef = React.createRef(null); const [searchTerm, setSearchTerm] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); @@ -216,7 +215,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i setSearchTerm(text); }, []); - useDelayedInputFocus(optionSelectorRef, 600); + const {inputCallbackRef} = useAutoFocusInput(); return ( 0 ? safeAreaPaddingBottomStyle : {}]}> {isSmallScreenWidth && } diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.js b/src/pages/tasks/TaskAssigneeSelectorModal.js index cb08d2928d88..131755e02dfa 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.js +++ b/src/pages/tasks/TaskAssigneeSelectorModal.js @@ -1,7 +1,7 @@ /* eslint-disable es/no-optional-chaining */ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -11,6 +11,7 @@ import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -80,7 +81,7 @@ function TaskAssigneeSelectorModal(props) { const [filteredCurrentUserOption, setFilteredCurrentUserOption] = useState(null); const [isLoading, setIsLoading] = React.useState(true); - const optionRef = useRef(); + const {inputCallbackRef} = useAutoFocusInput(); const updateOptions = useCallback(() => { const {recentReports, personalDetails, userToInvite, currentUserOption} = OptionsListUtils.getFilteredOptions( @@ -208,7 +209,6 @@ function TaskAssigneeSelectorModal(props) { return ( optionRef.current && optionRef.current.textInput.focus()} testID={TaskAssigneeSelectorModal.displayName} > {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( @@ -229,7 +229,7 @@ function TaskAssigneeSelectorModal(props) { textInputLabel={props.translate('optionsSelector.nameEmailOrPhoneNumber')} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus={false} - ref={optionRef} + ref={inputCallbackRef} /> diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.js b/src/pages/tasks/TaskShareDestinationSelectorModal.js index cd0d8166770e..e3f992ea9b5a 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.js +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.js @@ -1,6 +1,6 @@ /* eslint-disable es/no-optional-chaining */ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -8,6 +8,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useNetwork from '@hooks/useNetwork'; import * as Report from '@libs/actions/Report'; import compose from '@libs/compose'; @@ -51,8 +52,9 @@ function TaskShareDestinationSelectorModal(props) { const [searchValue, setSearchValue] = useState(''); const [headerMessage, setHeaderMessage] = useState(''); const [filteredRecentReports, setFilteredRecentReports] = useState([]); + + const {inputCallbackRef} = useAutoFocusInput(); const {isSearchingForReports} = props; - const optionRef = useRef(); const {isOffline} = useNetwork(); const filteredReports = useMemo(() => { @@ -127,7 +129,6 @@ function TaskShareDestinationSelectorModal(props) { return ( optionRef.current && optionRef.current.textInput.focus()} testID={TaskShareDestinationSelectorModal.displayName} > {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( @@ -151,7 +152,7 @@ function TaskShareDestinationSelectorModal(props) { textInputAlert={isOffline ? `${props.translate('common.youAppearToBeOffline')} ${props.translate('search.resultsAreLimited')}` : ''} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} autoFocus={false} - ref={optionRef} + ref={inputCallbackRef} isLoadingNewOptions={isSearchingForReports} /> diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 271dc45026c7..ad4bd651fb01 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -13,7 +13,7 @@ import Text from '@components/Text'; import TextInput from '@components/TextInput'; import ValuePicker from '@components/ValuePicker'; import withNavigationFocus from '@components/withNavigationFocus'; -import useDelayedInputFocus from '@hooks/useDelayedInputFocus'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -161,10 +161,7 @@ function WorkspaceNewRoomPage(props) { [translate], ); - const roomNameInputRef = useRef(null); - - // use a 600ms delay for delayed focus on the room name input field so that it works consistently on native iOS / Android - useDelayedInputFocus(roomNameInputRef, 600); + const {inputCallbackRef} = useAutoFocusInput(); return ( (roomNameInputRef.current = el)} + ref={inputCallbackRef} inputID="roomName" isFocused={props.isFocused} shouldDelayFocus