Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6820,7 +6820,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, SCREENS.MONEY_REQUEST.STEP_SCAN] as string[],
EXCLUDE_FROM_LAST_VISITED_PATH: [SCREENS.NOT_FOUND, SCREENS.SAML_SIGN_IN, SCREENS.VALIDATE_LOGIN, SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT] as string[],

CANCELLATION_TYPE: {
MANUAL: 'manual',
Expand Down
28 changes: 11 additions & 17 deletions src/libs/Navigation/NavigationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,6 @@ 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) {
Expand All @@ -152,20 +146,20 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N
);
}

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 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 (!isSpecificDeepLink) {
Log.info('Restoring last visited path on app startup', false, {lastVisitedPath, initialUrl, path});
return getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config);
}
// 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;
}

// Default behavior - let React Navigation handle the initial state
return undefined;
// Otherwise we want to redirect the user to the last visited path.
return getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config);

// The initialState value is relevant only on the first render.
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
Expand Down
30 changes: 1 addition & 29 deletions src/libs/actions/App.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// 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';
Expand All @@ -12,8 +11,7 @@ import * as Browser from '@libs/Browser';
import DateUtils from '@libs/DateUtils';
import Log from '@libs/Log';
import getCurrentUrl from '@libs/Navigation/currentUrl';
import {linkingConfig} from '@libs/Navigation/linkingConfig';
import Navigation, {navigationRef} from '@libs/Navigation/Navigation';
import Navigation from '@libs/Navigation/Navigation';
import Performance from '@libs/Performance';
import {isPublicRoom, isValidReport} from '@libs/ReportUtils';
import {isLoggingInAsNewUser as isLoggingInAsNewUserSessionUtils} from '@libs/SessionUtils';
Expand Down Expand Up @@ -209,36 +207,10 @@ 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;
});
Expand Down
6 changes: 6 additions & 0 deletions src/libs/shouldOpenLastVisitedPath/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {getReportIDFromLink} from '@libs/ReportUtils';
import type {Route} from '@src/ROUTES';

export default function shouldOpenLastVisitedPath(lastVisitedPath: Route) {
return !!lastVisitedPath && !!getReportIDFromLink(lastVisitedPath);
}
18 changes: 2 additions & 16 deletions src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ 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';
Expand All @@ -74,7 +73,6 @@ 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';
Expand Down Expand Up @@ -532,22 +530,10 @@ function IOURequestStepScan({

const updateScanAndNavigate = useCallback(
(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?.findLast((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();
}
navigateBack();
replaceReceipt({transactionID: initialTransactionID, file: file as File, source});
},
[initialTransactionID, backTo],
[initialTransactionID],
);

/**
Expand Down
Loading