11import React , { useCallback , useContext , useMemo , useRef , useState } from 'react' ;
2- import useOnyx from '@hooks/useOnyx' ;
2+ // We need direct access to useOnyx from react-native-onyx to avoid circular dependencies in SearchContext
3+ // eslint-disable-next-line no-restricted-imports
4+ import { useOnyx } from 'react-native-onyx' ;
5+ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' ;
6+ import useTodos from '@hooks/useTodos' ;
37import { isMoneyRequestReport } from '@libs/ReportUtils' ;
4- import { isTransactionListItemType , isTransactionReportGroupListItemType } from '@libs/SearchUIUtils' ;
8+ import { getSuggestedSearches , isTodoSearch , isTransactionListItemType , isTransactionReportGroupListItemType } from '@libs/SearchUIUtils' ;
59import type { SearchKey } from '@libs/SearchUIUtils' ;
610import CONST from '@src/CONST' ;
711import ONYXKEYS from '@src/ONYXKEYS' ;
12+ import type { SearchResults } from '@src/types/onyx' ;
13+ import type { SearchResultsInfo } from '@src/types/onyx/SearchResults' ;
814import type ChildrenProps from '@src/types/utils/ChildrenProps' ;
915import { isEmptyObject } from '@src/types/utils/EmptyObject' ;
1016import type { SearchContextData , SearchContextProps , SearchQueryJSON , SelectedTransactions } from './types' ;
1117
18+ // Default search info when building from live data
19+ // Used for to-do searches where we build SearchResults from live Onyx data instead of API snapshots
20+ const defaultSearchInfo : SearchResultsInfo = {
21+ offset : 0 ,
22+ type : CONST . SEARCH . DATA_TYPES . EXPENSE_REPORT ,
23+ status : CONST . SEARCH . STATUS . EXPENSE . ALL ,
24+ hasMoreResults : false ,
25+ hasResults : true ,
26+ isLoading : false ,
27+ count : 0 ,
28+ total : 0 ,
29+ currency : '' ,
30+ } ;
31+
1232const defaultSearchContextData : SearchContextData = {
1333 currentSearchHash : - 1 ,
1434 currentSearchKey : undefined ,
@@ -29,6 +49,7 @@ const defaultSearchContext: SearchContextProps = {
2949 showSelectAllMatchingItems : false ,
3050 shouldShowFiltersBarLoading : false ,
3151 currentSearchResults : undefined ,
52+ shouldUseLiveData : false ,
3253 setLastSearchType : ( ) => { } ,
3354 setCurrentSearchHashAndKey : ( ) => { } ,
3455 setCurrentSearchQueryJSON : ( ) => { } ,
@@ -51,7 +72,37 @@ function SearchContextProvider({children}: ChildrenProps) {
5172 const [ searchContextData , setSearchContextData ] = useState ( defaultSearchContextData ) ;
5273 const areTransactionsEmpty = useRef ( true ) ;
5374
54- const [ currentSearchResults ] = useOnyx ( `${ ONYXKEYS . COLLECTION . SNAPSHOT } ${ searchContextData . currentSearchHash } ` , { canBeMissing : true } ) ;
75+ const [ snapshotSearchResults ] = useOnyx ( `${ ONYXKEYS . COLLECTION . SNAPSHOT } ${ searchContextData . currentSearchHash } ` , { canBeMissing : true } ) ;
76+ const { todoSearchResultsData} = useTodos ( ) ;
77+
78+ const currentSearchKey = searchContextData . currentSearchKey ;
79+ const currentSearchHash = searchContextData . currentSearchHash ;
80+ const { accountID} = useCurrentUserPersonalDetails ( ) ;
81+ const suggestedSearches = useMemo ( ( ) => getSuggestedSearches ( accountID ) , [ accountID ] ) ;
82+ const shouldUseLiveData = ! ! currentSearchKey && isTodoSearch ( currentSearchHash , suggestedSearches ) ;
83+
84+ // If viewing a to-do search, use live data from useTodos, otherwise return the snapshot data
85+ // We do this so that we can show the counters for the to-do search results without visiting the specific to-do page, e.g. show `Approve [3]` while viewing the `Submit` to-do search.
86+ const currentSearchResults = useMemo ( ( ) : SearchResults | undefined => {
87+ if ( shouldUseLiveData ) {
88+ const liveData = todoSearchResultsData [ currentSearchKey as keyof typeof todoSearchResultsData ] ;
89+ const searchInfo : SearchResultsInfo = {
90+ ...( snapshotSearchResults ?. search ?? defaultSearchInfo ) ,
91+ count : liveData . metadata . count ,
92+ total : liveData . metadata . total ,
93+ currency : liveData . metadata . currency ,
94+ } ;
95+ const hasResults = Object . keys ( liveData . data ) . length > 0 ;
96+ // For to-do searches, always return a valid SearchResults object (even with empty data)
97+ // This ensures we show the empty state instead of loading/blocking views
98+ return {
99+ search : { ...searchInfo , isLoading : false , hasResults} ,
100+ data : liveData . data ,
101+ } ;
102+ }
103+
104+ return snapshotSearchResults ?? undefined ;
105+ } , [ shouldUseLiveData , currentSearchKey , todoSearchResultsData , snapshotSearchResults ] ) ;
55106
56107 const setCurrentSearchHashAndKey = useCallback ( ( searchHash : number , searchKey : SearchKey | undefined ) => {
57108 setSearchContextData ( ( prevState ) => {
@@ -207,6 +258,7 @@ function SearchContextProvider({children}: ChildrenProps) {
207258 ( ) => ( {
208259 ...searchContextData ,
209260 currentSearchResults,
261+ shouldUseLiveData,
210262 removeTransaction,
211263 setCurrentSearchHashAndKey,
212264 setCurrentSearchQueryJSON,
@@ -225,6 +277,7 @@ function SearchContextProvider({children}: ChildrenProps) {
225277 [
226278 searchContextData ,
227279 currentSearchResults ,
280+ shouldUseLiveData ,
228281 removeTransaction ,
229282 setCurrentSearchHashAndKey ,
230283 setCurrentSearchQueryJSON ,
0 commit comments