Skip to content

Commit 22e6ab7

Browse files
committed
Restore complete tab state after canceled pre-insert
Restore the full saved tab navigator route after the RHP has already been dismissed. This removes both same-tab and cross-tab state mutations instead of only correcting the currently focused screen.
1 parent fb12684 commit 22e6ab7

2 files changed

Lines changed: 23 additions & 32 deletions

File tree

src/libs/Navigation/Navigation.ts

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,39 +1198,22 @@ function removePreInsertedFullscreenIfNeeded() {
11981198
return;
11991199
}
12001200

1201-
// RHP already dismissed. For the tab-switch path, jump back to the original tab.
1202-
// For the push path, pop the pre-inserted route directly.
1201+
// RHP already dismissed. Restore the entire TAB_NAVIGATOR route from the snapshot so both the focused tab
1202+
// and every tab's nested state are put back. A bare jumpTo only restores focus, while resetting the active
1203+
// nested tab router can discard the inactive tabs that were also present in the snapshot.
12031204
const originalTabRoute = getPreInsertedOriginalTabRoute();
12041205
if (originalTabRoute) {
12051206
clearPreInsertedOriginalTabRoute();
1206-
const originalTabState = originalTabRoute.state;
1207-
const originalFocusedTabIndex = originalTabState?.index ?? 0;
1208-
const originalTabName = originalTabState?.routes?.[originalFocusedTabIndex]?.name;
1209-
if (originalTabName) {
1210-
requestAnimationFrame(() => {
1211-
const currentState = navigationRef.getRootState();
1212-
const tabNavRoute = currentState?.routes.findLast((r) => r.name === NAVIGATORS.TAB_NAVIGATOR);
1213-
if (!tabNavRoute?.state?.key) {
1214-
return;
1215-
}
1216-
// A same-tab pre-insert (e.g. a report pre-inserted while another report of the same split navigator was
1217-
// visible) rewrote the focused tab's inner state in place, so jumping to the tab name would be a no-op
1218-
// that leaves the pre-inserted screen visible. Reset the tab navigator to the saved original route state
1219-
// so the screen the user started on comes back.
1220-
const focusedTabName = tabNavRoute.state.routes?.at(tabNavRoute.state.index ?? 0)?.name;
1221-
if (focusedTabName === originalTabName && originalTabState) {
1222-
navigationRef.current?.dispatch({
1223-
...CommonActions.reset(originalTabState),
1224-
target: tabNavRoute.state.key,
1225-
});
1226-
return;
1227-
}
1228-
navigationRef.current?.dispatch({
1229-
...TabActions.jumpTo(originalTabName),
1230-
target: tabNavRoute.state.key,
1231-
});
1232-
});
1233-
}
1207+
requestAnimationFrame(() => {
1208+
const currentState = navigationRef.getRootState();
1209+
const tabNavIndex = currentState?.routes.findLastIndex((r) => r.name === NAVIGATORS.TAB_NAVIGATOR) ?? -1;
1210+
if (!currentState || tabNavIndex < 0) {
1211+
return;
1212+
}
1213+
const newRoutes = [...currentState.routes.slice(0, tabNavIndex), originalTabRoute, ...currentState.routes.slice(tabNavIndex + 1)];
1214+
const clampedIndex = currentState.index >= newRoutes.length ? newRoutes.length - 1 : currentState.index;
1215+
navigationRef.resetRoot({...currentState, routes: newRoutes, index: clampedIndex});
1216+
});
12341217
return;
12351218
}
12361219

tests/navigation/RemovePreInsertedFullscreenAfterRHPDismissTests.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ function getCentralPaneReportID() {
9595
return (topmostReport?.params as {reportID?: string} | undefined)?.reportID;
9696
}
9797

98+
function getReportsTabState() {
99+
const tabState = navigationRef.current?.getRootState().routes.find((route) => route.name === NAVIGATORS.TAB_NAVIGATOR)?.state;
100+
return tabState?.routes.find((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR)?.state;
101+
}
102+
98103
function isRHPOnTop() {
99104
return navigationRef.current?.getRootState().routes.at(-1)?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR;
100105
}
@@ -145,14 +150,16 @@ describe('removePreInsertedFullscreenIfNeeded after the RHP was dismissed by the
145150
expect(getFocusedTabName()).toBe(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR);
146151
});
147152

148-
it('still jumps back to the original tab when the pre-insert switched tabs', async () => {
153+
it('restores the original focus and nested state when the pre-insert switched tabs', async () => {
149154
// Given the RHP opened from the Search tab, with a report pre-inserted under it (switches to the Reports tab)
150155
render(<TestNavigationContainer initialState={buildInitialState(SEARCH_TAB_INDEX)} />);
156+
const originalReportsTabState = getReportsTabState();
151157
act(() => {
152158
Navigation.preInsertFullscreenUnderRHP(ROUTES.REPORT_WITH_ID.getRoute(WORKSPACE_CHAT_REPORT_ID));
153159
});
154160
expect(Navigation.getIsFullscreenPreInsertedUnderRHP()).toBe(true);
155161
expect(getFocusedTabName()).toBe(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR);
162+
expect(getCentralPaneReportID()).toBe(WORKSPACE_CHAT_REPORT_ID);
156163

157164
// When the device back button pops the RHP and the unmount cleanup runs afterwards
158165
act(() => {
@@ -163,8 +170,9 @@ describe('removePreInsertedFullscreenIfNeeded after the RHP was dismissed by the
163170
});
164171
await flushAnimationFrame();
165172

166-
// Then the user is back on the tab the flow started from
173+
// Then the user is back on the tab the flow started from and the destination tab was fully restored
167174
expect(getFocusedTabName()).toBe(NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR);
175+
expect(getReportsTabState()).toEqual(originalReportsTabState);
168176
});
169177

170178
it('restores the origin report through the router action when the RHP is still on top', () => {

0 commit comments

Comments
 (0)