Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libs/Navigation/NavigationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down
9 changes: 6 additions & 3 deletions src/libs/Navigation/linkTo/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -86,9 +86,12 @@ export default function linkTo(navigation: NavigationContainerRef<RootStackParam
const topmostBottomTabRoute = getTopmostBottomTabRoute(rootState);
const policyIDsFromState = extractPolicyIDsFromState(stateFromPath);
const matchingBottomTabRoute = getMatchingBottomTabRouteForState(stateFromPath, policyID || policyIDsFromState);
const isOpeningSearch = matchingBottomTabRoute.name === SCREENS.SEARCH.BOTTOM_TAB;
const isNewPolicyID =
(topmostBottomTabRoute?.params as Record<string, string | undefined>)?.policyID !== (matchingBottomTabRoute?.params as Record<string, string | undefined>)?.policyID;
if (topmostBottomTabRoute && (topmostBottomTabRoute.name !== matchingBottomTabRoute.name || isNewPolicyID)) {
((topmostBottomTabRoute?.params as Record<string, string | undefined>)?.policyID ?? '') !==
((matchingBottomTabRoute?.params as Record<string, string | undefined>)?.policyID ?? '');

if (topmostBottomTabRoute && (topmostBottomTabRoute.name !== matchingBottomTabRoute.name || isNewPolicyID || isOpeningSearch)) {
root.dispatch({
type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
payload: matchingBottomTabRoute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ function getMatchingBottomTabRouteForState(state: State<RootStackParamList>, pol
}

const tabName = CENTRAL_PANE_TO_TAB_MAPPING[topmostCentralPaneRoute.name];

if (tabName === SCREENS.SEARCH.BOTTOM_TAB) {
const topmostCentralPaneRouteParams = topmostCentralPaneRoute.params as Record<string, string | undefined>;
delete topmostCentralPaneRouteParams?.policyIDs;
if (policyID) {
topmostCentralPaneRouteParams.policyID = policyID;
}
return {name: tabName, params: topmostCentralPaneRouteParams};
}
return {name: tabName, params: paramsWithPolicyID};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<BottomTabNavigatorParamList, typeof SCREENS.SEARCH.BOTTOM_TAB>;

// 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<RootStackParamList>);
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;
4 changes: 4 additions & 0 deletions src/libs/Navigation/setupCustomAndroidBackHandler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Do nothing for platforms different than Android.
function setupCustomAndroidBackHandler() {}

export default setupCustomAndroidBackHandler;
8 changes: 7 additions & 1 deletion src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};
};

Expand Down
4 changes: 0 additions & 4 deletions src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<CentralPaneNavigatorParamList, typeof SCREENS.SEARCH.CENTRAL_PANE>;

Expand All @@ -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) {
Expand Down
34 changes: 0 additions & 34 deletions src/pages/Search/useCustomBackHandler/index.android.ts

This file was deleted.

3 changes: 0 additions & 3 deletions src/pages/Search/useCustomBackHandler/index.ts

This file was deleted.