From 5e25487ebb14809b0bfebcc40b14ec571d7d7207 Mon Sep 17 00:00:00 2001 From: Mukher Date: Fri, 11 Sep 2026 07:23:21 +0500 Subject: [PATCH 1/3] Keep the page an RHP was opened from underneath it Six RHP screens opened from a chat, an expense or Home were pinned to a Settings or Home central pane in the relation maps, so the first in-app click swapped the background page and closing the RHP landed there. The app download links screen becomes a dynamic route so its background follows the base path, like the contact methods list. The other screens move to deeplink-only relations, consulted only when state is built from a path, so a fresh load still lands on the owning page while an in-app click keeps whatever full screen is underneath. --- src/ROUTES.ts | 5 ++- src/SCREENS.ts | 2 +- src/components/ReceiptAlternativeMethods.tsx | 4 +- .../ModalStackNavigators/index.tsx | 2 +- .../helpers/getAdaptedStateFromPath.ts | 11 +++-- .../linkingConfig/RELATIONS/HOME_TO_RHP.ts | 3 +- .../RELATIONS/HOME_TO_RHP_DEEPLINK.ts | 13 ++++++ .../RELATIONS/SETTINGS_TO_RHP.ts | 4 -- .../RELATIONS/SETTINGS_TO_RHP_DEEPLINK.ts | 16 +++++++ .../linkingConfig/RELATIONS/index.ts | 19 ++++++++- src/libs/Navigation/linkingConfig/config.ts | 5 +-- src/libs/Navigation/types.ts | 1 + src/pages/settings/AboutPage/AboutPage.tsx | 4 +- ...ks.tsx => DynamicAppDownloadLinksPage.tsx} | 9 ++-- .../appDownloadLinksBackgroundTests.ts | 21 ++++++++++ .../getMatchingFullScreenRouteTests.ts | 30 +++++++++++++ .../rhpBackgroundDeeplinkRelationTests.ts | 42 +++++++++++++++++++ 17 files changed, 166 insertions(+), 25 deletions(-) create mode 100644 src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts create mode 100644 src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP_DEEPLINK.ts rename src/pages/settings/{AppDownloadLinks.tsx => DynamicAppDownloadLinksPage.tsx} (91%) create mode 100644 tests/navigation/appDownloadLinksBackgroundTests.ts create mode 100644 tests/navigation/rhpBackgroundDeeplinkRelationTests.ts diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 423dfdeecd48..57b005613db3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1412,6 +1412,10 @@ const DYNAMIC_ROUTES = { path: 'keyboard-shortcuts', entryScreens: ['*'], }, + APP_DOWNLOAD_LINKS: { + path: 'app-download-links', + entryScreens: ['*'], + }, SETTINGS_TAG_APPROVER: { path: 'tag-approver', entryScreens: [SCREENS.SETTINGS_TAGS.DYNAMIC_SETTINGS_TAG_SETTINGS], @@ -2319,7 +2323,6 @@ const ROUTES = { getRoute: (login: string, role: string) => `settings/security/delegate/${encodeURIComponent(login)}/role/${role}/confirm/validate-code` as const, }, SETTINGS_ABOUT: 'settings/about', - SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links', SETTINGS_WALLET: 'settings/wallet', SETTINGS_WALLET_DOMAIN_CARD: { route: 'settings/wallet/card/:cardID?', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6aaf8e51fe24..3b72325a509d 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -79,7 +79,6 @@ const SCREENS = { COPILOT: 'Settings_Copilot', ABOUT: 'Settings_About', SAVE_THE_WORLD: 'Settings_TeachersUnite', - APP_DOWNLOAD_LINKS: 'Settings_App_Download_Links', ADD_DEBIT_CARD: 'Settings_Add_Debit_Card', ADD_BANK_ACCOUNT: 'Settings_Add_Bank_Account', ADD_US_BANK_ACCOUNT: 'Settings_Add_US_Bank_Account', @@ -97,6 +96,7 @@ const SCREENS = { DYNAMIC_EXIT_SURVEY_CONFIRM: 'Dynamic_ExitSurvey_Confirm', DYNAMIC_EXIT_SURVEY_REASON: 'Dynamic_ExitSurvey_Reason', DYNAMIC_KEYBOARD_SHORTCUTS: 'Dynamic_Keyboard_Shortcuts', + DYNAMIC_APP_DOWNLOAD_LINKS: 'Dynamic_App_Download_Links', DYNAMIC_SAGE_INTACCT_EXPORT: 'Dynamic_Sage_Intacct_Export', PROFILE: { diff --git a/src/components/ReceiptAlternativeMethods.tsx b/src/components/ReceiptAlternativeMethods.tsx index 3484141df715..eda5655838b3 100644 --- a/src/components/ReceiptAlternativeMethods.tsx +++ b/src/components/ReceiptAlternativeMethods.tsx @@ -9,7 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import {addLeadingForwardSlash} from '@src/libs/Url'; -import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {LayoutChangeEvent} from 'react-native'; @@ -34,7 +34,7 @@ function ReceiptAlternativeMethods({onLayout}: ReceiptAlternativeMethodsProps) { const {hasPhoneNumberLogin, isPhoneNumberLoaded} = useHasPhoneNumberLogin(); const buildDynamicRoute = useScreenBoundDynamicRoute(); - const downloadAppHref = `${environmentURL}${addLeadingForwardSlash(ROUTES.SETTINGS_APP_DOWNLOAD_LINKS)}`; + const downloadAppHref = `${environmentURL}${addLeadingForwardSlash(buildDynamicRoute(DYNAMIC_ROUTES.APP_DOWNLOAD_LINKS.path))}`; const contactMethodsHref = `${environmentURL}${addLeadingForwardSlash(buildDynamicRoute(DYNAMIC_ROUTES.CONTACT_METHODS.path))}`; if (!isLastMobileAppLoginLoaded || !isPhoneNumberLoaded) { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9b6bd6a29d0e..37d5e26aeb2b 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -438,7 +438,6 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Preferences/PaymentCurrencyPage').default, [SCREENS.SETTINGS.DEVICE_MANAGEMENT]: () => require('../../../../pages/settings/Security/DeviceManagementPage').default, [SCREENS.SETTINGS.CLOSE]: () => require('../../../../pages/settings/Security/CloseAccountPage').default, - [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: () => require('../../../../pages/settings/AppDownloadLinks').default, [SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default, [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage/index').default, [SCREENS.SETTINGS.WALLET.EXPENSIFY_CARD_SPEND_RULES]: () => require('../../../../pages/settings/Wallet/WalletExpensifyCardSpendRulesPage').default, @@ -730,6 +729,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Wallet/ReportCardLostConfirmValidateCodePage').default, [SCREENS.SETTINGS.DYNAMIC_KEYBOARD_SHORTCUTS]: () => require('../../../../pages/settings/DynamicKeyboardShortcutsPage').default, + [SCREENS.SETTINGS.DYNAMIC_APP_DOWNLOAD_LINKS]: () => require('../../../../pages/settings/DynamicAppDownloadLinksPage').default, [SCREENS.SETTINGS.DYNAMIC_EXIT_SURVEY_REASON]: () => require('../../../../pages/settings/ExitSurvey/DynamicExitSurveyReasonPage').default, [SCREENS.SETTINGS.DYNAMIC_EXIT_SURVEY_CONFIRM]: () => require('../../../../pages/settings/ExitSurvey/DynamicExitSurveyConfirmPage').default, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_SETUP]: () => require('../../../../pages/workspace/accounting/qbo/QuickbooksOnlineSetupPage').default, diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts index 2362f71b5cc9..2d621e31cab0 100644 --- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts @@ -3,9 +3,11 @@ import TAB_SCREENS from '@libs/Navigation/AppNavigator/Navigators/TAB_SCREENS'; import { RHP_TO_DOMAIN, RHP_TO_HOME, + RHP_TO_HOME_DEEPLINK, RHP_TO_SEARCH, RHP_TO_SEARCH_DEEPLINK, RHP_TO_SETTINGS, + RHP_TO_SETTINGS_DEEPLINK, RHP_TO_SIDEBAR, RHP_TO_WORKSPACE, RHP_TO_WORKSPACES_LIST, @@ -152,7 +154,7 @@ function getMatchingFullScreenRoute(route: NavigationRoute, isDeeplink = false) return getTabNavigatorState({name: NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR, state: searchState}); } - if (RHP_TO_HOME[route.name]) { + if (RHP_TO_HOME[route.name] ?? (isDeeplink ? RHP_TO_HOME_DEEPLINK[route.name] : undefined)) { return { ...getTabNavigatorState({name: SCREENS.HOME}), path: normalizePath(ROUTES.HOME), @@ -193,13 +195,14 @@ function getMatchingFullScreenRoute(route: NavigationRoute, isDeeplink = false) }); } - if (RHP_TO_SETTINGS[route.name]) { - const paramsFromRoute = getParamsFromRoute(RHP_TO_SETTINGS[route.name]); + const matchingSettingsScreen = RHP_TO_SETTINGS[route.name] ?? (isDeeplink ? RHP_TO_SETTINGS_DEEPLINK[route.name] : undefined); + if (matchingSettingsScreen) { + const paramsFromRoute = getParamsFromRoute(matchingSettingsScreen); const settingsState = getInitialSplitNavigatorState( {name: SCREENS.SETTINGS.ROOT}, { - name: RHP_TO_SETTINGS[route.name], + name: matchingSettingsScreen, params: paramsFromRoute.length > 0 ? pick(route.params, paramsFromRoute) : undefined, }, ); diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP.ts index fd4f230b7cec..2f8885e0aa4a 100644 --- a/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP.ts @@ -2,8 +2,7 @@ import SCREENS from '@src/SCREENS'; const HOME_TO_RHP: Record = { // Transaction/report RHP screens opened from the Recently added slot keep Home underneath on refresh/deep-link. - // The Enter signer info flow is opened from the Time Sensitive section, so it keeps Home underneath too. - [SCREENS.HOME]: [SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, SCREENS.RIGHT_MODAL.EXPENSE_REPORT, SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO], + [SCREENS.HOME]: [SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, SCREENS.RIGHT_MODAL.EXPENSE_REPORT], }; export default HOME_TO_RHP; diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts b/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts new file mode 100644 index 000000000000..057b329f27e0 --- /dev/null +++ b/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts @@ -0,0 +1,13 @@ +import SCREENS from '@src/SCREENS'; + +/** + * Deeplink-only variant of HOME_TO_RHP. Consulted ONLY when navigation state is built from a path + * (deeplink / browser refresh / cold load) via the `isDeeplink` flag in getMatchingFullScreenRoute, + * so these RHP screens still get Home underneath on a fresh load without forcing it for in-app navigation. + */ +const HOME_TO_RHP_DEEPLINK: Record = { + // The Enter signer info flow is opened from the Time Sensitive section and from the chat message. + [SCREENS.HOME]: [SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO], +}; + +export default HOME_TO_RHP_DEEPLINK; diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts index 8d15bf8fbc22..2e0b33de81c9 100755 --- a/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts @@ -31,7 +31,6 @@ const SETTINGS_TO_RHP: Partial> = { + // These RHP screens are also opened from a chat or from Home, so they must not force the settings page underneath on click. + [SCREENS.SETTINGS.WALLET.ROOT]: [SCREENS.SETTINGS.WALLET.PERSONAL_CARD_DETAILS, SCREENS.SETTINGS.ADD_US_BANK_ACCOUNT], + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD], +}; + +export default SETTINGS_TO_RHP_DEEPLINK; diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/index.ts b/src/libs/Navigation/linkingConfig/RELATIONS/index.ts index 07620f2b769c..9ab9c618bd41 100644 --- a/src/libs/Navigation/linkingConfig/RELATIONS/index.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/index.ts @@ -1,8 +1,10 @@ import DOMAIN_TO_RHP from './DOMAIN_TO_RHP'; import HOME_TO_RHP from './HOME_TO_RHP'; +import HOME_TO_RHP_DEEPLINK from './HOME_TO_RHP_DEEPLINK'; import SEARCH_TO_RHP from './SEARCH_TO_RHP'; import SEARCH_TO_RHP_DEEPLINK from './SEARCH_TO_RHP_DEEPLINK'; import SETTINGS_TO_RHP from './SETTINGS_TO_RHP'; +import SETTINGS_TO_RHP_DEEPLINK from './SETTINGS_TO_RHP_DEEPLINK'; import SIDEBAR_TO_RHP from './SIDEBAR_TO_RHP'; import SIDEBAR_TO_SPLIT from './SIDEBAR_TO_SPLIT'; import WORKSPACE_TO_RHP from './WORKSPACE_TO_RHP'; @@ -29,6 +31,7 @@ function createInverseRelation(relations: Pa } const RHP_TO_SETTINGS = createInverseRelation(SETTINGS_TO_RHP); +const RHP_TO_SETTINGS_DEEPLINK = createInverseRelation(SETTINGS_TO_RHP_DEEPLINK); const RHP_TO_WORKSPACE = createInverseRelation(WORKSPACE_TO_RHP); const RHP_TO_SIDEBAR = createInverseRelation(SIDEBAR_TO_RHP); const SPLIT_TO_SIDEBAR = createInverseRelation(SIDEBAR_TO_SPLIT); @@ -39,5 +42,19 @@ const RHP_TO_SEARCH = createInverseRelation(SEARCH_TO_RHP); const RHP_TO_SEARCH_DEEPLINK = createInverseRelation(SEARCH_TO_RHP_DEEPLINK); const RHP_TO_DOMAIN = createInverseRelation(DOMAIN_TO_RHP); const RHP_TO_HOME = createInverseRelation(HOME_TO_RHP); +const RHP_TO_HOME_DEEPLINK = createInverseRelation(HOME_TO_RHP_DEEPLINK); -export {RHP_TO_SETTINGS, RHP_TO_WORKSPACE, RHP_TO_SIDEBAR, RHP_TO_SEARCH, RHP_TO_SEARCH_DEEPLINK, SIDEBAR_TO_SPLIT, SPLIT_TO_SIDEBAR, RHP_TO_WORKSPACES_LIST, RHP_TO_DOMAIN, RHP_TO_HOME}; +export { + RHP_TO_SETTINGS, + RHP_TO_SETTINGS_DEEPLINK, + RHP_TO_WORKSPACE, + RHP_TO_SIDEBAR, + RHP_TO_SEARCH, + RHP_TO_SEARCH_DEEPLINK, + SIDEBAR_TO_SPLIT, + SPLIT_TO_SIDEBAR, + RHP_TO_WORKSPACES_LIST, + RHP_TO_DOMAIN, + RHP_TO_HOME, + RHP_TO_HOME_DEEPLINK, +}; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index d1743b0e1a8b..f9f5c5302ad2 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -432,10 +432,6 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_TIMEZONE_SELECT, exact: true, }, - [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: { - path: ROUTES.SETTINGS_APP_DOWNLOAD_LINKS, - exact: true, - }, [SCREENS.SETTINGS.DYNAMIC_VERIFY_ACCOUNT]: DYNAMIC_ROUTES.VERIFY_ACCOUNT.path, [SCREENS.SETTINGS.DYNAMIC_ADD_BANK_ACCOUNT_VERIFY_ACCOUNT]: DYNAMIC_ROUTES.ADD_BANK_ACCOUNT_VERIFY_ACCOUNT.path, [SCREENS.SETTINGS.SUBSCRIPTION.DYNAMIC_PAYMENT_CARD_CURRENCY_SELECTOR]: DYNAMIC_ROUTES.PAYMENT_CARD_CURRENCY_SELECTOR.path, @@ -1213,6 +1209,7 @@ const config: LinkingOptions['config'] = { [SCREENS.DYNAMIC_REIMBURSEMENT_ACCOUNT_VERIFY_ACCOUNT]: DYNAMIC_ROUTES.BANK_ACCOUNT_VERIFY_ACCOUNT.path, [SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO]: ROUTES.BANK_ACCOUNT_ENTER_SIGNER_INFO.route, [SCREENS.SETTINGS.DYNAMIC_KEYBOARD_SHORTCUTS]: DYNAMIC_ROUTES.KEYBOARD_SHORTCUTS.path, + [SCREENS.SETTINGS.DYNAMIC_APP_DOWNLOAD_LINKS]: DYNAMIC_ROUTES.APP_DOWNLOAD_LINKS.path, [SCREENS.WORKSPACE.NAME]: ROUTES.WORKSPACE_OVERVIEW_NAME.route, [SCREENS.SETTINGS.SHARE_CODE]: { path: ROUTES.SETTINGS_SHARE_CODE, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index ca183bcd0e83..2f099d359660 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1446,6 +1446,7 @@ type SettingsNavigatorParamList = { isFromDomainCardDetail?: string; }; [SCREENS.SETTINGS.DYNAMIC_KEYBOARD_SHORTCUTS]: undefined; + [SCREENS.SETTINGS.DYNAMIC_APP_DOWNLOAD_LINKS]: undefined; [SCREENS.WORKSPACE.TAX_CREATE]: { policyID: string; }; diff --git a/src/pages/settings/AboutPage/AboutPage.tsx b/src/pages/settings/AboutPage/AboutPage.tsx index 91ec2738ef10..5553f49080cc 100644 --- a/src/pages/settings/AboutPage/AboutPage.tsx +++ b/src/pages/settings/AboutPage/AboutPage.tsx @@ -29,7 +29,7 @@ import {navigateToConciergeChat} from '@userActions/Report'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type IconAsset from '@src/types/utils/IconAsset'; import type WithSentryLabel from '@src/types/utils/SentryLabel'; @@ -84,7 +84,7 @@ function AboutPage() { translationKey: 'initialSettingsPage.aboutPage.appDownloadLinks', icon: icons.Link, sentryLabel: CONST.SENTRY_LABEL.SETTINGS_ABOUT.APP_DOWNLOAD_LINKS, - action: waitForNavigate(() => Navigation.navigate(ROUTES.SETTINGS_APP_DOWNLOAD_LINKS)), + action: waitForNavigate(() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.APP_DOWNLOAD_LINKS.path))), }, { translationKey: 'initialSettingsPage.aboutPage.viewKeyboardShortcuts', diff --git a/src/pages/settings/AppDownloadLinks.tsx b/src/pages/settings/DynamicAppDownloadLinksPage.tsx similarity index 91% rename from src/pages/settings/AppDownloadLinks.tsx rename to src/pages/settings/DynamicAppDownloadLinksPage.tsx index d08f55081883..80770292b247 100644 --- a/src/pages/settings/AppDownloadLinks.tsx +++ b/src/pages/settings/DynamicAppDownloadLinksPage.tsx @@ -7,6 +7,7 @@ import QRShare from '@components/QRShare'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -18,6 +19,7 @@ import {showContextMenu} from '@pages/inbox/report/ContextMenu/ReportActionConte import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {View} from 'react-native'; @@ -29,11 +31,12 @@ type DownloadMenuItem = MenuItemProps & { link: string; }; -function AppDownloadLinksPage() { +function DynamicAppDownloadLinksPage() { const icons = useMemoizedLazyExpensifyIcons(['Android', 'Apple', 'Monitor', 'NewWindow']); const styles = useThemeStyles(); const {translate} = useLocalize(); const popoverAnchor = useRef(null); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.APP_DOWNLOAD_LINKS.path); const menuItems: DownloadMenuItem[] = [ { @@ -60,7 +63,7 @@ function AppDownloadLinksPage() { Navigation.goBack()} + onBackButtonPress={() => Navigation.goBack(backPath)} /> route.name === NAVIGATORS.TAB_NAVIGATOR); + const tabState = tabRoute?.state; + return tabState?.routes?.at(tabState.index ?? (tabState.routes?.length ?? 1) - 1)?.name; +} + +describe('app download links background on a fresh load', () => { + it.each([ + ['a report', '/r/123/app-download-links', NAVIGATORS.REPORTS_SPLIT_NAVIGATOR], + ['an expense report', '/search/view/123/app-download-links', NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR], + ['a money request report', '/search/r/123/app-download-links', NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR], + ['the about page', '/settings/about/app-download-links', NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR], + ])('keeps the full screen it was opened from: %s', (_label, path, expected) => { + expect(getBackgroundFullScreenName(path)).toBe(expected); + }); +}); diff --git a/tests/navigation/getMatchingFullScreenRouteTests.ts b/tests/navigation/getMatchingFullScreenRouteTests.ts index d4fee769c7a6..e042223d7487 100644 --- a/tests/navigation/getMatchingFullScreenRouteTests.ts +++ b/tests/navigation/getMatchingFullScreenRouteTests.ts @@ -27,6 +27,7 @@ jest.mock('@libs/Navigation/linkingConfig/RELATIONS', () => { return { RHP_TO_DOMAIN: {}, RHP_TO_HOME: {Home: 'home'}, + RHP_TO_HOME_DEEPLINK: {[SCREENS_MOCK.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO]: SCREENS_MOCK.HOME}, RHP_TO_SEARCH: {}, // Deeplink-only mapping: create-flow entry points resolve to the Search fullscreen under the RHP. RHP_TO_SEARCH_DEEPLINK: { @@ -37,6 +38,7 @@ jest.mock('@libs/Navigation/linkingConfig/RELATIONS', () => { [SCREENS_MOCK.TRACK_EXPENSE]: SCREENS_MOCK.SEARCH.ROOT, }, RHP_TO_SETTINGS: {}, + RHP_TO_SETTINGS_DEEPLINK: {[SCREENS_MOCK.SETTINGS.WALLET.PERSONAL_CARD_DETAILS]: SCREENS_MOCK.SETTINGS.WALLET.ROOT}, RHP_TO_SIDEBAR: {}, RHP_TO_WORKSPACE: {}, RHP_TO_WORKSPACES_LIST: {}, @@ -292,3 +294,31 @@ describe('getMatchingFullScreenRoute - deeplink-only search relations', () => { }, ); }); + +describe('getMatchingFullScreenRoute - deeplink-only settings and home relations', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('resolves the personal card details screen to the wallet when built from a path (isDeeplink=true)', () => { + const result = getMatchingFullScreenRoute({name: SCREENS.SETTINGS.WALLET.PERSONAL_CARD_DETAILS, params: {cardID: '1'}}, true); + + expect(result?.name).toBe(NAVIGATORS.TAB_NAVIGATOR); + const activeTab = result && 'state' in result ? result.state?.routes.at(result.state.index ?? 0) : undefined; + expect(activeTab?.state?.routes.at(-1)?.name).toBe(SCREENS.SETTINGS.WALLET.ROOT); + }); + + it('resolves the enter signer info screen to Home when built from a path (isDeeplink=true)', () => { + const result = getMatchingFullScreenRoute({name: SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO}, true); + + expect(result?.name).toBe(NAVIGATORS.TAB_NAVIGATOR); + expect(result && 'state' in result ? result.state?.routes.at(0)?.name : undefined).toBe(SCREENS.HOME); + }); + + it.each([SCREENS.SETTINGS.WALLET.PERSONAL_CARD_DETAILS, SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO])( + 'does not resolve %s to a fullscreen for in-app navigation (isDeeplink=false)', + (screenName) => { + expect(getMatchingFullScreenRoute({name: screenName})).toBeUndefined(); + }, + ); +}); diff --git a/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts b/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts new file mode 100644 index 000000000000..cd3ddd0a657d --- /dev/null +++ b/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts @@ -0,0 +1,42 @@ +import getAdaptedStateFromPath from '@libs/Navigation/helpers/getAdaptedStateFromPath'; +import {RHP_TO_HOME, RHP_TO_HOME_DEEPLINK, RHP_TO_SETTINGS, RHP_TO_SETTINGS_DEEPLINK} from '@libs/Navigation/linkingConfig/RELATIONS'; + +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; + +function getFullScreenUnderRHP(path: string) { + const state = getAdaptedStateFromPath(path, undefined); + const tabRoute = state?.routes?.findLast((route) => route.name === NAVIGATORS.TAB_NAVIGATOR); + const tabState = tabRoute?.state; + const fullScreen = tabState?.routes?.at(tabState.index ?? (tabState.routes?.length ?? 1) - 1); + const innerRoutes = (fullScreen as {state?: {routes?: Array<{name: string}>}} | undefined)?.state?.routes; + return {name: fullScreen?.name, central: innerRoutes?.at(-1)?.name}; +} + +describe('RHP screens opened from a chat or Home', () => { + it.each([ + [SCREENS.SETTINGS.WALLET.PERSONAL_CARD_DETAILS, SCREENS.SETTINGS.WALLET.ROOT], + [SCREENS.SETTINGS.ADD_US_BANK_ACCOUNT, SCREENS.SETTINGS.WALLET.ROOT], + [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, SCREENS.SETTINGS.SUBSCRIPTION.ROOT], + ])('%s is pinned to %s only for a deep link', (screen, settingsScreen) => { + expect(RHP_TO_SETTINGS[screen]).toBeUndefined(); + expect(RHP_TO_SETTINGS_DEEPLINK[screen]).toBe(settingsScreen); + }); + + it('enter signer info is pinned to Home only for a deep link', () => { + expect(RHP_TO_HOME[SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO]).toBeUndefined(); + expect(RHP_TO_HOME_DEEPLINK[SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO]).toBe(SCREENS.HOME); + }); + + it.each([ + ['/settings/wallet/personal-card/123', SCREENS.SETTINGS.WALLET.ROOT], + ['/settings/wallet/add-us-bank-account', SCREENS.SETTINGS.WALLET.ROOT], + ['/settings/subscription/add-payment-card', SCREENS.SETTINGS.SUBSCRIPTION.ROOT], + ])('%s still lands on the settings page on a fresh load', (path, settingsScreen) => { + expect(getFullScreenUnderRHP(path)).toEqual({name: NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, central: settingsScreen}); + }); + + it('enter signer info still lands on Home on a fresh load', () => { + expect(getFullScreenUnderRHP('/bank-account/enter-signer-info/name').name).toBe(SCREENS.HOME); + }); +}); From f87b2d2a895098a709dfac067d03e9467f82b46f Mon Sep 17 00:00:00 2001 From: Mukher Date: Fri, 11 Sep 2026 07:29:55 +0500 Subject: [PATCH 2/3] fixed faling test --- tests/navigation/NavigateTests.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/navigation/NavigateTests.tsx b/tests/navigation/NavigateTests.tsx index c96c43906c86..90e916fa59f6 100644 --- a/tests/navigation/NavigateTests.tsx +++ b/tests/navigation/NavigateTests.tsx @@ -258,7 +258,7 @@ describe('Navigate', () => { expect(lastRootRoute?.state?.routes.at(-1)?.name).toBe(SCREENS.RIGHT_MODAL.SETTINGS); }); - it('shows Subscription behind the payment-card RHP when navigating from Reports', () => { + it('shows Profile behind the display name RHP when navigating from Reports', () => { render( { ); act(() => { - Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); + Navigation.navigate(ROUTES.SETTINGS_DISPLAY_NAME); }); const rootState = navigationRef.current?.getRootState(); @@ -307,7 +307,7 @@ describe('Navigate', () => { const tabState = rootState?.routes.at(0)?.state; const activeTab = tabState?.routes.at(tabState.index ?? 0); expect(activeTab?.name).toBe(NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR); - expect(activeTab?.state?.routes.at(-1)?.name).toBe(SCREENS.SETTINGS.SUBSCRIPTION.ROOT); + expect(activeTab?.state?.routes.at(-1)?.name).toBe(SCREENS.SETTINGS.PROFILE.ROOT); }); it('preserves report navigation history when opening a workspace from an RHP', () => { From c13db64a1af7767971a0405fc4545ec2abaf7a45 Mon Sep 17 00:00:00 2001 From: Mukher Date: Fri, 11 Sep 2026 08:32:49 +0500 Subject: [PATCH 3/3] Keep the origin under the card details back arrow and cover in-app navigation --- .../helpers/getAdaptedStateFromPath.ts | 9 ++-- .../linkingConfig/RELATIONS/HOME_TO_RHP.ts | 2 +- .../RELATIONS/HOME_TO_RHP_DEEPLINK.ts | 2 +- .../Wallet/PersonalCardDetailsPage.tsx | 14 ++++-- tests/navigation/NavigateTests.tsx | 50 ++++++++++++++++++- .../appDownloadLinksBackgroundTests.ts | 22 +++----- .../contactMethodsBackgroundTests.ts | 21 ++------ .../rhpBackgroundDeeplinkRelationTests.ts | 10 +--- tests/utils/getFullScreenUnderRHP.ts | 14 ++++++ 9 files changed, 94 insertions(+), 50 deletions(-) create mode 100644 tests/utils/getFullScreenUnderRHP.ts diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts index 2d621e31cab0..26a25a0618ab 100644 --- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts @@ -85,7 +85,7 @@ function isRouteWithReportID(route: NavigationRoute): route is Route = { - // Transaction/report RHP screens opened from the Recently added slot keep Home underneath on refresh/deep-link. + // Transaction/report RHP screens opened from the Recently added slot keep Home underneath. [SCREENS.HOME]: [SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, SCREENS.RIGHT_MODAL.EXPENSE_REPORT], }; diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts b/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts index 057b329f27e0..ea72050703f5 100644 --- a/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/HOME_TO_RHP_DEEPLINK.ts @@ -6,7 +6,7 @@ import SCREENS from '@src/SCREENS'; * so these RHP screens still get Home underneath on a fresh load without forcing it for in-app navigation. */ const HOME_TO_RHP_DEEPLINK: Record = { - // The Enter signer info flow is opened from the Time Sensitive section and from the chat message. + // Also opened from a chat message, so it must not force Home underneath on click. [SCREENS.HOME]: [SCREENS.REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO], }; diff --git a/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx b/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx index 5eaaaf501624..e7a919a5018a 100644 --- a/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx +++ b/src/pages/settings/Wallet/PersonalCardDetailsPage.tsx @@ -23,11 +23,13 @@ import {getActiveServer} from '@libs/ApiUtils'; import navigateToCardTransactions from '@libs/CardNavigationUtils'; import {getCardFeedIcon, getPlaidInstitutionIconUrl, isCardConnectionBroken, isPersonalCard} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; +import getActiveTabName from '@libs/Navigation/helpers/getActiveTabName'; +import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; -import Navigation from '@navigation/Navigation'; +import Navigation, {navigationRef} from '@navigation/Navigation'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -37,6 +39,7 @@ import {clearCardErrorField, deletePersonalCard, syncCard, unassignCard} from '@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; +import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; @@ -104,6 +107,11 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { }); }; + const getExitFallbackRoute = () => { + const topmostFullScreenRoute = navigationRef.current?.getRootState()?.routes.findLast((rootRoute) => isFullScreenName(rootRoute.name)); + return getActiveTabName(topmostFullScreenRoute) === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR ? ROUTES.SETTINGS_WALLET : undefined; + }; + const updateCard = () => { if (!card) { return; @@ -134,7 +142,7 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { return; } const savedColumnLayout = savedColumnLayouts?.[card.cardID]; - Navigation.goBack(ROUTES.SETTINGS_WALLET, { + Navigation.goBack(getExitFallbackRoute(), { afterTransition: () => deletePersonalCard({cardID: card.cardID, card, allTransactions, allReports, savedColumnLayout}), }); }); @@ -174,7 +182,7 @@ function PersonalCardDetailsPage({route}: PersonalCardDetailsPageProps) { > Navigation.goBack(ROUTES.SETTINGS_WALLET)} + onBackButtonPress={() => Navigation.goBack(getExitFallbackRoute())} /> diff --git a/tests/navigation/NavigateTests.tsx b/tests/navigation/NavigateTests.tsx index 90e916fa59f6..008f6b045f47 100644 --- a/tests/navigation/NavigateTests.tsx +++ b/tests/navigation/NavigateTests.tsx @@ -4,12 +4,13 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import SidePanelActions from '@libs/actions/SidePanel'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import navigationRef from '@libs/Navigation/navigationRef'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import React from 'react'; @@ -310,6 +311,53 @@ describe('Navigate', () => { expect(activeTab?.state?.routes.at(-1)?.name).toBe(SCREENS.SETTINGS.PROFILE.ROOT); }); + it.each([ + ['add payment card', () => ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD], + ['add US bank account', () => ROUTES.SETTINGS_ADD_US_BANK_ACCOUNT.getRoute()], + ['personal card details', () => ROUTES.SETTINGS_WALLET_PERSONAL_CARD_DETAILS.getRoute('123')], + ['enter signer info', () => ROUTES.BANK_ACCOUNT_ENTER_SIGNER_INFO.getRoute('1', '2', false)], + ['app download links', () => createDynamicRoute(DYNAMIC_ROUTES.APP_DOWNLOAD_LINKS.path)], + ])('keeps Reports behind the %s RHP when opened in-app', (_label, getRoute) => { + render( + , + ); + + act(() => { + Navigation.navigate(getRoute()); + }); + + const rootState = navigationRef.current?.getRootState(); + expect(rootState?.routes.at(-1)?.name).toBe(NAVIGATORS.RIGHT_MODAL_NAVIGATOR); + const tabState = rootState?.routes.at(0)?.state; + expect(tabState?.routes.at(tabState.index ?? 0)?.name).toBe(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR); + }); + it('preserves report navigation history when opening a workspace from an RHP', () => { render( route.name === NAVIGATORS.TAB_NAVIGATOR); - const tabState = tabRoute?.state; - return tabState?.routes?.at(tabState.index ?? (tabState.routes?.length ?? 1) - 1)?.name; -} +import getFullScreenUnderRHP from '../utils/getFullScreenUnderRHP'; describe('app download links background on a fresh load', () => { it.each([ - ['a report', '/r/123/app-download-links', NAVIGATORS.REPORTS_SPLIT_NAVIGATOR], - ['an expense report', '/search/view/123/app-download-links', NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR], - ['a money request report', '/search/r/123/app-download-links', NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR], - ['the about page', '/settings/about/app-download-links', NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR], - ])('keeps the full screen it was opened from: %s', (_label, path, expected) => { - expect(getBackgroundFullScreenName(path)).toBe(expected); + ['a report', '/r/123/app-download-links', NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, SCREENS.REPORT], + ['an expense report', '/search/view/123/app-download-links', NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR, SCREENS.SEARCH.ROOT], + ['a money request report', '/search/r/123/app-download-links', NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR, SCREENS.SEARCH.ROOT], + ['the about page', '/settings/about/app-download-links', NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, SCREENS.SETTINGS.ABOUT], + ])('keeps the full screen it was opened from: %s', (_label, path, name, central) => { + expect(getFullScreenUnderRHP(path)).toEqual({name, central}); }); }); diff --git a/tests/navigation/contactMethodsBackgroundTests.ts b/tests/navigation/contactMethodsBackgroundTests.ts index 53a0c6b5fab1..60dd1b7a1c4a 100644 --- a/tests/navigation/contactMethodsBackgroundTests.ts +++ b/tests/navigation/contactMethodsBackgroundTests.ts @@ -1,19 +1,6 @@ -import getAdaptedStateFromPath from '@libs/Navigation/helpers/getAdaptedStateFromPath'; - import NAVIGATORS from '@src/NAVIGATORS'; -/** - * The contact methods screens are dynamic routes appended to whatever screen opened them, so the - * full screen under the overlay has to come from the base path. In-app navigation never exercises - * that, because linkTo skips full screen matching while an RHP is already on top, so only a fresh - * load of the URL can catch a screen that is still pinned to a central pane in the relations. - */ -function getBackgroundFullScreenName(path: string): string | undefined { - const state = getAdaptedStateFromPath(path, undefined); - const tabRoute = state?.routes?.findLast((route) => route.name === NAVIGATORS.TAB_NAVIGATOR); - const tabState = tabRoute?.state; - return tabState?.routes?.at(tabState.index ?? (tabState.routes?.length ?? 1) - 1)?.name; -} +import getFullScreenUnderRHP from '../utils/getFullScreenUnderRHP'; describe('contact methods background on a fresh load', () => { describe('resolves the base path when the contact methods screens are opened from a report', () => { @@ -23,7 +10,7 @@ describe('contact methods background on a fresh load', () => { ['the new contact method form', '/r/123/contact-methods/new-contact-method'], ['the new contact method magic code', '/r/123/contact-methods/new/confirm-validate-code'], ])('%s', (_label, path) => { - expect(getBackgroundFullScreenName(path)).toBe(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR); + expect(getFullScreenUnderRHP(path).name).toBe(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR); }); }); @@ -34,7 +21,7 @@ describe('contact methods background on a fresh load', () => { ['the money request report details', '/search/r/123/contact-methods/a%40b.com/details'], ['the new contact method magic code', '/search/view/123/contact-methods/new/confirm-validate-code'], ])('%s', (_label, path) => { - expect(getBackgroundFullScreenName(path)).toBe(NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR); + expect(getFullScreenUnderRHP(path).name).toBe(NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR); }); }); @@ -44,7 +31,7 @@ describe('contact methods background on a fresh load', () => { ['the contact method details', '/settings/profile/contact-methods/a%40b.com/details'], ['the new contact method form', '/settings/profile/contact-methods/new-contact-method'], ])('%s', (_label, path) => { - expect(getBackgroundFullScreenName(path)).toBe(NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR); + expect(getFullScreenUnderRHP(path).name).toBe(NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR); }); }); }); diff --git a/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts b/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts index cd3ddd0a657d..d50ea044d847 100644 --- a/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts +++ b/tests/navigation/rhpBackgroundDeeplinkRelationTests.ts @@ -1,17 +1,9 @@ -import getAdaptedStateFromPath from '@libs/Navigation/helpers/getAdaptedStateFromPath'; import {RHP_TO_HOME, RHP_TO_HOME_DEEPLINK, RHP_TO_SETTINGS, RHP_TO_SETTINGS_DEEPLINK} from '@libs/Navigation/linkingConfig/RELATIONS'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; -function getFullScreenUnderRHP(path: string) { - const state = getAdaptedStateFromPath(path, undefined); - const tabRoute = state?.routes?.findLast((route) => route.name === NAVIGATORS.TAB_NAVIGATOR); - const tabState = tabRoute?.state; - const fullScreen = tabState?.routes?.at(tabState.index ?? (tabState.routes?.length ?? 1) - 1); - const innerRoutes = (fullScreen as {state?: {routes?: Array<{name: string}>}} | undefined)?.state?.routes; - return {name: fullScreen?.name, central: innerRoutes?.at(-1)?.name}; -} +import getFullScreenUnderRHP from '../utils/getFullScreenUnderRHP'; describe('RHP screens opened from a chat or Home', () => { it.each([ diff --git a/tests/utils/getFullScreenUnderRHP.ts b/tests/utils/getFullScreenUnderRHP.ts new file mode 100644 index 000000000000..04dd63a827ac --- /dev/null +++ b/tests/utils/getFullScreenUnderRHP.ts @@ -0,0 +1,14 @@ +import getAdaptedStateFromPath from '@libs/Navigation/helpers/getAdaptedStateFromPath'; + +import NAVIGATORS from '@src/NAVIGATORS'; + +/** Resolves the path the way a deep link or refresh does and returns the full screen left under the RHP. */ +function getFullScreenUnderRHP(path: string) { + const state = getAdaptedStateFromPath(path, undefined); + const tabRoute = state?.routes?.findLast((route) => route.name === NAVIGATORS.TAB_NAVIGATOR); + const tabState = tabRoute?.state; + const fullScreen = tabState?.routes?.at(tabState.index ?? (tabState.routes?.length ?? 1) - 1); + return {name: fullScreen?.name, central: fullScreen?.state?.routes?.at(-1)?.name}; +} + +export default getFullScreenUnderRHP;