Skip to content

Preserve deep-linked report route during onboarding guard redirect#86390

Open
MelvinBot wants to merge 4 commits intomainfrom
claude-preserveDeepLinkDuringOnboardingRedirect-v2
Open

Preserve deep-linked report route during onboarding guard redirect#86390
MelvinBot wants to merge 4 commits intomainfrom
claude-preserveDeepLinkDuringOnboardingRedirect-v2

Conversation

@MelvinBot
Copy link
Copy Markdown
Contributor

Explanation of Change

This is a re-creation of the reverted PR #85556 with identical changes.

When an unauthenticated user visits a direct report deep link (e.g., /r/7075912447943023), signs in as a new account, and gets redirected to onboarding, the deep-linked report route is lost. This happens because handleNavigationGuards in RootStackRouter.ts completely replaces the navigation state when the OnboardingGuard fires a REDIRECT — the state becomes [Home, OnboardingModalNavigator] instead of preserving the existing report route.

This PR modifies handleNavigationGuards so that when a REDIRECT adds a modal route (like the OnboardingModalNavigator), the existing fullscreen/base routes (like the deep-linked report) are preserved instead of being replaced with Home. The navigation state becomes [ReportsSplitNavigator(deep-linked report), OnboardingModalNavigator], so when onboarding completes and the modal is dismissed, the user lands on their deep-linked report.

When no existing fullscreen routes are present or the redirect doesn't include modal routes, the behavior falls back to the previous logic.

Fixed Issues

$ #85242
PROPOSAL: #85242 (comment)

Tests

  1. Go to https://dev.new.expensify.com/r/7075912447943023 while signed out
  2. Click "Sign in"
  3. Enter a new email and sign up as a new account
  4. Complete the onboarding flow
  5. Verify that after onboarding, you land on the deep-linked report room instead of the home page
  • Verify that no errors appear in the JS console

Offline tests

N/A - This change affects navigation state during the sign-in/onboarding flow which requires network connectivity.

QA Steps

  1. Go to https://staging.new.expensify.com/r/7075912447943023 while signed out
  2. Click "Sign in"
  3. Enter a new email and sign up as a new account
  4. Complete the onboarding flow
  5. Verify that after onboarding, you land on the deep-linked report room instead of the home page
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

When the OnboardingGuard fires a REDIRECT, handleNavigationGuards was
replacing the entire navigation state with [Home, OnboardingModalNavigator].
This discarded any existing fullscreen routes, including a deep-linked
report that the user was trying to access before signing in.

Now we separate the redirect state into fullscreen (base) and modal routes.
If the current state already has fullscreen routes and the redirect adds
modal routes, we preserve the existing fullscreen routes and layer the
modal routes on top. This means the navigation state becomes
[ReportsSplitNavigator(deep-linked report), OnboardingModalNavigator]
instead of [Home, OnboardingModalNavigator], so when onboarding completes
and the modal is dismissed, the user lands on their deep-linked report.

Co-authored-by: Bernhard Owen Josephus <bernhardoj@users.noreply.github.com>
@bernhardoj
Copy link
Copy Markdown
Contributor

Hold on #85660 (comment)

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot please merge with main

@MelvinBot
Copy link
Copy Markdown
Contributor Author

I have failed to help you. I let you down and for that I am sorry. Please try again later. This may be due to a Claude outage (check here).

Failure details: https://github.com/Expensify/Expensify/actions/runs/23635905944

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot please merge with the main branch

…nkDuringOnboardingRedirect-v2

Co-authored-by: Bernhard Owen Josephus <bernhardoj@users.noreply.github.com>
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Successfully merged main into this branch. The merge brought in 301 changed files with no conflicts.

@MelvinBot
Copy link
Copy Markdown
Contributor Author

The failing check (test (job 1)) is unrelated to this PR.

Analysis: The test tests/unit/CardFeedUtilsTest.ts failed with a SIGSEGV (segmentation fault) — a jest worker process was killed by the OS, likely due to memory pressure in CI. This is a flaky infrastructure issue, not a code error. All 1140 tests that actually ran passed successfully.

