diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 34cfe1c2f2da..f6fd595e6a6e 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -31,6 +31,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type Report from '@src/types/onyx/Report'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import {getQueryWithSubstitutions} from './getQueryWithSubstitutions'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; import {getUpdatedSubstitutionsMap} from './getUpdatedSubstitutionsMap'; @@ -75,7 +76,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) const styles = useThemeStyles(); const {translate} = useLocalize(); const [betas] = useOnyx(ONYXKEYS.BETAS); - const [recentSearches] = useOnyx(ONYXKEYS.RECENT_SEARCHES); + const [recentSearches, recentSearchesMetadata] = useOnyx(ONYXKEYS.RECENT_SEARCHES); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); const [autocompleteSubstitutions, setAutocompleteSubstitutions] = useState({}); @@ -83,7 +84,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) const [reports = {}] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const taxRates = getAllTaxRates(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {shouldUseNarrowLayout, isLargeScreenWidth} = useResponsiveLayout(); const listRef = useRef(null); // The actual input text that the user sees @@ -310,6 +311,10 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) const modalWidth = shouldUseNarrowLayout ? styles.w100 : {width: variables.searchRouterPopoverWidth}; + const isDataLoaded = useMemo(() => { + return (!contextualReportID || contextualReportID !== undefined) && !isLoadingOnyxValue(recentSearchesMetadata) && recentReports.length > 0; + }, [contextualReportID, recentSearchesMetadata, recentReports]); + return ( onRouterClose()} /> )} - { - submitSearch(textInputValue); - }} - caretHidden={shouldHideInputCaret} - routerListRef={listRef} - shouldShowOfflineMessage - wrapperStyle={[styles.border, styles.alignItemsCenter]} - outerWrapperStyle={[shouldUseNarrowLayout ? styles.mv3 : styles.mv2, shouldUseNarrowLayout ? styles.mh5 : styles.mh2]} - wrapperFocusedStyle={[styles.borderColorFocus]} - isSearchingForReports={isSearchingForReports} - /> - + {(isDataLoaded || !!debouncedInputValue) && ( + <> + { + submitSearch(textInputValue); + }} + caretHidden={shouldHideInputCaret} + routerListRef={listRef} + shouldShowOfflineMessage + wrapperStyle={[styles.border, styles.alignItemsCenter]} + outerWrapperStyle={[shouldUseNarrowLayout ? styles.mv3 : styles.mv2, shouldUseNarrowLayout ? styles.mh5 : styles.mh2]} + wrapperFocusedStyle={[styles.borderColorFocus]} + isSearchingForReports={isSearchingForReports} + /> + + + )} ); } diff --git a/src/components/Search/SearchRouter/SearchRouterList.tsx b/src/components/Search/SearchRouter/SearchRouterList.tsx index f626a3b34daf..3e7bae1e6661 100644 --- a/src/components/Search/SearchRouter/SearchRouterList.tsx +++ b/src/components/Search/SearchRouter/SearchRouterList.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import React, {forwardRef, useMemo} from 'react'; +import React, {forwardRef, useMemo, useState} from 'react'; import type {ForwardedRef} from 'react'; import {useOnyx} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -58,6 +58,9 @@ type SearchRouterListProps = { /** Callback to call when an item is focused via arrow buttons */ onListItemFocus: (item: SearchQueryItem) => void; + + /** Item `keyForList` to focus initially */ + initiallyFocusedOptionKey?: string | null; }; const defaultListOptions = { @@ -106,7 +109,7 @@ function SearchRouterItem(props: UserListItemProps | SearchQueryList // Todo rename to SearchAutocompleteList once it's used in both Router and SearchPage function SearchRouterList( - {autocompleteQueryValue, searchQueryItem, additionalSections, shouldPreventDefault = true, onListItemFocus, onListItemPress}: SearchRouterListProps, + {autocompleteQueryValue, searchQueryItem, additionalSections, shouldPreventDefault = true, onListItemFocus, onListItemPress, initiallyFocusedOptionKey}: SearchRouterListProps, ref: ForwardedRef, ) { const styles = useThemeStyles(); @@ -345,6 +348,8 @@ function SearchRouterList( const sections: Array> = []; + const [isInitialRender, setIsInitialRender] = useState(true); + if (searchQueryItem) { sections.push({data: [searchQueryItem]}); } @@ -385,13 +390,18 @@ function SearchRouterList( sectionListStyle={[shouldUseNarrowLayout ? styles.ph5 : styles.ph2, styles.pb2]} listItemWrapperStyle={[styles.pr0, styles.pl0]} getItemHeight={getItemHeight} - onLayout={setPerformanceTimersEnd} + onLayout={() => { + setPerformanceTimersEnd(); + setIsInitialRender(false); + }} showScrollIndicator={!shouldUseNarrowLayout} sectionTitleStyles={styles.mhn2} shouldSingleExecuteRowSelect onArrowFocus={onArrowFocus} shouldPreventDefault={shouldPreventDefault} ref={ref} + initiallyFocusedOptionKey={initiallyFocusedOptionKey} + shouldScrollToFocusedIndex={!isInitialRender} /> ); } diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index c621a4dc1820..58e1bc0e32fe 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -116,6 +116,7 @@ function BaseSelectionList( shouldKeepFocusedItemAtTopOfViewableArea = false, shouldDebounceScrolling = false, shouldPreventActiveCellVirtualization = false, + shouldScrollToFocusedIndex = true, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -323,7 +324,9 @@ function BaseSelectionList( if (focusedItem) { onArrowFocus(focusedItem); } - (shouldDebounceScrolling ? debouncedScrollToIndex : scrollToIndex)(index, true); + if (shouldScrollToFocusedIndex) { + (shouldDebounceScrolling ? debouncedScrollToIndex : scrollToIndex)(index, true); + } }, isFocused, }); @@ -592,10 +595,12 @@ function BaseSelectionList( if (!isInitialSectionListRender) { return; } - scrollToIndex(focusedIndex, false); + if (shouldScrollToFocusedIndex) { + scrollToIndex(focusedIndex, false); + } setIsInitialSectionListRender(false); }, - [focusedIndex, isInitialSectionListRender, scrollToIndex, shouldUseDynamicMaxToRenderPerBatch], + [focusedIndex, isInitialSectionListRender, scrollToIndex, shouldUseDynamicMaxToRenderPerBatch, shouldScrollToFocusedIndex], ); const onSectionListLayout = useCallback( diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 35cae42c89a6..1908bb47dc39 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -621,6 +621,9 @@ type BaseSelectionListProps = Partial & { /** Whether to prevent the active cell from being virtualized and losing focus in browsers */ shouldPreventActiveCellVirtualization?: boolean; + + /** Whether to scroll to the focused index */ + shouldScrollToFocusedIndex?: boolean; } & TRightHandSideComponent; type SelectionListHandle = {