From 412244ed35143a2259360fc128903f2932900d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Ch=C3=A1vez=20=28via=20MelvinBot=29?= Date: Fri, 24 Jul 2026 21:17:18 +0000 Subject: [PATCH] Fix: land onboarding Track-business users on Home instead of Inbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap isReportTopmostSplitNavigator() for getCentralPaneReportID() at the onboarding navigation guards so we only preserve the current view when a real report is revealed, not when the empty Inbox tab merely happens to be topmost. Co-authored-by: Marco Chávez --- .../SidePanel/RHPVariantTest/index.ts | 6 ++--- src/libs/navigateAfterOnboarding.ts | 4 ++-- .../SidePanel/RHPVariantTest.test.ts | 22 ++++++++++-------- tests/unit/navigateAfterOnboardingTest.ts | 23 ++++++++++++++----- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/components/SidePanel/RHPVariantTest/index.ts b/src/components/SidePanel/RHPVariantTest/index.ts index 1ab833b45289..6bdd945d1ede 100644 --- a/src/components/SidePanel/RHPVariantTest/index.ts +++ b/src/components/SidePanel/RHPVariantTest/index.ts @@ -1,5 +1,5 @@ import SidePanelActions from '@libs/actions/SidePanel'; -import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator'; +import getCentralPaneReportID from '@libs/Navigation/helpers/getCentralPaneReportID'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; @@ -67,7 +67,7 @@ const shouldOpenRHPVariant: ShouldOpenRHPVariant = (variantOverride) => { const handleRHPVariantNavigation: HandleRHPVariantNavigation = (onboardingPolicyID, variantOverride) => { const variant = variantOverride ?? onboardingRHPVariant; if (variant === CONST.ONBOARDING_RHP_VARIANT.TRACK_EXPENSES_WITH_CONCIERGE) { - const shouldPreserveRevealedReport = isReportTopmostSplitNavigator(); + const shouldPreserveRevealedReport = !!getCentralPaneReportID(); if (!shouldPreserveRevealedReport) { Navigation.navigate(ROUTES.HOME); } @@ -78,7 +78,7 @@ const handleRHPVariantNavigation: HandleRHPVariantNavigation = (onboardingPolicy const isRHPHomePage = variant === CONST.ONBOARDING_RHP_VARIANT.RHP_HOME_PAGE; if (isRHPHomePage) { - const shouldPreserveRevealedReport = isReportTopmostSplitNavigator(); + const shouldPreserveRevealedReport = !!getCentralPaneReportID(); if (!shouldPreserveRevealedReport) { Navigation.navigate(ROUTES.HOME); } diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 07eacf9333be..1bed998af2f7 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -13,7 +13,7 @@ import Onyx from 'react-native-onyx'; import {setDisableDismissOnEscape} from './actions/Modal'; import SidePanelActions from './actions/SidePanel'; import {setOnboardingRHPVariant} from './actions/Welcome'; -import isReportTopmostSplitNavigator from './Navigation/helpers/isReportTopmostSplitNavigator'; +import getCentralPaneReportID from './Navigation/helpers/getCentralPaneReportID'; import {dismissOnboardingModalBeforeExit} from './Navigation/helpers/OnboardingNavigationUtils'; import shouldOpenOnAdminRoom from './Navigation/helpers/shouldOpenOnAdminRoom'; import Navigation from './Navigation/Navigation'; @@ -101,7 +101,7 @@ function navigateAfterOnboarding( ); if (reportID) { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID)); - } else if (!isReportTopmostSplitNavigator()) { + } else if (!getCentralPaneReportID()) { // Navigate to home to trigger guard evaluation Navigation.navigate(ROUTES.HOME); } diff --git a/tests/unit/components/SidePanel/RHPVariantTest.test.ts b/tests/unit/components/SidePanel/RHPVariantTest.test.ts index 7a301751d0e2..1a0bc0acdda3 100644 --- a/tests/unit/components/SidePanel/RHPVariantTest.test.ts +++ b/tests/unit/components/SidePanel/RHPVariantTest.test.ts @@ -6,7 +6,7 @@ import ROUTES from '@src/ROUTES'; import type * as RHPVariantTest from '../../../../src/components/SidePanel/RHPVariantTest/index'; -const mockIsReportTopmostSplitNavigator = jest.fn(() => false); +const mockGetCentralPaneReportID = jest.fn(() => undefined); jest.mock('@expensify/react-native-hybrid-app', () => ({ __esModule: true, @@ -35,9 +35,9 @@ jest.mock('react-native-onyx', () => ({ }, })); -jest.mock('@libs/Navigation/helpers/isReportTopmostSplitNavigator', () => ({ +jest.mock('@libs/Navigation/helpers/getCentralPaneReportID', () => ({ __esModule: true, - default: () => mockIsReportTopmostSplitNavigator(), + default: () => mockGetCentralPaneReportID(), })); jest.mock('@libs/Navigation/Navigation', () => ({ @@ -59,11 +59,11 @@ const {handleRHPVariantNavigation} = jest.requireActual(' describe('handleRHPVariantNavigation', () => { beforeEach(() => { jest.clearAllMocks(); - mockIsReportTopmostSplitNavigator.mockReturnValue(false); + mockGetCentralPaneReportID.mockReturnValue(undefined); }); - it('preserves the topmost report for the rhpHomePage variant', () => { - mockIsReportTopmostSplitNavigator.mockReturnValue(true); + it('preserves the revealed report for the rhpHomePage variant', () => { + mockGetCentralPaneReportID.mockReturnValue('reportID'); handleRHPVariantNavigation('policyID', CONST.ONBOARDING_RHP_VARIANT.RHP_HOME_PAGE); @@ -71,15 +71,15 @@ describe('handleRHPVariantNavigation', () => { expect(SidePanelActions.openSidePanel).toHaveBeenCalledWith(true); }); - it('navigates home for the rhpHomePage variant when no report is topmost', () => { + it('navigates home for the rhpHomePage variant when no report is revealed', () => { handleRHPVariantNavigation('policyID', CONST.ONBOARDING_RHP_VARIANT.RHP_HOME_PAGE); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.HOME); expect(SidePanelActions.openSidePanel).toHaveBeenCalledWith(true); }); - it('preserves the topmost report for the trackExpensesWithConcierge variant and opens the side panel on top of it', () => { - mockIsReportTopmostSplitNavigator.mockReturnValue(true); + it('preserves the revealed report for the trackExpensesWithConcierge variant and opens the side panel on top of it', () => { + mockGetCentralPaneReportID.mockReturnValue('reportID'); handleRHPVariantNavigation('policyID', CONST.ONBOARDING_RHP_VARIANT.TRACK_EXPENSES_WITH_CONCIERGE); @@ -87,7 +87,9 @@ describe('handleRHPVariantNavigation', () => { expect(SidePanelActions.openSidePanel).toHaveBeenCalledWith(true); }); - it('navigates home for the trackExpensesWithConcierge variant when no report is topmost', () => { + it('navigates home for the trackExpensesWithConcierge variant when the Inbox tab is topmost but no report is revealed', () => { + // Reproduces the reported bug: the Reports split navigator is topmost showing only the empty Inbox + // sidebar (getCentralPaneReportID returns undefined), so onboarding must still land the user on Home. handleRHPVariantNavigation('policyID', CONST.ONBOARDING_RHP_VARIANT.TRACK_EXPENSES_WITH_CONCIERGE); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.HOME); diff --git a/tests/unit/navigateAfterOnboardingTest.ts b/tests/unit/navigateAfterOnboardingTest.ts index cb3769c5dfa9..f1594254d356 100644 --- a/tests/unit/navigateAfterOnboardingTest.ts +++ b/tests/unit/navigateAfterOnboardingTest.ts @@ -21,7 +21,7 @@ const REPORT_ID = '3'; const USER_ID = '4'; const mockFindLastAccessedReport = jest.fn, Parameters>(); const mockShouldOpenOnAdminRoom = jest.fn(); -const mockIsReportTopmostSplitNavigator = jest.fn(() => false); +const mockGetCentralPaneReportID = jest.fn(() => undefined); jest.mock('@expensify/react-native-hybrid-app', () => ({ __esModule: true, @@ -74,9 +74,9 @@ jest.mock('@libs/Navigation/helpers/shouldOpenOnAdminRoom', () => ({ default: () => mockShouldOpenOnAdminRoom() as boolean, })); -jest.mock('@libs/Navigation/helpers/isReportTopmostSplitNavigator', () => ({ +jest.mock('@libs/Navigation/helpers/getCentralPaneReportID', () => ({ __esModule: true, - default: () => mockIsReportTopmostSplitNavigator(), + default: () => mockGetCentralPaneReportID(), })); describe('navigateAfterOnboarding', () => { @@ -88,7 +88,7 @@ describe('navigateAfterOnboarding', () => { beforeEach(async () => { jest.clearAllMocks(); - mockIsReportTopmostSplitNavigator.mockReturnValue(false); + mockGetCentralPaneReportID.mockReturnValue(undefined); return Onyx.clear(); }); @@ -109,14 +109,25 @@ describe('navigateAfterOnboarding', () => { expect(navigate).toHaveBeenCalledWith(ROUTES.HOME); }); - it('should preserve the topmost report if onboardingAdminsChatReportID is not provided on larger screens', () => { + it('should preserve the revealed report if onboardingAdminsChatReportID is not provided on larger screens', () => { const navigate = jest.spyOn(Navigation, 'navigate'); - mockIsReportTopmostSplitNavigator.mockReturnValue(true); + // A real report is revealed in the topmost split navigator, so we should not yank the user to Home. + mockGetCentralPaneReportID.mockReturnValue(REPORT_ID); navigateAfterOnboarding(false, true, '', {}, undefined, undefined); expect(navigate).not.toHaveBeenCalled(); }); + it('should navigate to home when the Inbox tab is topmost but no report is revealed on larger screens', () => { + const navigate = jest.spyOn(Navigation, 'navigate'); + // The Reports split navigator can be topmost showing only the empty Inbox sidebar (no report revealed). + // getCentralPaneReportID returns undefined in that case, so we must still land the onboarding user on Home. + mockGetCentralPaneReportID.mockReturnValue(undefined); + + navigateAfterOnboarding(false, true, '', {}, undefined, undefined); + expect(navigate).toHaveBeenCalledWith(ROUTES.HOME); + }); + it('should not navigate to last accessed report if it is a concierge chat on small screens', async () => { const navigate = jest.spyOn(Navigation, 'navigate'); const lastAccessedReport = {