From e6e271dab4fc4b3c8570de71edd1f6d277c11239 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Wed, 28 Aug 2024 08:08:42 +0700 Subject: [PATCH 1/4] Make onboarding continue from last visited onboarding page Signed-off-by: Tsaqif --- src/ONYXKEYS.ts | 4 ++ src/components/ExplanationModal.tsx | 3 +- .../BottomTabBar.tsx | 9 +--- src/libs/Navigation/NavigationRoot.tsx | 7 ++- .../linkingConfig/getAdaptedStateFromPath.ts | 8 +-- src/libs/actions/Report.ts | 4 +- src/libs/actions/Welcome.ts | 50 +++++++++++++++++-- .../BaseOnboardingPersonalDetails.tsx | 5 +- 8 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 6a1fc8a629ed..e79e37d547f8 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -336,6 +336,9 @@ const ONYXKEYS = { /** Onboarding Purpose selected by the user during Onboarding flow */ ONBOARDING_ADMINS_CHAT_REPORT_ID: 'onboardingAdminsChatReportID', + // Stores onboarding last visited path + ONBOARDING_LAST_VISITED_PATH: 'onboardingLastVisitedPath', + // Max width supported for HTML element MAX_CANVAS_WIDTH: 'maxCanvasWidth', @@ -882,6 +885,7 @@ type OnyxValuesMapping = { [ONYXKEYS.ONBOARDING_ERROR_MESSAGE]: string; [ONYXKEYS.ONBOARDING_POLICY_ID]: string; [ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID]: string; + [ONYXKEYS.ONBOARDING_LAST_VISITED_PATH]: string; [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: boolean; [ONYXKEYS.LAST_VISITED_PATH]: string | undefined; [ONYXKEYS.VERIFY_3DS_SUBSCRIPTION]: string; diff --git a/src/components/ExplanationModal.tsx b/src/components/ExplanationModal.tsx index c6294f600993..bf6bd04d4277 100644 --- a/src/components/ExplanationModal.tsx +++ b/src/components/ExplanationModal.tsx @@ -4,7 +4,6 @@ import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; import FeatureTrainingModal from './FeatureTrainingModal'; function ExplanationModal() { @@ -18,7 +17,7 @@ function ExplanationModal() { onNotCompleted: () => { setTimeout(() => { Navigation.isNavigationReady().then(() => { - Navigation.navigate(ROUTES.ONBOARDING_ROOT.route); + Welcome.startOnboardingFlow(); }); }, variables.welcomeVideoDelay); }, diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 394a617278d4..9154528e2439 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -13,9 +13,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Session from '@libs/actions/Session'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; -import linkingConfig from '@libs/Navigation/linkingConfig'; -import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; -import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; import {isCentralPaneName} from '@libs/NavigationUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -95,10 +93,7 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) { } Welcome.isOnboardingFlowCompleted({ - onNotCompleted: () => { - const {adaptedState} = getAdaptedStateFromPath(ROUTES.ONBOARDING_ROOT.route, linkingConfig.config); - navigationRef.resetRoot(adaptedState); - }, + onNotCompleted: () => Welcome.startOnboardingFlow(), }); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 152594ba6b3e..b2191bc476f5 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -14,6 +14,8 @@ import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFl import Log from '@libs/Log'; import {getPathFromURL} from '@libs/Url'; import {updateLastVisitedPath} from '@userActions/App'; +import {updateOnboardingLastVisitedPath} from '@userActions/Welcome'; +import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -58,6 +60,9 @@ function parseAndLogRoute(state: NavigationState) { if (focusedRoute && !CONST.EXCLUDE_FROM_LAST_VISITED_PATH.includes(focusedRoute?.name)) { updateLastVisitedPath(currentPath); + if (currentPath.startsWith(`/${ROUTES.ONBOARDING_ROOT.route}`)) { + updateOnboardingLastVisitedPath(currentPath); + } } // Don't log the route transitions from OldDot because they contain authTokens @@ -98,7 +103,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh // 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. if (!hasCompletedGuidedSetupFlow && authenticated && !shouldShowRequire2FAModal) { - const {adaptedState} = getAdaptedStateFromPath(ROUTES.ONBOARDING_ROOT.route, linkingConfig.config); + const {adaptedState} = getAdaptedStateFromPath(Welcome.getOnboardingInitialPath(), linkingConfig.config); return adaptedState; } diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 10e68ad4a6a8..2c96e5796309 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -47,7 +47,7 @@ type GetAdaptedStateReturnType = { metainfo: Metainfo; }; -type GetAdaptedStateFromPath = (...args: Parameters) => GetAdaptedStateReturnType; +type GetAdaptedStateFromPath = (...args: [...Parameters, shouldReplacePathInNestedState?: boolean]) => GetAdaptedStateReturnType; // The function getPathFromState that we are using in some places isn't working correctly without defined index. const getRoutesWithIndex = (routes: NavigationPartialRoute[]): PartialState => ({routes, index: routes.length - 1}); @@ -365,7 +365,7 @@ function getAdaptedState(state: PartialState }; } -const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options) => { +const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options, shouldReplacePathInNestedState = true) => { const normalizedPath = !path.startsWith('/') ? `/${path}` : path; const pathWithoutPolicyID = getPathWithoutPolicyID(normalizedPath); const isAnonymous = isAnonymousUser(); @@ -374,7 +374,9 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options) => { const policyID = isAnonymous ? undefined : extractPolicyIDFromPath(path); const state = getStateFromPath(pathWithoutPolicyID, options) as PartialState>; - replacePathInNestedState(state, path); + if (shouldReplacePathInNestedState) { + replacePathInNestedState(state, path); + } if (state === undefined) { throw new Error('Unable to parse path'); } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index edded7a4c4c6..4f1f3679e486 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2701,7 +2701,9 @@ function openReportFromDeepLink(url: string) { // We need skip deeplinking if the user hasn't completed the guided setup flow. if (!hasCompletedGuidedSetupFlow) { - Welcome.isOnboardingFlowCompleted({onNotCompleted: () => Navigation.navigate(ROUTES.ONBOARDING_ROOT.getRoute())}); + Welcome.isOnboardingFlowCompleted({ + onNotCompleted: () => Welcome.startOnboardingFlow(), + }); return; } diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index d54314ae6f05..3b998d3fef68 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -3,11 +3,16 @@ import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import {WRITE_COMMANDS} from '@libs/API/types'; -import Navigation from '@libs/Navigation/Navigation'; +import linkingConfig from '@libs/Navigation/linkingConfig'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import getStateFromPath from '@navigation/getStateFromPath'; +import getAdaptedStateFromPath from '@navigation/linkingConfig/getAdaptedStateFromPath'; import variables from '@styles/variables'; import type {OnboardingPurposeType} from '@src/CONST'; +import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type Onboarding from '@src/types/onyx/Onboarding'; import type TryNewDot from '@src/types/onyx/TryNewDot'; @@ -15,6 +20,7 @@ type OnboardingData = Onboarding | [] | undefined; let isLoadingReportData = true; let tryNewDotData: TryNewDot | undefined; +let onboardingInitialPath = ''; let onboarding: OnboardingData; type HasCompletedOnboardingFlowProps = { @@ -46,6 +52,7 @@ function onServerDataReady(): Promise { return isServerDataReadyPromise; } +let isOnboardingInProgress = false; function isOnboardingFlowCompleted({onCompleted, onNotCompleted}: HasCompletedOnboardingFlowProps) { isOnboardingFlowStatusKnownPromise.then(() => { if (Array.isArray(onboarding) || onboarding?.hasCompletedGuidedSetupFlow === undefined) { @@ -53,8 +60,10 @@ function isOnboardingFlowCompleted({onCompleted, onNotCompleted}: HasCompletedOn } if (onboarding?.hasCompletedGuidedSetupFlow) { + isOnboardingInProgress = false; onCompleted?.(); - } else { + } else if (!isOnboardingInProgress) { + isOnboardingInProgress = true; onNotCompleted?.(); } }); @@ -97,7 +106,7 @@ function handleHybridAppOnboarding() { isOnboardingFlowCompleted({ onNotCompleted: () => setTimeout(() => { - Navigation.navigate(ROUTES.ONBOARDING_ROOT.route); + startOnboardingFlow(); }, variables.explanationModalDelay), }), }); @@ -152,6 +161,19 @@ function setOnboardingPolicyID(policyID?: string) { Onyx.set(ONYXKEYS.ONBOARDING_POLICY_ID, policyID ?? null); } +function updateOnboardingLastVisitedPath(path: string) { + Onyx.merge(ONYXKEYS.ONBOARDING_LAST_VISITED_PATH, path); +} + +function getOnboardingInitialPath(): string { + const state = getStateFromPath(onboardingInitialPath as Route); + if (state?.routes?.at(-1)?.name !== NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR) { + return ROUTES.ONBOARDING_ROOT.route; + } + + return onboardingInitialPath; +} + function completeHybridAppOnboarding() { const optimisticData: OnyxUpdate[] = [ { @@ -180,6 +202,11 @@ function completeHybridAppOnboarding() { API.write(WRITE_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING, {}, {optimisticData, failureData}); } +function startOnboardingFlow() { + const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(), linkingConfig.config, false); + navigationRef.resetRoot(adaptedState); +} + Onyx.connect({ key: ONYXKEYS.NVP_ONBOARDING, callback: (value) => { @@ -188,6 +215,18 @@ Onyx.connect({ }, }); +const onboardingLastVisitedPathConnection = Onyx.connect({ + key: ONYXKEYS.ONBOARDING_LAST_VISITED_PATH, + callback: (value) => { + if (value === undefined) { + return; + } + + onboardingInitialPath = value.substring(1); + Onyx.disconnect(onboardingLastVisitedPathConnection); + }, +}); + Onyx.connect({ key: ONYXKEYS.IS_LOADING_REPORT_DATA, initWithStoredValues: false, @@ -213,16 +252,21 @@ function resetAllChecks() { resolveOnboardingFlowStatus = resolve; }); isLoadingReportData = true; + onboardingInitialPath = ''; + isOnboardingInProgress = false; } export { onServerDataReady, isOnboardingFlowCompleted, setOnboardingPurposeSelected, + getOnboardingInitialPath, + updateOnboardingLastVisitedPath, resetAllChecks, setOnboardingAdminsChatReportID, setOnboardingPolicyID, completeHybridAppOnboarding, handleHybridAppOnboarding, setOnboardingErrorMessage, + startOnboardingFlow, }; diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 67a520ab7267..64b8aebe19be 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -25,6 +25,7 @@ import * as Report from '@userActions/Report'; import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/DisplayNameForm'; import type {BaseOnboardingPersonalDetailsOnyxProps, BaseOnboardingPersonalDetailsProps} from './types'; @@ -73,7 +74,9 @@ function BaseOnboardingPersonalDetails({ Welcome.setOnboardingAdminsChatReportID(); Welcome.setOnboardingPolicyID(); - Navigation.dismissModal(); + // Navigate to HOME instead of dismissModal, because there is bug in small screen + // where the onboarding puropose page will be disaplayed briefly + Navigation.navigate(ROUTES.HOME); // Only navigate to concierge chat when central pane is visible // Otherwise stay on the chats screen. From b862e13a02a052fcf53cd8392fb556beb6965b99 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Tue, 3 Sep 2024 14:12:47 +0700 Subject: [PATCH 2/4] Fix onboarding navigation issues, briefly visible purpose and wrong going back navigation Signed-off-by: Tsaqif --- src/components/ExplanationModal.tsx | 3 +- .../BottomTabBar.tsx | 3 +- src/libs/Navigation/NavigationRoot.tsx | 4 +- src/libs/Navigation/linkingConfig/config.ts | 3 +- src/libs/actions/Report.ts | 3 +- src/libs/actions/Welcome/OnboardingFlow.ts | 128 ++++++++++++++++++ .../actions/{Welcome.ts => Welcome/index.ts} | 34 +---- .../BaseOnboardingPersonalDetails.tsx | 8 +- .../OnboardingWork/BaseOnboardingWork.tsx | 4 +- 9 files changed, 147 insertions(+), 43 deletions(-) create mode 100644 src/libs/actions/Welcome/OnboardingFlow.ts rename src/libs/actions/{Welcome.ts => Welcome/index.ts} (88%) diff --git a/src/components/ExplanationModal.tsx b/src/components/ExplanationModal.tsx index bf6bd04d4277..73290c43d39a 100644 --- a/src/components/ExplanationModal.tsx +++ b/src/components/ExplanationModal.tsx @@ -3,6 +3,7 @@ import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import * as Welcome from '@userActions/Welcome'; +import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; import CONST from '@src/CONST'; import FeatureTrainingModal from './FeatureTrainingModal'; @@ -17,7 +18,7 @@ function ExplanationModal() { onNotCompleted: () => { setTimeout(() => { Navigation.isNavigationReady().then(() => { - Welcome.startOnboardingFlow(); + OnboardingFlow.startOnboardingFlow(); }); }, variables.welcomeVideoDelay); }, diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 9154528e2439..d03547ede1be 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -24,6 +24,7 @@ import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; import variables from '@styles/variables'; import * as Welcome from '@userActions/Welcome'; +import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -93,7 +94,7 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) { } Welcome.isOnboardingFlowCompleted({ - onNotCompleted: () => Welcome.startOnboardingFlow(), + onNotCompleted: () => OnboardingFlow.startOnboardingFlow(), }); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index b2191bc476f5..9cbe08a444df 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -15,7 +15,7 @@ import Log from '@libs/Log'; import {getPathFromURL} from '@libs/Url'; import {updateLastVisitedPath} from '@userActions/App'; import {updateOnboardingLastVisitedPath} from '@userActions/Welcome'; -import * as Welcome from '@userActions/Welcome'; +import {getOnboardingInitialPath} from '@userActions/Welcome/OnboardingFlow'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -103,7 +103,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh // 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. if (!hasCompletedGuidedSetupFlow && authenticated && !shouldShowRequire2FAModal) { - const {adaptedState} = getAdaptedStateFromPath(Welcome.getOnboardingInitialPath(), linkingConfig.config); + const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(), linkingConfig.config); return adaptedState; } diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 6ee3b14b64ed..4a41a410a9cd 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -110,8 +110,9 @@ const config: LinkingOptions['config'] = { }, }, [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: { + // Don't set the initialRouteName, because when the user continues from the last visited onboarding page, + // the onboarding purpose page will be briefly visible. path: ROUTES.ONBOARDING_ROOT.route, - initialRouteName: SCREENS.ONBOARDING.PURPOSE, screens: { [SCREENS.ONBOARDING.PURPOSE]: { path: ROUTES.ONBOARDING_PURPOSE.route, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4f1f3679e486..3a781c4cbd7d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -111,6 +111,7 @@ import * as Modal from './Modal'; import navigateFromNotification from './navigateFromNotification'; import * as Session from './Session'; import * as Welcome from './Welcome'; +import * as OnboardingFlow from './Welcome/OnboardingFlow'; type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string | undefined) => void; @@ -2702,7 +2703,7 @@ function openReportFromDeepLink(url: string) { // We need skip deeplinking if the user hasn't completed the guided setup flow. if (!hasCompletedGuidedSetupFlow) { Welcome.isOnboardingFlowCompleted({ - onNotCompleted: () => Welcome.startOnboardingFlow(), + onNotCompleted: () => OnboardingFlow.startOnboardingFlow(), }); return; } diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts new file mode 100644 index 000000000000..54d50aa44c67 --- /dev/null +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -0,0 +1,128 @@ +import {findFocusedRoute, getStateFromPath} from '@react-navigation/native'; +import type {NavigationState, PartialState} from '@react-navigation/native'; +import Onyx from 'react-native-onyx'; +import linkingConfig from '@libs/Navigation/linkingConfig'; +import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import type {NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import CONST from '@src/CONST'; +import NAVIGATORS from '@src/NAVIGATORS'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; + +let selectedPurpose: string | undefined = ''; +Onyx.connect({ + key: ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, + callback: (value) => { + selectedPurpose = value; + }, +}); + +let onboardingInitialPath = ''; +const onboardingLastVisitedPathConnection = Onyx.connect({ + key: ONYXKEYS.ONBOARDING_LAST_VISITED_PATH, + callback: (value) => { + if (value === undefined) { + return; + } + onboardingInitialPath = value; + Onyx.disconnect(onboardingLastVisitedPathConnection); + }, +}); + +/** + * Build the correct stack order for `onboardingModalNavigator`, + * based on onboarding data (currently from the selected purpose). + * The correct stack order will ensure that navigation and + * the `goBack` navigatoin work properly. + */ +function adaptOnboardingRouteState() { + const currentRoute: NavigationPartialRoute | undefined = navigationRef.getCurrentRoute(); + if (!currentRoute || currentRoute?.name === SCREENS.ONBOARDING.PURPOSE) { + return; + } + + const rootState = navigationRef.getRootState(); + const adaptedState = rootState; + const lastRouteIndex = (adaptedState?.routes?.length ?? 0) - 1; + const onBoardingModalNavigatorState = adaptedState?.routes[lastRouteIndex]?.state; + if (!onBoardingModalNavigatorState || onBoardingModalNavigatorState?.routes?.length > 1) { + return; + } + + let adaptedOnboardingModalNavigatorState = {} as Readonly>; + if (currentRoute?.name === SCREENS.ONBOARDING.PERSONAL_DETAILS && selectedPurpose === CONST.ONBOARDING_CHOICES.MANAGE_TEAM) { + adaptedOnboardingModalNavigatorState = { + index: 2, + routes: [ + { + name: SCREENS.ONBOARDING.PURPOSE, + params: currentRoute?.params, + }, + { + name: SCREENS.ONBOARDING.WORK, + params: currentRoute?.params, + path: '/onboarding/works', + }, + {...currentRoute}, + ], + } as Readonly>; + } else { + adaptedOnboardingModalNavigatorState = { + index: 1, + routes: [ + { + name: SCREENS.ONBOARDING.PURPOSE, + params: currentRoute?.params, + }, + {...currentRoute}, + ], + } as Readonly>; + } + + adaptedState.routes[lastRouteIndex].state = adaptedOnboardingModalNavigatorState; + navigationRef.resetRoot(adaptedState); +} + +/** + * Start a new onboarding flow or continue from the last visited onboarding page. + */ +function startOnboardingFlow() { + const currentRoute = navigationRef.getCurrentRoute(); + const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(), linkingConfig.config, false); + const focusedRoute = findFocusedRoute(adaptedState as PartialState>); + if (focusedRoute?.name === currentRoute?.name) { + return; + } + navigationRef.resetRoot(adaptedState); +} + +function getOnboardingInitialPath(): string { + const state = getStateFromPath(onboardingInitialPath, linkingConfig.config); + if (state?.routes?.at(-1)?.name !== NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR) { + return `/${ROUTES.ONBOARDING_ROOT.route}`; + } + + return onboardingInitialPath; +} + +function clearInitialPath() { + onboardingInitialPath = ''; +} + +/** + * Onboarding flow: Go back to the previous page. + * Since there is no `initialRoute` for `onBoardingModalNavigator`, + * firstly, adjust the current onboarding modal navigator to establish the correct stack order. + * Then, navigate to the previous onboarding page using the usual `goBack` function. + */ +function goBack() { + adaptOnboardingRouteState(); + Navigation.isNavigationReady().then(() => { + Navigation.goBack(); + }); +} + +export {getOnboardingInitialPath, startOnboardingFlow, clearInitialPath, goBack}; +// export default adaptOnboardingRouteState; diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome/index.ts similarity index 88% rename from src/libs/actions/Welcome.ts rename to src/libs/actions/Welcome/index.ts index 3b998d3fef68..80958b16c853 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome/index.ts @@ -5,6 +5,7 @@ import * as API from '@libs/API'; import {WRITE_COMMANDS} from '@libs/API/types'; import linkingConfig from '@libs/Navigation/linkingConfig'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import getStateFromPath from '@navigation/getStateFromPath'; import getAdaptedStateFromPath from '@navigation/linkingConfig/getAdaptedStateFromPath'; import variables from '@styles/variables'; @@ -15,6 +16,7 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type Onboarding from '@src/types/onyx/Onboarding'; import type TryNewDot from '@src/types/onyx/TryNewDot'; +import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; type OnboardingData = Onboarding | [] | undefined; @@ -106,7 +108,7 @@ function handleHybridAppOnboarding() { isOnboardingFlowCompleted({ onNotCompleted: () => setTimeout(() => { - startOnboardingFlow(); + OnboardingFlow.startOnboardingFlow(); }, variables.explanationModalDelay), }), }); @@ -165,15 +167,6 @@ function updateOnboardingLastVisitedPath(path: string) { Onyx.merge(ONYXKEYS.ONBOARDING_LAST_VISITED_PATH, path); } -function getOnboardingInitialPath(): string { - const state = getStateFromPath(onboardingInitialPath as Route); - if (state?.routes?.at(-1)?.name !== NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR) { - return ROUTES.ONBOARDING_ROOT.route; - } - - return onboardingInitialPath; -} - function completeHybridAppOnboarding() { const optimisticData: OnyxUpdate[] = [ { @@ -202,11 +195,6 @@ function completeHybridAppOnboarding() { API.write(WRITE_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING, {}, {optimisticData, failureData}); } -function startOnboardingFlow() { - const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(), linkingConfig.config, false); - navigationRef.resetRoot(adaptedState); -} - Onyx.connect({ key: ONYXKEYS.NVP_ONBOARDING, callback: (value) => { @@ -215,18 +203,6 @@ Onyx.connect({ }, }); -const onboardingLastVisitedPathConnection = Onyx.connect({ - key: ONYXKEYS.ONBOARDING_LAST_VISITED_PATH, - callback: (value) => { - if (value === undefined) { - return; - } - - onboardingInitialPath = value.substring(1); - Onyx.disconnect(onboardingLastVisitedPathConnection); - }, -}); - Onyx.connect({ key: ONYXKEYS.IS_LOADING_REPORT_DATA, initWithStoredValues: false, @@ -252,15 +228,14 @@ function resetAllChecks() { resolveOnboardingFlowStatus = resolve; }); isLoadingReportData = true; - onboardingInitialPath = ''; isOnboardingInProgress = false; + OnboardingFlow.clearInitialPath(); } export { onServerDataReady, isOnboardingFlowCompleted, setOnboardingPurposeSelected, - getOnboardingInitialPath, updateOnboardingLastVisitedPath, resetAllChecks, setOnboardingAdminsChatReportID, @@ -268,5 +243,4 @@ export { completeHybridAppOnboarding, handleHybridAppOnboarding, setOnboardingErrorMessage, - startOnboardingFlow, }; diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 64b8aebe19be..33367d28361d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -23,9 +23,9 @@ import * as ValidationUtils from '@libs/ValidationUtils'; import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Report from '@userActions/Report'; import * as Welcome from '@userActions/Welcome'; +import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/DisplayNameForm'; import type {BaseOnboardingPersonalDetailsOnyxProps, BaseOnboardingPersonalDetailsProps} from './types'; @@ -74,9 +74,7 @@ function BaseOnboardingPersonalDetails({ Welcome.setOnboardingAdminsChatReportID(); Welcome.setOnboardingPolicyID(); - // Navigate to HOME instead of dismissModal, because there is bug in small screen - // where the onboarding puropose page will be disaplayed briefly - Navigation.navigate(ROUTES.HOME); + Navigation.dismissModal(); // Only navigate to concierge chat when central pane is visible // Otherwise stay on the chats screen. @@ -136,7 +134,7 @@ function BaseOnboardingPersonalDetails({ From 514d56f239814ffcd2e795faed5cbea2ca99e571 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Tue, 3 Sep 2024 14:32:39 +0700 Subject: [PATCH 3/4] Fix lint errors Signed-off-by: Tsaqif --- src/libs/actions/Welcome/OnboardingFlow.ts | 1 - src/libs/actions/Welcome/index.ts | 11 ++--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts index 54d50aa44c67..72db88292110 100644 --- a/src/libs/actions/Welcome/OnboardingFlow.ts +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -125,4 +125,3 @@ function goBack() { } export {getOnboardingInitialPath, startOnboardingFlow, clearInitialPath, goBack}; -// export default adaptOnboardingRouteState; diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 80958b16c853..f5995aa1e2a9 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -3,26 +3,19 @@ import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import {WRITE_COMMANDS} from '@libs/API/types'; -import linkingConfig from '@libs/Navigation/linkingConfig'; -import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; -import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; -import getStateFromPath from '@navigation/getStateFromPath'; -import getAdaptedStateFromPath from '@navigation/linkingConfig/getAdaptedStateFromPath'; +import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import type {OnboardingPurposeType} from '@src/CONST'; -import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Route} from '@src/ROUTES'; import type Onboarding from '@src/types/onyx/Onboarding'; import type TryNewDot from '@src/types/onyx/TryNewDot'; -import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; +import * as OnboardingFlow from './OnboardingFlow'; type OnboardingData = Onboarding | [] | undefined; let isLoadingReportData = true; let tryNewDotData: TryNewDot | undefined; -let onboardingInitialPath = ''; let onboarding: OnboardingData; type HasCompletedOnboardingFlowProps = { From 5eed8099eedb5895038a53f8aba727fa3a13df0e Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Wed, 4 Sep 2024 23:23:19 +0700 Subject: [PATCH 4/4] Remove path parameter on work route Signed-off-by: Tsaqif --- src/libs/actions/Welcome/OnboardingFlow.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts index 72db88292110..4e780090299d 100644 --- a/src/libs/actions/Welcome/OnboardingFlow.ts +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -63,7 +63,6 @@ function adaptOnboardingRouteState() { { name: SCREENS.ONBOARDING.WORK, params: currentRoute?.params, - path: '/onboarding/works', }, {...currentRoute}, ],