Evidence: This PR only changes RootStackRouter.ts and handleNavigationGuardRedirect.test.ts (navigation code). The crashing test (CardFeedUtilsTest.ts) is in a completely different domain (card feed utilities) and has no relation to the PR's changes. The navigation guard test added by this PR (handleNavigationGuardRedirect.test.ts) passed successfully.

Re-running the failing job should resolve this.

@bernhardoj
Copy link
Copy Markdown
Contributor

bernhardoj commented Mar 27, 2026

@MelvinBot mark this PR as ready

@MelvinBot
Copy link
Copy Markdown
Contributor Author

I have failed to help you. I let you down and for that I am sorry. Please try again later. This may be due to a Claude outage (check here).

Failure details: https://github.com/Expensify/Expensify/actions/runs/23638630176

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot mark this PR as ready

@MelvinBot
Copy link
Copy Markdown
Contributor Author

I have failed to help you. I let you down and for that I am sorry. Please try again later. This may be due to a Claude outage (check here).

Failure details: https://github.com/Expensify/Expensify/actions/runs/23638673642

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot mark this PR as ready

@MelvinBot
Copy link
Copy Markdown
Contributor Author

I have failed to help you. I let you down and for that I am sorry. Please try again later. This may be due to a Claude outage (check here).

Failure details: https://github.com/Expensify/Expensify/actions/runs/23639248265

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot mark this PR as ready

@MelvinBot MelvinBot marked this pull request as ready for review March 27, 2026 10:53
@MelvinBot MelvinBot requested a review from a team as a code owner March 27, 2026 10:53
@melvin-bot melvin-bot bot requested a review from bernhardoj March 27, 2026 10:53
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Done! This PR has been marked as ready for review.

@melvin-bot melvin-bot bot requested review from heyjennahay and removed request for a team March 27, 2026 10:53
@melvin-bot
Copy link
Copy Markdown

melvin-bot bot commented Mar 27, 2026

