From 25acc96fcba9e6e32751d4b97a7e2adaa8e278cb Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Fri, 19 Jul 2024 13:29:31 +0200 Subject: [PATCH 01/13] use new query syntax --- src/CONST.ts | 15 +++- src/ROUTES.ts | 26 +++---- src/components/PromotedActionsBar.tsx | 5 +- .../Search/SearchListWithHeader.tsx | 10 +-- src/components/Search/SearchPageHeader.tsx | 13 ++-- src/components/Search/index.tsx | 56 +++++++++----- src/components/Search/types.ts | 36 ++++++++- .../SelectionList/Search/ActionCell.tsx | 2 +- .../SelectionList/Search/ReportListItem.tsx | 5 +- .../Navigation/AppNavigator/AuthScreens.tsx | 4 +- .../BottomTabBar/index.tsx | 2 +- .../BottomTabBar/index.website.tsx | 2 +- src/libs/Navigation/switchPolicyID.ts | 2 +- src/libs/Navigation/types.ts | 21 ++--- src/libs/SearchUtils.ts | 76 +++++++++++++------ src/libs/actions/Search.ts | 18 +++++ src/pages/Search/SearchPage.tsx | 21 +++-- src/pages/Search/SearchPageBottomTab.tsx | 46 ++++------- ...SearchFilters.tsx => SearchStatusMenu.tsx} | 40 +++++----- ...sNarrow.tsx => SearchStatusMenuNarrow.tsx} | 12 +-- .../Subscription/CardSection/CardSection.tsx | 2 +- src/types/onyx/SearchResults.ts | 15 +++- 22 files changed, 250 insertions(+), 179 deletions(-) rename src/pages/Search/{SearchFilters.tsx => SearchStatusMenu.tsx} (69%) rename src/pages/Search/{SearchFiltersNarrow.tsx => SearchStatusMenuNarrow.tsx} (90%) diff --git a/src/CONST.ts b/src/CONST.ts index 3f141905e84c..f9f2b7114979 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5202,12 +5202,20 @@ const CONST = { ASC: 'asc', DESC: 'desc', }, - TAB: { + STATUS: { ALL: 'all', SHARED: 'shared', DRAFTS: 'drafts', FINISHED: 'finished', }, + TAB: { + EXPENSE: { + ALL: 'type:expense status:all', + SHARED: 'type:expense status:shared', + DRAFTS: 'type:expense status:drafts', + FINISHED: 'type:expense status:finished', + }, + }, TABLE_COLUMNS: { RECEIPT: 'receipt', DATE: 'date', @@ -5263,6 +5271,11 @@ const CONST = { REPORT_ID: 'reportID', KEYWORD: 'keyword', }, + QUERY_KIND: { + CANNED_QUERY: 'cannedQuery', + CUSTOM_QUERY: 'customQuery', + }, + DEFAULT_QUERY: 'type:all sortBy:date sortOrder:desc', }, REFERRER: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f13724bf4322..876fb3c41058 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1,10 +1,9 @@ import type {TupleToUnion, ValueOf} from 'type-fest'; -import type CONST from './CONST'; +import type {QueryKind, SearchQueryString} from './components/Search/types'; +import CONST from './CONST'; import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; -import type {AuthScreensParamList} from './libs/Navigation/types'; import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; -import type {SearchQuery} from './types/onyx/SearchResults'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; // This is a file containing constants for all the routes we want to be able to go to @@ -37,26 +36,19 @@ const ROUTES = { ALL_SETTINGS: 'all-settings', SEARCH: { - route: '/search/:query', - getRoute: (searchQuery: SearchQuery, queryParams?: AuthScreensParamList['Search_Central_Pane']) => { - const {sortBy, sortOrder} = queryParams ?? {}; - - if (!sortBy && !sortOrder) { - return `search/${searchQuery}` as const; - } - - return `search/${searchQuery}?sortBy=${sortBy}&sortOrder=${sortOrder}` as const; - }, + route: '/search', + getRoute: ({query, queryKind = CONST.SEARCH.QUERY_KIND.CANNED_QUERY, policyIDs}: {query: SearchQueryString; queryKind?: QueryKind; policyIDs?: string}) => + `search?${queryKind === CONST.SEARCH.QUERY_KIND.CANNED_QUERY ? 'q' : 'cq'}=${query}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const, }, SEARCH_REPORT: { - route: '/search/:query/view/:reportID', - getRoute: (query: string, reportID: string) => `search/${query}/view/${reportID}` as const, + route: '/search/view/:reportID', + getRoute: (reportID: string) => `search/view/${reportID}` as const, }, TRANSACTION_HOLD_REASON_RHP: { - route: '/search/:query/hold/:transactionID', - getRoute: (query: string, transactionID: string) => `search/${query}/hold/${transactionID}` as const, + route: '/search/hold/:transactionID', + getRoute: (transactionID: string) => `search/hold/${transactionID}` as const, }, // This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx index 1aefe941011b..014673ce70bc 100644 --- a/src/components/PromotedActionsBar.tsx +++ b/src/components/PromotedActionsBar.tsx @@ -7,7 +7,7 @@ import * as HeaderUtils from '@libs/HeaderUtils'; import * as Localize from '@libs/Localize'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; -import type {AuthScreensParamList, RootStackParamList, State} from '@libs/Navigation/types'; +import type {RootStackParamList, State} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; import * as ReportActions from '@userActions/Report'; import * as Session from '@userActions/Session'; @@ -80,8 +80,7 @@ const PromotedActions = { return; } - const currentQuery = topmostCentralPaneRoute?.params as AuthScreensParamList['Search_Central_Pane']; - ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(currentQuery?.query ?? CONST.SEARCH.TAB.ALL, reportAction?.childReportID ?? '')); + ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(reportAction?.childReportID ?? '')); }, }), } satisfies PromotedActionsType; diff --git a/src/components/Search/SearchListWithHeader.tsx b/src/components/Search/SearchListWithHeader.tsx index 283b68bf17af..c0c40d8181ec 100644 --- a/src/components/Search/SearchListWithHeader.tsx +++ b/src/components/Search/SearchListWithHeader.tsx @@ -11,12 +11,12 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as SearchActions from '@libs/actions/Search'; import * as SearchUtils from '@libs/SearchUtils'; import CONST from '@src/CONST'; -import type {SearchDataTypes, SearchQuery} from '@src/types/onyx/SearchResults'; +import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import SearchPageHeader from './SearchPageHeader'; -import type {SelectedTransactionInfo, SelectedTransactions} from './types'; +import type {SearchStatus, SelectedTransactionInfo, SelectedTransactions} from './types'; type SearchListWithHeaderProps = Omit, 'onSelectAll' | 'onCheckboxPress' | 'sections'> & { - query: SearchQuery; + status: SearchStatus; hash: number; data: TransactionListItemType[] | ReportListItemType[]; searchType: SearchDataTypes; @@ -43,7 +43,7 @@ function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListIt } function SearchListWithHeader( - {ListItem, onSelectRow, query, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps, + {ListItem, onSelectRow, status, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps, ref: ForwardedRef, ) { const {isSmallScreenWidth} = useWindowDimensions(); @@ -173,7 +173,7 @@ function SearchListWithHeader( void; hash: number; @@ -30,13 +29,13 @@ type SearchPageHeaderProps = { type SearchHeaderOptionValue = DeepValueOf | undefined; -function SearchPageHeader({query, selectedItems = {}, hash, clearSelectedItems, onSelectDeleteOption, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchPageHeaderProps) { +function SearchPageHeader({status, selectedItems = {}, hash, clearSelectedItems, onSelectDeleteOption, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchPageHeaderProps) { const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useResponsiveLayout(); - const headerContent: {[key in SearchQuery]: {icon: IconAsset; title: string}} = { + const headerContent: {[key in SearchStatus]: {icon: IconAsset; title: string}} = { all: {icon: Illustrations.MoneyReceipts, title: translate('common.expenses')}, shared: {icon: Illustrations.SendMoney, title: translate('common.shared')}, drafts: {icon: Illustrations.Pencil, title: translate('common.drafts')}, @@ -146,8 +145,8 @@ function SearchPageHeader({query, selectedItems = {}, hash, clearSelectedItems, return ( {headerButtonsOptions.length > 0 && ( diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 78992496f031..4b348b9ea594 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -23,27 +23,25 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SearchResults from '@src/types/onyx/SearchResults'; -import type {SearchDataTypes, SearchQuery} from '@src/types/onyx/SearchResults'; +import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import {useSearchContext} from './SearchContext'; import SearchListWithHeader from './SearchListWithHeader'; import SearchPageHeader from './SearchPageHeader'; -import type {SearchColumnType, SortOrder} from './types'; +import type {SearchColumnType, SearchQueryJSON, SearchStatus, SortOrder} from './types'; type SearchProps = { - query: SearchQuery; + queryJSON: SearchQueryJSON; policyIDs?: string; - sortBy?: SearchColumnType; - sortOrder?: SortOrder; isMobileSelectionModeActive?: boolean; setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void; }; -const sortableSearchTabs: SearchQuery[] = [CONST.SEARCH.TAB.ALL]; +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({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchProps) { +function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchProps) { const {isOffline} = useNetwork(); const styles = useThemeStyles(); const {isLargeScreenWidth, isSmallScreenWidth} = useWindowDimensions(); @@ -51,7 +49,8 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv const lastSearchResultsRef = useRef>(); const {setCurrentSearchHash} = useSearchContext(); - const hash = SearchUtils.getQueryHash(query, policyIDs, sortBy, sortOrder); + const {status, sortBy, sortOrder, hash} = queryJSON; + const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); const getItemHeight = useCallback( @@ -97,7 +96,9 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv } setCurrentSearchHash(hash); - SearchActions.search({hash, query, policyIDs, offset: 0, sortBy, sortOrder}); + + // TODO_SEARCH: Function below will be deprecated soon. No point in refactoring to use status instead of query. + SearchActions.search({hash, query: status, policyIDs, offset: 0, sortBy, sortOrder}); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [hash, isOffline]); @@ -108,7 +109,7 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv return ( <> @@ -122,7 +123,7 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv return ( <> @@ -143,15 +144,27 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv SearchActions.createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID); } - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(query, reportID)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(reportID)); }; const fetchMoreResults = () => { if (!searchResults?.search?.hasMoreResults || isLoadingItems || isLoadingMoreItems) { return; } - const currentOffset = searchResults?.search?.offset ?? 0; - SearchActions.search({hash, query, offset: currentOffset + CONST.SEARCH.RESULTS_PAGE_SIZE, sortBy, sortOrder}); + + const currentSearchParams = SearchUtils.getCurrentSearchParams(); + const currentQueryString = SearchUtils.getQueryStringFromParams(currentSearchParams); + const isCustomQuery = SearchUtils.isCustomQueryFromParams(currentSearchParams); + const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentQueryString); + + if (!currentQueryJSON) { + return; + } + + // TODO_SEARCH: offset should be a number but it is a string. + const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, offset: Number(currentQueryJSON.offset) + CONST.SEARCH.RESULTS_PAGE_SIZE}); + + navigation.setParams(isCustomQuery ? {cq: newQuery} : {q: newQuery}); }; const type = SearchUtils.getSearchType(searchResults?.search); @@ -167,13 +180,16 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv const sortedData = SearchUtils.getSortedSections(type, data, sortBy, sortOrder); const onSortPress = (column: SearchColumnType, order: SortOrder) => { - navigation.setParams({ - sortBy: column, - sortOrder: order, - }); + const currentSearchParams = SearchUtils.getCurrentSearchParams(); + const currentQueryString = SearchUtils.getQueryStringFromParams(currentSearchParams); + const isCustomQuery = SearchUtils.isCustomQueryFromParams(currentSearchParams); + const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentQueryString); + + const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, sortBy: column, sortOrder: order}); + navigation.setParams(isCustomQuery ? {cq: newQuery} : {q: newQuery}); }; - const isSortingAllowed = sortableSearchTabs.includes(query); + const isSortingAllowed = sortableSearchTabs.includes(status); const shouldShowYear = SearchUtils.shouldShowYear(searchResults?.data); @@ -181,7 +197,7 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv return ( ; type SortOrder = ValueOf; type SearchColumnType = ValueOf; +type SearchStatus = ValueOf; type SearchContext = { currentSearchHash: number; @@ -43,4 +44,37 @@ type QueryFilters = { [K in AllFieldKeys]: QueryFilter | QueryFilter[]; }; -export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilter, QueryFilters, AllFieldKeys}; +type QueryKind = ValueOf; + +type SearchQueryString = string; + +type SearchQueryAST = { + type: string; + status: SearchStatus; + sortBy: SearchColumnType; + sortOrder: SortOrder; + offset: number; + filters: ASTNode; +}; + +type SearchQueryJSON = { + input: string; + hash: number; +} & SearchQueryAST; + +export type { + QueryKind, + SelectedTransactionInfo, + SelectedTransactions, + SearchColumnType, + SearchStatus, + SearchQueryAST, + SearchQueryJSON, + SearchQueryString, + SortOrder, + SearchContext, + ASTNode, + QueryFilter, + QueryFilters, + AllFieldKeys, +}; diff --git a/src/components/SelectionList/Search/ActionCell.tsx b/src/components/SelectionList/Search/ActionCell.tsx index 7888a8b26114..a5f977f40bf5 100644 --- a/src/components/SelectionList/Search/ActionCell.tsx +++ b/src/components/SelectionList/Search/ActionCell.tsx @@ -57,7 +57,7 @@ function ActionCell({ } if (action === CONST.SEARCH.ACTION_TYPES.HOLD) { - Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP.getRoute(CONST.SEARCH.TAB.ALL, transactionID)); + Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP.getRoute(transactionID)); } else if (action === CONST.SEARCH.ACTION_TYPES.UNHOLD) { SearchActions.unholdMoneyRequestOnSearch(currentSearchHash, [transactionID]); } diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index e6358698d414..e2dde846f020 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -10,7 +10,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getSearchParams} from '@libs/SearchUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import ActionCell from './ActionCell'; @@ -78,9 +77,7 @@ function ReportListItem({ }; const openReportInRHP = (transactionItem: TransactionListItemType) => { - const searchParams = getSearchParams(); - const currentQuery = searchParams?.query ?? CONST.SEARCH.TAB.ALL; - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(currentQuery, transactionItem.transactionThreadReportID)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(transactionItem.transactionThreadReportID)); }; if (!reportItem?.reportName && reportItem.transactions.length > 1) { diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 0e40fa4d4037..866253ae5646 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -19,6 +19,7 @@ import type {AuthScreensParamList, CentralPaneName, CentralPaneScreensParamList} import NetworkConnection from '@libs/NetworkConnection'; import * as Pusher from '@libs/Pusher/pusher'; import PusherConnectionManager from '@libs/PusherConnectionManager'; +import {buildSearchQueryString} from '@libs/SearchUtils'; import * as SessionUtils from '@libs/SessionUtils'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -84,7 +85,8 @@ function shouldOpenOnAdminRoom() { function getCentralPaneScreenInitialParams(screenName: CentralPaneName): Partial> { if (screenName === SCREENS.SEARCH.CENTRAL_PANE) { - return {sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, sortOrder: CONST.SEARCH.SORT_ORDER.DESC}; + // Generate default query string with buildSearchQueryString without argument. + return {q: buildSearchQueryString()}; } if (screenName === SCREENS.REPORT) { diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx index f244342c28ae..90e62a9ecda7 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx @@ -112,7 +112,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps if (currentTabName === SCREENS.SEARCH.BOTTOM_TAB || currentTabName === SCREENS.SEARCH.CENTRAL_PANE) { return; } - interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL))); + interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}))); }} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.search')} diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 556365b473c3..01023435a8bc 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -112,7 +112,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps if (isSearchTabName(activeBottomTabRoute?.name)) { return; } - interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL))); + interceptAnonymousUser(() => Navigation.navigate(ROUTES.SEARCH.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}))); }} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.search')} diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 19626a400b9d..283ab5c1235d 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -84,7 +84,7 @@ export default function switchPolicyID(navigation: NavigationContainerRef>; const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config); diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index c273b2db6a10..bfa2f4dddb11 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -11,7 +11,7 @@ import type { Route, } from '@react-navigation/native'; import type {TupleToUnion, ValueOf} from 'type-fest'; -import type {SearchColumnType, SortOrder} from '@components/Search/types'; +import type {SearchQueryString} from '@components/Search/types'; import type {IOURequestType} from '@libs/actions/IOU'; import type CONST from '@src/CONST'; import type {Country, IOUAction, IOUType} from '@src/CONST'; @@ -64,13 +64,9 @@ type CentralPaneScreensParamList = { [SCREENS.SETTINGS.ABOUT]: undefined; [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; [SCREENS.SETTINGS.WORKSPACES]: undefined; - [SCREENS.SEARCH.CENTRAL_PANE]: { - query: string; - policyIDs?: string; - offset?: number; - sortBy?: SearchColumnType; - sortOrder?: SortOrder; - }; + + // Param types of the search central pane are also used for the search bottom tab screen. + [SCREENS.SEARCH.CENTRAL_PANE]: ({cq: SearchQueryString; q?: never} | {q: SearchQueryString; cq?: never}) & {policyIDs?: string}; [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined; }; @@ -1164,13 +1160,7 @@ type BottomTabScreensParamList = {[SCREENS.HOME]: undefined; [SCREENS.REPORT]: u type BottomTabNavigatorParamList = { [SCREENS.HOME]: {policyID?: string}; - [SCREENS.SEARCH.BOTTOM_TAB]: { - query: string; - policyID?: string; - offset?: number; - sortBy?: SearchColumnType; - sortOrder?: SortOrder; - }; + [SCREENS.SEARCH.BOTTOM_TAB]: CentralPaneScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; [SCREENS.SETTINGS.ROOT]: {policyID?: string}; }; @@ -1245,7 +1235,6 @@ type AuthScreensParamList = CentralPaneScreensParamList & type SearchReportParamList = { [SCREENS.SEARCH.REPORT_RHP]: { - query: string; reportID: string; }; }; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 4ec3fa9fb314..4c0e5e8d2451 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,10 +1,11 @@ import type {ValueOf} from 'type-fest'; -import type {AllFieldKeys, ASTNode, QueryFilter, QueryFilters, SearchColumnType, SortOrder} from '@components/Search/types'; +import type {AllFieldKeys, 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'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {SearchAccountDetails, SearchDataTypes, SearchPersonalDetails, SearchTransaction, SearchTypeToItemMap, SectionsType} from '@src/types/onyx/SearchResults'; import type SearchResults from '@src/types/onyx/SearchResults'; @@ -299,34 +300,32 @@ function getSortedReportData(data: ReportListItemType[]) { }); } -function getSearchParams() { +function getCurrentSearchParams() { const topmostCentralPaneRoute = getTopmostCentralPaneRoute(navigationRef.getRootState() as State); return topmostCentralPaneRoute?.params as AuthScreensParamList['Search_Central_Pane']; } +// Query may be in the q or cq parameter +function getQueryStringFromParams(params: AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]) { + return params.q ?? params.cq; +} + +function isCustomQueryFromParams(params: AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]) { + return !!params.cq; +} + function isSearchResultsEmpty(searchResults: SearchResults) { return !Object.keys(searchResults?.data).some((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION)); } -function getQueryHashFromString(query: string): number { +function getQueryHashFromString(query: SearchQueryString): number { return UserUtils.hashText(query, 2 ** 32); } -type JSONQuery = { - input: string; - hash: number; - type: string; - status: string; - sortBy: string; - sortOrder: string; - offset: number; - filters: ASTNode; -}; - -function buildJSONQuery(query: string) { +function buildSearchQueryJSON(query: SearchQueryString) { try { // Add the full input and hash to the results - const result = searchParser.parse(query) as JSONQuery; + const result = searchParser.parse(query) as SearchQueryJSON; result.input = query; result.hash = getQueryHashFromString(query); return result; @@ -335,10 +334,33 @@ function buildJSONQuery(query: string) { } } -function getFilters(query: string, fields: Array>) { - let jsonQuery; +function buildSearchQueryString(partialQueryJSON?: Partial) { + const queryParts: string[] = []; + const defualtQueryJSON = 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]}`); + } else if (defualtQueryJSON) { + queryParts.push(`${value}:${defualtQueryJSON[value]}`); + } + } + + return queryParts.join(' '); +} + +// Fill query string with all default params. +function normalizeQuery(query: string) { + const normalizedQueryJSON = buildSearchQueryJSON(query); + return buildSearchQueryString(normalizedQueryJSON); +} + +function getFilters(query: SearchQueryString, fields: Array>) { + let queryAST; + try { - jsonQuery = searchParser.parse(query) as JSONQuery; + queryAST = searchParser.parse(query) as SearchQueryJSON; } catch (e) { console.error(e); return; @@ -348,13 +370,13 @@ function getFilters(query: string, fields: Array>) { fields.forEach((field) => { const rootFieldKey = field as ValueOf; - if (jsonQuery[rootFieldKey] === undefined) { + if (queryAST[rootFieldKey] === undefined) { return; } filters[field] = { operator: 'eq', - value: jsonQuery[rootFieldKey], + value: queryAST[rootFieldKey], }; }); @@ -387,25 +409,29 @@ function getFilters(query: string, fields: Array>) { }); } - if (jsonQuery.filters) { - traverse(jsonQuery.filters); + if (queryAST.filters) { + traverse(queryAST.filters); } return filters; } export { - buildJSONQuery, + isCustomQueryFromParams, + buildSearchQueryJSON, + buildSearchQueryString, + getQueryStringFromParams, + getCurrentSearchParams, getListItem, getQueryHash, getSections, getSortedSections, getShouldShowMerchant, getSearchType, - getSearchParams, shouldShowYear, isReportListItemType, isTransactionListItemType, isSearchResultsEmpty, getFilters, + normalizeQuery, }; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 4ce82a027a12..a45890a190b2 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -1,8 +1,10 @@ import Onyx from 'react-native-onyx'; import type {OnyxUpdate} from 'react-native-onyx'; +import type {SearchQueryString} from '@components/Search/types'; import * as API from '@libs/API'; import type {SearchParams} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import {buildSearchQueryJSON} from '@libs/SearchUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {SearchTransaction} from '@src/types/onyx/SearchResults'; import * as Report from './Report'; @@ -49,6 +51,22 @@ function search({hash, query, policyIDs, offset, sortBy, sortOrder}: SearchParam 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; + } + + const {optimisticData, finallyData} = getOnyxLoadingData(queryJSON.hash); + + // TODO_SEARCH: uncomment this line after backend changes + // @ts-expect-error waiting for backend changes + API.read(READ_COMMANDS.SEARCH, queryJSON, {optimisticData, finallyData}); +} + /** * It's possible that we return legacy transactions that don't have a transaction thread created yet. * In that case, when users select the search result row, we need to create the transaction thread on the fly and update the search result with the new transactionThreadReport diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 6e734fd835d2..3644bc10eca0 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React from 'react'; +import React, {useMemo} from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; @@ -7,10 +7,10 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; +import {buildSearchQueryJSON, getQueryStringFromParams} from '@libs/SearchUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {SearchQuery} from '@src/types/onyx/SearchResults'; type SearchPageProps = StackScreenProps; @@ -18,12 +18,9 @@ function SearchPage({route}: SearchPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); - const {query: rawQuery, policyIDs, sortBy, sortOrder} = route?.params ?? {}; + const queryJSON = useMemo(() => buildSearchQueryJSON(getQueryStringFromParams(route.params)), [route.params]); - const query = rawQuery as SearchQuery; - const isValidQuery = Object.values(CONST.SEARCH.TAB).includes(query); - - const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL)); + const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); // On small screens this page is not displayed, the configuration is in the file: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx // To avoid calling hooks in the Search component when this page isn't visible, we return null here. @@ -39,15 +36,15 @@ function SearchPage({route}: SearchPageProps) { > diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 9ae6423a6cc1..fa72ae20f24c 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -1,4 +1,3 @@ -import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo, useState} from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -9,22 +8,12 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; -import type {CentralPaneScreensParamList} from '@libs/Navigation/types'; +import {buildSearchQueryJSON} from '@libs/SearchUtils'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import type {SearchQuery} from '@src/types/onyx/SearchResults'; -import SearchFilters from './SearchFilters'; +import SearchStatuses from './SearchStatusMenu'; -type SearchPageProps = StackScreenProps; - -const defaultSearchProps = { - query: '' as SearchQuery, - policyIDs: undefined, - sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, - sortOrder: CONST.SEARCH.SORT_ORDER.DESC, -}; function SearchPageBottomTab() { const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -32,23 +21,16 @@ function SearchPageBottomTab() { const styles = useThemeStyles(); const [isMobileSelectionModeActive, setIsMobileSelectionModeActive] = useState(false); - const { - query: rawQuery, - policyIDs, - sortBy, - sortOrder, - } = useMemo(() => { - if (activeBottomTabRoute?.name !== SCREENS.SEARCH.CENTRAL_PANE || !activeBottomTabRoute.params) { - return defaultSearchProps; - } - return {...defaultSearchProps, ...activeBottomTabRoute.params} as SearchPageProps['route']['params']; - }, [activeBottomTabRoute]); - - const query = rawQuery as SearchQuery; + // TODO_SEARCH: types for the activeBottomTabRoute are broken. + const queryJSON = useMemo(() => buildSearchQueryJSON(activeBottomTabRoute.params.cq ?? activeBottomTabRoute.params.q), [activeBottomTabRoute.params]); + const policyIDs = activeBottomTabRoute.params.policyIDs as string | undefined; - const isValidQuery = Object.values(CONST.SEARCH.TAB).includes(query); + const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); - const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL)); + // TODO_SEARCH: not sure how we should handle possible undefined for queryJSON. + if (!queryJSON) { + return null; + } return ( @@ -68,7 +50,7 @@ function SearchPageBottomTab() { breadcrumbLabel={translate('common.search')} shouldDisplaySearch={false} /> - + ) : ( diff --git a/src/pages/Search/SearchFilters.tsx b/src/pages/Search/SearchStatusMenu.tsx similarity index 69% rename from src/pages/Search/SearchFilters.tsx rename to src/pages/Search/SearchStatusMenu.tsx index bbb861364f00..eb3a072281d5 100644 --- a/src/pages/Search/SearchFilters.tsx +++ b/src/pages/Search/SearchStatusMenu.tsx @@ -1,63 +1,65 @@ import React from 'react'; import {View} from 'react-native'; import MenuItem from '@components/MenuItem'; +import type {SearchStatus} from '@components/Search/types'; import useLocalize from '@hooks/useLocalize'; import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; +import {normalizeQuery} from '@libs/SearchUtils'; import variables from '@styles/variables'; import * as Expensicons from '@src/components/Icon/Expensicons'; import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type IconAsset from '@src/types/utils/IconAsset'; -import SearchFiltersNarrow from './SearchFiltersNarrow'; +import SearchFiltersNarrow from './SearchStatusMenuNarrow'; -type SearchFiltersProps = { - query: string; +type SearchStatusMenuProps = { + status: SearchStatus; }; -type SearchMenuFilterItem = { +type SearchStatusMenuItem = { title: string; - query: string; + status: SearchStatus; icon: IconAsset; route: Route; }; -function SearchFilters({query}: SearchFiltersProps) { +function SearchStatusMenu({status}: SearchStatusMenuProps) { const styles = useThemeStyles(); const {isSmallScreenWidth} = useWindowDimensions(); const {singleExecution} = useSingleExecution(); const {translate} = useLocalize(); - const filterItems: SearchMenuFilterItem[] = [ + const filterItems: SearchStatusMenuItem[] = [ { title: translate('common.expenses'), - query: CONST.SEARCH.TAB.ALL, + status: CONST.SEARCH.STATUS.ALL, icon: Expensicons.Receipt, - route: ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL), + route: ROUTES.SEARCH.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.ALL)}), }, { title: translate('common.shared'), - query: CONST.SEARCH.TAB.SHARED, + status: CONST.SEARCH.STATUS.SHARED, icon: Expensicons.Send, - route: ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.SHARED), + route: ROUTES.SEARCH.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.SHARED)}), }, { title: translate('common.drafts'), - query: CONST.SEARCH.TAB.DRAFTS, + status: CONST.SEARCH.STATUS.DRAFTS, icon: Expensicons.Pencil, - route: ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.DRAFTS), + route: ROUTES.SEARCH.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.DRAFTS)}), }, { title: translate('common.finished'), - query: CONST.SEARCH.TAB.FINISHED, + status: CONST.SEARCH.STATUS.FINISHED, icon: Expensicons.CheckCircle, - route: ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.FINISHED), + route: ROUTES.SEARCH.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.FINISHED)}), }, ]; - const activeItemIndex = filterItems.findIndex((item) => item.query === query); + const activeItemIndex = filterItems.findIndex((item) => item.status === status); if (isSmallScreenWidth) { return ( @@ -94,7 +96,7 @@ function SearchFilters({query}: SearchFiltersProps) { ); } -SearchFilters.displayName = 'SearchFilters'; +SearchStatusMenu.displayName = 'SearchStatusMenu'; -export default SearchFilters; -export type {SearchMenuFilterItem}; +export default SearchStatusMenu; +export type {SearchStatusMenuItem}; diff --git a/src/pages/Search/SearchFiltersNarrow.tsx b/src/pages/Search/SearchStatusMenuNarrow.tsx similarity index 90% rename from src/pages/Search/SearchFiltersNarrow.tsx rename to src/pages/Search/SearchStatusMenuNarrow.tsx index e890432df7f1..896c15c0fd13 100644 --- a/src/pages/Search/SearchFiltersNarrow.tsx +++ b/src/pages/Search/SearchStatusMenuNarrow.tsx @@ -10,14 +10,14 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import * as Expensicons from '@src/components/Icon/Expensicons'; -import type {SearchMenuFilterItem} from './SearchFilters'; +import type {SearchStatusMenuItem} from './SearchStatusMenu'; -type SearchFiltersNarrowProps = { - filterItems: SearchMenuFilterItem[]; +type SearchStatusMenuNarrowProps = { + filterItems: SearchStatusMenuItem[]; activeItemIndex: number; }; -function SearchFiltersNarrow({filterItems, activeItemIndex}: SearchFiltersNarrowProps) { +function SearchStatusMenuNarrow({filterItems, activeItemIndex}: SearchStatusMenuNarrowProps) { const theme = useTheme(); const styles = useThemeStyles(); const {singleExecution} = useSingleExecution(); @@ -77,6 +77,6 @@ function SearchFiltersNarrow({filterItems, activeItemIndex}: SearchFiltersNarrow ); } -SearchFiltersNarrow.displayName = 'SearchFiltersNarrow'; +SearchStatusMenuNarrow.displayName = 'SearchStatusMenuNarrow'; -export default SearchFiltersNarrow; +export default SearchStatusMenuNarrow; diff --git a/src/pages/settings/Subscription/CardSection/CardSection.tsx b/src/pages/settings/Subscription/CardSection/CardSection.tsx index 4cc160fc13b2..6b8d15cccfec 100644 --- a/src/pages/settings/Subscription/CardSection/CardSection.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSection.tsx @@ -148,7 +148,7 @@ function CardSection() { title={translate('subscription.cardSection.viewPaymentHistory')} titleStyle={styles.textStrong} style={styles.mt5} - onPress={() => Navigation.navigate(ROUTES.SEARCH.getRoute(CONST.SEARCH.TAB.ALL))} + onPress={() => Navigation.navigate(ROUTES.SEARCH.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}))} hoverAndPressStyle={styles.hoveredComponentBG} /> )} diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts index 97a8b459ff0f..fb787903dd8a 100644 --- a/src/types/onyx/SearchResults.ts +++ b/src/types/onyx/SearchResults.ts @@ -64,6 +64,17 @@ type SearchResultsInfo = { /** The optional columns that should be shown according to policy settings */ columnsToShow: ColumnsToShow; + + /** The status of the search results to show */ + statusToShow: { + // eslint-disable-next-line jsdoc/require-jsdoc + expense: { + // eslint-disable-next-line jsdoc/require-jsdoc + drafts: boolean; + // eslint-disable-next-line jsdoc/require-jsdoc + approved: boolean; + }; + }; }; /** Model of personal details search result */ @@ -237,9 +248,6 @@ type SearchAccountDetails = Partial /** Types of searchable transactions */ type SearchTransactionType = ValueOf; -/** Types of search queries */ -type SearchQuery = ValueOf; - /** Model of search results */ type SearchResults = { /** Current search results state */ @@ -255,7 +263,6 @@ type SearchResults = { export default SearchResults; export type { - SearchQuery, SearchTransaction, SearchTransactionType, SearchTransactionAction, From 6f9ba6ded6247aa0bf6ab8dd07f446687e76ef24 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Fri, 19 Jul 2024 14:34:17 +0200 Subject: [PATCH 02/13] fix problems after merge --- .../API/parameters/ExportSearchItemsToCSVParams.ts | 4 ++-- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/Navigation/types.ts | 2 +- src/pages/Search/SearchPageBottomTab.tsx | 14 +++++--------- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libs/API/parameters/ExportSearchItemsToCSVParams.ts b/src/libs/API/parameters/ExportSearchItemsToCSVParams.ts index 979a7b99886f..2659fac6810a 100644 --- a/src/libs/API/parameters/ExportSearchItemsToCSVParams.ts +++ b/src/libs/API/parameters/ExportSearchItemsToCSVParams.ts @@ -1,7 +1,7 @@ -import type {SearchQuery} from '@src/types/onyx/SearchResults'; +import type {SearchStatus} from '@components/Search/types'; type ExportSearchItemsToCSVParams = { - query: SearchQuery; + query: SearchStatus; reportIDList: string[]; transactionIDList: string[]; policyIDs: string[]; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 4b4cc26f4254..a3b5a98e3de8 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -968,7 +968,7 @@ const config: LinkingOptions['config'] = { [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { screens: { [SCREENS.SEARCH.REPORT_RHP]: ROUTES.SEARCH_REPORT.route, - [SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: ROUTES.TRANSACTION_HOLD_REASON_RHP.route, + [SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: ROUTES.TRANSACTION_HOLD_REASON_RHP, }, }, [SCREENS.RIGHT_MODAL.SEARCH_ADVANCED_FILTERS]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index d96d28ebe067..fa5b4f8fa780 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1158,7 +1158,7 @@ type ExplanationModalNavigatorParamList = { type BottomTabNavigatorParamList = { [SCREENS.HOME]: {policyID?: string}; - [SCREENS.SEARCH.BOTTOM_TAB]: CentralPaneScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; + [SCREENS.SEARCH.BOTTOM_TAB]: Omit & {policyID: string}; [SCREENS.SETTINGS.ROOT]: {policyID?: string}; }; diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 827b93caff98..916531a7ec37 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -23,7 +23,6 @@ function SearchPageBottomTab() { const styles = useThemeStyles(); const [isMobileSelectionModeActive, setIsMobileSelectionModeActive] = useState(false); - // TODO_SEARCH: types for the activeBottomTabRoute are broken. const {queryJSON, policyIDs} = useMemo(() => { if (!activeCentralPaneRoute || activeCentralPaneRoute.name !== SCREENS.SEARCH.CENTRAL_PANE) { return {queryJSON: undefined, policyIDs: undefined}; @@ -40,11 +39,6 @@ function SearchPageBottomTab() { const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); - // TODO_SEARCH: not sure how we should handle possible undefined for queryJSON. - if (!queryJSON || !policyIDs) { - return null; - } - return ( @@ -63,7 +57,8 @@ function SearchPageBottomTab() { breadcrumbLabel={translate('common.search')} shouldDisplaySearch={false} /> - + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} + ) : ( Date: Fri, 19 Jul 2024 14:47:21 +0200 Subject: [PATCH 03/13] remove default query --- src/CONST.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 9979e1bdca82..6bbe096659d4 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5275,7 +5275,6 @@ const CONST = { CANNED_QUERY: 'cannedQuery', CUSTOM_QUERY: 'customQuery', }, - DEFAULT_QUERY: 'type:all sortBy:date sortOrder:desc', }, REFERRER: { From 1c7e9085338c8139f3b58ccd8068fbbf1e6e3a7f Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Mon, 22 Jul 2024 12:42:16 +0200 Subject: [PATCH 04/13] improve handling undefined queryJSON --- src/pages/Search/SearchPage.tsx | 12 ++++++------ src/pages/Search/SearchPageBottomTab.tsx | 10 ++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 9ad570ca2d1b..262f579d464b 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -40,12 +40,12 @@ function SearchPage({route}: SearchPageProps) { onBackButtonPress={handleOnBackButtonPress} shouldShowLink={false} > - + {queryJSON && ( + + )} ); diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 295e4cf1c4e0..63ffd35834af 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -50,15 +50,14 @@ function SearchPageBottomTab() { onBackButtonPress={handleOnBackButtonPress} shouldShowLink={false} > - {!isMobileSelectionModeActive ? ( + {!isMobileSelectionModeActive && queryJSON ? ( <> - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - + ) : ( setIsMobileSelectionModeActive(false)} /> )} - {isSmallScreenWidth && ( + {isSmallScreenWidth && queryJSON && ( Date: Mon, 22 Jul 2024 17:10:28 +0200 Subject: [PATCH 05/13] fix imports for SearchStatusMenu --- src/pages/Search/SearchPageBottomTab.tsx | 4 ++-- src/pages/Search/SearchStatusMenu.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 63ffd35834af..74f3541b30a0 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -14,7 +14,7 @@ import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopB import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import SearchStatuses from './SearchStatusMenu'; +import SearchStatusMenu from './SearchStatusMenu'; function SearchPageBottomTab() { const {translate} = useLocalize(); @@ -57,7 +57,7 @@ function SearchPageBottomTab() { breadcrumbLabel={translate('common.search')} shouldDisplaySearch={false} /> - + ) : ( From a4d185a39d4f957e657374da58c6fe7e1bf5fd69 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 23 Jul 2024 12:34:58 +0200 Subject: [PATCH 06/13] add review suggestions --- src/components/Search/index.tsx | 3 +-- src/components/Search/types.ts | 2 +- src/libs/SearchUtils.ts | 6 +++--- src/pages/Search/SearchStatusMenu.tsx | 8 ++++---- src/pages/Search/SearchStatusMenuNarrow.tsx | 6 +++--- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index e63309269ff5..8b476d61f770 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -97,7 +97,6 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS setCurrentSearchHash(hash); - // TODO_SEARCH: Function below will be deprecated soon. No point in refactoring to use status instead of query. SearchActions.search({hash, query: status, policyIDs, offset: 0, sortBy, sortOrder}); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [hash, isOffline]); @@ -163,7 +162,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS } // TODO_SEARCH: offset should be a number but it is a string. - const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, offset: Number(currentQueryJSON.offset) + CONST.SEARCH.RESULTS_PAGE_SIZE}); + const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, offset: String(Number(currentQueryJSON.offset) + CONST.SEARCH.RESULTS_PAGE_SIZE)}); navigation.setParams(isCustomQuery ? {cq: newQuery} : {q: newQuery}); }; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index c98742d987b2..64c1e7325b0f 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -59,7 +59,7 @@ type SearchQueryAST = { status: SearchStatus; sortBy: SearchColumnType; sortOrder: SortOrder; - offset: number; + offset: string; filters: ASTNode; }; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 4c0e5e8d2451..2321df88d341 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -336,14 +336,14 @@ function buildSearchQueryJSON(query: SearchQueryString) { function buildSearchQueryString(partialQueryJSON?: Partial) { const queryParts: string[] = []; - const defualtQueryJSON = buildSearchQueryJSON(''); + 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]}`); - } else if (defualtQueryJSON) { - queryParts.push(`${value}:${defualtQueryJSON[value]}`); + } else if (defaultQueryJSON) { + queryParts.push(`${value}:${defaultQueryJSON[value]}`); } } diff --git a/src/pages/Search/SearchStatusMenu.tsx b/src/pages/Search/SearchStatusMenu.tsx index 52ee54d8514e..9f5d8c5c5ea3 100644 --- a/src/pages/Search/SearchStatusMenu.tsx +++ b/src/pages/Search/SearchStatusMenu.tsx @@ -33,7 +33,7 @@ function SearchStatusMenu({status}: SearchStatusMenuProps) { const {singleExecution} = useSingleExecution(); const {translate} = useLocalize(); - const filterItems: SearchStatusMenuItem[] = [ + const statusMenuItems: SearchStatusMenuItem[] = [ { title: translate('common.expenses'), status: CONST.SEARCH.STATUS.ALL, @@ -59,12 +59,12 @@ function SearchStatusMenu({status}: SearchStatusMenuProps) { route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.FINISHED)}), }, ]; - const activeItemIndex = filterItems.findIndex((item) => item.status === status); + const activeItemIndex = statusMenuItems.findIndex((item) => item.status === status); if (isSmallScreenWidth) { return ( ); @@ -72,7 +72,7 @@ function SearchStatusMenu({status}: SearchStatusMenuProps) { return ( - {filterItems.map((item, index) => { + {statusMenuItems.map((item, index) => { const onPress = singleExecution(() => Navigation.navigate(item.route)); return ( diff --git a/src/pages/Search/SearchStatusMenuNarrow.tsx b/src/pages/Search/SearchStatusMenuNarrow.tsx index 896c15c0fd13..e80db44ce3dc 100644 --- a/src/pages/Search/SearchStatusMenuNarrow.tsx +++ b/src/pages/Search/SearchStatusMenuNarrow.tsx @@ -13,11 +13,11 @@ import * as Expensicons from '@src/components/Icon/Expensicons'; import type {SearchStatusMenuItem} from './SearchStatusMenu'; type SearchStatusMenuNarrowProps = { - filterItems: SearchStatusMenuItem[]; + statusMenuItems: SearchStatusMenuItem[]; activeItemIndex: number; }; -function SearchStatusMenuNarrow({filterItems, activeItemIndex}: SearchStatusMenuNarrowProps) { +function SearchStatusMenuNarrow({statusMenuItems, activeItemIndex}: SearchStatusMenuNarrowProps) { const theme = useTheme(); const styles = useThemeStyles(); const {singleExecution} = useSingleExecution(); @@ -29,7 +29,7 @@ function SearchStatusMenuNarrow({filterItems, activeItemIndex}: SearchStatusMenu const openMenu = () => setIsPopoverVisible(true); const closeMenu = () => setIsPopoverVisible(false); - const popoverMenuItems = filterItems.map((item, index) => ({ + const popoverMenuItems = statusMenuItems.map((item, index) => ({ text: item.title, onSelected: singleExecution(() => Navigation.navigate(item.route)), icon: item.icon, From ddd1b91305f32b704aae24a670cf3c4c95db1187 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 23 Jul 2024 12:45:01 +0200 Subject: [PATCH 07/13] remove unecessary comment --- src/components/Search/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 8b476d61f770..4f11b8d50115 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -161,7 +161,6 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS return; } - // TODO_SEARCH: offset should be a number but it is a string. const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, offset: String(Number(currentQueryJSON.offset) + CONST.SEARCH.RESULTS_PAGE_SIZE)}); navigation.setParams(isCustomQuery ? {cq: newQuery} : {q: newQuery}); From fb83ab10801e12ef20a7568389850f6744f72cc9 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 23 Jul 2024 14:54:45 +0200 Subject: [PATCH 08/13] remove cq and add isCustomQuery --- src/CONST.ts | 4 ---- src/ROUTES.ts | 8 ++++---- src/components/Search/index.tsx | 12 ++++-------- src/components/Search/types.ts | 3 --- src/libs/Navigation/types.ts | 6 +++++- src/libs/SearchUtils.ts | 12 ------------ src/pages/Search/SearchPage.tsx | 4 ++-- src/pages/Search/SearchPageBottomTab.tsx | 4 ++-- src/pages/Search/SearchStatusMenu.tsx | 2 +- 9 files changed, 18 insertions(+), 37 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 6bbe096659d4..22421602243b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5271,10 +5271,6 @@ const CONST = { REPORT_ID: 'reportID', KEYWORD: 'keyword', }, - QUERY_KIND: { - CANNED_QUERY: 'cannedQuery', - CUSTOM_QUERY: 'customQuery', - }, }, REFERRER: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 44c9e5fd7dc2..5004ebfe9344 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1,6 +1,6 @@ import type {TupleToUnion, ValueOf} from 'type-fest'; -import type {QueryKind, SearchQueryString} from './components/Search/types'; -import CONST from './CONST'; +import type {SearchQueryString} from './components/Search/types'; +import type CONST from './CONST'; import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; @@ -37,8 +37,8 @@ const ROUTES = { SEARCH_CENTRAL_PANE: { route: 'search', - getRoute: ({query, queryKind = CONST.SEARCH.QUERY_KIND.CANNED_QUERY, policyIDs}: {query: SearchQueryString; queryKind?: QueryKind; policyIDs?: string}) => - `search?${queryKind === CONST.SEARCH.QUERY_KIND.CANNED_QUERY ? 'q' : 'cq'}=${query}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const, + getRoute: ({query, isCustomQuery = false, policyIDs}: {query: SearchQueryString; isCustomQuery?: boolean; policyIDs?: string}) => + `search?q=${query}&isCustomQuery=${isCustomQuery}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const, }, SEARCH_ADVANCED_FILTERS: 'search/filters', diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4f11b8d50115..4a92ad611e54 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -153,9 +153,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS } const currentSearchParams = SearchUtils.getCurrentSearchParams(); - const currentQueryString = SearchUtils.getQueryStringFromParams(currentSearchParams); - const isCustomQuery = SearchUtils.isCustomQueryFromParams(currentSearchParams); - const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentQueryString); + const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q); if (!currentQueryJSON) { return; @@ -163,7 +161,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, offset: String(Number(currentQueryJSON.offset) + CONST.SEARCH.RESULTS_PAGE_SIZE)}); - navigation.setParams(isCustomQuery ? {cq: newQuery} : {q: newQuery}); + navigation.setParams({q: newQuery}); }; const type = SearchUtils.getSearchType(searchResults?.search); @@ -180,12 +178,10 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS const onSortPress = (column: SearchColumnType, order: SortOrder) => { const currentSearchParams = SearchUtils.getCurrentSearchParams(); - const currentQueryString = SearchUtils.getQueryStringFromParams(currentSearchParams); - const isCustomQuery = SearchUtils.isCustomQueryFromParams(currentSearchParams); - const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentQueryString); + const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q); const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, sortBy: column, sortOrder: order}); - navigation.setParams(isCustomQuery ? {cq: newQuery} : {q: newQuery}); + navigation.setParams({q: newQuery}); }; const isSortingAllowed = sortableSearchTabs.includes(status); diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 64c1e7325b0f..532f7733dce7 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -50,8 +50,6 @@ type QueryFilters = { [K in AllFieldKeys]: QueryFilter | QueryFilter[]; }; -type QueryKind = ValueOf; - type SearchQueryString = string; type SearchQueryAST = { @@ -69,7 +67,6 @@ type SearchQueryJSON = { } & SearchQueryAST; export type { - QueryKind, SelectedTransactionInfo, SelectedTransactions, SearchColumnType, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 626416609221..4131c8b0d2c1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -70,7 +70,11 @@ type CentralPaneScreensParamList = { [SCREENS.SETTINGS.WORKSPACES]: undefined; // Param types of the search central pane are also used for the search bottom tab screen. - [SCREENS.SEARCH.CENTRAL_PANE]: ({cq: SearchQueryString; q?: never} | {q: SearchQueryString; cq?: never}) & {policyIDs?: string}; + [SCREENS.SEARCH.CENTRAL_PANE]: { + q: SearchQueryString; + isCustomQuery: boolean; + policyIDs?: string; + }; [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined; }; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 2321df88d341..8eaf335850bf 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -5,7 +5,6 @@ import TransactionListItem from '@components/SelectionList/Search/TransactionLis import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {SearchAccountDetails, SearchDataTypes, SearchPersonalDetails, SearchTransaction, SearchTypeToItemMap, SectionsType} from '@src/types/onyx/SearchResults'; import type SearchResults from '@src/types/onyx/SearchResults'; @@ -305,15 +304,6 @@ function getCurrentSearchParams() { return topmostCentralPaneRoute?.params as AuthScreensParamList['Search_Central_Pane']; } -// Query may be in the q or cq parameter -function getQueryStringFromParams(params: AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]) { - return params.q ?? params.cq; -} - -function isCustomQueryFromParams(params: AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]) { - return !!params.cq; -} - function isSearchResultsEmpty(searchResults: SearchResults) { return !Object.keys(searchResults?.data).some((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION)); } @@ -417,10 +407,8 @@ function getFilters(query: SearchQueryString, fields: Array buildSearchQueryJSON(getQueryStringFromParams(route.params)), [route.params]); + const queryJSON = useMemo(() => buildSearchQueryJSON(route.params.q), [route.params]); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 74f3541b30a0..189aa51bfac4 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -9,7 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; -import {buildSearchQueryJSON, getQueryStringFromParams} from '@libs/SearchUtils'; +import {buildSearchQueryJSON} from '@libs/SearchUtils'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -32,7 +32,7 @@ function SearchPageBottomTab() { const searchParams = activeCentralPaneRoute.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; return { - queryJSON: buildSearchQueryJSON(getQueryStringFromParams(searchParams)), + queryJSON: buildSearchQueryJSON(searchParams.q), policyIDs: searchParams.policyIDs, }; }, [activeCentralPaneRoute]); diff --git a/src/pages/Search/SearchStatusMenu.tsx b/src/pages/Search/SearchStatusMenu.tsx index 9f5d8c5c5ea3..38ea2f7ef2a3 100644 --- a/src/pages/Search/SearchStatusMenu.tsx +++ b/src/pages/Search/SearchStatusMenu.tsx @@ -64,7 +64,7 @@ function SearchStatusMenu({status}: SearchStatusMenuProps) { if (isSmallScreenWidth) { return ( ); From ab849e01ff2e53434b6546848ec40a4fffd646de Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 24 Jul 2024 15:39:44 +0200 Subject: [PATCH 09/13] fix searchV2 --- src/libs/actions/Search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 1bd8e94ea2e3..30a3b2145ac6 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -68,7 +68,7 @@ function searchV2(queryString: SearchQueryString) { // TODO_SEARCH: uncomment this line after backend changes // @ts-expect-error waiting for backend changes - API.read(READ_COMMANDS.SEARCH, queryJSON, {optimisticData, finallyData}); + API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery: JSON.stringify(queryJSON)}, {optimisticData, finallyData}); } /** From c7af40c84f7486286852b4e3a552691c55d236fe Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 24 Jul 2024 16:00:24 +0200 Subject: [PATCH 10/13] fix offset and remove it from query --- src/CONST.ts | 1 - src/components/Search/index.tsx | 17 ++++------------- src/components/Search/types.ts | 1 - src/libs/SearchParser/searchParser.peggy | 2 -- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 22421602243b..507bcf081029 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5253,7 +5253,6 @@ const CONST = { STATUS: 'status', SORT_BY: 'sortBy', SORT_ORDER: 'sortOrder', - OFFSET: 'offset', }, SYNTAX_FILTER_KEYS: { DATE: 'date', diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4a92ad611e54..b99c2377de9a 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -48,6 +48,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS const navigation = useNavigation>(); const lastSearchResultsRef = useRef>(); const {setCurrentSearchHash} = useSearchContext(); + const [offset, setOffset] = React.useState(0); const {status, sortBy, sortOrder, hash} = queryJSON; @@ -97,9 +98,9 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS setCurrentSearchHash(hash); - SearchActions.search({hash, query: status, policyIDs, offset: 0, sortBy, sortOrder}); + SearchActions.search({hash, query: status, policyIDs, offset, sortBy, sortOrder}); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [hash, isOffline]); + }, [hash, isOffline, offset]); const isDataLoaded = searchResults?.data !== undefined; const shouldShowLoadingState = !isOffline && !isDataLoaded; @@ -151,17 +152,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS if (!searchResults?.search?.hasMoreResults || shouldShowLoadingState || shouldShowLoadingMoreItems) { return; } - - const currentSearchParams = SearchUtils.getCurrentSearchParams(); - const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q); - - if (!currentQueryJSON) { - return; - } - - const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, offset: String(Number(currentQueryJSON.offset) + CONST.SEARCH.RESULTS_PAGE_SIZE)}); - - navigation.setParams({q: newQuery}); + setOffset(offset + CONST.SEARCH.RESULTS_PAGE_SIZE); }; const type = SearchUtils.getSearchType(searchResults?.search); diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 532f7733dce7..cf8a2eb04e14 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -57,7 +57,6 @@ type SearchQueryAST = { status: SearchStatus; sortBy: SearchColumnType; sortOrder: SortOrder; - offset: string; filters: ASTNode; }; diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 0957239b6acb..494ded6ae52d 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -21,7 +21,6 @@ "status": "all", "sortBy": "date", "sortOrder": "desc", - "offset": 0 }; function buildFilter(operator, left, right) { @@ -101,7 +100,6 @@ key / "keyword" { return "keyword"; } / "sortBy" { return "sortBy"; } / "sortOrder" { return "sortOrder"; } - / "offset" { return "offset"; } identifier = parts:(quotedString / alphanumeric)+ { return parts.join(''); } From 84ee1f96610dce719fced2fccd879648063bc259 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 25 Jul 2024 16:06:16 +0200 Subject: [PATCH 11/13] Add fixes after review --- src/libs/SearchUtils.ts | 4 +++- src/libs/actions/Search.ts | 2 +- src/pages/Search/SearchHoldReasonPage.tsx | 4 ++-- src/types/onyx/SearchResults.ts | 11 ----------- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 8eaf335850bf..bd7b21e59ab9 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -340,7 +340,9 @@ function buildSearchQueryString(partialQueryJSON?: Partial) { return queryParts.join(' '); } -// Fill query string with all default params. +/** + * Update string query with all the default params that are set by parser + */ function normalizeQuery(query: string) { const normalizedQueryJSON = buildSearchQueryJSON(query); return buildSearchQueryString(normalizedQueryJSON); diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 0ff3ee8ea713..10af02f8b2a6 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; import type {OnyxUpdate} from 'react-native-onyx'; -import type {SearchQueryString} from '@components/Search/types'; import type {FormOnyxValues} from '@components/Form/types'; +import type {SearchQueryString} from '@components/Search/types'; import * as API from '@libs/API'; import type {SearchParams} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; diff --git a/src/pages/Search/SearchHoldReasonPage.tsx b/src/pages/Search/SearchHoldReasonPage.tsx index 1f70776c71ac..d8d11662e34a 100644 --- a/src/pages/Search/SearchHoldReasonPage.tsx +++ b/src/pages/Search/SearchHoldReasonPage.tsx @@ -19,14 +19,14 @@ type SearchHoldReasonPageRouteParams = { type SearchHoldReasonPageProps = { /** Navigation route context info provided by react navigation */ - route: RouteProp<{params: SearchHoldReasonPageRouteParams}>; + route: RouteProp<{params?: SearchHoldReasonPageRouteParams}>; }; function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { const {translate} = useLocalize(); const {currentSearchHash, selectedTransactionIDs} = useSearchContext(); - const {backTo} = route.params; + const {backTo = ''} = route.params ?? {}; const onSubmit = (values: FormOnyxValues) => { SearchActions.holdMoneyRequestOnSearch(currentSearchHash, selectedTransactionIDs, values.comment); diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts index 3a8b4710709a..94fb182a4ad9 100644 --- a/src/types/onyx/SearchResults.ts +++ b/src/types/onyx/SearchResults.ts @@ -64,17 +64,6 @@ type SearchResultsInfo = { /** The optional columns that should be shown according to policy settings */ columnsToShow: ColumnsToShow; - - /** The status of the search results to show */ - statusToShow: { - // eslint-disable-next-line jsdoc/require-jsdoc - expense: { - // eslint-disable-next-line jsdoc/require-jsdoc - drafts: boolean; - // eslint-disable-next-line jsdoc/require-jsdoc - approved: boolean; - }; - }; }; /** Model of personal details search result */ From 4741f7a2edf4cb1dc6e39fd10e673e2582d31907 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 25 Jul 2024 17:14:37 +0200 Subject: [PATCH 12/13] Fix search hash not reacting to policyIDs change --- src/components/Search/index.tsx | 2 +- src/libs/SearchUtils.ts | 7 +++++-- src/pages/Search/SearchPage.tsx | 6 ++++-- src/pages/Search/SearchPageBottomTab.tsx | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 8bae05ebc792..b8d7c02fc7d8 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -168,7 +168,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS const onSortPress = (column: SearchColumnType, order: SortOrder) => { const currentSearchParams = SearchUtils.getCurrentSearchParams(); - const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q); + const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q, policyIDs); const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, sortBy: column, sortOrder: order}); navigation.setParams({q: newQuery}); diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index bd7b21e59ab9..18888903053e 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -312,12 +312,15 @@ function getQueryHashFromString(query: SearchQueryString): number { return UserUtils.hashText(query, 2 ** 32); } -function buildSearchQueryJSON(query: SearchQueryString) { +function buildSearchQueryJSON(query: SearchQueryString, policyID?: string) { try { // Add the full input and hash to the results const result = searchParser.parse(query) as SearchQueryJSON; result.input = query; - result.hash = getQueryHashFromString(query); + + // Temporary solution until we move policyID filter into the AST - then remove this line and keep only query + const policyIDPart = policyID ?? ''; + result.hash = getQueryHashFromString(query + policyIDPart); return result; } catch (e) { console.error(e); diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 2917acf19827..e2809085533b 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -18,7 +18,9 @@ function SearchPage({route}: SearchPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); - const queryJSON = useMemo(() => buildSearchQueryJSON(route.params.q), [route.params]); + const {policyIDs} = route.params; + + const queryJSON = useMemo(() => buildSearchQueryJSON(route.params.q, policyIDs), [route.params]); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); @@ -43,7 +45,7 @@ function SearchPage({route}: SearchPageProps) { {queryJSON && ( )} diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 189aa51bfac4..5b135c751ffa 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -32,7 +32,7 @@ function SearchPageBottomTab() { const searchParams = activeCentralPaneRoute.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; return { - queryJSON: buildSearchQueryJSON(searchParams.q), + queryJSON: buildSearchQueryJSON(searchParams.q, searchParams.policyIDs), policyIDs: searchParams.policyIDs, }; }, [activeCentralPaneRoute]); From 1fc7be157e0849b99f2d86911adc77d5e45f108e Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 25 Jul 2024 17:43:09 +0200 Subject: [PATCH 13/13] Fix lint --- src/pages/Search/SearchPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index e2809085533b..34a75300ef77 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -20,7 +20,7 @@ function SearchPage({route}: SearchPageProps) { const {policyIDs} = route.params; - const queryJSON = useMemo(() => buildSearchQueryJSON(route.params.q, policyIDs), [route.params]); + const queryJSON = useMemo(() => buildSearchQueryJSON(route.params.q, policyIDs), [route.params.q, policyIDs]); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}));