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
29 changes: 25 additions & 4 deletions src/components/Navigation/BottomTabBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import useThemeStyles from '@hooks/useThemeStyles';
import clearSelectedText from '@libs/clearSelectedText/clearSelectedText';
import getPlatform from '@libs/getPlatform';
import interceptAnonymousUser from '@libs/interceptAnonymousUser';
import {buildCannedSearchQuery} from '@libs/SearchQueryUtils';
import {getPreservedNavigatorState} from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState';
import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils';
import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils';
import {getPreservedSplitNavigatorState} from '@navigation/AppNavigator/createSplitNavigator/usePreserveSplitNavigatorState';
import {isFullScreenName} from '@navigation/helpers/isNavigatorName';
import Navigation from '@navigation/Navigation';
import navigationRef from '@navigation/navigationRef';
import type {WorkspaceSplitNavigatorParamList} from '@navigation/types';
import type {RootNavigatorParamList, SearchFullscreenNavigatorParamList, State, WorkspaceSplitNavigatorParamList} from '@navigation/types';
import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar';
import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton';
import variables from '@styles/variables';
Expand Down Expand Up @@ -79,6 +79,27 @@ function BottomTabBar({selectedTab, isTooltipAllowed = false}: BottomTabBarProps
clearSelectedText();
interceptAnonymousUser(() => {
const defaultCannedQuery = buildCannedSearchQuery();

const rootState = navigationRef.getRootState() as State<RootNavigatorParamList>;
const lastSearchNavigator = rootState.routes.findLast((route) => route.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the fix for preserving state when navigating from BottomBar:

Suggested change
const lastSearchNavigator = rootState.routes.findLast((route) => route.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR);
const lastSearchNavigator = rootState.routes.findLast((route) => route.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR);
const lastSearchNavigatorState = lastSearchNavigator && lastSearchNavigator.state && lastSearchNavigator.key ? getPreservedSplitNavigatorState(lastSearchNavigator?.key) : undefined;
const lastSearchRoute = lastSearchNavigatorState?.routes.findLast((route) => route.name === SCREENS.SEARCH.ROOT);

This solution came from @WojtekBoman - thanks!

A few words about it:
previously Search was "flat" so we didn't need to do any extra steps to try and preserve it's state. The route with params was on the top level of navigation so it worked.
Now we need this extra steps of finding the last search route within the navigator to grab the query.

If you are able to improve the dirty logic expression that I did lastSearchNavigator && lastSearchNavigator.state && lastSearchNavigator.key then feel free 😅

const lastSearchNavigatorState = lastSearchNavigator && lastSearchNavigator.key ? getPreservedNavigatorState(lastSearchNavigator?.key) : undefined;
const lastSearchRoute = lastSearchNavigatorState?.routes.findLast((route) => route.name === SCREENS.SEARCH.ROOT);

if (lastSearchRoute) {
const {q, ...rest} = lastSearchRoute.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.ROOT];
const queryJSON = buildSearchQueryJSON(q);
if (queryJSON) {
queryJSON.policyID = activeWorkspaceID;
const query = buildSearchQueryString(queryJSON);
Navigation.navigate(
ROUTES.SEARCH_ROOT.getRoute({
query,
...rest,
}),
);
return;
}
}
// 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_ROOT.getRoute({query}));
Expand Down Expand Up @@ -122,7 +143,7 @@ function BottomTabBar({selectedTab, isTooltipAllowed = false}: BottomTabBarProps
return;
}

const state = lastSettingsOrWorkspaceNavigatorRoute.state ?? getPreservedSplitNavigatorState(lastSettingsOrWorkspaceNavigatorRoute.key);
const state = lastSettingsOrWorkspaceNavigatorRoute.state ?? getPreservedNavigatorState(lastSettingsOrWorkspaceNavigatorRoute.key);

// If there is a workspace navigator route, then we should open the workspace initial screen as it should be "remembered".
if (lastSettingsOrWorkspaceNavigatorRoute.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';
import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
import type {PlatformStackNavigationOptions, PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {AuthScreensParamList, SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
import * as SearchQueryUtils from '@libs/SearchQueryUtils';
import createSearchFullscreenNavigator from '@navigation/AppNavigator/createSearchFullscreenNavigator';
import FreezeWrapper from '@navigation/AppNavigator/FreezeWrapper';
import useRootNavigatorScreenOptions from '@navigation/AppNavigator/useRootNavigatorScreenOptions';
import type NAVIGATORS from '@src/NAVIGATORS';
import SCREENS from '@src/SCREENS';
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';

Expand All @@ -13,22 +14,21 @@ const loadSearchMoneyReportPage = () => require<ReactComponentModule>('@pages/Se

const Stack = createSearchFullscreenNavigator<SearchFullscreenNavigatorParamList>();

function SearchFullscreenNavigator() {
function SearchFullscreenNavigator({route}: PlatformStackScreenProps<AuthScreensParamList, typeof NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR>) {
const rootNavigatorScreenOptions = useRootNavigatorScreenOptions();

const searchRootScreenOptions: PlatformStackNavigationOptions = {
...rootNavigatorScreenOptions.fullScreen,
web: {
...rootNavigatorScreenOptions.fullScreen.web,
cardStyleInterpolator: undefined,
},
};

return (
<FreezeWrapper>
<Stack.Navigator
screenOptions={searchRootScreenOptions}
defaultCentralScreen={SCREENS.SEARCH.ROOT}
parentRoute={route}
>
<Stack.Screen
name={SCREENS.SEARCH.ROOT}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {ParamListBase} from '@react-navigation/native';
import {createNavigatorFactory} from '@react-navigation/native';
import usePreserveNavigatorState from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState';
import useNavigationResetOnLayoutChange from '@libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange';
import createPlatformStackNavigatorComponent from '@navigation/PlatformStackNavigation/createPlatformStackNavigatorComponent';
import defaultPlatformStackScreenOptions from '@navigation/PlatformStackNavigation/defaultPlatformStackScreenOptions';
Expand All @@ -8,6 +9,7 @@ import SearchFullscreenRouter from './SearchFullscreenRouter';

function useCustomEffects(props: CustomEffectsHookProps) {
useNavigationResetOnLayoutChange(props);
usePreserveNavigatorState(props.state, props.parentRoute);
}

const SearchFullscreenNavigatorComponent = createPlatformStackNavigatorComponent('SearchFullscreenNavigator', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {shouldDisplayPolicyNotFoundPage} from '@libs/PolicyUtils';
import CONST from '@src/CONST';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {SplitNavigatorRouterOptions} from './types';
import {getPreservedSplitNavigatorState} from './usePreserveSplitNavigatorState';
import {getPreservedNavigatorState} from './usePreserveNavigatorState';

type StackState = StackNavigationState<ParamListBase> | PartialState<StackNavigationState<ParamListBase>>;

Expand Down Expand Up @@ -73,7 +73,7 @@ function adaptStateIfNecessary({state, options: {sidebarScreen, defaultCentralSc
const previousSameNavigator = rootState?.routes.filter((route) => route.name === parentRoute.name).at(-2);

// If we have optimization for not rendering all split navigators, then last selected option may not be in the state. In this case state has to be read from the preserved state.
const previousSameNavigatorState = previousSameNavigator?.state ?? (previousSameNavigator?.key ? getPreservedSplitNavigatorState(previousSameNavigator.key) : undefined);
const previousSameNavigatorState = previousSameNavigator?.state ?? (previousSameNavigator?.key ? getPreservedNavigatorState(previousSameNavigator.key) : undefined);
const previousSelectedCentralScreen =
previousSameNavigatorState?.routes && previousSameNavigatorState.routes.length > 1 ? previousSameNavigatorState.routes.at(-1)?.name : undefined;

Expand Down Expand Up @@ -113,7 +113,7 @@ function SplitRouter(options: SplitNavigatorRouterOptions) {
return stackRouter.getStateForAction(state, action, configOptions);
},
getInitialState({routeNames, routeParamList, routeGetIdList}: RouterConfigOptions) {
const preservedState = getPreservedSplitNavigatorState(options.parentRoute.key);
const preservedState = getPreservedNavigatorState(options.parentRoute.key);
const initialState = preservedState ?? stackRouter.getInitialState({routeNames, routeParamList, routeGetIdList});

adaptStateIfNecessary({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import type {
} from '@libs/Navigation/PlatformStackNavigation/types';
import SidebarSpacerWrapper from './SidebarSpacerWrapper';
import SplitRouter from './SplitRouter';
import usePreserveSplitNavigatorState from './usePreserveSplitNavigatorState';
import usePreserveNavigatorState from './usePreserveNavigatorState';

function useCustomEffects(props: CustomEffectsHookProps) {
useNavigationResetOnLayoutChange(props);
usePreserveSplitNavigatorState(props.state, props.parentRoute);
usePreserveNavigatorState(props.state, props.parentRoute);
}

function useCustomSplitNavigatorState({state}: CustomStateHookProps) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {NavigationState, ParamListBase, RouteProp, StackNavigationState} from '@react-navigation/native';
import {useEffect} from 'react';

const preservedNavigatorStates: Record<string, StackNavigationState<ParamListBase>> = {};

const cleanPreservedNavigatorStates = (state: NavigationState) => {
const currentSplitNavigatorKeys = state.routes.map((route) => route.key);

for (const key of Object.keys(preservedNavigatorStates)) {
if (!currentSplitNavigatorKeys.includes(key)) {
delete preservedNavigatorStates[key];
}
}
};

const getPreservedNavigatorState = (key: string) => preservedNavigatorStates[key];

function usePreserveNavigatorState(state: StackNavigationState<ParamListBase>, route: RouteProp<ParamListBase> | undefined) {
useEffect(() => {
if (!route) {
return;
}
preservedNavigatorStates[route.key] = state;
}, [route, state]);
}

export default usePreserveNavigatorState;

export {getPreservedNavigatorState, cleanPreservedNavigatorStates};

This file was deleted.

4 changes: 2 additions & 2 deletions src/libs/Navigation/NavigationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import AppNavigator from './AppNavigator';
import {cleanPreservedSplitNavigatorStates} from './AppNavigator/createSplitNavigator/usePreserveSplitNavigatorState';
import {cleanPreservedNavigatorStates} from './AppNavigator/createSplitNavigator/usePreserveNavigatorState';
import customGetPathFromState from './helpers/customGetPathFromState';
import getAdaptedStateFromPath from './helpers/getAdaptedStateFromPath';
import {linkingConfig} from './linkingConfig';
Expand Down Expand Up @@ -209,7 +209,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh

// We want to clean saved scroll offsets for screens that aren't anymore in the state.
cleanStaleScrollOffsets(state);
cleanPreservedSplitNavigatorStates(state);
cleanPreservedNavigatorStates(state);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions src/pages/home/sidebar/BaseSidebarScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {updateLastAccessedWorkspace} from '@libs/actions/Policy/Policy';
import {isMobile} from '@libs/Browser';
import getInitialSplitNavigatorState from '@libs/Navigation/AppNavigator/createSplitNavigator/getInitialSplitNavigatorState';
import {getPreservedSplitNavigatorState} from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveSplitNavigatorState';
import {getPreservedNavigatorState} from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState';
import getTopmostReportsSplitNavigator from '@libs/Navigation/helpers/getTopmostReportsSplitNavigator';
import Navigation from '@libs/Navigation/Navigation';
import Performance from '@libs/Performance';
Expand Down Expand Up @@ -48,7 +48,7 @@ function BaseSidebarScreen() {
}

// Switching workspace to global should only be performed from the currently opened sidebar screen
const topmostReportSplitState = topmostReportSplit?.state ?? getPreservedSplitNavigatorState(topmostReportSplit?.key);
const topmostReportSplitState = topmostReportSplit?.state ?? getPreservedNavigatorState(topmostReportSplit?.key);
const isCurrentSidebar = topmostReportSplitState?.routes.some((route) => currentRoute.key === route.key);

if (!isCurrentSidebar) {
Expand Down