From 8d8c6064832df1daf60db8956a584cb0518ee099 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 11 Jun 2024 11:36:23 -0600 Subject: [PATCH] Merge pull request #43500 from software-mansion-labs/fix/android-search-ws [CP Staging] [Search v1] Fix for workspace switcher and back to search tab on Android (cherry picked from commit e17664e266704e7ce0964f421fe101e9e1032c9a) --- src/libs/Navigation/NavigationRoot.tsx | 3 + src/libs/Navigation/linkTo/index.ts | 9 ++- .../getMatchingBottomTabRouteForState.ts | 9 +++ .../index.android.ts | 68 +++++++++++++++++++ .../setupCustomAndroidBackHandler/index.ts | 4 ++ src/libs/Navigation/types.ts | 8 ++- src/pages/Search/SearchPage.tsx | 4 -- .../useCustomBackHandler/index.android.ts | 34 ---------- .../Search/useCustomBackHandler/index.ts | 3 - 9 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts create mode 100644 src/libs/Navigation/setupCustomAndroidBackHandler/index.ts delete mode 100644 src/pages/Search/useCustomBackHandler/index.android.ts delete mode 100644 src/pages/Search/useCustomBackHandler/index.ts diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 06a3dce8d59a..e4927c6d5f0d 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -18,6 +18,7 @@ import linkingConfig from './linkingConfig'; import customGetPathFromState from './linkingConfig/customGetPathFromState'; import getAdaptedStateFromPath from './linkingConfig/getAdaptedStateFromPath'; import Navigation, {navigationRef} from './Navigation'; +import setupCustomAndroidBackHandler from './setupCustomAndroidBackHandler'; import type {RootStackParamList} from './types'; type NavigationRootProps = { @@ -109,6 +110,8 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N useEffect(() => { if (firstRenderRef.current) { + setupCustomAndroidBackHandler(); + // we don't want to make the report back button go back to LHN if the user // started on the small screen so we don't set it on the first render // making it only work on consecutive changes of the screen size diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 313f525f390b..7d6d62b9a5aa 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -1,6 +1,6 @@ import {getActionFromState} from '@react-navigation/core'; -import {findFocusedRoute} from '@react-navigation/native'; import type {NavigationContainerRef, NavigationState, PartialState} from '@react-navigation/native'; +import {findFocusedRoute} from '@react-navigation/native'; import {omitBy} from 'lodash'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import extractPolicyIDsFromState from '@libs/Navigation/linkingConfig/extractPolicyIDsFromState'; @@ -86,9 +86,12 @@ export default function linkTo(navigation: NavigationContainerRef)?.policyID !== (matchingBottomTabRoute?.params as Record)?.policyID; - if (topmostBottomTabRoute && (topmostBottomTabRoute.name !== matchingBottomTabRoute.name || isNewPolicyID)) { + ((topmostBottomTabRoute?.params as Record)?.policyID ?? '') !== + ((matchingBottomTabRoute?.params as Record)?.policyID ?? ''); + + if (topmostBottomTabRoute && (topmostBottomTabRoute.name !== matchingBottomTabRoute.name || isNewPolicyID || isOpeningSearch)) { root.dispatch({ type: CONST.NAVIGATION.ACTION_TYPE.PUSH, payload: matchingBottomTabRoute, diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts index fd45685acf23..4b4ed25959f0 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts @@ -21,6 +21,15 @@ function getMatchingBottomTabRouteForState(state: State, pol } const tabName = CENTRAL_PANE_TO_TAB_MAPPING[topmostCentralPaneRoute.name]; + + 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 new file mode 100644 index 000000000000..ac272155289d --- /dev/null +++ b/src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts @@ -0,0 +1,68 @@ +import {findFocusedRoute, StackActions} from '@react-navigation/native'; +import type {StackScreenProps} from '@react-navigation/stack'; +import {BackHandler} from 'react-native'; +import getTopmostCentralPaneRoute from '@navigation/getTopmostCentralPaneRoute'; +import navigationRef from '@navigation/navigationRef'; +import type {BottomTabNavigatorParamList, RootStackParamList, State} from '@navigation/types'; +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; + +type SearchPageProps = StackScreenProps; + +// We need to do some custom handling for the back button on Android for actions related to the search page. +function setupCustomAndroidBackHandler() { + const onBackPress = () => { + const rootState = navigationRef.getRootState(); + + const bottomTabRoute = rootState.routes.find((route) => route.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR); + const bottomTabRoutes = bottomTabRoute?.state?.routes; + const focusedRoute = findFocusedRoute(rootState); + + // Shoudn't happen but for type safety. + if (!bottomTabRoutes) { + return false; + } + + // Handle back press on the search page. + // We need to pop two screens, from the central pane and from the bottom tab. + if (bottomTabRoutes[bottomTabRoutes.length - 1].name === SCREENS.SEARCH.BOTTOM_TAB && focusedRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { + navigationRef.dispatch({...StackActions.pop(), target: bottomTabRoute?.state?.key}); + navigationRef.dispatch({...StackActions.pop()}); + + const centralPaneRouteAfterPop = getTopmostCentralPaneRoute({routes: [rootState.routes.at(-2)]} as State); + 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. + // 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(NAVIGATORS.CENTRAL_PANE_NAVIGATOR, {screen: SCREENS.SEARCH.CENTRAL_PANE, params: {...restParams, policyIDs: policyID}})}); + } + + return true; + } + + // 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. + // 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(NAVIGATORS.CENTRAL_PANE_NAVIGATOR, {screen: SCREENS.SEARCH.CENTRAL_PANE, params: {...restParams, policyIDs: policyID}})}); + navigationRef.dispatch({...StackActions.pop(), target: bottomTabRoute?.state?.key}); + return true; + } + + // Handle all other cases with default handler. + return false; + }; + + BackHandler.addEventListener('hardwareBackPress', onBackPress); +} + +export default setupCustomAndroidBackHandler; diff --git a/src/libs/Navigation/setupCustomAndroidBackHandler/index.ts b/src/libs/Navigation/setupCustomAndroidBackHandler/index.ts new file mode 100644 index 000000000000..aa9077e1220f --- /dev/null +++ b/src/libs/Navigation/setupCustomAndroidBackHandler/index.ts @@ -0,0 +1,4 @@ +// Do nothing for platforms different than Android. +function setupCustomAndroidBackHandler() {} + +export default setupCustomAndroidBackHandler; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5597e7ce00da..e87f0380a2da 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -854,7 +854,13 @@ type WelcomeVideoModalNavigatorParamList = { type BottomTabNavigatorParamList = { [SCREENS.HOME]: {policyID?: string}; - [SCREENS.SEARCH.BOTTOM_TAB]: {policyID?: string}; + [SCREENS.SEARCH.BOTTOM_TAB]: { + query: string; + policyID?: string; + offset?: number; + sortBy?: SearchColumnType; + sortOrder?: SortOrder; + }; [SCREENS.SETTINGS.ROOT]: {policyID?: string}; }; diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 8decc6477e21..f7b21592b090 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -14,7 +14,6 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {SearchQuery} from '@src/types/onyx/SearchResults'; import type IconAsset from '@src/types/utils/IconAsset'; -import useCustomBackHandler from './useCustomBackHandler'; type SearchPageProps = StackScreenProps; @@ -36,9 +35,6 @@ function SearchPage({route}: SearchPageProps) { const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL)); - // We need to override default back button behavior on Android because we need to pop two screens, from the central pane and from the bottom tab. - useCustomBackHandler(); - // 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) { diff --git a/src/pages/Search/useCustomBackHandler/index.android.ts b/src/pages/Search/useCustomBackHandler/index.android.ts deleted file mode 100644 index f168cb0b9008..000000000000 --- a/src/pages/Search/useCustomBackHandler/index.android.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {StackActions, useFocusEffect} from '@react-navigation/native'; -import {useCallback} from 'react'; -import {BackHandler} from 'react-native'; -import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import navigationRef from '@libs/Navigation/navigationRef'; -import type {RootStackParamList, State} from '@libs/Navigation/types'; -import NAVIGATORS from '@src/NAVIGATORS'; -import SCREENS from '@src/SCREENS'; - -// We need to make sure that the central pane screen and bottom tab won't be desynchronized after using the physical back button on Android. -// To achieve that we will call additional POP on the bottom tab navigator if the search page would disappear from the central pane. -function useCustomBackHandler() { - useFocusEffect( - useCallback(() => { - const onBackPress = () => { - const rootState = navigationRef.getRootState(); - - const bottomTabRoute = rootState.routes.find((route) => route.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR); - const centralPaneRouteAfterPop = getTopmostCentralPaneRoute({routes: [rootState.routes.at(-2)]} as State); - - if (bottomTabRoute && bottomTabRoute.state && (!centralPaneRouteAfterPop || centralPaneRouteAfterPop.name !== SCREENS.SEARCH.CENTRAL_PANE)) { - navigationRef.dispatch({...StackActions.pop(), target: bottomTabRoute.state.key}); - } - return false; - }; - - const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress); - - return () => subscription.remove(); - }, []), - ); -} - -export default useCustomBackHandler; diff --git a/src/pages/Search/useCustomBackHandler/index.ts b/src/pages/Search/useCustomBackHandler/index.ts deleted file mode 100644 index be753de818a6..000000000000 --- a/src/pages/Search/useCustomBackHandler/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -function useCustomBackHandler() {} - -export default useCustomBackHandler;