1- import type { NavigationState , PartialState } from '@react-navigation/native' ;
21import { getPreservedNavigatorState } from '@libs/Navigation/AppNavigator/createSplitNavigator/usePreserveNavigatorState' ;
32import { isFullScreenName , isWorkspaceNavigatorRouteName } from '@libs/Navigation/helpers/isNavigatorName' ;
43import { getWorkspacesTabStateFromSessionStorage } from '@libs/Navigation/helpers/lastVisitedTabPathUtils' ;
@@ -13,60 +12,16 @@ import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';
1312import useOnyx from './useOnyx' ;
1413import useResponsiveLayout from './useResponsiveLayout' ;
1514
16- type WorkspaceRouteType = NavigationState [ 'routes' ] [ number ] | NonNullable < PartialState < NavigationState > [ 'routes' ] > [ number ] ;
17- type WorkspaceParams = WorkspaceSplitNavigatorParamList [ typeof SCREENS . WORKSPACE . INITIAL ] | DomainSplitNavigatorParamList [ typeof SCREENS . DOMAIN . INITIAL ] ;
18-
19- /**
20- * Walks the root nav state to find the last workspace/domain route the user had open.
21- * Falls back to session storage when no live workspace route exists.
22- *
23- * Multiple TAB_NAVIGATOR instances can coexist in the root stack — when navigation from
24- * inside an RHP targets a tab, linkTo PUSHes a fresh TabNavigator above the modal, and that
25- * new instance's WORKSPACE_NAVIGATOR slot starts empty. Older instances kept alive by
26- * ensureTabNavigatorRoutes still hold the previous workspace state, so flatten every
27- * workspace route from every TabNavigator in stack order and take the most recent one.
28- */
29- function findLastWorkspaceRoute ( rootState : NavigationState | undefined ) : WorkspaceRouteType | undefined {
30- const topmostFullScreenRoute = rootState ?. routes ?. findLast ( ( route ) => isFullScreenName ( route . name ) ) ;
31- if ( ! topmostFullScreenRoute ) {
32- return undefined ;
33- }
34-
35- const lastWorkspaceRoute = ( rootState ?. routes ?? [ ] )
36- . filter ( ( route ) => route . name === NAVIGATORS . TAB_NAVIGATOR )
37- . flatMap ( ( tabNavigatorRoute ) => {
38- const workspaceNavigatorRoute = getTabState ( tabNavigatorRoute ) ?. routes ?. find ( ( route ) => route . name === NAVIGATORS . WORKSPACE_NAVIGATOR ) ;
39- const workspaceNavigatorState = workspaceNavigatorRoute ?. state ?? ( workspaceNavigatorRoute ?. key ? getPreservedNavigatorState ( workspaceNavigatorRoute . key ) : undefined ) ;
40- return workspaceNavigatorState ?. routes ?. filter ( ( route ) => isWorkspaceNavigatorRouteName ( route . name ) ) ?? [ ] ;
41- } )
42- . at ( - 1 ) ;
43-
44- if ( lastWorkspaceRoute ) {
45- return lastWorkspaceRoute ;
46- }
47-
48- return getWorkspacesTabStateFromSessionStorage ( )
49- ?. routes ?. findLast ( ( route ) => route . name === NAVIGATORS . WORKSPACE_NAVIGATOR )
50- ?. state ?. routes ?. findLast ( ( route ) => isWorkspaceNavigatorRouteName ( route . name ) ) ;
51- }
52-
53- function getWorkspacesTabState ( route : WorkspaceRouteType | undefined ) : NavigationState | PartialState < NavigationState > | undefined {
54- if ( ! route ) {
55- return undefined ;
56- }
57- return route . state ?? ( route . key ? getPreservedNavigatorState ( route . key ) : undefined ) ;
58- }
59-
6015/**
6116 * The Workspaces tab can show three things: the workspaces list, a specific workspace page,
6217 * or a specific domain page. When the user navigates away and comes back to the tab,
6318 * this hook ensures they return to whichever of those they had open last — not always the list.
6419 *
65- * Resolves nav state and route IDs at click time inside the returned callback so the hook has
66- * no reactive subscriptions to nav state — unrelated navigations (e.g. opening a report) don't
67- * trigger re-renders. The POLICY/DOMAIN collections are subscribed at hook level so the click
68- * handler can validate the last viewed workspace/domain via closure; these collections rarely
69- * tick during navigation .
20+ * It resolves the last visited route from navigation state, fetches the matching policy/domain
21+ * from Onyx ( to verify it's still accessible), and returns a callback that performs the navigation.
22+ *
23+ * Nav state is resolved at click time so the hook has no reactive subscriptions to navigation
24+ * — unrelated navigations (e.g. opening a report) don't trigger re-renders .
7025 */
7126function useRestoreWorkspacesTabOnNavigate ( ) {
7227 const { shouldUseNarrowLayout} = useResponsiveLayout ( ) ;
@@ -75,27 +30,67 @@ function useRestoreWorkspacesTabOnNavigate() {
7530 const [ allDomains ] = useOnyx ( ONYXKEYS . COLLECTION . DOMAIN ) ;
7631
7732 return ( ) => {
33+ // Find the last route the user had open in the Workspaces tab (workspace, domain, or list).
34+ // Priority: live nav state (root level) -> inside TabNavigator -> preserved state -> session storage.
7835 const rootState = navigationRef . isReady ( ) ? navigationRef . getRootState ( ) : undefined ;
79- const topmostFullScreenRoute = rootState ?. routes ?. findLast ( ( route ) => isFullScreenName ( route . name ) ) ;
80- const lastWorkspacesTabNavigatorRoute = findLastWorkspaceRoute ( rootState ) ;
81- const workspacesTabState = getWorkspacesTabState ( lastWorkspacesTabNavigatorRoute ) ;
36+ const routeState = ( ( ) => {
37+ const topmostFullScreenRoute = rootState ?. routes ?. findLast ( ( route ) => isFullScreenName ( route . name ) ) ;
38+ if ( ! topmostFullScreenRoute ) {
39+ return { } ;
40+ }
41+
42+ // Multiple TAB_NAVIGATOR instances can coexist in the root stack — when navigation from
43+ // inside an RHP targets a tab, linkTo PUSHes a fresh TabNavigator above the modal, and that
44+ // new instance's WORKSPACE_NAVIGATOR slot starts empty. Older instances kept alive by
45+ // ensureTabNavigatorRoutes still hold the previous workspace state, so flatten every
46+ // workspace route from every TabNavigator in stack order and take the most recent one.
47+ const lastWorkspaceRoute = ( rootState ?. routes ?? [ ] )
48+ . filter ( ( route ) => route . name === NAVIGATORS . TAB_NAVIGATOR )
49+ . flatMap ( ( tabNavigatorRoute ) => {
50+ const workspaceNavigatorRoute = getTabState ( tabNavigatorRoute ) ?. routes ?. find ( ( route ) => route . name === NAVIGATORS . WORKSPACE_NAVIGATOR ) ;
51+ const workspaceNavigatorState = workspaceNavigatorRoute ?. state ?? ( workspaceNavigatorRoute ?. key ? getPreservedNavigatorState ( workspaceNavigatorRoute . key ) : undefined ) ;
52+ return workspaceNavigatorState ?. routes ?. filter ( ( route ) => isWorkspaceNavigatorRouteName ( route . name ) ) ?? [ ] ;
53+ } )
54+ . at ( - 1 ) ;
55+
56+ if ( lastWorkspaceRoute ) {
57+ const tabState = lastWorkspaceRoute . state ?? ( lastWorkspaceRoute . key ? getPreservedNavigatorState ( lastWorkspaceRoute . key ) : undefined ) ;
58+ return { lastWorkspacesTabNavigatorRoute : lastWorkspaceRoute , workspacesTabState : tabState , topmostFullScreenRoute} ;
59+ }
60+
61+ // Fall back to session storage when no workspace route exists anywhere in the navigation tree.
62+ const sessionRoute = getWorkspacesTabStateFromSessionStorage ( )
63+ ?. routes ?. findLast ( ( route ) => route . name === NAVIGATORS . WORKSPACE_NAVIGATOR )
64+ ?. state ?. routes ?. findLast ( ( route ) => isWorkspaceNavigatorRouteName ( route . name ) ) ;
65+ if ( sessionRoute ) {
66+ return { lastWorkspacesTabNavigatorRoute : sessionRoute , workspacesTabState : sessionRoute . state , topmostFullScreenRoute} ;
67+ }
68+
69+ return { topmostFullScreenRoute} ;
70+ } ) ( ) ;
71+
72+ const { lastWorkspacesTabNavigatorRoute, workspacesTabState, topmostFullScreenRoute} = routeState ;
8273
83- const params = workspacesTabState ?. routes ?. at ( 0 ) ?. params as WorkspaceParams | undefined ;
74+ // If the last route was a specific workspace or domain, extract its ID from params
75+ const params = workspacesTabState ?. routes ?. at ( 0 ) ?. params as
76+ | WorkspaceSplitNavigatorParamList [ typeof SCREENS . WORKSPACE . INITIAL ]
77+ | DomainSplitNavigatorParamList [ typeof SCREENS . DOMAIN . INITIAL ] ;
8478 const paramsPolicyID = params && 'policyID' in params ? params . policyID : undefined ;
8579 const paramsDomainAccountID = params && 'domainAccountID' in params ? params . domainAccountID : undefined ;
8680
87- const policy =
81+ // Fetch the policy/domain to verify it's still accessible (not deleted/hidden) before restoring
82+ const lastViewedPolicy =
8883 lastWorkspacesTabNavigatorRoute ?. name === NAVIGATORS . WORKSPACE_SPLIT_NAVIGATOR && paramsPolicyID ? allPolicies ?. [ `${ ONYXKEYS . COLLECTION . POLICY } ${ paramsPolicyID } ` ] : undefined ;
89- const domain =
84+ const lastViewedDomain =
9085 lastWorkspacesTabNavigatorRoute ?. name === NAVIGATORS . DOMAIN_SPLIT_NAVIGATOR && paramsDomainAccountID
9186 ? allDomains ?. [ `${ ONYXKEYS . COLLECTION . DOMAIN } ${ paramsDomainAccountID } ` ]
9287 : undefined ;
9388
9489 navigateToWorkspacesPage ( {
9590 shouldUseNarrowLayout,
9691 currentUserLogin,
97- policy,
98- domain,
92+ policy : lastViewedPolicy ,
93+ domain : lastViewedDomain ,
9994 lastWorkspacesTabNavigatorRoute,
10095 topmostFullScreenRoute,
10196 workspacesTabState,
0 commit comments