From 66834ca056d1bfa3eafcc61665c58bf04f53585c Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 30 Jul 2024 11:08:45 +0200 Subject: [PATCH 1/6] send JSON ast query to Search api --- src/components/Search/index.tsx | 4 ++-- src/libs/API/parameters/Search.ts | 9 +++------ src/libs/actions/Search.ts | 24 ++++-------------------- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 3f2ed651eadd..62bcdb7766ac 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -98,9 +98,9 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS setCurrentSearchHash(hash); - SearchActions.search({hash, query: status, policyIDs, offset, sortBy, sortOrder}); + SearchActions.search({queryJSON, offset}); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [hash, isOffline, offset]); + }, [isOffline, offset, queryJSON]); const isDataLoaded = searchResults?.data !== undefined; const shouldShowLoadingState = !isOffline && !isDataLoaded; diff --git a/src/libs/API/parameters/Search.ts b/src/libs/API/parameters/Search.ts index 60ea54419492..f88ed3056ca9 100644 --- a/src/libs/API/parameters/Search.ts +++ b/src/libs/API/parameters/Search.ts @@ -1,12 +1,9 @@ -import type {SortOrder} from '@components/Search/types'; +import type {SearchQueryString} from '@components/Search/types'; type SearchParams = { hash: number; - query: string; - policyIDs?: string; - sortBy?: string; - sortOrder?: SortOrder; - offset: number; + jsonQuery: SearchQueryString; + offset?: number; }; export default SearchParams; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 4b782e8b103c..e3ef78815210 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -1,14 +1,13 @@ import Onyx from 'react-native-onyx'; import type {OnyxUpdate} from 'react-native-onyx'; import type {FormOnyxValues} from '@components/Form/types'; -import type {SearchQueryString} from '@components/Search/types'; +import type {SearchQueryJSON} from '@components/Search/types'; import * as API from '@libs/API'; -import type {ExportSearchItemsToCSVParams, SearchParams} from '@libs/API/parameters'; +import type {ExportSearchItemsToCSVParams} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ApiUtils from '@libs/ApiUtils'; import fileDownload from '@libs/fileDownload'; import enhanceParameters from '@libs/Network/enhanceParameters'; -import {buildSearchQueryJSON} from '@libs/SearchUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {SearchTransaction} from '@src/types/onyx/SearchResults'; @@ -50,25 +49,10 @@ function getOnyxLoadingData(hash: number): {optimisticData: OnyxUpdate[]; finall return {optimisticData, finallyData}; } -function search({hash, query, policyIDs, offset, sortBy, sortOrder}: SearchParams) { - const {optimisticData, finallyData} = getOnyxLoadingData(hash); - - API.read(READ_COMMANDS.SEARCH, {hash, query, offset, policyIDs, sortBy, sortOrder}, {optimisticData, finallyData}); -} - -// TODO_SEARCH: use this function after backend changes. -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function searchV2(queryString: SearchQueryString) { - const queryJSON = buildSearchQueryJSON(queryString); - - if (!queryJSON) { - return; - } - +function search({queryJSON}: {queryJSON: SearchQueryJSON; offset?: number}) { const {optimisticData, finallyData} = getOnyxLoadingData(queryJSON.hash); - // TODO_SEARCH: uncomment this line after backend changes - // @ts-expect-error waiting for backend changes + // Todo Don't know how to correctly pass offset - passing on top level triggers endless fetch loop API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery: JSON.stringify(queryJSON)}, {optimisticData, finallyData}); } From 9e2ac8f0f12ef33fb9a1e3076ab9291ca94c76d5 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 31 Jul 2024 11:47:25 +0200 Subject: [PATCH 2/6] Correctly pass offset param when calling search api --- src/libs/API/parameters/Search.ts | 1 - src/libs/actions/Search.ts | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libs/API/parameters/Search.ts b/src/libs/API/parameters/Search.ts index f88ed3056ca9..64bfc5baf5a1 100644 --- a/src/libs/API/parameters/Search.ts +++ b/src/libs/API/parameters/Search.ts @@ -3,7 +3,6 @@ import type {SearchQueryString} from '@components/Search/types'; type SearchParams = { hash: number; jsonQuery: SearchQueryString; - offset?: number; }; export default SearchParams; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index e3ef78815210..d4a8fc57ddc5 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -49,11 +49,16 @@ function getOnyxLoadingData(hash: number): {optimisticData: OnyxUpdate[]; finall return {optimisticData, finallyData}; } -function search({queryJSON}: {queryJSON: SearchQueryJSON; offset?: number}) { +function search({queryJSON, offset}: {queryJSON: SearchQueryJSON; offset?: number}) { const {optimisticData, finallyData} = getOnyxLoadingData(queryJSON.hash); - // Todo Don't know how to correctly pass offset - passing on top level triggers endless fetch loop - API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery: JSON.stringify(queryJSON)}, {optimisticData, finallyData}); + const queryWithOffset = { + ...queryJSON, + offset, + }; + const jsonQuery = JSON.stringify(queryWithOffset); + + API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery}, {optimisticData, finallyData}); } /** From 3ba594e7f1d1f30f44c351381a214a99a02f6a6e Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 31 Jul 2024 16:32:46 +0200 Subject: [PATCH 3/6] Fix missing filter query params when sorting search results --- src/components/Search/index.tsx | 16 ++--- src/components/Search/types.ts | 2 +- .../SelectionList/SearchTableHeader.tsx | 6 +- src/libs/SearchUtils.ts | 72 +++++++++---------- 4 files changed, 41 insertions(+), 55 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 1ebae9449b5d..7e84872428c4 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -27,21 +27,19 @@ import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import {useSearchContext} from './SearchContext'; import SearchListWithHeader from './SearchListWithHeader'; import SearchPageHeader from './SearchPageHeader'; -import type {SearchColumnType, SearchQueryJSON, SearchStatus, SortOrder} from './types'; +import type {SearchColumnType, SearchQueryJSON, SortOrder} from './types'; type SearchProps = { queryJSON: SearchQueryJSON; - policyIDs?: string; isCustomQuery: boolean; }; -const sortableSearchTabs: SearchStatus[] = [CONST.SEARCH.STATUS.ALL]; const transactionItemMobileHeight = 100; const reportItemTransactionHeight = 52; const listItemPadding = 12; // this is equivalent to 'mb3' on every transaction/report list item const searchHeaderHeight = 54; -function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { +function Search({queryJSON, isCustomQuery}: SearchProps) { const {isOffline} = useNetwork(); const styles = useThemeStyles(); const {isLargeScreenWidth, isSmallScreenWidth} = useWindowDimensions(); @@ -51,7 +49,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); const [offset, setOffset] = React.useState(0); - const {status, sortBy, sortOrder, hash} = queryJSON; + const {sortBy, sortOrder, hash} = queryJSON; const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); @@ -170,15 +168,10 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { const sortedData = SearchUtils.getSortedSections(type, data, sortBy, sortOrder); const onSortPress = (column: SearchColumnType, order: SortOrder) => { - const currentSearchParams = SearchUtils.getCurrentSearchParams(); - const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q, policyIDs); - - const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, sortBy: column, sortOrder: order}); + const newQuery = SearchUtils.buildSearchQueryString({...queryJSON, sortBy: column, sortOrder: order}); navigation.setParams({q: newQuery}); }; - const isSortingAllowed = sortableSearchTabs.includes(status); - const shouldShowYear = SearchUtils.shouldShowYear(searchResults?.data); const canSelectMultiple = isSmallScreenWidth ? selectionMode?.isEnabled : true; @@ -197,7 +190,6 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { metadata={searchResults?.search} onSortPress={onSortPress} sortOrder={sortOrder} - isSortingAllowed={isSortingAllowed} sortBy={sortBy} shouldShowYear={shouldShowYear} /> diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 360a8456bda7..da11370f99e2 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -47,7 +47,7 @@ type QueryFilter = { type AdvancedFiltersKeys = ValueOf | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS; type QueryFilters = { - [K in AdvancedFiltersKeys]?: QueryFilter | QueryFilter[]; + [K in AdvancedFiltersKeys]?: QueryFilter[]; }; type SearchQueryString = string; diff --git a/src/components/SelectionList/SearchTableHeader.tsx b/src/components/SelectionList/SearchTableHeader.tsx index 4bf1715e0434..a260ee6bd0f0 100644 --- a/src/components/SelectionList/SearchTableHeader.tsx +++ b/src/components/SelectionList/SearchTableHeader.tsx @@ -90,12 +90,11 @@ type SearchTableHeaderProps = { metadata: OnyxTypes.SearchResults['search']; sortBy?: SearchColumnType; sortOrder?: SortOrder; - isSortingAllowed: boolean; onSortPress: (column: SearchColumnType, order: SortOrder) => void; shouldShowYear: boolean; }; -function SearchTableHeader({data, metadata, sortBy, sortOrder, isSortingAllowed, onSortPress, shouldShowYear}: SearchTableHeaderProps) { +function SearchTableHeader({data, metadata, sortBy, sortOrder, onSortPress, shouldShowYear}: SearchTableHeaderProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {isSmallScreenWidth, isMediumScreenWidth} = useWindowDimensions(); @@ -116,7 +115,6 @@ function SearchTableHeader({data, metadata, sortBy, sortOrder, isSortingAllowed, const isActive = sortBy === columnName; const textStyle = columnName === CONST.SEARCH.TABLE_COLUMNS.RECEIPT ? StyleUtils.getTextOverflowStyle('clip') : null; - const isSortable = isSortingAllowed && isColumnSortable; return ( onSortPress(columnName, order)} /> ); diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 367f414eb53d..08fd85449fd3 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,5 +1,5 @@ import type {ValueOf} from 'type-fest'; -import type {AdvancedFiltersKeys, ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SortOrder} from '@components/Search/types'; +import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SortOrder} from '@components/Search/types'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; @@ -341,16 +341,31 @@ function buildSearchQueryJSON(query: SearchQueryString, policyID?: string) { } } -function buildSearchQueryString(partialQueryJSON?: Partial) { +function buildSearchQueryString(queryJSON?: SearchQueryJSON) { const queryParts: string[] = []; const defaultQueryJSON = buildSearchQueryJSON(''); // For this const values are lowercase version of the keys. We are using lowercase for ast keys. - for (const [, value] of Object.entries(CONST.SEARCH.SYNTAX_ROOT_KEYS)) { - if (partialQueryJSON?.[value]) { - queryParts.push(`${value}:${partialQueryJSON[value]}`); + for (const [, key] of Object.entries(CONST.SEARCH.SYNTAX_ROOT_KEYS)) { + if (queryJSON?.[key]) { + queryParts.push(`${key}:${queryJSON[key]}`); } else if (defaultQueryJSON) { - queryParts.push(`${value}:${defaultQueryJSON[value]}`); + queryParts.push(`${key}:${defaultQueryJSON[key]}`); + } + } + + if (!queryJSON) { + return queryParts.join(' '); + } + + const filters = getFilters(queryJSON); + + for (const [, filterKey] of Object.entries(CONST.SEARCH.SYNTAX_FILTER_KEYS)) { + const queryFilter = filters[filterKey]; + + if (queryFilter) { + const filterValueString = buildFilterString(filterKey, queryFilter); + queryParts.push(filterValueString); } } @@ -412,29 +427,9 @@ function buildQueryStringFromFilters(filterValues: Partial>) { - let queryAST; - - try { - queryAST = searchParser.parse(query) as SearchQueryJSON; - } catch (e) { - console.error(e); - return; - } - +function getFilters(queryJSON: SearchQueryJSON) { const filters = {} as QueryFilters; - - fields.forEach((field) => { - const rootFieldKey = field as ValueOf; - if (queryAST[rootFieldKey] === undefined) { - return; - } - - filters[field] = { - operator: 'eq', - value: queryAST[rootFieldKey], - }; - }); + const filterKeys = Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS); function traverse(node: ASTNode) { if (!node.operator) { @@ -450,7 +445,7 @@ function getFilters(query: SearchQueryString, fields: Array; - if (!fields.includes(nodeKey)) { + if (!filterKeys.includes(nodeKey)) { return; } @@ -458,26 +453,27 @@ function getFilters(query: SearchQueryString, fields: Array { // If the previous queryFilter has the same operator (this rule applies only to eq and neq operators) then append the current value if ((queryFilter.operator === 'eq' && queryFilters[index - 1]?.operator === 'eq') || (queryFilter.operator === 'neq' && queryFilters[index - 1]?.operator === 'neq')) { - filterValueString += `,${queryFilter.value}`; + filterValueString += `,${filterName}:${queryFilter.value}`; } else { filterValueString += ` ${filterName}${operatorToSignMap[queryFilter.operator]}${queryFilter.value}`; } @@ -487,14 +483,14 @@ function buildFilterValueString(filterName: string, queryFilters: QueryFilter[]) } function getSearchHeaderTitle(queryJSON: SearchQueryJSON) { - const {inputQuery, type, status} = queryJSON; - const filters = getFilters(inputQuery, Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS)) ?? {}; + const {type, status} = queryJSON; + const filters = getFilters(queryJSON) ?? {}; let title = `type:${type} status:${status}`; Object.keys(filters).forEach((key) => { - const queryFilter = filters[key as ValueOf] as QueryFilter[]; - title += buildFilterValueString(key, queryFilter); + const queryFilter = filters[key as ValueOf] ?? []; + title += buildFilterString(key, queryFilter); }); return title; From b92ecb0545adc0f4627cec0780812204ae86de3d Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 31 Jul 2024 16:43:27 +0200 Subject: [PATCH 4/6] Fix TS errors in Search --- src/pages/Search/SearchPage.tsx | 1 - src/pages/Search/SearchPageBottomTab.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index e2495c02d44f..9eddd5deae8d 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -45,7 +45,6 @@ function SearchPage({route}: SearchPageProps) { )} diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index c8254af295d7..f7285fb07ac1 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -75,7 +75,6 @@ function SearchPageBottomTab() { {shouldUseNarrowLayout && queryJSON && ( )} From f7b1cddfca995c1ee2ef8b1eecdfc0a32a63a83a Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 1 Aug 2024 10:50:51 +0200 Subject: [PATCH 5/6] Fix effects in Search after merge conflicts --- src/components/Search/index.tsx | 115 ++++++++++++++------------------ 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index b1e66bf5588f..8cec49c89d6d 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -47,7 +47,7 @@ function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [stri } function mapToTransactionItemWithSelectionInfo(item: TransactionListItemType, selectedTransactions: SelectedTransactions) { - return {...item, isSelected: !!selectedTransactions[item.keyForList]?.isSelected}; + return {...item, isSelected: selectedTransactions[item.keyForList]?.isSelected}; } function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListItemType, selectedTransactions: SelectedTransactions) { @@ -56,7 +56,7 @@ function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListIt : { ...item, transactions: item.transactions?.map((transaction) => mapToTransactionItemWithSelectionInfo(transaction, selectedTransactions)), - isSelected: item.transactions.every((transaction) => !!selectedTransactions[transaction.keyForList]?.isSelected), + isSelected: item.transactions.every((transaction) => selectedTransactions[transaction.keyForList]?.isSelected), }; } @@ -90,14 +90,9 @@ function Search({queryJSON, isCustomQuery}: SearchProps) { const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); useEffect(() => { - if (isSmallScreenWidth) { - return; - } clearSelectedTransactions(hash); setCurrentSearchHash(hash); - - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [hash]); + }, [hash, clearSelectedTransactions, setCurrentSearchHash]); useEffect(() => { const selectedKeys = Object.keys(selectedTransactions).filter((key) => selectedTransactions[key]); @@ -112,6 +107,15 @@ function Search({queryJSON, isCustomQuery}: SearchProps) { } }, [isSmallScreenWidth, selectedTransactions, selectionMode?.isEnabled]); + useEffect(() => { + if (isOffline) { + return; + } + + SearchActions.search({queryJSON, offset}); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [isOffline, offset, queryJSON]); + const handleOnCancelConfirmModal = () => { setSelectedTransactionsToDelete([]); setDeleteExpensesConfirmModalVisible(false); @@ -132,19 +136,6 @@ function Search({queryJSON, isCustomQuery}: SearchProps) { setDeleteExpensesConfirmModalVisible(true); }; - useEffect(() => { - const selectedKeys = Object.keys(selectedTransactions).filter((key) => selectedTransactions[key]); - if (!isSmallScreenWidth) { - if (selectedKeys.length === 0) { - turnOffMobileSelectionMode(); - } - return; - } - if (selectedKeys.length > 0 && !selectionMode?.isEnabled) { - turnOnMobileSelectionMode(); - } - }, [isSmallScreenWidth, selectedTransactions, selectionMode?.isEnabled]); - const getItemHeight = useCallback( (item: TransactionListItemType | ReportListItemType) => { if (SearchUtils.isTransactionListItemType(item)) { @@ -181,15 +172,6 @@ function Search({queryJSON, isCustomQuery}: SearchProps) { const searchResults = currentSearchResults?.data ? currentSearchResults : lastSearchResultsRef.current; - useEffect(() => { - if (isOffline) { - return; - } - - SearchActions.search({queryJSON, offset}); - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [isOffline, offset, queryJSON]); - const isDataLoaded = searchResults?.data !== undefined; const shouldShowLoadingState = !isOffline && !isDataLoaded; const shouldShowLoadingMoreItems = !shouldShowLoadingState && searchResults?.search?.isLoading && searchResults?.search?.offset > 0; @@ -338,42 +320,43 @@ function Search({queryJSON, isCustomQuery}: SearchProps) { onSortPress={onSortPress} sortOrder={sortOrder} sortBy={sortBy} - shouldShowYear={shouldShowYear} - /> - ) - } - canSelectMultiple={canSelectMultiple} - customListHeaderHeight={searchHeaderHeight} - // To enhance the smoothness of scrolling and minimize the risk of encountering blank spaces during scrolling, - // we have configured a larger windowSize and a longer delay between batch renders. - // The windowSize determines the number of items rendered before and after the currently visible items. - // A larger windowSize helps pre-render more items, reducing the likelihood of blank spaces appearing. - // The updateCellsBatchingPeriod sets the delay (in milliseconds) between rendering batches of cells. - // A longer delay allows the UI to handle rendering in smaller increments, which can improve performance and smoothness. - // For more information, refer to the React Native documentation: - // https://reactnative.dev/docs/0.73/optimizing-flatlist-configuration#windowsize - // https://reactnative.dev/docs/0.73/optimizing-flatlist-configuration#updatecellsbatchingperiod - windowSize={111} - updateCellsBatchingPeriod={200} - ListItem={ListItem} - onSelectRow={openReport} - getItemHeight={getItemHeightMemoized} - shouldDebounceRowSelect - shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - listHeaderWrapperStyle={[styles.ph8, styles.pv3, styles.pb5]} - containerStyle={[styles.pv0]} - showScrollIndicator={false} - onEndReachedThreshold={0.75} - onEndReached={fetchMoreResults} - listFooterContent={ - shouldShowLoadingMoreItems ? ( - - ) : undefined - } - /> + ) + } + canSelectMultiple={canSelectMultiple} + customListHeaderHeight={searchHeaderHeight} + // To enhance the smoothness of scrolling and minimize the risk of encountering blank spaces during scrolling, + // we have configured a larger windowSize and a longer delay between batch renders. + // The windowSize determines the number of items rendered before and after the currently visible items. + // A larger windowSize helps pre-render more items, reducing the likelihood of blank spaces appearing. + // The updateCellsBatchingPeriod sets the delay (in milliseconds) between rendering batches of cells. + // A longer delay allows the UI to handle rendering in smaller increments, which can improve performance and smoothness. + // For more information, refer to the React Native documentation: + // https://reactnative.dev/docs/0.73/optimizing-flatlist-configuration#windowsize + // https://reactnative.dev/docs/0.73/optimizing-flatlist-configuration#updatecellsbatchingperiod + windowSize={111} + updateCellsBatchingPeriod={200} + ListItem={ListItem} + onSelectRow={openReport} + getItemHeight={getItemHeightMemoized} + shouldDebounceRowSelect + shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} + listHeaderWrapperStyle={[styles.ph8, styles.pv3, styles.pb5]} + containerStyle={[styles.pv0]} + showScrollIndicator={false} + onEndReachedThreshold={0.75} + onEndReached={fetchMoreResults} + listFooterContent={ + shouldShowLoadingMoreItems ? ( + + ) : undefined + } + /> + Date: Thu, 1 Aug 2024 12:30:11 +0200 Subject: [PATCH 6/6] Fix not sending policyIDs in search --- src/components/Search/index.tsx | 5 +++-- src/libs/API/parameters/Search.ts | 2 ++ src/libs/actions/Search.ts | 4 ++-- src/pages/Search/SearchPage.tsx | 1 + src/pages/Search/SearchPageBottomTab.tsx | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 8cec49c89d6d..af952613f48f 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -35,6 +35,7 @@ import type {SearchColumnType, SearchQueryJSON, SelectedTransactionInfo, Selecte type SearchProps = { queryJSON: SearchQueryJSON; isCustomQuery: boolean; + policyIDs?: string; }; const transactionItemMobileHeight = 100; @@ -70,7 +71,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact return {...selectedTransactions, [item.keyForList]: {isSelected: true, canDelete: item.canDelete, canHold: item.canHold, canUnhold: item.canUnhold, action: item.action}}; } -function Search({queryJSON, isCustomQuery}: SearchProps) { +function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { const {isOffline} = useNetwork(); const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -112,7 +113,7 @@ function Search({queryJSON, isCustomQuery}: SearchProps) { return; } - SearchActions.search({queryJSON, offset}); + SearchActions.search({queryJSON, offset, policyIDs}); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [isOffline, offset, queryJSON]); diff --git a/src/libs/API/parameters/Search.ts b/src/libs/API/parameters/Search.ts index 64bfc5baf5a1..530388dc7f47 100644 --- a/src/libs/API/parameters/Search.ts +++ b/src/libs/API/parameters/Search.ts @@ -3,6 +3,8 @@ import type {SearchQueryString} from '@components/Search/types'; type SearchParams = { hash: number; jsonQuery: SearchQueryString; + // Tod this is temporary, remove top level policyIDs as part of: https://github.com/Expensify/App/issues/46592 + policyIDs?: string; }; export default SearchParams; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index d4a8fc57ddc5..040fd6e47491 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -49,7 +49,7 @@ function getOnyxLoadingData(hash: number): {optimisticData: OnyxUpdate[]; finall return {optimisticData, finallyData}; } -function search({queryJSON, offset}: {queryJSON: SearchQueryJSON; offset?: number}) { +function search({queryJSON, offset, policyIDs}: {queryJSON: SearchQueryJSON; offset?: number; policyIDs?: string}) { const {optimisticData, finallyData} = getOnyxLoadingData(queryJSON.hash); const queryWithOffset = { @@ -58,7 +58,7 @@ function search({queryJSON, offset}: {queryJSON: SearchQueryJSON; offset?: numbe }; const jsonQuery = JSON.stringify(queryWithOffset); - API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery}, {optimisticData, finallyData}); + API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery, policyIDs}, {optimisticData, finallyData}); } /** diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 9eddd5deae8d..e2495c02d44f 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -45,6 +45,7 @@ function SearchPage({route}: SearchPageProps) { )} diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index c436276ff9e4..1b9e81c8a035 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -81,6 +81,7 @@ function SearchPageBottomTab() { )}