Skip to content

Commit

Permalink
Merge pull request #43500 from software-mansion-labs/fix/android-sear…
Browse files Browse the repository at this point in the history
…ch-ws

[CP Staging] [Search v1] Fix for workspace switcher and back to search tab on Android
  • Loading branch information
luacmartins authored Jun 11, 2024
2 parents 56eb982 + 149847b commit e17664e
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 45 deletions.
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
68 changes: 68 additions & 0 deletions src/libs/Navigation/setupCustomAndroidBackHandler/index.android.ts
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.

0 comments on commit e17664e

Please sign in to comment.