diff --git a/src/CONST.ts b/src/CONST.ts index 8a380188d05e..63fa39ea718c 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4747,6 +4747,8 @@ const CONST = { DISTANCE: 'distance', }, + SEARCH_BOTTOM_TAB_URL: '/Search_Bottom_Tab', + SEARCH_DATA_TYPES: { TRANSACTION: 'transaction', }, diff --git a/src/components/Search.tsx b/src/components/Search.tsx index fbd352a798c6..0e55fb5edc26 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -19,14 +19,15 @@ import TableListItemSkeleton from './Skeletons/TableListItemSkeleton'; type SearchProps = { query: string; + policyIDs?: string; }; -function Search({query}: SearchProps) { +function Search({query, policyIDs}: SearchProps) { const {isOffline} = useNetwork(); const styles = useThemeStyles(); useCustomBackHandler(); - const hash = SearchUtils.getQueryHash(query); + const hash = SearchUtils.getQueryHash(query, policyIDs); const [searchResults, searchResultsMeta] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`); useEffect(() => { @@ -34,8 +35,9 @@ function Search({query}: SearchProps) { return; } - SearchActions.search(query); - }, [query, isOffline]); + SearchActions.search(hash, query, policyIDs); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hash, isOffline]); const isLoading = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined; const shouldShowEmptyState = !isLoading && isEmptyObject(searchResults?.data); diff --git a/src/libs/API/parameters/Search.ts b/src/libs/API/parameters/Search.ts index 44e8c9f2d0fb..aa8671de8dc3 100644 --- a/src/libs/API/parameters/Search.ts +++ b/src/libs/API/parameters/Search.ts @@ -1,5 +1,6 @@ type SearchParams = { query: string; + policyIDs?: string; hash: number; }; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx index 6fdc075d9458..68cadc8cd4d8 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx @@ -45,7 +45,7 @@ function TopBar({policy, session, breadcrumbLabel, shouldDisplaySearch = true}: return ( diff --git a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts index 4017b1b2b17c..b6b458094b14 100644 --- a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts +++ b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts @@ -2,9 +2,12 @@ import {getPathFromState} from '@react-navigation/native'; import _ from 'lodash'; import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; -import type {RootStackParamList, State} from '@libs/Navigation/types'; +import type {BottomTabName, RootStackParamList, State} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; +// The policy ID parameter should be included in the URL when any of these pages is opened in the bottom tab. +const SCREENS_WITH_POLICY_ID_IN_URL: BottomTabName[] = [SCREENS.HOME, SCREENS.SEARCH.BOTTOM_TAB] as const; + const removePolicyIDParamFromState = (state: State) => { const stateCopy = _.cloneDeep(state); const bottomTabRoute = getTopmostBottomTabRoute(stateCopy); @@ -19,8 +22,9 @@ const customGetPathFromState: typeof getPathFromState = (state, options) => { const stateWithoutPolicyID = removePolicyIDParamFromState(state as State); const path = getPathFromState(stateWithoutPolicyID, options); const policyIDFromState = getPolicyIDFromState(state as State); - const isHomeOpened = getTopmostBottomTabRoute(state as State)?.name === SCREENS.HOME; - return `${policyIDFromState && isHomeOpened ? `/w/${policyIDFromState}` : ''}${path}`; + const topmostBottomTabRouteName = getTopmostBottomTabRoute(state as State)?.name; + const shouldAddPolicyID = !!topmostBottomTabRouteName && SCREENS_WITH_POLICY_ID_IN_URL.includes(topmostBottomTabRouteName); + return `${policyIDFromState && shouldAddPolicyID ? `/w/${policyIDFromState}` : ''}${path}`; }; export default customGetPathFromState; diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 685c21d88e79..576e272d0a0f 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -1,11 +1,12 @@ import {getActionFromState} from '@react-navigation/core'; import type {NavigationAction, NavigationContainerRef, NavigationState, PartialState} from '@react-navigation/native'; import {getPathFromState} from '@react-navigation/native'; -import type {ValueOf, Writable} from 'type-fest'; +import type {Writable} from 'type-fest'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import type {Route} from '@src/ROUTES'; +import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import getStateFromPath from './getStateFromPath'; import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; @@ -35,14 +36,8 @@ function getActionForBottomTabNavigator(action: StackNavigationAction, state: Na let payloadParams = params?.params as Record; let screen = params.screen; - // Case when the user is on the AllSettingsScreen and selects the specific workspace. The user is redirected then to the specific workspace settings. - if (screen === SCREENS.ALL_SETTINGS && policyID) { - screen = SCREENS.WORKSPACE.INITIAL; - } - - // Alternative case when the user is on the specific workspace settings screen and selects "All" workspace. - else if (!policyID && screen === SCREENS.WORKSPACE.INITIAL) { - screen = SCREENS.ALL_SETTINGS; + if (screen === SCREENS.SEARCH.CENTRAL_PANE) { + screen = SCREENS.SEARCH.BOTTOM_TAB; } if (!payloadParams) { @@ -65,7 +60,6 @@ export default function switchPolicyID(navigation: NavigationContainerRef; - const newPath = route ?? getPathFromState({routes: rootState.routes} as State, linkingConfig.config); + let newPath = route ?? getPathFromState({routes: rootState.routes} as State, linkingConfig.config); + + // Currently, the search page displayed in the bottom tab has the same URL as the page in the central pane, so we need to redirect to the correct search route. + // Here's the configuration: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx + const isOpeningSearchFromBottomTab = newPath.startsWith(CONST.SEARCH_BOTTOM_TAB_URL); + if (isOpeningSearchFromBottomTab) { + newPath = ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL); + } const stateFromPath = getStateFromPath(newPath as Route) as PartialState>; const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config); @@ -93,51 +94,31 @@ export default function switchPolicyID(navigation: NavigationContainerRef); - - // Only workspace settings screens have to store the policyID in the params. - // In other case, the policyID is read from the BottomTab params. - if (!isWorkspaceScreen) { - delete params.policyID; - } else { - params.policyID = policyID; - } - - // If the user is on the home page and changes the current workspace, then should be displayed a report from the selected workspace. - // To achieve that, it's necessary to navigate without the reportID param. - if (checkIfActionPayloadNameIsEqual(actionForBottomTabNavigator, SCREENS.HOME)) { - delete params.reportID; - } - - root.dispatch({ - type: CONST.NAVIGATION.ACTION_TYPE.PUSH, - payload: { - name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, - params: { - screen, - params, - }, - }, - }); + if (shouldAddToCentralPane) { + const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState); + const screen = topmostCentralPaneRoute?.name; + const params: CentralPaneRouteParams = {...topmostCentralPaneRoute?.params}; + + // If the user is on the home page and changes the current workspace, then should be displayed a report from the selected workspace. + // To achieve that, it's necessary to navigate without the reportID param. + if (checkIfActionPayloadNameIsEqual(actionForBottomTabNavigator, SCREENS.HOME)) { + delete params.reportID; } + + root.dispatch({ + type: CONST.NAVIGATION.ACTION_TYPE.PUSH, + payload: { + name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, + params: { + screen, + params, + }, + }, + }); } else { // If the layout is small we need to pop everything from the central pane so the bottom tab navigator is visible. root.dispatch({ diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 55e81a717bac..8f3c6d1d64a7 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -57,8 +57,9 @@ function getSections(data: OnyxTypes.Search return searchTypeToItemMap[type].getSections(data) as ReturnType; } -function getQueryHash(query: string): number { - return UserUtils.hashText(query, 2 ** 32); +function getQueryHash(query: string, policyID?: string): number { + const textToHash = [query, policyID].filter(Boolean).join('_'); + return UserUtils.hashText(textToHash, 2 ** 32); } export {getListItem, getQueryHash, getSections, getShouldShowMerchant, getSearchType}; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 03179fae93cc..6b394f1c890a 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -1,10 +1,8 @@ import * as API from '@libs/API'; import {READ_COMMANDS} from '@libs/API/types'; -import * as SearchUtils from '@libs/SearchUtils'; -function search(query: string) { - const hash = SearchUtils.getQueryHash(query); - API.read(READ_COMMANDS.SEARCH, {query, hash}); +function search(hash: number, query: string, policyIDs?: string) { + API.read(READ_COMMANDS.SEARCH, {hash, query, policyIDs}); } export { diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 1717e35e505e..bc36b7bd2d2f 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -5,7 +5,9 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types'; import CONST from '@src/CONST'; @@ -18,6 +20,7 @@ type SearchPageProps = StackScreenProps Navigation.goBack(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL)); + const {activeWorkspaceID} = useActiveWorkspace(); + + // 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. + if (isSmallScreenWidth) { + return null; + } + return ( - + ); diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 3d2ee267b7a9..f26f5db300da 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -3,6 +3,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; import useActiveRoute from '@hooks/useActiveRoute'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -18,7 +19,7 @@ function SearchPageBottomTab() { const {isSmallScreenWidth} = useWindowDimensions(); const activeRoute = useActiveRoute(); const styles = useThemeStyles(); - + const {activeWorkspaceID} = useActiveWorkspace(); const currentQuery = activeRoute?.params && 'query' in activeRoute.params ? activeRoute?.params?.query : ''; const query = currentQuery as SearchQuery; const isValidQuery = Object.values(CONST.TAB_SEARCH).includes(query); @@ -36,11 +37,17 @@ function SearchPageBottomTab() { shouldShowLink={false} > - {isSmallScreenWidth && } + {isSmallScreenWidth && ( + + )} );