Skip to content

Commit 176bd19

Browse files
committed
Tighten idempotency check to focused route only
Check state.routes[state.index] instead of .some() so the guard only ALLOWs when OnboardingModalNavigator is actually focused, not merely present in the stack. Adds a test for the unfocused edge case. Made-with: Cursor
1 parent 656cfdf commit 176bd19

2 files changed

Lines changed: 34 additions & 3 deletions

File tree

src/libs/Navigation/guards/OnboardingGuard.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,11 @@ const OnboardingGuard: NavigationGuard = {
198198
return {type: 'ALLOW'};
199199
}
200200

201-
// If the OnboardingModalNavigator is already in the navigation state, the user is already
201+
// If the OnboardingModalNavigator is the currently focused route, the user is already
202202
// on the onboarding flow. Redirecting again would produce a redundant state reset that
203203
// triggers further actions, creating an infinite navigation loop (APP-7FR).
204-
const isAlreadyOnOnboarding = state.routes.some((route) => route.name === NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR);
205-
if (isAlreadyOnOnboarding) {
204+
const isOnboardingFocused = state.routes[state.index]?.name === NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR;
205+
if (isOnboardingFocused) {
206206
return {type: 'ALLOW'};
207207
}
208208

tests/unit/Navigation/guards/OnboardingGuard.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,37 @@ describe('OnboardingGuard', () => {
449449
expect(result.route).toContain('onboarding');
450450
});
451451

452+
it('should still redirect when onboarding is in routes but not focused', async () => {
453+
// Given a user who needs onboarding, and a state where OnboardingModalNavigator
454+
// exists in routes but HOME is focused (index: 0)
455+
const stateWithOnboardingNotFocused: NavigationState = {
456+
key: 'root',
457+
index: 0,
458+
routeNames: [SCREENS.HOME, NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR],
459+
routes: [
460+
{key: 'home', name: SCREENS.HOME},
461+
{key: 'onboarding-modal', name: NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR},
462+
],
463+
stale: false,
464+
type: 'stack',
465+
};
466+
467+
await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, {
468+
hasCompletedGuidedSetupFlow: false,
469+
});
470+
await Onyx.merge(ONYXKEYS.ACCOUNT, {
471+
isFromPublicDomain: true,
472+
});
473+
await waitForBatchedUpdates();
474+
475+
// When the guard evaluates while onboarding is NOT focused
476+
const result = OnboardingGuard.evaluate(stateWithOnboardingNotFocused, mockAction, authenticatedContext) as {type: 'REDIRECT'; route: string};
477+
478+
// Then the guard should still redirect because the user isn't actively on onboarding
479+
expect(result.type).toBe('REDIRECT');
480+
expect(result.route).toContain('onboarding');
481+
});
482+
452483
it('should still BLOCK RESET to non-onboarding even when on onboarding', async () => {
453484
// Given a user on onboarding who has not completed it
454485
await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, {

0 commit comments

Comments
 (0)