@bernhardoj Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot melvin-bot bot removed the request for review from a team March 27, 2026 10:53
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed1c51ff6e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +101 to +102
if (redirectRoute && state.routes[state.index]?.name === redirectRoute.name) {
return state;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Compare full redirect target before short-circuiting

This early return only compares the top-level route name, so redirects to different onboarding steps are treated as duplicates once OnboardingModalNavigator is focused. OnboardingGuard computes step-specific redirect paths (e.g., purpose vs employees) but they share the same root navigator name; returning state here skips the reset and also bypasses downstream action handling, which can leave users stuck on an outdated onboarding step instead of being redirected to the newly required one.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I really think this is unneeded as well as wrong.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this, otherwise multiple onboarding modal will be added when we refresh the app. (multiple navigation actions are fired).

I wanna test comparing findFocusedRoute(state).name === findFocusedRoute(redirectState).name, but got distracted with other things and forget to come back to this 🤦

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong this change BTW caused a regression. If you proceed up to accounting stage of onboarding(selecting manage teams) and deeplink to purpose page you are now not correctly redirected to oboarding-account page.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanna test comparing findFocusedRoute(state).name === findFocusedRoute(redirectState).name, but got distracted with other things and forget to come back to this

Yep, that's why I say this ^. If we use findFocusedRoute(state).name === findFocusedRoute(redirectState).name, then that won't happen. But there is another problem.

When we open onboarding/purpose by changing the URL, the root state will be [Home, OnboardingModalNavigator (with the onboarding purpose screen)], then the REDIRECT guard will add another OnboardingModalNavigator, so we have 2 OnboardingModalNavigator on the stack, which we don't want.

// append only the redirect target on top of the existing routes so the user returns
// to them after the redirect screen is dismissed. Otherwise (fresh app with no stack),
// use the full redirect state which includes the base route (e.g., Home).
const routes = hasExistingFullScreenRoute && redirectRoute ? [...state.routes, redirectRoute] : redirectState.routes;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve stack only for modal redirects

This condition preserves the existing stack for any redirect as long as a fullscreen route exists, but OnboardingGuard also issues non-modal redirects to ROUTES.HOME for completed users. In those cases the new behavior pushes Home on top of prior routes instead of replacing the stack, so back navigation can return users to pre-redirect screens that the guard intended to exit. The stack-preservation path should be gated to modal redirect targets only.

Useful? React with 👍 / 👎.

@MelvinBot
Copy link
Copy Markdown
Contributor Author

The failing checks are unrelated to this PR's changes.

test (job 1) failure: tests/unit/CardFeedUtilsTest.ts crashed with SIGSEGV (segmentation fault in the jest worker process). This is a flaky infrastructure issue, not a code problem. This PR only modifies RootStackRouter.ts and handleNavigationGuardRedirect.test.ts — the CardFeedUtilsTest is completely unrelated.

checklist failure: This is the PR Reviewer Checklist, which is expected to fail until a reviewer completes it.

A re-run of the test job should resolve the test (job 1) failure.

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot can you create an ad-hoc build, please?

@MelvinBot
Copy link
Copy Markdown
Contributor Author

Sorry, creating ad-hoc builds is outside my capabilities. You can trigger an ad-hoc build manually through the GitHub Actions workflow or by asking someone with access to the build pipeline.

@bernhardoj
Copy link
Copy Markdown
Contributor

@m-natarajan hi, the previous PR was reverted because of this regression: #86258. I recreated the same PR, but I still can't reproduce the regression. Can you use the ad-hoc build and see if you can reproduce the regression, please? Thanks!

@m-natarajan
Copy link
Copy Markdown

@bernhardoj Expensify logo not displayed during onboarding in the Ad-hoc build, Tried multiple times signing up for new account as well.

Screen.Recording.2026-03-30.At.17.06.17.mp4

@bernhardoj
Copy link
Copy Markdown
Contributor

@m-natarajan thanks!

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 1, 2026

@MelvinBot merge main

@MelvinBot
Copy link
Copy Markdown
Contributor Author

I can only make code changes (including merging main) when directed by the PR author, an assignee, or an existing co-author. To have me merge main into this branch, the PR assignee (bernhardoj) or a co-author should reply with a request tagging me.

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 1, 2026

@bernhardoj ^

@bernhardoj
Copy link
Copy Markdown
Contributor

@MelvinBot please merge main

…nkDuringOnboardingRedirect-v2

Co-authored-by: Bernhard Owen Josephus <bernhardoj@users.noreply.github.com>
@MelvinBot MelvinBot requested a review from a team as a code owner April 2, 2026 04:24
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Merged main into this branch. ✅

@melvin-bot melvin-bot bot requested a review from Valforte April 2, 2026 04:25
@melvin-bot
Copy link
Copy Markdown

melvin-bot bot commented Apr 2, 2026

@Valforte Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@bernhardoj
Copy link
Copy Markdown
Contributor

@FitseTLT done.

Can you please test #86258 in this PR? I and QA can't reproduce it.

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 2, 2026

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified there are no new alerts related to the canBeMissing param for useOnyx
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • [ x If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Android: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari

return state;
}

const hasExistingFullScreenRoute = state.routes.some((route) => isFullScreenName(route.name));
Copy link
Copy Markdown
Contributor

@FitseTLT FitseTLT Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using some I think it is more logical to check on the focused route only.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that too, but if in some way, we have stack like this, [Home, any RHP], then the focused route will be the any RHP, and another fullscreen will be added to the stack.

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 2, 2026

The test step should specify a specific onboarding flow b/c for manage teams choice I saw that you are navigated to the admins room of the workspace.

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 2, 2026

@FitseTLT done.

Can you please test #86258 in this PR? I and QA can't reproduce it.

I did not reproduce it on the branch. However, I was a bit doubtful of the code in terms of what we are trying to achieve and when I saw melvin's break down here, I thought maybe there is some race condition that will make the bug occur on some conditions. So I think we should update the logic, I think, in terms of what we want to achieve, the logic should be:
Check if the current focused route (from the state) is from fullscreen navigator and if it is the routes should include that focused route only followed by the redirect route. This will be future-proof too.

@bernhardoj
Copy link
Copy Markdown
Contributor

The test step should specify a specific onboarding flow b/c for manage teams choice I saw that you are navigated to the admins room of the workspace.

Yeah. Also, after completing the onboarding, we won't be redirected to Home if we aren't merging public email with work email. As the comment said:

  1. When merging public email with work email -> go to concierge
  2. Else, go to the last accessed report

// When we merge public email with work email, we now want to navigate to the
// concierge chat report of the new work email and not the last accessed report.
const mergedAccountConciergeReportID = !onboardingValues?.shouldRedirectToClassicAfterMerge && onboardingValues?.shouldValidate ? conciergeChatReportID : undefined;

But after this commit, we navigate the user to Home.

if (reportID) {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID));
} else {
// Navigate to home to trigger guard evaluation
Navigation.navigate(ROUTES.HOME);
}

