From 3f4192dfbd10dc40a7d791aba52da39da76b1f18 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 28 Oct 2025 21:23:07 +0530 Subject: [PATCH] fix input not focused after skeleton loader. Signed-off-by: krishna2323 --- .../Search/SearchAutocompleteInput.tsx | 31 ++++++++++++++++--- .../Search/SearchRouter/SearchRouter.tsx | 4 +-- src/hooks/useFocusAfterNav/index.native.ts | 6 +++- src/hooks/useFocusAfterNav/type.ts | 2 +- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 09afd73908e2..82b9373cbe04 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,14 +1,16 @@ /* eslint-disable rulesdir/no-acc-spread-in-reduce */ import type {ForwardedRef, RefObject} from 'react'; -import React, {useCallback, useEffect, useMemo} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {View} from 'react-native'; import Animated, {interpolateColor, useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; import FormHelpMessage from '@components/FormHelpMessage'; +import type {AnimatedTextInputRef} from '@components/RNTextInput'; import type {SelectionListHandle} from '@components/SelectionListWithSections/types'; import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useFocusAfterNav from '@hooks/useFocusAfterNav'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -68,6 +70,9 @@ type SearchAutocompleteInputProps = { /** Map of autocomplete suggestions. Required for highlighting to work properly */ substitutionMap: SubstitutionMap; + /** Whether the focus should be delayed */ + shouldDelayFocus?: boolean; + /** Reference to the outer element */ ref?: ForwardedRef; } & Pick; @@ -79,8 +84,9 @@ function SearchAutocompleteInput({ autocompleteListRef, isFullWidth, disabled = false, - shouldShowOfflineMessage = false, + shouldDelayFocus = false, autoFocus = true, + shouldShowOfflineMessage = false, onFocus, onBlur, caretHidden = false, @@ -97,7 +103,8 @@ function SearchAutocompleteInput({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - + const inputRef = useRef(null); + const autoFocusAfterNav = useFocusAfterNav(inputRef, shouldDelayFocus); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: false}); const currencyAutocompleteList = Object.keys(currencyList ?? {}).filter((currencyCode) => !currencyList?.[currencyCode]?.retired); const currencySharedValue = useSharedValue(currencyAutocompleteList); @@ -203,7 +210,7 @@ function SearchAutocompleteInput({ testID="search-autocomplete-text-input" value={value} onChangeText={onSearchQueryChange} - autoFocus={autoFocus} + autoFocus={shouldDelayFocus ? autoFocusAfterNav : autoFocus} caretHidden={caretHidden} role={CONST.ROLE.PRESENTATION} placeholder={translate('search.searchPlaceholder')} @@ -232,7 +239,21 @@ function SearchAutocompleteInput({ onBlur?.(); }} isLoading={isSearchingForReports} - ref={ref} + ref={(element) => { + if (!ref) { + return; + } + + inputRef.current = element as AnimatedTextInputRef; + + if (typeof ref === 'function') { + ref(element); + return; + } + + // eslint-disable-next-line no-param-reassign + ref.current = element; + }} type="markdown" multiline={false} parser={parser} diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 34ffe10ae112..60be4647bc3c 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -19,7 +19,6 @@ import type {SearchQueryItem} from '@components/SelectionListWithSections/Search import {isSearchQueryItem} from '@components/SelectionListWithSections/Search/SearchQueryListItem'; import type {SelectionListHandle} from '@components/SelectionListWithSections/types'; import useDebouncedState from '@hooks/useDebouncedState'; -import useFocusAfterNav from '@hooks/useFocusAfterNav'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -483,7 +482,6 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla const updateAndScrollToFocusedIndex = useCallback(() => listRef.current?.updateAndScrollToFocusedIndex(1, true), []); const modalWidth = shouldUseNarrowLayout ? styles.w100 : {width: variables.searchRouterPopoverWidth}; - const autoFocus = useFocusAfterNav(textInputRef); return ( {shouldShowList && ( diff --git a/src/hooks/useFocusAfterNav/index.native.ts b/src/hooks/useFocusAfterNav/index.native.ts index a9a2563b34e0..2e92724cf45f 100644 --- a/src/hooks/useFocusAfterNav/index.native.ts +++ b/src/hooks/useFocusAfterNav/index.native.ts @@ -4,8 +4,12 @@ import type UseFocusAfterNav from './type'; /** We added a delay to focus on text input to allow navigation/modal animations to get completed, see issue https://github.com/Expensify/App/issues/65855 for more details */ -const useFocusAfterNav: UseFocusAfterNav = (ref) => { +const useFocusAfterNav: UseFocusAfterNav = (ref, shouldDelayFocus = true) => { useFocusEffect(() => { + if (!shouldDelayFocus) { + return; + } + const timeoutId = setTimeout(() => { ref.current?.focus(); }, CONST.ANIMATED_TRANSITION); diff --git a/src/hooks/useFocusAfterNav/type.ts b/src/hooks/useFocusAfterNav/type.ts index 379dadbde120..60fa76815022 100644 --- a/src/hooks/useFocusAfterNav/type.ts +++ b/src/hooks/useFocusAfterNav/type.ts @@ -1,6 +1,6 @@ import type {RefObject} from 'react'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; -type UseFocusAfterNav = (ref: RefObject) => boolean; +type UseFocusAfterNav = (ref: RefObject, shouldDelayFocus: boolean) => boolean; export default UseFocusAfterNav;