diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx index 18cb758c5703..a5746f6f8e81 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx @@ -1,6 +1,9 @@ import type {ParamListBase, PartialState, RouterConfigOptions, StackNavigationState} from '@react-navigation/native'; import {StackRouter} from '@react-navigation/native'; +import Onyx from 'react-native-onyx'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import type {FullScreenNavigatorRouterOptions} from './types'; @@ -8,12 +11,29 @@ type StackState = StackNavigationState | PartialState state.routes.some((route) => route.name === screenName); +let isLoadingReportData = true; +Onyx.connect({ + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + initWithStoredValues: false, + callback: (value) => (isLoadingReportData = value ?? false), +}); + function adaptStateIfNecessary(state: StackState) { const isNarrowLayout = getIsNarrowLayout(); const workspaceCentralPane = state.routes.at(-1); + const policyID = + workspaceCentralPane?.params && 'policyID' in workspaceCentralPane.params && typeof workspaceCentralPane.params.policyID === 'string' + ? workspaceCentralPane.params.policyID + : undefined; + const policy = PolicyUtils.getPolicy(policyID ?? ''); + const isPolicyAccessible = PolicyUtils.isPolicyAccessible(policy); // There should always be WORKSPACE.INITIAL screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. + // The only exception is when the workspace is invalid or inaccessible. if (!isAtLeastOneInState(state, SCREENS.WORKSPACE.INITIAL)) { + if (isNarrowLayout && !isLoadingReportData && !isPolicyAccessible) { + return; + } // @ts-expect-error Updating read only property // noinspection JSConstantReassignment state.stale = true; // eslint-disable-line diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index d5e9c5229a89..d54668bf3f69 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -12,6 +12,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {HybridAppRoute, Route} from '@src/ROUTES'; import ROUTES, {HYBRID_APP_ROUTES} from '@src/ROUTES'; import {PROTECTED_SCREENS} from '@src/SCREENS'; +import type {Screen} from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; import originalCloseRHPFlow from './closeRHPFlow'; import originalDismissModal from './dismissModal'; @@ -418,6 +419,20 @@ function getTopMostCentralPaneRouteFromRootState() { return getTopmostCentralPaneRoute(navigationRef.getRootState() as State); } +function removeScreenFromNavigationState(screen: Screen) { + isNavigationReady().then(() => { + navigationRef.dispatch((state) => { + const routes = state.routes?.filter((item) => item.name !== screen); + + return CommonActions.reset({ + ...state, + routes, + index: routes.length < state.routes.length ? state.index - 1 : state.index, + }); + }); + }); +} + export default { setShouldPopAllStateOnUP, navigate, @@ -442,6 +457,7 @@ export default { closeRHPFlow, setNavigationActionToMicrotaskQueue, getTopMostCentralPaneRouteFromRootState, + removeScreenFromNavigationState, }; export {navigationRef}; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 36e5ccef3308..7c5b24b4827b 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1064,6 +1064,10 @@ function getActivePolicy(): OnyxEntry { return getPolicy(activePolicyId); } +function isPolicyAccessible(policy: OnyxEntry): boolean { + return !isEmptyObject(policy) && (Object.keys(policy).length !== 1 || isEmptyObject(policy.errors)) && !!policy?.id; +} + export { canEditTaxRate, extractPolicyIDFromPath, @@ -1181,6 +1185,7 @@ export { getNetSuiteImportCustomFieldLabel, getAllPoliciesLength, getActivePolicy, + isPolicyAccessible, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index 45bce8c2d1ba..9bda7f3972f9 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -17,6 +17,7 @@ import type {IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {PolicyFeatureName} from '@src/types/onyx/Policy'; import callOrReturn from '@src/types/utils/callOrReturn'; @@ -152,7 +153,7 @@ function AccessOrNotFoundWrapper({ return acc && accessFunction(policy, login, report, allPolicies ?? null, iouType); }, true); - const isPolicyNotAccessible = isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors)) || !policy?.id; + const isPolicyNotAccessible = !PolicyUtils.isPolicyAccessible(policy); const shouldShowNotFoundPage = (!isMoneyRequest && !isFromGlobalCreate && isPolicyNotAccessible) || !isPageAccessible || !isPolicyFeatureEnabled || shouldBeBlocked; // We only update the feature state if it isn't pending. @@ -165,6 +166,14 @@ function AccessOrNotFoundWrapper({ setIsPolicyFeatureEnabled(isFeatureEnabled); }, [pendingField, isOffline, isFeatureEnabled]); + useEffect(() => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (isLoadingReportData || !isPolicyNotAccessible) { + return; + } + Navigation.removeScreenFromNavigationState(SCREENS.WORKSPACE.INITIAL); + }, [isLoadingReportData, isPolicyNotAccessible]); + if (shouldShowFullScreenLoadingIndicator) { return ; }