But I say let's come back to this after we find the best solution for the first issue.

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 3, 2026

The test step should specify a specific onboarding flow b/c for manage teams choice I saw that you are navigated to the admins room of the workspace.

Yeah. Also, after completing the onboarding, we won't be redirected to Home if we aren't merging public email with work email. As the comment said:

  1. When merging public email with work email -> go to concierge
  2. Else, go to the last accessed report

// When we merge public email with work email, we now want to navigate to the
// concierge chat report of the new work email and not the last accessed report.
const mergedAccountConciergeReportID = !onboardingValues?.shouldRedirectToClassicAfterMerge && onboardingValues?.shouldValidate ? conciergeChatReportID : undefined;

But after this commit, we navigate the user to Home.

if (reportID) {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID));
} else {
// Navigate to home to trigger guard evaluation
Navigation.navigate(ROUTES.HOME);
}

But I say let's come back to this after we find the best solution for the first issue.

I am not at all suggesting we should always redirect them to the deeplinked route. I am just suggesting a test step change.

@FitseTLT
Copy link
Copy Markdown
Contributor

FitseTLT commented Apr 3, 2026

So regarding the other comment I assume you 're 🤔 of a better solution.

@bernhardoj
Copy link
Copy Markdown
Contributor

Yeah, but nothing come to my mind yet.

@MelvinBot please merge with main

…nkDuringOnboardingRedirect-v2

Co-authored-by: Bernhard Owen Josephus <bernhardoj@users.noreply.github.com>
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Merged main into this branch. No conflicts found.

@bernhardoj
Copy link
Copy Markdown
Contributor

So, we have 2 cases:

  1. When opening the app for the first time, for example, /settings, the initial state routes will be [SettingsSplitNavigator], and then it goes through getStateForAction.
  2. When firing a navigation action, it will go through getStateForAction, if it passes any guard, then the action is applied.

I think the problem here is the 1st case.

Let's say we have a guard that prevents users from accessing the create expense pages if the subscription has expired, and wants to redirect them to the restricted action RHP.

If we use the code in main,
when we open /create/create/start/xx/xx/manual, the initial state routes will be
[ReportSplitNavigator, RightModalNavigator (create expense screens)],
but after going through handleNavigationGuards, the state becomes
[ReportSplitNavigator, RightModalNavigator (restricted action page)]

No problem here since we RESET the whole state.

But if we use the code in this PR (+ comparing the focused route), the state becomes
[ReportSplitNavigator, RightModalNavigator (create expense screen), RightModalNavigator (restricted action page)]

because we preserve the existing state (and now we have 2 RightModalNavigator).

Now, for the 2nd case,

Let's say we have this state:
[Home, RightModalNavigator (any screen)]
then navigate to the create expense page.

If we use the code on main, the state becomes
[Home, RightModalNavigator (create expense screen)]

If we use the code in this PR, then the previous screen is preserved.
[Home, RightModalNavigator (any screen), RightModalNavigator (create expense screen)]
but we will still end up with 2 RightModalNavigator.

This is really tricky.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants