diff --git a/src/CONST.ts b/src/CONST.ts index 8aaeca6443fd..2703de383dd0 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6312,6 +6312,7 @@ const CONST = { ACTIVE_WORKSPACE_ID: 'ACTIVE_WORKSPACE_ID', RETRY_LAZY_REFRESHED: 'RETRY_LAZY_REFRESHED', LAST_REFRESH_TIMESTAMP: 'LAST_REFRESH_TIMESTAMP', + LAST_VISITED_SETTINGS_TAB_PATH: 'LAST_VISITED_SETTINGS_TAB_PATH', }, RESERVATION_TYPE: { diff --git a/src/components/Navigation/BottomTabBar/index.tsx b/src/components/Navigation/BottomTabBar/index.tsx index a104479c7eb3..2112c377182e 100644 --- a/src/components/Navigation/BottomTabBar/index.tsx +++ b/src/components/Navigation/BottomTabBar/index.tsx @@ -19,10 +19,11 @@ import clearSelectedText from '@libs/clearSelectedText/clearSelectedText'; import getPlatform from '@libs/getPlatform'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import {getPreservedNavigatorState} from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState'; +import {getLastVisitedSettingsPath, getLastVisitedWorkspaceScreen, getSettingsTabStateFromSessionStorage} from '@libs/Navigation/helpers/getLastVisitedWorkspace'; import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; -import {isFullScreenName} from '@navigation/helpers/isNavigatorName'; +import {isFullScreenName, isSettingsTabScreenName} from '@navigation/helpers/isNavigatorName'; import Navigation from '@navigation/Navigation'; import navigationRef from '@navigation/navigationRef'; import type {RootNavigatorParamList, SearchFullscreenNavigatorParamList, State, WorkspaceSplitNavigatorParamList} from '@navigation/types'; @@ -116,7 +117,6 @@ function BottomTabBar({selectedTab, isTooltipAllowed = false}: BottomTabBarProps const showSettingsPage = useCallback(() => { const rootState = navigationRef.getRootState(); const topmostFullScreenRoute = rootState.routes.findLast((route) => isFullScreenName(route.name)); - if (!topmostFullScreenRoute) { return; } @@ -134,37 +134,48 @@ function BottomTabBar({selectedTab, isTooltipAllowed = false}: BottomTabBarProps } interceptAnonymousUser(() => { - const lastSettingsOrWorkspaceNavigatorRoute = rootState.routes.findLast( - (rootRoute) => rootRoute.name === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR || rootRoute.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, - ); - + const state = getSettingsTabStateFromSessionStorage() ?? rootState; + const lastSettingsOrWorkspaceNavigatorRoute = state.routes.findLast((route) => isSettingsTabScreenName(route.name)); // If there is no settings or workspace navigator route, then we should open the settings navigator. if (!lastSettingsOrWorkspaceNavigatorRoute) { Navigation.navigate(ROUTES.SETTINGS); return; } - const state = lastSettingsOrWorkspaceNavigatorRoute.state ?? getPreservedNavigatorState(lastSettingsOrWorkspaceNavigatorRoute.key); + let settingsTabState = lastSettingsOrWorkspaceNavigatorRoute.state; + if (!settingsTabState && lastSettingsOrWorkspaceNavigatorRoute.key) { + settingsTabState = 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) { - const params = state?.routes.at(0)?.params as WorkspaceSplitNavigatorParamList[typeof SCREENS.WORKSPACE.INITIAL]; - + const params = settingsTabState?.routes.at(0)?.params as WorkspaceSplitNavigatorParamList[typeof SCREENS.WORKSPACE.INITIAL]; // Screens of this navigator should always have policyID if (params.policyID) { + const workspaceScreenName = !shouldUseNarrowLayout ? getLastVisitedWorkspaceScreen() : SCREENS.WORKSPACE.INITIAL; // This action will put settings split under the workspace split to make sure that we can swipe back to settings split. navigationRef.dispatch({ type: CONST.NAVIGATION.ACTION_TYPE.OPEN_WORKSPACE_SPLIT, payload: { policyID: params.policyID, + screenName: workspaceScreenName, }, }); } return; } + // If the path stored in the session storage leads to a settings screen, we just navigate to it on a wide layout. + // On a small screen, we want to go to the page containing the bottom tab bar (ROUTES.SETTINGS or ROUTES.SETTINGS_WORKSPACES) when changing tabs + if (settingsTabState && !shouldUseNarrowLayout) { + const lastVisitedSettingsRoute = getLastVisitedSettingsPath(settingsTabState); + if (lastVisitedSettingsRoute) { + Navigation.navigate(lastVisitedSettingsRoute); + return; + } + } // If there is settings workspace screen in the settings navigator, then we should open the settings workspaces as it should be "remembered". - if (state?.routes?.at(-1)?.name === SCREENS.SETTINGS.WORKSPACES) { + if (settingsTabState?.routes?.at(-1)?.name === SCREENS.SETTINGS.WORKSPACES) { Navigation.navigate(ROUTES.SETTINGS_WORKSPACES.route); return; } diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts index 658332d99294..653ecbcdfdbe 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts @@ -54,7 +54,7 @@ function handleOpenWorkspaceSplitAction( }); const actionToPushWorkspaceSplitNavigator = StackActions.push(NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, { - screen: SCREENS.WORKSPACE.INITIAL, + screen: action.payload.screenName, params: { policyID: action.payload.policyID, }, diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts index 67d69f90a205..1727c8fa61c6 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts @@ -1,5 +1,6 @@ import type {CommonActions, DefaultNavigatorOptions, ParamListBase, StackActionType, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; +import type {WorkspaceScreenName} from '@libs/Navigation/types'; import type CONST from '@src/CONST'; type RootStackNavigatorActionType = @@ -16,6 +17,7 @@ type RootStackNavigatorActionType = type: typeof CONST.NAVIGATION.ACTION_TYPE.OPEN_WORKSPACE_SPLIT; payload: { policyID: string; + screenName: WorkspaceScreenName; }; }; diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 6b7a9cbd13b9..edd4a0ba2bd8 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -1,5 +1,5 @@ -import type {NavigationState} from '@react-navigation/native'; import {DarkTheme, DefaultTheme, findFocusedRoute, NavigationContainer} from '@react-navigation/native'; +import type {NavigationState} from '@react-navigation/native'; import React, {useContext, useEffect, useMemo, useRef} from 'react'; import {useOnyx} from 'react-native-onyx'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; @@ -28,6 +28,8 @@ import AppNavigator from './AppNavigator'; import {cleanPreservedNavigatorStates} from './AppNavigator/createSplitNavigator/usePreserveNavigatorState'; import customGetPathFromState from './helpers/customGetPathFromState'; import getAdaptedStateFromPath from './helpers/getAdaptedStateFromPath'; +import {saveSettingsTabPathToSessionStorage} from './helpers/getLastVisitedWorkspace'; +import {isSettingsTabScreenName} from './helpers/isNavigatorName'; import {linkingConfig} from './linkingConfig'; import Navigation, {navigationRef} from './Navigation'; @@ -72,6 +74,9 @@ function parseAndLogRoute(state: NavigationState) { } Navigation.setIsNavigationReady(); + if (isSettingsTabScreenName(state.routes.at(-1)?.name)) { + saveSettingsTabPathToSessionStorage(currentPath); + } // Fullstory Page navigation tracking const focusedRouteName = focusedRoute?.name; diff --git a/src/libs/Navigation/helpers/getLastVisitedWorkspace/index.native.ts b/src/libs/Navigation/helpers/getLastVisitedWorkspace/index.native.ts new file mode 100644 index 000000000000..451a35d5bf6c --- /dev/null +++ b/src/libs/Navigation/helpers/getLastVisitedWorkspace/index.native.ts @@ -0,0 +1,20 @@ +import type {NavigationState, PartialState} from '@react-navigation/native'; +import type {Route} from '@src/ROUTES'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function saveSettingsTabPathToSessionStorage(url: string) {} + +function getSettingsTabStateFromSessionStorage(): PartialState | undefined { + return undefined; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function getLastVisitedSettingsPath(state: NavigationState | PartialState): Route | undefined { + return undefined; +} + +function getLastVisitedWorkspaceScreen() { + return undefined; +} + +export {getLastVisitedWorkspaceScreen, getLastVisitedSettingsPath, saveSettingsTabPathToSessionStorage, getSettingsTabStateFromSessionStorage}; diff --git a/src/libs/Navigation/helpers/getLastVisitedWorkspace/index.ts b/src/libs/Navigation/helpers/getLastVisitedWorkspace/index.ts new file mode 100644 index 000000000000..b5540400019c --- /dev/null +++ b/src/libs/Navigation/helpers/getLastVisitedWorkspace/index.ts @@ -0,0 +1,37 @@ +import type {NavigationState, PartialState} from '@react-navigation/native'; +import {findFocusedRoute} from '@react-navigation/native'; +import getStateFromPath from '@libs/Navigation/helpers/getStateFromPath'; +import CONST from '@src/CONST'; +import type {Route} from '@src/ROUTES'; + +function saveSettingsTabPathToSessionStorage(url: string) { + sessionStorage.setItem(CONST.SESSION_STORAGE_KEYS.LAST_VISITED_SETTINGS_TAB_PATH, url); +} + +function getSettingsTabStateFromSessionStorage(): PartialState | undefined { + const lastVisitedSettingsPath = sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.LAST_VISITED_SETTINGS_TAB_PATH); + if (!lastVisitedSettingsPath) { + return undefined; + } + return getStateFromPath(lastVisitedSettingsPath as Route); +} + +function getWorkspaceScreenNameFromState(state?: PartialState) { + return state?.routes.at(-1)?.state?.routes.at(-1)?.name; +} + +function getLastVisitedSettingsPath(state: NavigationState | PartialState): Route | undefined { + const lastVisitedSettingsPath = findFocusedRoute(state)?.path; + if (!lastVisitedSettingsPath) { + return undefined; + } + return lastVisitedSettingsPath as Route; +} + +function getLastVisitedWorkspaceScreen() { + const settingsState = getSettingsTabStateFromSessionStorage(); + const workspaceScreenName = getWorkspaceScreenNameFromState(settingsState); + return workspaceScreenName ?? undefined; +} + +export {getLastVisitedWorkspaceScreen, getLastVisitedSettingsPath, saveSettingsTabPathToSessionStorage, getSettingsTabStateFromSessionStorage}; diff --git a/src/libs/Navigation/helpers/isNavigatorName.ts b/src/libs/Navigation/helpers/isNavigatorName.ts index cb90d6935df7..c4e8ec1cac6f 100644 --- a/src/libs/Navigation/helpers/isNavigatorName.ts +++ b/src/libs/Navigation/helpers/isNavigatorName.ts @@ -1,5 +1,5 @@ import {SIDEBAR_TO_SPLIT, SPLIT_TO_SIDEBAR} from '@libs/Navigation/linkingConfig/RELATIONS'; -import type {FullScreenName, OnboardingFlowName, SplitNavigatorName, SplitNavigatorSidebarScreen} from '@libs/Navigation/types'; +import type {FullScreenName, OnboardingFlowName, SettingsTabScreenName, SplitNavigatorName, SplitNavigatorSidebarScreen} from '@libs/Navigation/types'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; @@ -17,6 +17,7 @@ const FULL_SCREENS_SET = new Set([...Object.values(SIDEBAR_TO_SPLIT), NAVIGATORS const SIDEBARS_SET = new Set(Object.values(SPLIT_TO_SIDEBAR)); const ONBOARDING_SCREENS_SET = new Set(ONBOARDING_SCREENS); const SPLIT_NAVIGATORS_SET = new Set(Object.values(SIDEBAR_TO_SPLIT)); +const SETTINGS_TAB_SET = new Set(Object.values([NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR])); /** * Functions defined below are used to check whether a screen belongs to a specific group. @@ -45,5 +46,7 @@ function isFullScreenName(screen: string | undefined) { function isSidebarScreenName(screen: string | undefined) { return checkIfScreenHasMatchingNameToSetValues(screen, SIDEBARS_SET); } - -export {isFullScreenName, isOnboardingFlowName, isSidebarScreenName, isSplitNavigatorName}; +function isSettingsTabScreenName(screen: string | undefined) { + return checkIfScreenHasMatchingNameToSetValues(screen, SETTINGS_TAB_SET); +} +export {isFullScreenName, isOnboardingFlowName, isSidebarScreenName, isSplitNavigatorName, isSettingsTabScreenName}; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 20313e1aef0c..c261d5408f09 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1954,6 +1954,10 @@ type SearchFullscreenNavigatorName = typeof NAVIGATORS.SEARCH_FULLSCREEN_NAVIGAT type FullScreenName = SplitNavigatorName | SearchFullscreenNavigatorName; +type SettingsTabScreenName = typeof NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR | typeof NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR; + +type WorkspaceScreenName = keyof WorkspaceSplitNavigatorParamList; + declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace ReactNavigation { @@ -2029,4 +2033,6 @@ export type { WorkspaceConfirmationNavigatorParamList, TwoFactorAuthNavigatorParamList, ConsoleNavigatorParamList, + WorkspaceScreenName, + SettingsTabScreenName, };