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
6 changes: 5 additions & 1 deletion src/pages/ValidateLoginPage/index.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ function ValidateLoginPage({
credentials?.validateCode === validateCode &&
credentials?.accountID === Number(accountID) &&
(autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN || autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.FAILED);
const isUserClickedSignIn = !login && isSignedIn && (autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.SIGNING_IN || autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN);
// Exclude `exitTo` deep links (e.g. a workspace-chat invite link): those carry a specific
// destination that `handleExitToNavigation` owns, so this flag's Home redirect (focus effect
// below) must not fire and clobber it.
const isUserClickedSignIn =
!login && !exitTo && isSignedIn && (autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.SIGNING_IN || autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN);
const shouldStartSignInWithValidateCode = !isUserClickedSignIn && !isSignedIn && (!!login || !!exitTo) && isValidValidateCode(validateCode);
const isNavigatingToExitTo = isSignedIn && !!exitTo;
// Fresh-session magic-link sign-in. Not gated on `isSignedIn` because `autoAuthState` lands
Expand Down
32 changes: 32 additions & 0 deletions tests/ui/ValidateLoginPageTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,38 @@ describe('ValidateLoginPage', () => {
expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.HOME, {forceReplace: true});
});

it('Should hand off to exitTo (not redirect Home) for a first-time invitee opening an exitTo magic link', async () => {
// Regression for #94549: an invited member opens `/v/<id>/<code>?exitTo=<destination>` with no
// cached `login` and JUST_SIGNED_IN. Before the fix `isUserClickedSignIn` matched this exactly and
// its focus effect force-redirected Home, clobbering the exitTo navigation. Excluding `exitTo`
// keeps that Home redirect from firing so `handleExitToNavigation` owns the deep-link destination.
await act(async () => {
await Onyx.set(ONYXKEYS.SESSION, {
authToken: 'abcd',
autoAuthState: CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN,
});
await Onyx.set(ONYXKEYS.CREDENTIALS, {
accountID: 1,
validateCode: '123456',
});
});

renderPage({accountID: '1', validateCode: '123456', exitTo: 'concierge'});
await waitForBatchedUpdatesWithAct();

// The deferred destination handoff is registered with the exitTo route...
expect(handleExitToNavigation).toHaveBeenCalledWith('concierge');

// ...and even once protected routes become available, the competing Home redirect must not fire.
await act(async () => {
mockWaitForProtectedRoutes.resolve();
await Promise.resolve();
});
await waitForBatchedUpdatesWithAct();

expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.HOME, {forceReplace: true});
});

it('Should not navigate to home when a signed-in session opens /v/ to view the code (autoAuthState !== JUST_SIGNED_IN)', async () => {
await act(async () => {
await Onyx.set(ONYXKEYS.SESSION, {
Expand Down
Loading