From 773b3aa95e9ea3a91be5210a3599d509103797fc Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Thu, 2 Oct 2025 10:03:49 +0200 Subject: [PATCH 1/3] recreated a previous Pr and attempt to fix the issue with redirecting to inbox --- src/CONST/index.ts | 2 +- src/libs/Navigation/NavigationRoot.tsx | 28 ++++++++++------- src/libs/actions/App.ts | 30 ++++++++++++++++++- .../shouldOpenLastVisitedPath/index.native.ts | 3 +- .../step/IOURequestStepScan/index.native.tsx | 18 +++++++++-- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index b52f7bc9bb79..096dfee12ade 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -6801,7 +6801,7 @@ const CONST = { CASH_BACK: 'earnedCashback', }, - EXCLUDE_FROM_LAST_VISITED_PATH: [SCREENS.NOT_FOUND, SCREENS.SAML_SIGN_IN, SCREENS.VALIDATE_LOGIN, SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT] as string[], + EXCLUDE_FROM_LAST_VISITED_PATH: [SCREENS.NOT_FOUND, SCREENS.SAML_SIGN_IN, SCREENS.VALIDATE_LOGIN, SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT, SCREENS.MONEY_REQUEST.STEP_SCAN] as string[], CANCELLATION_TYPE: { MANUAL: 'manual', diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index dc5c3a2c385a..f25fb2d23695 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -131,6 +131,12 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const isTransitioning = path?.includes(ROUTES.TRANSITION_BETWEEN_APPS); + // If we have a transition URL, don't restore last visited path - let React Navigation handle it + // This prevents reusing deep links after logout regardless of authentication status + if (isTransitioning) { + return undefined; + } + // If the user haven't completed the flow, we want to always redirect them to the onboarding flow. // We also make sure that the user is authenticated, isn't part of a group workspace, isn't in the transition flow & wasn't invited to NewDot. if (!CONFIG.IS_HYBRID_APP && !hasNonPersonalPolicy && !isOnboardingCompleted && !wasInvitedToNewDot && authenticated && !isTransitioning) { @@ -146,20 +152,20 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N ); } - // If there is no lastVisitedPath, we can do early return. We won't modify the default behavior. - // The same applies to HybridApp, as we always define the route to which we want to transition. - if (!shouldOpenLastVisitedPath(lastVisitedPath) || CONFIG.IS_HYBRID_APP) { - return undefined; - } + if (shouldOpenLastVisitedPath(lastVisitedPath) && authenticated) { + // Only skip restoration if there's a specific deep link that's not the root + // This allows restoration when app is killed and reopened without a deep link + const isRootPath = !path || path === '' || path === '/'; + const isSpecificDeepLink = path && !isRootPath; - // If the user opens the root of app "/" it will be parsed to empty string "". - // If the path is defined and different that empty string we don't want to modify the default behavior. - if (path) { - return; + if (!isSpecificDeepLink) { + Log.info('Restoring last visited path on app startup', false, {lastVisitedPath, initialUrl, path}); + return getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config); + } } - // Otherwise we want to redirect the user to the last visited path. - return getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config); + // Default behavior - let React Navigation handle the initial state + return undefined; // The initialState value is relevant only on the first render. // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 37ebc4ffe2dd..f108929ec5e7 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -1,4 +1,5 @@ // Issue - https://github.com/Expensify/App/issues/26719 +import {getPathFromState} from '@react-navigation/native'; import {Str} from 'expensify-common'; import type {AppStateStatus} from 'react-native'; import {AppState} from 'react-native'; @@ -11,7 +12,8 @@ import * as Browser from '@libs/Browser'; import DateUtils from '@libs/DateUtils'; import Log from '@libs/Log'; import getCurrentUrl from '@libs/Navigation/currentUrl'; -import Navigation from '@libs/Navigation/Navigation'; +import {linkingConfig} from '@libs/Navigation/linkingConfig'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import {isPublicRoom, isValidReport} from '@libs/ReportUtils'; import {isLoggingInAsNewUser as isLoggingInAsNewUserSessionUtils} from '@libs/SessionUtils'; @@ -207,10 +209,36 @@ function setAppLoading(isLoading: boolean) { Onyx.set(ONYXKEYS.IS_LOADING_APP, isLoading); } +/** + * Saves the current navigation path to lastVisitedPath before app goes to background + */ +function saveCurrentPathBeforeBackground() { + try { + if (!navigationRef.isReady()) { + return; + } + + const currentState = navigationRef.getRootState(); + if (!currentState) { + return; + } + + const currentPath = getPathFromState(currentState, linkingConfig.config); + + if (currentPath) { + Log.info('Saving current path before background', false, {currentPath}); + updateLastVisitedPath(currentPath); + } + } catch (error) { + Log.warn('Failed to save current path before background', {error}); + } +} + let appState: AppStateStatus; AppState.addEventListener('change', (nextAppState) => { if (nextAppState.match(/inactive|background/) && appState === 'active') { Log.info('Flushing logs as app is going inactive', true, {}, true); + saveCurrentPathBeforeBackground(); } appState = nextAppState; }); diff --git a/src/libs/shouldOpenLastVisitedPath/index.native.ts b/src/libs/shouldOpenLastVisitedPath/index.native.ts index c63444aaeeb7..e73603714264 100644 --- a/src/libs/shouldOpenLastVisitedPath/index.native.ts +++ b/src/libs/shouldOpenLastVisitedPath/index.native.ts @@ -1,6 +1,5 @@ -import {getReportIDFromLink} from '@libs/ReportUtils'; import type {Route} from '@src/ROUTES'; export default function shouldOpenLastVisitedPath(lastVisitedPath: Route) { - return !!lastVisitedPath && !!getReportIDFromLink(lastVisitedPath); + return !!lastVisitedPath; } diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 4300c3738958..eff8d94f86a9 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -48,6 +48,7 @@ import HapticFeedback from '@libs/HapticFeedback'; import {navigateToParticipantPage} from '@libs/IOUUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; +import navigationRef from '@libs/Navigation/navigationRef'; import {getManagerMcTestParticipant, getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; import {findSelfDMReportID, generateReportID, getPolicyExpenseChat, isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils'; @@ -72,6 +73,7 @@ import type {GpsPoint} from '@userActions/IOU'; import {buildOptimisticTransactionAndCreateDraft, removeDraftTransactions, removeTransactionReceipt} from '@userActions/TransactionEdit'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; @@ -527,10 +529,22 @@ function IOURequestStepScan({ const updateScanAndNavigate = useCallback( (file: FileObject, source: string) => { - navigateBack(); + // Fix for the issue where the navigation state is lost after returning from device settings https://github.com/Expensify/App/issues/65992 + const navigationState = navigationRef.current?.getState(); + const reportsSplitNavigator = navigationState?.routes?.find((route) => route.name === 'ReportsSplitNavigator'); + const hasLostNavigationsState = reportsSplitNavigator && !reportsSplitNavigator.state; + if (hasLostNavigationsState) { + if (backTo) { + Navigation.navigate(backTo as Route); + } else { + Navigation.navigate(ROUTES.HOME); + } + } else { + navigateBack(); + } replaceReceipt({transactionID: initialTransactionID, file: file as File, source}); }, - [initialTransactionID], + [initialTransactionID, backTo], ); /** From 2d6c9e28f51e2e450c431ae1671f6bb8ac658982 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Thu, 9 Oct 2025 17:16:14 +0200 Subject: [PATCH 2/3] fix: update navigation state handling in IOURequestStepScan to use findLast for ReportsSplitNavigator --- src/pages/iou/request/step/IOURequestStepScan/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 16dc87afe2df..078e5a2660b5 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -532,7 +532,7 @@ function IOURequestStepScan({ (file: FileObject, source: string) => { // Fix for the issue where the navigation state is lost after returning from device settings https://github.com/Expensify/App/issues/65992 const navigationState = navigationRef.current?.getState(); - const reportsSplitNavigator = navigationState?.routes?.find((route) => route.name === 'ReportsSplitNavigator'); + const reportsSplitNavigator = navigationState?.routes?.findLast((route) => route.name === 'ReportsSplitNavigator'); const hasLostNavigationsState = reportsSplitNavigator && !reportsSplitNavigator.state; if (hasLostNavigationsState) { if (backTo) { From 98ce64561416a1c664f355bd677aef9dfb9db9cf Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Tue, 14 Oct 2025 12:36:36 +0200 Subject: [PATCH 3/3] remove native file for shouldOpenLastVisitedPath as its the same as index now --- src/libs/shouldOpenLastVisitedPath/index.native.ts | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/libs/shouldOpenLastVisitedPath/index.native.ts diff --git a/src/libs/shouldOpenLastVisitedPath/index.native.ts b/src/libs/shouldOpenLastVisitedPath/index.native.ts deleted file mode 100644 index e73603714264..000000000000 --- a/src/libs/shouldOpenLastVisitedPath/index.native.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type {Route} from '@src/ROUTES'; - -export default function shouldOpenLastVisitedPath(lastVisitedPath: Route) { - return !!lastVisitedPath; -}