diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 84bb1a4d6257..5d03ecca0488 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -2,10 +2,10 @@ import {isSingleNewDotEntrySelector} from '@selectors/HybridApp'; import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector, wasInvitedToNewDotSelector} from '@selectors/Onboarding'; import {emailSelector} from '@selectors/Session'; import {useEffect} from 'react'; +// eslint-disable-next-line no-restricted-imports +import {InteractionManager} from 'react-native'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import Navigation from '@libs/Navigation/Navigation'; -// eslint-disable-next-line no-restricted-imports -import TransitionTracker from '@libs/Navigation/TransitionTracker'; import {isLoggingInAsNewUser} from '@libs/SessionUtils'; import {startOnboardingFlow} from '@userActions/Welcome/OnboardingFlow'; import CONFIG from '@src/CONFIG'; @@ -47,65 +47,62 @@ function useOnboardingFlowRouter() { useEffect(() => { // This should delay opening the onboarding modal so it does not interfere with the ongoing ReportScreen params changes - - const handle = TransitionTracker.runAfterTransitions({ - callback: () => { - // Prevent showing onboarding if we are logging in as a new user with short lived token - if (currentUrl?.includes(ROUTES.TRANSITION_BETWEEN_APPS) && isLoggingInAsNewSessionUser) { - return; - } - - if (isLoadingApp !== false || isOnboardingLoading) { - return; - } - - if (isLoadingOnyxValue(isOnboardingCompletedMetadata, tryNewDotMetadata, dismissedProductTrainingMetadata)) { + const handle = InteractionManager.runAfterInteractions(() => { + // Prevent showing onboarding if we are logging in as a new user with short lived token + if (currentUrl?.includes(ROUTES.TRANSITION_BETWEEN_APPS) && isLoggingInAsNewSessionUser) { + return; + } + + if (isLoadingApp !== false || isOnboardingLoading) { + return; + } + + if (isLoadingOnyxValue(isOnboardingCompletedMetadata, tryNewDotMetadata, dismissedProductTrainingMetadata)) { + return; + } + + if (CONFIG.IS_HYBRID_APP && isLoadingOnyxValue(isSingleNewDotEntryMetadata)) { + return; + } + + if (CONFIG.IS_HYBRID_APP) { + // For single entries, such as using the Travel feature from OldDot, we don't want to show onboarding + if (isSingleNewDotEntry) { return; } - if (CONFIG.IS_HYBRID_APP && isLoadingOnyxValue(isSingleNewDotEntryMetadata)) { - return; + // When user is transitioning from OldDot to NewDot, we usually show the explanation modal + if (isHybridAppOnboardingCompleted === false) { + Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT); } - - if (CONFIG.IS_HYBRID_APP) { - // For single entries, such as using the Travel feature from OldDot, we don't want to show onboarding - if (isSingleNewDotEntry) { - return; - } - - // When user is transitioning from OldDot to NewDot, we usually show the explanation modal - if (isHybridAppOnboardingCompleted === false) { - Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT); - } - } - - const isMigratedUser = hasBeenAddedToNudgeMigration ?? false; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const isInvitedOrGroupMember = (!CONFIG.IS_HYBRID_APP && (hasNonPersonalPolicy || wasInvitedToNewDot)) ?? false; - // OD signup sets inviteType + creates a workspace, so invited/group members can still need NewDot onboarding. - if (isMigratedUser || (isInvitedOrGroupMember && isOnboardingCompleted)) { - return; - } - - // Explicitly start the onboarding flow when onboarding is not completed. - // We use startOnboardingFlow (which calls resetRoot) instead of Navigation.navigate because - // navigate goes through the router where OnboardingGuard would block the navigation. - // waitForProtectedRoutes ensures navigation is ready, which is critical during fresh login. - // Skip when HybridApp explanation modal is active (OldDot-transitioning users). - if (isOnboardingCompleted === false && !(CONFIG.IS_HYBRID_APP && isHybridAppOnboardingCompleted === false)) { - Navigation.waitForProtectedRoutes().then(() => { - startOnboardingFlow({ - onboardingValuesParam: onboardingValues ?? undefined, - isUserFromPublicDomain: !!account?.isFromPublicDomain, - hasAccessiblePolicies: !!account?.hasAccessibleDomainPolicies, - currentOnboardingCompanySize: onboardingCompanySize, - currentOnboardingPurposeSelected: onboardingPurposeSelected, - onboardingInitialPath, - onboardingValues, - }); + } + + const isMigratedUser = hasBeenAddedToNudgeMigration ?? false; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const isInvitedOrGroupMember = (!CONFIG.IS_HYBRID_APP && (hasNonPersonalPolicy || wasInvitedToNewDot)) ?? false; + // OD signup sets inviteType + creates a workspace, so invited/group members can still need NewDot onboarding. + if (isMigratedUser || (isInvitedOrGroupMember && isOnboardingCompleted)) { + return; + } + + // Explicitly start the onboarding flow when onboarding is not completed. + // We use startOnboardingFlow (which calls resetRoot) instead of Navigation.navigate because + // navigate goes through the router where OnboardingGuard would block the navigation. + // waitForProtectedRoutes ensures navigation is ready, which is critical during fresh login. + // Skip when HybridApp explanation modal is active (OldDot-transitioning users). + if (isOnboardingCompleted === false && !(CONFIG.IS_HYBRID_APP && isHybridAppOnboardingCompleted === false)) { + Navigation.waitForProtectedRoutes().then(() => { + startOnboardingFlow({ + onboardingValuesParam: onboardingValues ?? undefined, + isUserFromPublicDomain: !!account?.isFromPublicDomain, + hasAccessiblePolicies: !!account?.hasAccessibleDomainPolicies, + currentOnboardingCompanySize: onboardingCompanySize, + currentOnboardingPurposeSelected: onboardingPurposeSelected, + onboardingInitialPath, + onboardingValues, }); - } - }, + }); + } }); return () => { diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index 04087edb88e3..1ad31145bf9f 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -1,6 +1,7 @@ import {hasSeenTourSelector} from '@selectors/Onboarding'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; -import {View} from 'react-native'; +// eslint-disable-next-line no-restricted-imports +import {InteractionManager, View} from 'react-native'; import Button from '@components/Button'; import Checkbox from '@components/Checkbox'; import FixedFooter from '@components/FixedFooter'; @@ -32,8 +33,6 @@ import {setOnboardingAdminsChatReportID, setOnboardingPolicyID} from '@libs/acti import Log from '@libs/Log'; import {navigateAfterOnboardingWithMicrotaskQueue} from '@libs/navigateAfterOnboarding'; import Navigation from '@libs/Navigation/Navigation'; -// eslint-disable-next-line no-restricted-imports -import TransitionTracker from '@libs/Navigation/TransitionTracker'; import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -241,12 +240,9 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin }); // Avoid creating new WS because onboardingPolicyID is cleared before unmounting - TransitionTracker.runAfterTransitions({ - callback: () => { - setOnboardingAdminsChatReportID(); - setOnboardingPolicyID(); - }, - waitForUpcomingTransition: true, + InteractionManager.runAfterInteractions(() => { + setOnboardingAdminsChatReportID(); + setOnboardingPolicyID(); }); // We need to wait the policy is created before navigating out the onboarding flow