From 5dc06468b26be3f112d64bf300caae8caa649817 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 8 Aug 2024 16:17:03 +0200 Subject: [PATCH 1/8] Add policyID to search grammar --- src/libs/SearchParser/searchParser.js | 66 +++++++++++++++--------- src/libs/SearchParser/searchParser.peggy | 1 + 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 53d0ac82bec0..91923cd50d2b 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -199,7 +199,8 @@ function peg$parse(input, options) { var peg$c21 = "keyword"; var peg$c22 = "sortBy"; var peg$c23 = "sortOrder"; - var peg$c24 = "\""; + var peg$c24 = "policyID"; + var peg$c25 = "\""; var peg$r0 = /^[:=]/; var peg$r1 = /^[^"\r\n]/; @@ -231,11 +232,12 @@ function peg$parse(input, options) { var peg$e22 = peg$literalExpectation("keyword", false); var peg$e23 = peg$literalExpectation("sortBy", false); var peg$e24 = peg$literalExpectation("sortOrder", false); - var peg$e25 = peg$literalExpectation("\"", false); - var peg$e26 = peg$classExpectation(["\"", "\r", "\n"], true, false); - var peg$e27 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ","], false, false); - var peg$e28 = peg$otherExpectation("whitespace"); - var peg$e29 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); + var peg$e25 = peg$literalExpectation("policyID", false); + var peg$e26 = peg$literalExpectation("\"", false); + var peg$e27 = peg$classExpectation(["\"", "\r", "\n"], true, false); + var peg$e28 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ","], false, false); + var peg$e29 = peg$otherExpectation("whitespace"); + var peg$e30 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); var peg$f0 = function(filters) { return applyDefaults(filters); }; var peg$f1 = function(head, tail) { @@ -285,10 +287,11 @@ function peg$parse(input, options) { var peg$f25 = function() { return "keyword"; }; var peg$f26 = function() { return "sortBy"; }; var peg$f27 = function() { return "sortOrder"; }; - var peg$f28 = function(parts) { return parts.join(''); }; - var peg$f29 = function(chars) { return chars.join(''); }; + var peg$f28 = function() { return "policyID"; }; + var peg$f29 = function(parts) { return parts.join(''); }; var peg$f30 = function(chars) { return chars.join(''); }; - var peg$f31 = function() { return "and"; }; + var peg$f31 = function(chars) { return chars.join(''); }; + var peg$f32 = function() { return "and"; }; var peg$currPos = options.peg$currPos | 0; var peg$savedPos = peg$currPos; var peg$posDetailsCache = [{ line: 1, column: 1 }]; @@ -897,6 +900,21 @@ function peg$parse(input, options) { s1 = peg$f27(); } s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c24) { + s1 = peg$c24; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f28(); + } + s0 = s1; + } } } } @@ -941,7 +959,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f28(s1); + s1 = peg$f29(s1); } s0 = s1; @@ -953,11 +971,11 @@ function peg$parse(input, options) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c24; + s1 = peg$c25; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e25); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } if (s1 !== peg$FAILED) { s2 = []; @@ -966,7 +984,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e26); } + if (peg$silentFails === 0) { peg$fail(peg$e27); } } while (s3 !== peg$FAILED) { s2.push(s3); @@ -975,19 +993,19 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e26); } + if (peg$silentFails === 0) { peg$fail(peg$e27); } } } if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c24; + s3 = peg$c25; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e25); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f29(s2); + s0 = peg$f30(s2); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1010,7 +1028,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e27); } + if (peg$silentFails === 0) { peg$fail(peg$e28); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { @@ -1020,7 +1038,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e27); } + if (peg$silentFails === 0) { peg$fail(peg$e28); } } } } else { @@ -1028,7 +1046,7 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f30(s1); + s1 = peg$f31(s1); } s0 = s1; @@ -1041,7 +1059,7 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parse_(); peg$savedPos = s0; - s1 = peg$f31(); + s1 = peg$f32(); s0 = s1; return s0; @@ -1057,7 +1075,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e29); } + if (peg$silentFails === 0) { peg$fail(peg$e30); } } while (s1 !== peg$FAILED) { s0.push(s1); @@ -1066,12 +1084,12 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e29); } + if (peg$silentFails === 0) { peg$fail(peg$e30); } } } peg$silentFails--; s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e28); } + if (peg$silentFails === 0) { peg$fail(peg$e29); } return s0; } diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 310e942bbb80..d13adcfd2656 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -100,6 +100,7 @@ key / "keyword" { return "keyword"; } / "sortBy" { return "sortBy"; } / "sortOrder" { return "sortOrder"; } + / "policyID" { return "policyID"; } identifier = parts:(quotedString / alphanumeric)+ { return parts.join(''); } From df18001ea7c45206318ea9004979c7934ffa5b54 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 8 Aug 2024 16:58:30 +0200 Subject: [PATCH 2/8] Add todo comments --- src/CONST.ts | 1 + src/ROUTES.ts | 3 +-- src/components/Search/SearchPageHeader.tsx | 1 + src/components/Search/index.tsx | 5 ++--- src/libs/API/parameters/Search.ts | 2 -- src/libs/Navigation/linkTo/index.ts | 2 ++ .../Navigation/linkingConfig/extractPolicyIDsFromState.ts | 1 + src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts | 1 + src/libs/Navigation/switchPolicyID.ts | 4 ++-- src/libs/Navigation/types.ts | 3 +-- src/libs/actions/Search.ts | 4 ++-- 11 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index eaae7b82ef74..87c837721c20 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5324,6 +5324,7 @@ const CONST = { CARD_ID: 'cardID', REPORT_ID: 'reportID', KEYWORD: 'keyword', + POLICY_ID: 'policyID', }, }, diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 27f565929c56..af1224d666dc 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -35,8 +35,7 @@ const ROUTES = { SEARCH_CENTRAL_PANE: { route: 'search', - getRoute: ({query, isCustomQuery = false, policyIDs}: {query: SearchQueryString; isCustomQuery?: boolean; policyIDs?: string}) => - `search?q=${query}&isCustomQuery=${isCustomQuery}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const, + getRoute: ({query, isCustomQuery = false}: {query: SearchQueryString; isCustomQuery?: boolean}) => `search?q=${query}&isCustomQuery=${isCustomQuery}` as const, }, SEARCH_ADVANCED_FILTERS: 'search/filters', diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 039526923b3a..fd8ff41173bb 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -112,6 +112,7 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); + // @TODO: Obtain the policyID from queryJSON const {activeWorkspaceID} = useActiveWorkspace(); const {isSmallScreenWidth} = useResponsiveLayout(); const {selectedTransactions, clearSelectedTransactions} = useSearchContext(); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 7283a36e9f59..1c003e19b467 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -36,7 +36,6 @@ import type {SearchColumnType, SearchQueryJSON, SearchStatus, SelectedTransactio type SearchProps = { queryJSON: SearchQueryJSON; isCustomQuery: boolean; - policyIDs?: string; }; const transactionItemMobileHeight = 100; @@ -73,7 +72,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, policyIDs, isCustomQuery}: SearchProps) { +function Search({queryJSON, isCustomQuery}: SearchProps) { const {isOffline} = useNetwork(); const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -115,7 +114,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) { return; } - SearchActions.search({queryJSON, offset, policyIDs}); + SearchActions.search({queryJSON, offset}); // 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 530388dc7f47..64bfc5baf5a1 100644 --- a/src/libs/API/parameters/Search.ts +++ b/src/libs/API/parameters/Search.ts @@ -3,8 +3,6 @@ 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/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index d5a72db3a304..8bda6107fe53 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -49,6 +49,7 @@ export default function linkTo(navigation: NavigationContainerRef>; // Creating path with /w/ included if necessary. const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState); + // @TODO Here check if policyID exists in q param instead of reading it from policyIDs, rename this variable const policyIDs = !!topmostCentralPaneRoute?.params && 'policyIDs' in topmostCentralPaneRoute.params ? (topmostCentralPaneRoute?.params?.policyIDs as string) : ''; const extractedPolicyID = extractPolicyIDFromPath(`/${path}`); const policyIDFromState = getPolicyIDFromState(rootState); @@ -117,6 +118,7 @@ export default function linkTo(navigation: NavigationContainerRef).policyIDs = policyID; } diff --git a/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts b/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts index fdaf7e6eb490..2dc099ba8695 100644 --- a/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts +++ b/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts @@ -3,6 +3,7 @@ import {findFocusedRoute} from '@react-navigation/native'; import SCREENS from '@src/SCREENS'; function extractPolicyIDsFromState(state: InitialState) { + // @TODO Try extracting policyID from q param, if there are multiple then return undefined const focusedRoute = findFocusedRoute(state); if (focusedRoute && focusedRoute.name === SCREENS.SEARCH.CENTRAL_PANE && focusedRoute.params && 'policyIDs' in focusedRoute.params) { return focusedRoute.params.policyIDs as string; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 594f76eee2fe..0bc3b98b6902 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -381,6 +381,7 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options) => { } // Only on SCREENS.SEARCH.CENTRAL_PANE policyID is stored differently as "policyIDs" param, so we're handling this case here + // @TODO Modify this method to handle extracting ids from q param const policyIDs = extractPolicyIDsFromState(state); return getAdaptedState(state, policyID ?? policyIDs); diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 28de413b0904..9ae49b4ff4ef 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -19,7 +19,7 @@ type ActionPayloadParams = { path?: string; }; -type CentralPaneRouteParams = Record & {policyID?: string; policyIDs?: string; reportID?: string}; +type CentralPaneRouteParams = Record & {policyID?: string; q?: string; reportID?: string}; function checkIfActionPayloadNameIsEqual(action: Writable, screenName: string) { return action?.payload && 'name' in action.payload && action?.payload?.name === screenName; @@ -108,9 +108,9 @@ export default function switchPolicyID(navigation: NavigationContainerRef & {policyID: string}; + [SCREENS.SEARCH.BOTTOM_TAB]: CentralPaneScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE] & {policyID: string}; [SCREENS.SETTINGS.ROOT]: {policyID?: string}; }; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 040fd6e47491..d4a8fc57ddc5 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, policyIDs}: {queryJSON: SearchQueryJSON; offset?: number; policyIDs?: string}) { +function search({queryJSON, offset}: {queryJSON: SearchQueryJSON; offset?: number}) { const {optimisticData, finallyData} = getOnyxLoadingData(queryJSON.hash); const queryWithOffset = { @@ -58,7 +58,7 @@ function search({queryJSON, offset, policyIDs}: {queryJSON: SearchQueryJSON; off }; const jsonQuery = JSON.stringify(queryWithOffset); - API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery, policyIDs}, {optimisticData, finallyData}); + API.read(READ_COMMANDS.SEARCH, {hash: queryJSON.hash, jsonQuery}, {optimisticData, finallyData}); } /** From 9140b8fe557d099d6e1eec7215ddda3c63aac551 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 21 Aug 2024 15:01:56 +0200 Subject: [PATCH 3/8] Remove policyIDs param and store policyID inside SearchQuery --- src/CONST.ts | 2 +- src/components/Search/SearchPageHeader.tsx | 3 +- src/components/Search/types.ts | 1 + .../BottomTabBar.tsx | 49 ++++++++++++++++--- .../Navigation/extractPolicyIDFromQuery.ts | 22 +++++++++ src/libs/Navigation/getPolicyIDFromState.ts | 17 +++++-- src/libs/Navigation/linkTo/index.ts | 23 ++++----- .../extractPolicyIDsFromState.ts | 14 ------ .../linkingConfig/getAdaptedStateFromPath.ts | 10 ++-- .../getMatchingBottomTabRouteForState.ts | 5 +- .../index.android.ts | 14 +++--- src/libs/Navigation/switchPolicyID.ts | 16 ++++-- src/libs/Navigation/types.ts | 2 +- src/libs/SearchParser/searchParser.js | 28 ++++++++++- src/libs/SearchParser/searchParser.peggy | 31 ++++++++++-- src/libs/SearchUtils.ts | 49 +++++++++++++------ src/pages/Search/SearchPage.tsx | 5 +- src/pages/Search/SearchPageBottomTab.tsx | 15 +++--- 18 files changed, 209 insertions(+), 97 deletions(-) create mode 100644 src/libs/Navigation/extractPolicyIDFromQuery.ts delete mode 100644 src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts diff --git a/src/CONST.ts b/src/CONST.ts index 42e5b84881df..6fe653a74da7 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5328,6 +5328,7 @@ const CONST = { STATUS: 'status', SORT_BY: 'sortBy', SORT_ORDER: 'sortOrder', + POLICY_ID: 'policyID', }, SYNTAX_FILTER_KEYS: { DATE: 'date', @@ -5344,7 +5345,6 @@ const CONST = { CARD_ID: 'cardID', REPORT_ID: 'reportID', KEYWORD: 'keyword', - POLICY_ID: 'policyID', }, }, diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index efb8038372eb..6e40853b7b88 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -112,7 +112,6 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); - // @TODO: Obtain the policyID from queryJSON const {activeWorkspaceID} = useActiveWorkspace(); const {isSmallScreenWidth} = useResponsiveLayout(); const {selectedTransactions, clearSelectedTransactions} = useSearchContext(); @@ -133,7 +132,7 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa [data, selectedTransactions], ); const {status} = queryJSON; - const headerSubtitle = isCustomQuery ? SearchUtils.getSearchHeaderTitle(queryJSON) : translate(headerContent[status]?.titleText); + const headerSubtitle = isCustomQuery ? SearchUtils.getSearchHeaderTitle(queryJSON) : translate(headerContent[status]?.titleText ?? ''); const headerTitle = isCustomQuery ? translate('search.filtersHeader') : ''; const headerIcon = isCustomQuery ? Illustrations.Filters : headerContent[status]?.icon; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 54b26ea6bac4..2c8758488f45 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -60,6 +60,7 @@ type SearchQueryAST = { sortBy: SearchColumnType; sortOrder: SortOrder; filters: ASTNode; + policyID?: string; }; type SearchQueryJSON = { diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index f023e94bec9b..5e079aaef133 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -5,6 +5,7 @@ import {useOnyx} from 'react-native-onyx'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithFeedback} from '@components/Pressable'; +import type {SearchQueryString} from '@components/Search/types'; import Tooltip from '@components/Tooltip'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; @@ -18,7 +19,7 @@ import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; import {isCentralPaneName} from '@libs/NavigationUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import {getCurrentSearchParams} from '@libs/SearchUtils'; +import * as SearchUtils from '@libs/SearchUtils'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; @@ -36,6 +37,34 @@ type BottomTabBarProps = { selectedTab: string | undefined; }; +/** + * Returns SearchQueryString that has policyID correctly set. + * + * When we're coming back to Search Screen we might have pre-existing policyID inside SearchQuery. + * There are 2 cases when we might want to remove this `policyID`: + * - if Policy was removed in another screen + * - if WorkspaceSwitcher was used to globally unset a policyID + * Otherwise policyID will be inserted into query + */ +function handleQueryWithPolicyID(query: SearchQueryString, activePolicyID?: string): SearchQueryString { + const queryJSON = SearchUtils.buildSearchQueryJSON(query); + if (!queryJSON) { + return query; + } + + const policyID = queryJSON.policyID ?? activePolicyID; + const policy = PolicyUtils.getPolicy(policyID); + + // In case policy is missing or there is no policy currently selected via WorkspaceSwitcher we remove it + if (!activePolicyID || !policy) { + delete queryJSON.policyID; + } else { + queryJSON.policyID = policyID; + } + + return SearchUtils.buildSearchQueryString(queryJSON); +} + function BottomTabBar({selectedTab}: BottomTabBarProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -88,16 +117,24 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) { return; } interceptAnonymousUser(() => { - const currentSearchParams = getCurrentSearchParams(); + const currentSearchParams = SearchUtils.getCurrentSearchParams(); if (currentSearchParams) { const {q, ...rest} = currentSearchParams; - const policy = PolicyUtils.getPolicy(currentSearchParams?.policyIDs); - Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: q, ...rest, policyIDs: policy ? currentSearchParams?.policyIDs : undefined})); + const cleanedQuery = handleQueryWithPolicyID(q, activeWorkspaceID); + + Navigation.navigate( + ROUTES.SEARCH_CENTRAL_PANE.getRoute({ + query: cleanedQuery, + ...rest, + }), + ); return; } - Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); + + const query = activeWorkspaceID ? `${CONST.SEARCH.TAB.EXPENSE.ALL} policyID:${activeWorkspaceID}` : CONST.SEARCH.TAB.EXPENSE.ALL; + Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query})); }); - }, [selectedTab]); + }, [activeWorkspaceID, selectedTab]); return ( diff --git a/src/libs/Navigation/extractPolicyIDFromQuery.ts b/src/libs/Navigation/extractPolicyIDFromQuery.ts new file mode 100644 index 000000000000..bd0464f4aab6 --- /dev/null +++ b/src/libs/Navigation/extractPolicyIDFromQuery.ts @@ -0,0 +1,22 @@ +import * as SearchUtils from '@libs/SearchUtils'; +import type {NavigationPartialRoute} from './types'; + +function extractPolicyIDFromQuery(route?: NavigationPartialRoute) { + if (!route?.params) { + return undefined; + } + + if (!('q' in route.params)) { + return undefined; + } + + const queryString = route.params.q as string; + const queryJSON = SearchUtils.buildSearchQueryJSON(queryString); + if (!queryJSON) { + return undefined; + } + + return SearchUtils.getPolicyIDFromSearchQuery(queryJSON); +} + +export default extractPolicyIDFromQuery; diff --git a/src/libs/Navigation/getPolicyIDFromState.ts b/src/libs/Navigation/getPolicyIDFromState.ts index 00236fb0fce0..62690f29a98f 100644 --- a/src/libs/Navigation/getPolicyIDFromState.ts +++ b/src/libs/Navigation/getPolicyIDFromState.ts @@ -1,16 +1,23 @@ +import extractPolicyIDFromQuery from './extractPolicyIDFromQuery'; import getTopmostBottomTabRoute from './getTopmostBottomTabRoute'; import type {RootStackParamList, State} from './types'; +/** + * returns policyID value if one exists in navigation state + * + * PolicyID in this app can be stored in two ways: + * - on most screens but NOT Search as `policyID` param + * - on Search related screens as policyID filter inside `q` (SearchQuery) param + */ const getPolicyIDFromState = (state: State): string | undefined => { const topmostBottomTabRoute = getTopmostBottomTabRoute(state); - const shouldAddPolicyIDToUrl = !!topmostBottomTabRoute && !!topmostBottomTabRoute.params && 'policyID' in topmostBottomTabRoute.params && !!topmostBottomTabRoute.params?.policyID; - - if (!shouldAddPolicyIDToUrl) { - return undefined; + const policyID = topmostBottomTabRoute && topmostBottomTabRoute.params && 'policyID' in topmostBottomTabRoute.params && topmostBottomTabRoute.params?.policyID; + if (policyID) { + return topmostBottomTabRoute.params?.policyID as string; } - return topmostBottomTabRoute.params?.policyID as string; + return extractPolicyIDFromQuery(topmostBottomTabRoute); }; export default getPolicyIDFromState; diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 8bda6107fe53..66eff48b5446 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -4,13 +4,13 @@ import {findFocusedRoute} from '@react-navigation/native'; import {omitBy} from 'lodash'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import isReportOpenInRHP from '@libs/Navigation/isReportOpenInRHP'; -import extractPolicyIDsFromState from '@libs/Navigation/linkingConfig/extractPolicyIDsFromState'; import {isCentralPaneName} from '@libs/NavigationUtils'; import shallowCompare from '@libs/ObjectUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import getActionsFromPartialDiff from '@navigation/AppNavigator/getActionsFromPartialDiff'; import getPartialStateDiff from '@navigation/AppNavigator/getPartialStateDiff'; import dismissModal from '@navigation/dismissModal'; +import extractPolicyIDFromQuery from '@navigation/extractPolicyIDFromQuery'; import extrapolateStateFromParams from '@navigation/extrapolateStateFromParams'; import getPolicyIDFromState from '@navigation/getPolicyIDFromState'; import getStateFromPath from '@navigation/getStateFromPath'; @@ -49,19 +49,18 @@ export default function linkTo(navigation: NavigationContainerRef>; // Creating path with /w/ included if necessary. const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState); - // @TODO Here check if policyID exists in q param instead of reading it from policyIDs, rename this variable - const policyIDs = !!topmostCentralPaneRoute?.params && 'policyIDs' in topmostCentralPaneRoute.params ? (topmostCentralPaneRoute?.params?.policyIDs as string) : ''; + const extractedPolicyID = extractPolicyIDFromPath(`/${path}`); const policyIDFromState = getPolicyIDFromState(rootState); - const policyID = extractedPolicyID ?? policyIDFromState ?? policyIDs; + const policyID = extractedPolicyID ?? policyIDFromState; const lastRoute = rootState?.routes?.at(-1); const isNarrowLayout = getIsNarrowLayout(); const isFullScreenOnTop = lastRoute?.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR; - // policyIDs is present only on SCREENS.SEARCH.CENTRAL_PANE and it's displayed in the url as a query param, on the other pages this parameter is called policyID and it's shown in the url in the format: /w/:policyID - if (policyID && !isFullScreenOnTop && !policyIDs) { + // Policy on SCREENS.SEARCH.CENTRAL_PANE can be present only as part of SearchQuery, while on other pages it's called `policyID` and stored in the url in the format: /w/:policyID/ + if (policyID && !isFullScreenOnTop && !policyIDFromState) { // The stateFromPath doesn't include proper path if there is a policy passed with /w/id. // We need to replace the path in the state with the proper one. // To avoid this hacky solution we may want to create custom getActionFromState function in the future. @@ -96,8 +95,10 @@ export default function linkTo(navigation: NavigationContainerRef)?.policyID ?? '') !== @@ -116,12 +117,6 @@ export default function linkTo(navigation: NavigationContainerRef).policyIDs = policyID; - } - // If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow // and at the same time we want the back button to go to the page we were before the deeplink } else if (type === CONST.NAVIGATION.TYPE.UP) { diff --git a/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts b/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts deleted file mode 100644 index 2dc099ba8695..000000000000 --- a/src/libs/Navigation/linkingConfig/extractPolicyIDsFromState.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type {InitialState} from '@react-navigation/native'; -import {findFocusedRoute} from '@react-navigation/native'; -import SCREENS from '@src/SCREENS'; - -function extractPolicyIDsFromState(state: InitialState) { - // @TODO Try extracting policyID from q param, if there are multiple then return undefined - const focusedRoute = findFocusedRoute(state); - if (focusedRoute && focusedRoute.name === SCREENS.SEARCH.CENTRAL_PANE && focusedRoute.params && 'policyIDs' in focusedRoute.params) { - return focusedRoute.params.policyIDs as string; - } - return undefined; -} - -export default extractPolicyIDsFromState; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index b95436ed56e7..0cf4e66c0e09 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -8,6 +8,7 @@ import type {BottomTabName, CentralPaneName, FullScreenName, NavigationPartialRo import {isCentralPaneName} from '@libs/NavigationUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import * as ReportConnection from '@libs/ReportConnection'; +import extractPolicyIDFromQuery from '@navigation/extractPolicyIDFromQuery'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -15,7 +16,6 @@ import type {Screen} from '@src/SCREENS'; import SCREENS from '@src/SCREENS'; import CENTRAL_PANE_TO_RHP_MAPPING from './CENTRAL_PANE_TO_RHP_MAPPING'; import config, {normalizedConfigs} from './config'; -import extractPolicyIDsFromState from './extractPolicyIDsFromState'; import FULL_SCREEN_TO_RHP_MAPPING from './FULL_SCREEN_TO_RHP_MAPPING'; import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState'; import getMatchingCentralPaneRouteForState from './getMatchingCentralPaneRouteForState'; @@ -380,11 +380,11 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options) => { throw new Error('Unable to parse path'); } - // Only on SCREENS.SEARCH.CENTRAL_PANE policyID is stored differently as "policyIDs" param, so we're handling this case here - // @TODO Modify this method to handle extracting ids from q param - const policyIDs = extractPolicyIDsFromState(state); + // On SCREENS.SEARCH.CENTRAL_PANE policyID is stored differently inside search query ("q" param), so we're handling this case + const focusedRoute = findFocusedRoute(state); + const policyIDFromQuery = extractPolicyIDFromQuery(focusedRoute); - return getAdaptedState(state, policyID ?? policyIDs); + return getAdaptedState(state, policyID ?? policyIDFromQuery); }; export default getAdaptedStateFromPath; diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts index 67d76de4932d..7b213fdfeb6e 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts @@ -24,12 +24,9 @@ function getMatchingBottomTabRouteForState(state: State, pol if (tabName === SCREENS.SEARCH.BOTTOM_TAB) { const topmostCentralPaneRouteParams = {...topmostCentralPaneRoute.params} as Record; - delete topmostCentralPaneRouteParams?.policyIDs; - if (policyID) { - topmostCentralPaneRouteParams.policyID = policyID; - } return {name: tabName, params: topmostCentralPaneRouteParams}; } + return {name: tabName, params: paramsWithPolicyID}; } diff --git a/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts index 10aa8b99a484..a899c510899e 100644 --- a/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts +++ b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts @@ -18,7 +18,7 @@ function setupCustomAndroidBackHandler() { const bottomTabRoutes = bottomTabRoute?.state?.routes; const focusedRoute = findFocusedRoute(rootState); - // Shoudn't happen but for type safety. + // Shouldn't happen but for type safety. if (!bottomTabRoutes) { return false; } @@ -39,15 +39,15 @@ function setupCustomAndroidBackHandler() { const bottomTabRouteAfterPop = bottomTabRoutes.at(-2); // It's possible that central pane search is desynchronized with the bottom tab search. - // e.g. opening a tab different than search will wipe out central pane screens. + // e.g. opening a tab different from search will wipe out central pane screens. // In that case we have to push the proper one. if ( bottomTabRouteAfterPop && bottomTabRouteAfterPop.name === SCREENS.SEARCH.BOTTOM_TAB && (!centralPaneRouteAfterPop || centralPaneRouteAfterPop.name !== SCREENS.SEARCH.CENTRAL_PANE) ) { - const {policyID, ...restParams} = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params']; - navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, {...restParams, policyIDs: policyID})}); + const searchParams = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params']; + navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, searchParams)}); } return true; @@ -55,11 +55,11 @@ function setupCustomAndroidBackHandler() { // Handle back press to go back to the search page. // It's possible that central pane search is desynchronized with the bottom tab search. - // e.g. opening a tab different than search will wipe out central pane screens. + // e.g. opening a tab different from search will wipe out central pane screens. // In that case we have to push the proper one. if (bottomTabRoutes && bottomTabRoutes?.length >= 2 && bottomTabRoutes[bottomTabRoutes.length - 2].name === SCREENS.SEARCH.BOTTOM_TAB && rootState.routes.length === 1) { - const {policyID, ...restParams} = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params']; - navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, {...restParams, policyIDs: policyID})}); + const searchParams = bottomTabRoutes[bottomTabRoutes.length - 2].params as SearchPageProps['route']['params']; + navigationRef.dispatch({...StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, searchParams)}); navigationRef.dispatch({...StackActions.pop(), target: bottomTabRoute?.state?.key}); return true; } diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 9ae49b4ff4ef..66aec8202f50 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -4,6 +4,7 @@ import {getPathFromState} from '@react-navigation/native'; import type {Writable} from 'type-fest'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import {isCentralPaneName} from '@libs/NavigationUtils'; +import * as SearchUtils from '@libs/SearchUtils'; import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; @@ -108,12 +109,19 @@ export default function switchPolicyID(navigation: NavigationContainerRef filter)].filter(filter => filter !== null); if (!allFilters.length) { return null; } + return allFilters.reduce((result, filter) => buildFilter("and", result, filter)); }; var peg$f2 = function(field, op, value) { @@ -253,6 +261,11 @@ function peg$parse(input, options) { return null; } + if (isPolicyID(field)) { + updateDefaultValues(field, value.trim()); + return null; + } + if (!field && !op) { return buildFilter('eq', 'keyword', value.trim()); } @@ -1112,7 +1125,14 @@ function peg$parse(input, options) { filters }; } - + + function applyPolicyID(filtersWithDefaults) { + return { + ...filtersWithDefaults, + policyID: filtersWithDefaults.policyID + }; + } + function updateDefaultValues(field, value) { defaultValues[field] = value; } @@ -1121,6 +1141,10 @@ function peg$parse(input, options) { return defaultValues.hasOwnProperty(field); } + function isPolicyID(field) { + return field === 'policyID'; + } + peg$result = peg$startRuleFunction(); if (options.peg$library) { diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index d13adcfd2656..a4e747d57337 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -33,7 +33,14 @@ filters }; } - + + function applyPolicyID(filtersWithDefaults) { + return { + ...filtersWithDefaults, + policyID: filtersWithDefaults.policyID + }; + } + function updateDefaultValues(field, value) { defaultValues[field] = value; } @@ -41,10 +48,21 @@ function isDefaultField(field) { return defaultValues.hasOwnProperty(field); } + + function isPolicyID(field) { + return field === 'policyID'; + } } query - = _ filters:filterList? _ { return applyDefaults(filters); } + = _ filters:filterList? _ { + const withDefaults = applyDefaults(filters); + if (defaultValues.policyID) { + return applyPolicyID(withDefaults); + } + + return withDefaults; + } filterList = head:filter tail:(logicalAnd filter)* { @@ -52,6 +70,7 @@ filterList if (!allFilters.length) { return null; } + return allFilters.reduce((result, filter) => buildFilter("and", result, filter)); } @@ -62,6 +81,11 @@ filter return null; } + if (isPolicyID(field)) { + updateDefaultValues(field, value.trim()); + return null; + } + if (!field && !op) { return buildFilter('eq', 'keyword', value.trim()); } @@ -116,6 +140,3 @@ logicalAnd _ "whitespace" = [ \t\r\n]* - -start - = query diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index bb49697a8345..94421abe2621 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -3,6 +3,7 @@ import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJS import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; +import type {AuthScreensParamList, BottomTabNavigatorParamList, RootStackParamList, State} from '@navigation/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -16,7 +17,6 @@ import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import {translateLocal} from './Localize'; import navigationRef from './Navigation/navigationRef'; -import type {AuthScreensParamList, BottomTabNavigatorParamList, RootStackParamList, State} from './Navigation/types'; import * as searchParser from './SearchParser/searchParser'; import * as TransactionUtils from './TransactionUtils'; import * as UserUtils from './UserUtils'; @@ -351,9 +351,7 @@ function getCurrentSearchParams() { } if (lastSearchBottomTabRoute) { - const {policyID, ...rest} = lastSearchBottomTabRoute.params as BottomTabNavigatorParamList[typeof SCREENS.SEARCH.BOTTOM_TAB]; - const params: AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE] = {policyIDs: policyID, ...rest}; - return params; + return lastSearchBottomTabRoute.params as BottomTabNavigatorParamList[typeof SCREENS.SEARCH.BOTTOM_TAB]; } } @@ -365,18 +363,16 @@ function getQueryHashFromString(query: SearchQueryString): number { return UserUtils.hashText(query, 2 ** 32); } -function buildSearchQueryJSON(query: SearchQueryString, policyID?: string) { +function buildSearchQueryJSON(query: SearchQueryString) { try { - // Add the full input and hash to the results const result = searchParser.parse(query) as SearchQueryJSON; - result.inputQuery = 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); + // Add the full input and hash to the results + result.inputQuery = query; + result.hash = getQueryHashFromString(query); return result; } catch (e) { - console.error(e); + console.error(`Error when parsing SearchQuery: "${query}"`, e); } } @@ -384,12 +380,12 @@ 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 [, key] of Object.entries(CONST.SEARCH.SYNTAX_ROOT_KEYS)) { - if (queryJSON?.[key]) { - queryParts.push(`${key}:${queryJSON[key]}`); - } else if (defaultQueryJSON) { - queryParts.push(`${key}:${defaultQueryJSON[key]}`); + const existingFieldValue = queryJSON?.[key]; + const queryFieldValue = existingFieldValue ?? defaultQueryJSON?.[key]; + + if (queryFieldValue) { + queryParts.push(`${key}:${queryFieldValue}`); } } @@ -543,6 +539,26 @@ function getFilters(queryJSON: SearchQueryJSON) { return filters; } +/** + * Given a SearchQueryJSON this function will try to find the value of policyID filter saved in query + * and return just the first policyID value from the filter. + * + * Note: `policyID` property can store multiple policy ids (just like many other search filters) as a comma separated value; + * however there are several places in the app (related to WorkspaceSwitcher) that will accept only a single policyID. + */ +function getPolicyIDFromSearchQuery(queryJSON: SearchQueryJSON) { + const policyIDFilter = queryJSON.policyID; + + if (!policyIDFilter) { + return; + } + + // policyID is a comma-separated value + const [policyID] = policyIDFilter.split(','); + + return policyID; +} + function buildFilterString(filterName: string, queryFilters: QueryFilter[]) { let filterValueString = ''; queryFilters.forEach((queryFilter, index) => { @@ -577,6 +593,7 @@ export { buildSearchQueryString, getCurrentSearchParams, getFilters, + getPolicyIDFromSearchQuery, getListItem, getQueryHash, getSearchHeaderTitle, diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index e2495c02d44f..f77c4e14d1c1 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -17,9 +17,9 @@ type SearchPageProps = StackScreenProps buildSearchQueryJSON(q, policyIDs), [q, policyIDs]); + const queryJSON = useMemo(() => buildSearchQueryJSON(q), [q]); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); @@ -45,7 +45,6 @@ function SearchPage({route}: SearchPageProps) { )} diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 1b9e81c8a035..913008b0dd7d 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -12,7 +12,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; -import {buildSearchQueryJSON} from '@libs/SearchUtils'; +import * as SearchUtils from '@libs/SearchUtils'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -28,22 +28,22 @@ function SearchPageBottomTab() { const {clearSelectedTransactions} = useSearchContext(); const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); - const {queryJSON, policyIDs, isCustomQuery} = useMemo(() => { + const {queryJSON, isCustomQuery} = useMemo(() => { if (!activeCentralPaneRoute || activeCentralPaneRoute.name !== SCREENS.SEARCH.CENTRAL_PANE) { - return {queryJSON: undefined, policyIDs: undefined}; + return {queryJSON: undefined, isCustomQuery: undefined}; } - // This will be SEARCH_CENTRAL_PANE as we checked that in if. + // This has to be SEARCH_CENTRAL_PANE const searchParams = activeCentralPaneRoute.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; return { - queryJSON: buildSearchQueryJSON(searchParams.q, searchParams.policyIDs), - policyIDs: searchParams.policyIDs, + queryJSON: SearchUtils.buildSearchQueryJSON(searchParams.q), isCustomQuery: searchParams.isCustomQuery, }; }, [activeCentralPaneRoute]); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); + const policyID = queryJSON && SearchUtils.getPolicyIDFromSearchQuery(queryJSON); return ( @@ -81,7 +81,6 @@ function SearchPageBottomTab() { )} From aa784a0bed00f956be31bb2235f3e3d3376345b7 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 21 Aug 2024 15:10:54 +0200 Subject: [PATCH 4/8] Fix handling of policyID from bottom tab screens --- src/hooks/useActiveWorkspaceFromNavigationState.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hooks/useActiveWorkspaceFromNavigationState.ts b/src/hooks/useActiveWorkspaceFromNavigationState.ts index db7d13a00aaa..d2851e83ab6c 100644 --- a/src/hooks/useActiveWorkspaceFromNavigationState.ts +++ b/src/hooks/useActiveWorkspaceFromNavigationState.ts @@ -12,22 +12,20 @@ import SCREENS from '@src/SCREENS'; */ function useActiveWorkspaceFromNavigationState() { // The last policyID value is always stored in the last route in BottomTabNavigator. - const activeWorkpsaceID = useNavigationState((state) => { + const activeWorkspaceID = useNavigationState((state) => { // SCREENS.HOME is a screen located in the BottomTabNavigator, if it's not in state.routeNames it means that this hook was called from a screen in another navigator. if (!state.routeNames.includes(SCREENS.HOME)) { Log.warn('useActiveWorkspaceFromNavigationState should be called only from BottomTab screens'); } - const policyID = state.routes.at(-1)?.params?.policyID; + const params = state.routes.at(-1)?.params ?? {}; - if (!policyID) { - return undefined; + if ('policyID' in params) { + return params?.policyID; } - - return policyID; }); - return activeWorkpsaceID; + return activeWorkspaceID; } export default useActiveWorkspaceFromNavigationState; From df66d26ed1334a455e43081bb949d9332838c038 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Wed, 21 Aug 2024 15:40:13 +0200 Subject: [PATCH 5/8] Fix eslint in search --- src/libs/Navigation/linkTo/index.ts | 2 +- src/libs/SearchUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index bf39fe93d701..8cdeee0c80bf 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -98,7 +98,7 @@ export default function linkTo(navigation: NavigationContainerRef)?.policyID ?? '') !== diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 94421abe2621..3f78cc46346c 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -3,7 +3,6 @@ import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJS import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; import type {ListItem, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; -import type {AuthScreensParamList, BottomTabNavigatorParamList, RootStackParamList, State} from '@navigation/types'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -17,6 +16,7 @@ import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import {translateLocal} from './Localize'; import navigationRef from './Navigation/navigationRef'; +import type {AuthScreensParamList, BottomTabNavigatorParamList, RootStackParamList, State} from './Navigation/types'; import * as searchParser from './SearchParser/searchParser'; import * as TransactionUtils from './TransactionUtils'; import * as UserUtils from './UserUtils'; From d67037f7b476ce330cf17abe6d87cbb964fa82e4 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Thu, 22 Aug 2024 11:36:29 +0200 Subject: [PATCH 6/8] Fix react-compiler for SearchPageBottomTab component --- src/pages/Search/SearchPageBottomTab.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 913008b0dd7d..c66b0a1d310c 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -28,22 +28,22 @@ function SearchPageBottomTab() { const {clearSelectedTransactions} = useSearchContext(); const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); - const {queryJSON, isCustomQuery} = useMemo(() => { - if (!activeCentralPaneRoute || activeCentralPaneRoute.name !== SCREENS.SEARCH.CENTRAL_PANE) { - return {queryJSON: undefined, isCustomQuery: undefined}; + const {queryJSON, policyID, isCustomQuery} = useMemo(() => { + if (activeCentralPaneRoute?.name !== SCREENS.SEARCH.CENTRAL_PANE) { + return {queryJSON: undefined, policyID: undefined, isCustomQuery: undefined}; } - // This has to be SEARCH_CENTRAL_PANE - const searchParams = activeCentralPaneRoute.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; + const searchParams = activeCentralPaneRoute?.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; + const parsedQuery = SearchUtils.buildSearchQueryJSON(searchParams?.q); return { - queryJSON: SearchUtils.buildSearchQueryJSON(searchParams.q), + queryJSON: parsedQuery, + policyID: parsedQuery && SearchUtils.getPolicyIDFromSearchQuery(parsedQuery), isCustomQuery: searchParams.isCustomQuery, }; }, [activeCentralPaneRoute]); const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL})); - const policyID = queryJSON && SearchUtils.getPolicyIDFromSearchQuery(queryJSON); return ( Date: Tue, 27 Aug 2024 11:25:07 +0200 Subject: [PATCH 7/8] fix BottomTabBar to handle policyID when navigating to search --- ios/tmp.xcconfig | 9 --------- output.txt | 5 ----- .../createCustomBottomTabNavigator/BottomTabBar.tsx | 8 ++++---- 3 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 ios/tmp.xcconfig delete mode 100644 output.txt diff --git a/ios/tmp.xcconfig b/ios/tmp.xcconfig deleted file mode 100644 index 2f2502669450..000000000000 --- a/ios/tmp.xcconfig +++ /dev/null @@ -1,9 +0,0 @@ -NEW_EXPENSIFY_URL=https:/$()/new.expensify.com/ -SECURE_EXPENSIFY_URL=https:/$()/secure.expensify.com/ -EXPENSIFY_URL=https:/$()/www.expensify.com/ -EXPENSIFY_PARTNER_NAME=chat-expensify-com -EXPENSIFY_PARTNER_PASSWORD=e21965746fd75f82bb66 -PUSHER_APP_KEY=268df511a204fbb60884 -USE_WEB_PROXY=false -ENVIRONMENT=production -SEND_CRASH_REPORTS=true diff --git a/output.txt b/output.txt deleted file mode 100644 index 2e171753e0be..000000000000 --- a/output.txt +++ /dev/null @@ -1,5 +0,0 @@ - -> new.expensify@9.0.23-0 react-compiler-healthcheck -> react-compiler-healthcheck --verbose - -- Checking diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index a419759ab456..394a617278d4 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -130,11 +130,11 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) { ); return; } - Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()})); - // Fixme add policyID - // const query = activeWorkspaceID ? `${CONST.SEARCH.TAB.EXPENSE.ALL} policyID:${activeWorkspaceID}` : CONST.SEARCH.TAB.EXPENSE.ALL; - // Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query})); + const defaultCannedQuery = SearchUtils.buildCannedSearchQuery(); + // when navigating to search we might have an activePolicyID set from workspace switcher + const query = activeWorkspaceID ? `${defaultCannedQuery} ${CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID}:${activeWorkspaceID}` : defaultCannedQuery; + Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query})); }); }, [activeWorkspaceID, selectedTab]); From 4207b25b9c1dd583bb229d4410002dbcb7785240 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 27 Aug 2024 11:36:51 +0200 Subject: [PATCH 8/8] tweak code comment --- src/libs/Navigation/linkTo/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 8cdeee0c80bf..1fc99c771ca5 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -59,7 +59,7 @@ export default function linkTo(navigation: NavigationContainerRef