From 3438e763142ac49eaddb15fc1662f9ee85c412c6 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 27 Jun 2024 13:33:15 +0200 Subject: [PATCH 01/19] Fix show onboarding modal functions --- src/libs/actions/Report.ts | 2 +- src/libs/actions/Welcome.ts | 16 ++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e528fde34ffe..7894107e12c1 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2553,7 +2553,7 @@ function openReportFromDeepLink(url: string, shouldNavigate = true) { return; } - Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + // Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); }); }); }); diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 82af0765e179..7f3cb71eba92 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -28,7 +28,7 @@ function onServerDataReady(): Promise { } function isOnboardingFlowCompleted({onCompleted, onNotCompleted}: HasCompletedOnboardingFlowProps) { - isOnboardingFlowStatusKnownPromise.then(() => { + isOnboardingFlowStatusKnownPromise.then((onboarding) => { if (Array.isArray(onboarding) || onboarding?.hasCompletedGuidedSetupFlow === undefined) { return; } @@ -41,17 +41,6 @@ function isOnboardingFlowCompleted({onCompleted, onNotCompleted}: HasCompletedOn }); } -/** - * Check if onboarding data is ready in order to check if the user has completed onboarding or not - */ -function checkOnboardingDataReady() { - if (onboarding === undefined) { - return; - } - - resolveOnboardingFlowStatus?.(); -} - /** * Check if user dismissed modal and if report data are loaded */ @@ -77,7 +66,6 @@ function setOnboardingPolicyID(policyID?: string) { Onyx.connect({ key: ONYXKEYS.NVP_ONBOARDING, - initWithStoredValues: false, callback: (value) => { if (value === undefined) { return; @@ -85,7 +73,7 @@ Onyx.connect({ onboarding = value; - checkOnboardingDataReady(); + resolveOnboardingFlowStatus(onboarding); }, }); From 0b0ece675908e665ab1391aef9359508692fa7aa Mon Sep 17 00:00:00 2001 From: zfurtak Date: Thu, 27 Jun 2024 17:38:42 +0200 Subject: [PATCH 02/19] Add util function to check if screen is from onboarding flow --- .../BottomTabBar/index.tsx | 2 +- .../BottomTabBar/index.website.tsx | 2 +- .../createCustomStackNavigator/CustomRouter.ts | 2 +- .../createCustomStackNavigator/index.tsx | 2 +- src/libs/Navigation/Navigation.ts | 2 +- src/libs/Navigation/dismissModalWithReport.ts | 2 +- src/libs/Navigation/getTopmostCentralPaneRoute.ts | 2 +- src/libs/Navigation/getTopmostReportActionID.ts | 2 +- src/libs/Navigation/getTopmostReportId.ts | 2 +- src/libs/Navigation/linkTo/index.ts | 2 +- .../linkingConfig/getAdaptedStateFromPath.ts | 2 +- src/libs/Navigation/switchPolicyID.ts | 2 +- src/libs/Navigation/types.ts | 3 +++ src/libs/NavigationUtils.ts | 14 ++++++++++++-- 14 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx index 472d2c7d6d29..7fdb8a19edc7 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx @@ -17,7 +17,7 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute' import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 9fe78273bdb0..87c7a2cf4619 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -17,7 +17,7 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute' import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index fa7e8a55d1fc..a1768df5e0d6 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -7,7 +7,7 @@ import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRo import linkingConfig from '@libs/Navigation/linkingConfig'; import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index 84123dbfa569..310766f80e9d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -9,7 +9,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import navigationRef from '@libs/Navigation/navigationRef'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; import CustomRouter from './CustomRouter'; import type {ResponsiveStackNavigatorProps, ResponsiveStackNavigatorRouterOptions} from './types'; diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 5a7182405681..f479e5cdd6de 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -4,7 +4,7 @@ import {CommonActions, getPathFromState, StackActions} from '@react-navigation/n import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index 1bb939f5230f..1579a0565726 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -4,7 +4,7 @@ import {StackActions} from '@react-navigation/native'; import {findLastIndex} from 'lodash'; import type {OnyxEntry} from 'react-native-onyx'; import Log from '@libs/Log'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import NAVIGATORS from '@src/NAVIGATORS'; diff --git a/src/libs/Navigation/getTopmostCentralPaneRoute.ts b/src/libs/Navigation/getTopmostCentralPaneRoute.ts index 977f23cd3cd7..5ac72281eaf6 100644 --- a/src/libs/Navigation/getTopmostCentralPaneRoute.ts +++ b/src/libs/Navigation/getTopmostCentralPaneRoute.ts @@ -1,4 +1,4 @@ -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import type {CentralPaneName, NavigationPartialRoute, RootStackParamList, State} from './types'; // Get the name of topmost central pane route in the navigation stack. diff --git a/src/libs/Navigation/getTopmostReportActionID.ts b/src/libs/Navigation/getTopmostReportActionID.ts index ade982c87b7d..d3c6e41887d8 100644 --- a/src/libs/Navigation/getTopmostReportActionID.ts +++ b/src/libs/Navigation/getTopmostReportActionID.ts @@ -1,5 +1,5 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; import type {RootStackParamList} from './types'; diff --git a/src/libs/Navigation/getTopmostReportId.ts b/src/libs/Navigation/getTopmostReportId.ts index 19bf24f1ba74..dc53d040f087 100644 --- a/src/libs/Navigation/getTopmostReportId.ts +++ b/src/libs/Navigation/getTopmostReportId.ts @@ -1,5 +1,5 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; import type {RootStackParamList} from './types'; diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 3c4608d6b5de..2c23cf573248 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -4,7 +4,7 @@ import {findFocusedRoute} from '@react-navigation/native'; import {omitBy} from 'lodash'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import extractPolicyIDsFromState from '@libs/Navigation/linkingConfig/extractPolicyIDsFromState'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import shallowCompare from '@libs/ObjectUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import getActionsFromPartialDiff from '@navigation/AppNavigator/getActionsFromPartialDiff'; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 17ea0e17d1b9..2b057bf5edaa 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -4,7 +4,7 @@ import type {TupleToUnion} from 'type-fest'; import {isAnonymousUser} from '@libs/actions/Session'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import type {BottomTabName, CentralPaneName, FullScreenName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 59461bfc3c8f..0f6477a9ee0e 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -3,7 +3,7 @@ import type {NavigationAction, NavigationContainerRef, NavigationState, PartialS import {getPathFromState} from '@react-navigation/native'; import type {Writable} from 'type-fest'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 6c4e03aa2018..68e8bae40b91 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -973,6 +973,8 @@ type FullScreenName = keyof FullScreenNavigatorParamList; type CentralPaneName = keyof CentralPaneScreensParamList; +type OnboardingFlowName = keyof OnboardingModalNavigatorParamList; + type SwitchPolicyIDParams = { policyID?: string; route?: Routes; @@ -1002,6 +1004,7 @@ export type { NewChatNavigatorParamList, NewTaskNavigatorParamList, OnboardingModalNavigatorParamList, + OnboardingFlowName, ParticipantsNavigatorParamList, PrivateNotesNavigatorParamList, ProfileNavigatorParamList, diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index 4fdc03c3d334..93e758369481 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -1,5 +1,5 @@ import SCREENS from '@src/SCREENS'; -import type {CentralPaneName} from './Navigation/types'; +import type {CentralPaneName, OnboardingFlowName} from './Navigation/types'; const CENTRAL_PANE_SCREEN_NAMES = new Set([ SCREENS.SETTINGS.WORKSPACES, @@ -15,6 +15,8 @@ const CENTRAL_PANE_SCREEN_NAMES = new Set([ SCREENS.REPORT, ]); +const ONBOARDING_SCREEN_NAMES = new Set([SCREENS.ONBOARDING.PERSONAL_DETAILS, SCREENS.ONBOARDING.PURPOSE, SCREENS.ONBOARDING.WORK, SCREENS.ONBOARDING_MODAL.ONBOARDING]); + function isCentralPaneName(screen: string | undefined): screen is CentralPaneName { if (!screen) { return false; @@ -23,4 +25,12 @@ function isCentralPaneName(screen: string | undefined): screen is CentralPaneNam return CENTRAL_PANE_SCREEN_NAMES.has(screen as CentralPaneName); } -export default isCentralPaneName; +function isOnboardingFlowName(screen: string | undefined): screen is OnboardingFlowName { + if (!screen) { + return false; + } + + return ONBOARDING_SCREEN_NAMES.has(screen as OnboardingFlowName); +} + +export {isCentralPaneName, isOnboardingFlowName}; From 4b7b93ab3261046af6d8b1d012007438b7f40bad Mon Sep 17 00:00:00 2001 From: zfurtak Date: Thu, 27 Jun 2024 17:58:26 +0200 Subject: [PATCH 03/19] Added back navigation blocking from onboarding screens --- .../BottomTabBar/index.website.tsx | 7 ++++- .../CustomRouter.ts | 27 ++++++++++++++++--- src/libs/actions/Report.ts | 12 +++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 87c7a2cf4619..fa8114c9913e 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -53,7 +53,12 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return; } - Welcome.isOnboardingFlowCompleted({onNotCompleted: () => Navigation.navigate(ROUTES.ONBOARDING_ROOT)}); + Welcome.isOnboardingFlowCompleted({ + onNotCompleted: () => { + Navigation.dismissModal(); + Navigation.navigate(ROUTES.ONBOARDING_PURPOSE); + }, + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoadingApp]); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index a1768df5e0d6..dc88610beb33 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -1,5 +1,5 @@ -import type {RouterConfigOptions, StackNavigationState} from '@react-navigation/native'; -import {getPathFromState, StackRouter} from '@react-navigation/native'; +import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigationState} from '@react-navigation/native'; +import {findFocusedRoute, getPathFromState, StackRouter} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/routers'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; @@ -7,7 +7,7 @@ import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRo import linkingConfig from '@libs/Navigation/linkingConfig'; import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; -import {isCentralPaneName} from '@libs/NavigationUtils'; +import {isCentralPaneName, isOnboardingFlowName} from '@libs/NavigationUtils'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; @@ -97,6 +97,21 @@ function compareAndAdaptState(state: StackNavigationState) { } } +function shouldPreventReset(state: StackNavigationState, action: CommonActions.Action | StackActionType) { + if (action.type !== 'RESET' || !action?.payload) { + return false; + } + const currentFocusedRoute = findFocusedRoute(state); + const targetFocusedRoute = findFocusedRoute(action?.payload); + // We want to prevent the user from navigating back to a non-onboarding screen if they are currently on an onboarding screen + if (isOnboardingFlowName(currentFocusedRoute?.name) && !isOnboardingFlowName(targetFocusedRoute?.name)) { + // We reset the URL as the browser sets it in a way that doesn't match the navigation state + // eslint-disable-next-line no-restricted-globals + history.replaceState({}, '', getPathFromState(state, linkingConfig.config)); + return true; + } +} + function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); @@ -107,6 +122,12 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); return state; }, + getStateForAction(state: StackNavigationState, action: CommonActions.Action | StackActionType, configOptions: RouterConfigOptions) { + if (shouldPreventReset(state, action)) { + return state; + } + return stackRouter.getStateForAction(state, action, configOptions); + }, }; } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7894107e12c1..71755c26a12d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1,3 +1,4 @@ +import {findFocusedRoute} from '@react-navigation/native'; import {format as timezoneFormat, utcToZonedTime} from 'date-fns-tz'; import {ExpensiMark, Str} from 'expensify-common'; import isEmpty from 'lodash/isEmpty'; @@ -58,7 +59,8 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import isPublicScreenRoute from '@libs/isPublicScreenRoute'; import * as Localize from '@libs/Localize'; import Log from '@libs/Log'; -import Navigation from '@libs/Navigation/Navigation'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import {isOnboardingFlowName} from '@libs/NavigationUtils'; import type {NetworkStatus} from '@libs/NetworkConnection'; import LocalNotification from '@libs/Notification/LocalNotification'; import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; @@ -2545,15 +2547,21 @@ function openReportFromDeepLink(url: string, shouldNavigate = true) { return; } + const state = navigationRef.getRootState(); + const currentFocusedRoute = findFocusedRoute(state); if (shouldSkipDeepLinkNavigation(route)) { return; } + if (isOnboardingFlowName(currentFocusedRoute?.name)) { + return; + } + if (!shouldNavigate) { return; } - // Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); }); }); }); From 4a31c728fedc4b34ba2d0904af71d39513731f9f Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 28 Jun 2024 09:38:26 +0200 Subject: [PATCH 04/19] Fix TS errors --- src/libs/actions/Welcome.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 7f3cb71eba92..95eec105b96d 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -5,7 +5,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type Onboarding from '@src/types/onyx/Onboarding'; import type OnyxPolicy from '@src/types/onyx/Policy'; -let onboarding: Onboarding | [] | undefined; +type OnboardingData = Onboarding | [] | undefined; + +let onboarding: OnboardingData; let isLoadingReportData = true; type HasCompletedOnboardingFlowProps = { @@ -13,13 +15,13 @@ type HasCompletedOnboardingFlowProps = { onNotCompleted?: () => void; }; -let resolveIsReadyPromise: (value?: Promise) => void | undefined; +let resolveIsReadyPromise: (value?: void) => void; let isServerDataReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; }); -let resolveOnboardingFlowStatus: (value?: Promise) => void | undefined; -let isOnboardingFlowStatusKnownPromise = new Promise((resolve) => { +let resolveOnboardingFlowStatus: (value?: OnboardingData) => void; +let isOnboardingFlowStatusKnownPromise = new Promise((resolve) => { resolveOnboardingFlowStatus = resolve; }); @@ -107,7 +109,7 @@ function resetAllChecks() { isServerDataReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; }); - isOnboardingFlowStatusKnownPromise = new Promise((resolve) => { + isOnboardingFlowStatusKnownPromise = new Promise((resolve) => { resolveOnboardingFlowStatus = resolve; }); onboarding = undefined; From f916987e04f29ba03e6bc4ff5bef281bb5a81255 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 28 Jun 2024 10:04:21 +0200 Subject: [PATCH 05/19] Fix lint --- src/libs/actions/Welcome.ts | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index dd298ff279f3..b883711df50c 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -14,7 +14,6 @@ import type TryNewDot from '@src/types/onyx/TryNewDot'; type OnboardingData = Onboarding | [] | undefined; -let onboarding: OnboardingData; let isLoadingReportData = true; let tryNewDotData: TryNewDot | undefined; @@ -104,22 +103,6 @@ function handleHybridAppOnboarding() { }); } -/** - * Check that a few requests have completed so that the welcome action can proceed: - * - * - Whether we are a first time new expensify user - * - Whether we have loaded all policies the server knows about - * - Whether we have loaded all reports the server knows about - * Check if onboarding data is ready in order to check if the user has completed onboarding or not - */ -function checkOnboardingDataReady() { - if (onboarding === undefined) { - return; - } - - resolveOnboardingFlowStatus?.(); -} - /** * Check if user dismissed modal and if report data are loaded @@ -190,9 +173,7 @@ Onyx.connect({ return; } - onboarding = value; - - resolveOnboardingFlowStatus(onboarding); + resolveOnboardingFlowStatus(value); }, }); @@ -237,7 +218,6 @@ function resetAllChecks() { isOnboardingFlowStatusKnownPromise = new Promise((resolve) => { resolveOnboardingFlowStatus = resolve; }); - onboarding = undefined; isLoadingReportData = true; } From 6a36309803da1dec7fa075913084008cd725fe2a Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 28 Jun 2024 12:57:43 +0200 Subject: [PATCH 06/19] Move error to onyx to handle back button errors --- src/ONYXKEYS.ts | 4 ++++ .../CustomRouter.ts | 3 +++ src/libs/actions/Report.ts | 2 ++ src/libs/actions/Welcome.ts | 7 +++++- .../BaseOnboardingPersonalDetails.tsx | 6 ++++- .../BaseOnboardingPurpose.tsx | 23 ++++++++++--------- src/pages/OnboardingPurpose/types.ts | 3 +++ 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index a15ae54cedea..d7c0972ce09c 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -311,6 +311,9 @@ const ONYXKEYS = { /** Onboarding Purpose selected by the user during Onboarding flow */ ONBOARDING_PURPOSE_SELECTED: 'onboardingPurposeSelected', + /** Onboarding error message to be displayed to the user */ + ONBOARDING_ERROR_MESSAGE: 'onboardingErrorMessage', + /** Onboarding policyID selected by the user during Onboarding flow */ ONBOARDING_POLICY_ID: 'onboardingPolicyID', @@ -728,6 +731,7 @@ type OnyxValuesMapping = { [ONYXKEYS.MAX_CANVAS_HEIGHT]: number; [ONYXKEYS.MAX_CANVAS_WIDTH]: number; [ONYXKEYS.ONBOARDING_PURPOSE_SELECTED]: string; + [ONYXKEYS.ONBOARDING_ERROR_MESSAGE]: string; [ONYXKEYS.ONBOARDING_POLICY_ID]: string; [ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID]: string; [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: boolean; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index dc88610beb33..7de7e22b9aa4 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -8,6 +8,7 @@ import linkingConfig from '@libs/Navigation/linkingConfig'; import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; import {isCentralPaneName, isOnboardingFlowName} from '@libs/NavigationUtils'; +import * as Welcome from '@userActions/Welcome'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; @@ -105,6 +106,8 @@ function shouldPreventReset(state: StackNavigationState, action: const targetFocusedRoute = findFocusedRoute(action?.payload); // We want to prevent the user from navigating back to a non-onboarding screen if they are currently on an onboarding screen if (isOnboardingFlowName(currentFocusedRoute?.name) && !isOnboardingFlowName(targetFocusedRoute?.name)) { + // TODO - add translation + Welcome.setOnboardingErrorMessage('Finish onboarding'); // We reset the URL as the browser sets it in a way that doesn't match the navigation state // eslint-disable-next-line no-restricted-globals history.replaceState({}, '', getPathFromState(state, linkingConfig.config)); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4b820b12e740..3a6e9ddcee36 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2555,6 +2555,8 @@ function openReportFromDeepLink(url: string) { } if (isOnboardingFlowName(currentFocusedRoute?.name)) { + // TODO - add translation + Welcome.setOnboardingErrorMessage('Finish onboarding'); return; } diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index b883711df50c..b95051225ed8 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -105,7 +105,7 @@ function handleHybridAppOnboarding() { /** -* Check if user dismissed modal and if report data are loaded +* Check if report data are loaded */ function checkServerDataReady() { if (isLoadingReportData) { @@ -130,6 +130,10 @@ function setOnboardingPurposeSelected(value: OnboardingPurposeType) { Onyx.set(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, value ?? null); } +function setOnboardingErrorMessage(value: string) { + Onyx.set(ONYXKEYS.ONBOARDING_ERROR_MESSAGE, value ?? null); +} + function setOnboardingAdminsChatReportID(adminsChatReportID?: string) { Onyx.set(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID, adminsChatReportID ?? null); } @@ -230,4 +234,5 @@ export { setOnboardingPolicyID, completeHybridAppOnboarding, handleHybridAppOnboarding, + setOnboardingErrorMessage, }; diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index ba90def232d5..e850eb9207f1 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -42,6 +42,10 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat useDisableModalDismissOnEscape(); + useEffect(() => { + Welcome.setOnboardingErrorMessage(''); + }, []); + const completeEngagement = useCallback( (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { const firstName = values.firstName.trim(); diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index 03a4b790bc5f..acdcaa600396 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -38,7 +38,7 @@ const menuIcons = { [CONST.ONBOARDING_CHOICES.LOOKING_AROUND]: Illustrations.Binoculars, }; -function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, onboardingPurposeSelected}: BaseOnboardingPurposeProps) { +function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, onboardingPurposeSelected, onboardingErrorMessage}: BaseOnboardingPurposeProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useOnboardingLayout(); @@ -83,8 +83,6 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS); }, [selectedPurpose]); - const [errorMessage, setErrorMessage] = useState(''); - const menuItems: MenuItemProps[] = Object.values(CONST.ONBOARDING_CHOICES).map((choice) => { const translationKey = `onboarding.purpose.${choice}` as const; const isSelected = selectedPurpose === choice; @@ -103,7 +101,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on numberOfLinesTitle: 0, onPress: () => { Welcome.setOnboardingPurposeSelected(choice); - setErrorMessage(''); + Welcome.setOnboardingErrorMessage(''); }, }; }); @@ -111,11 +109,11 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on const handleOuterClick = useCallback(() => { if (!selectedPurpose) { - setErrorMessage(translate('onboarding.purpose.errorSelection')); + Welcome.setOnboardingErrorMessage(translate('onboarding.purpose.errorSelection')); } else { - setErrorMessage(translate('onboarding.purpose.errorContinue')); + Welcome.setOnboardingErrorMessage(translate('onboarding.purpose.errorContinue')); } - }, [selectedPurpose, setErrorMessage, translate]); + }, [selectedPurpose, translate]); const onboardingLocalRef = useRef(null); useImperativeHandle(isFocused ? OnboardingRefManager.ref : onboardingLocalRef, () => ({handleOuterClick}), [handleOuterClick]); @@ -148,14 +146,14 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on buttonText={translate('common.continue')} onSubmit={() => { if (!selectedPurpose) { - setErrorMessage(translate('onboarding.purpose.errorSelection')); + Welcome.setOnboardingErrorMessage(translate('onboarding.purpose.errorSelection')); return; } - setErrorMessage(''); + Welcome.setOnboardingErrorMessage(''); saveAndNavigate(); }} - message={errorMessage} - isAlertVisible={!!errorMessage} + message={onboardingErrorMessage} + isAlertVisible={!!onboardingErrorMessage} containerStyles={[styles.w100, styles.mb5, styles.mh0, paddingHorizontal]} /> @@ -170,6 +168,9 @@ export default withOnyx; type BaseOnboardingPurposeOnyxProps = { /** Saved onboarding purpose selected by the user */ onboardingPurposeSelected: OnyxEntry; + + /** Saved error message to be displayed */ + onboardingErrorMessage: OnyxEntry; }; type BaseOnboardingPurposeProps = OnboardingPurposeProps & From f7857483e4e5dcef25a30e57262af99ee33887a3 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Mon, 1 Jul 2024 10:39:31 +0200 Subject: [PATCH 07/19] Add translations --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + .../AppNavigator/createCustomStackNavigator/CustomRouter.ts | 4 ++-- src/libs/actions/Report.ts | 3 +-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 095cc12a3896..13fe78002c43 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1433,6 +1433,7 @@ export default { title: 'What do you want to do today?', errorSelection: 'Please make a selection to continue.', errorContinue: 'Please press continue to get set up.', + errorBackButton: 'Finish onboarding so you can use the app.', [CONST.ONBOARDING_CHOICES.EMPLOYER]: 'Get paid back by my employer', [CONST.ONBOARDING_CHOICES.MANAGE_TEAM]: "Manage my team's expenses", [CONST.ONBOARDING_CHOICES.PERSONAL_SPEND]: 'Track and budget expenses', diff --git a/src/languages/es.ts b/src/languages/es.ts index e2a00222c62a..4f0f9e0f0b81 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1434,6 +1434,7 @@ export default { title: '¿Qué quieres hacer hoy?', errorSelection: 'Por favor selecciona una opción para continuar.', errorContinue: 'Por favor, haz click en continuar para configurar tu cuenta.', + errorBackButton: 'Finalizar la integración para poder utilizar la aplicación.', [CONST.ONBOARDING_CHOICES.EMPLOYER]: 'Cobrar de mi empresa', [CONST.ONBOARDING_CHOICES.MANAGE_TEAM]: 'Gestionar los gastos de mi equipo', [CONST.ONBOARDING_CHOICES.PERSONAL_SPEND]: 'Controlar y presupuestar gastos', diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 7de7e22b9aa4..b83c9e59a0c2 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -2,6 +2,7 @@ import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigatio import {findFocusedRoute, getPathFromState, StackRouter} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/routers'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; +import * as Localize from '@libs/Localize'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import linkingConfig from '@libs/Navigation/linkingConfig'; @@ -106,8 +107,7 @@ function shouldPreventReset(state: StackNavigationState, action: const targetFocusedRoute = findFocusedRoute(action?.payload); // We want to prevent the user from navigating back to a non-onboarding screen if they are currently on an onboarding screen if (isOnboardingFlowName(currentFocusedRoute?.name) && !isOnboardingFlowName(targetFocusedRoute?.name)) { - // TODO - add translation - Welcome.setOnboardingErrorMessage('Finish onboarding'); + Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); // We reset the URL as the browser sets it in a way that doesn't match the navigation state // eslint-disable-next-line no-restricted-globals history.replaceState({}, '', getPathFromState(state, linkingConfig.config)); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3a6e9ddcee36..f4599bc3a2ef 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2555,8 +2555,7 @@ function openReportFromDeepLink(url: string) { } if (isOnboardingFlowName(currentFocusedRoute?.name)) { - // TODO - add translation - Welcome.setOnboardingErrorMessage('Finish onboarding'); + Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); return; } From 32084b6fd8f573c686eb71cefa112ee34838abe7 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 2 Jul 2024 17:26:34 +0200 Subject: [PATCH 08/19] generate onboarding state for initial state --- src/libs/Navigation/NavigationRoot.tsx | 49 ++++++++++++++++++-------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index dd3a2890d0ec..21b992d10a1b 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -1,6 +1,7 @@ import type {NavigationState} from '@react-navigation/native'; import {DefaultTheme, findFocusedRoute, NavigationContainer} from '@react-navigation/native'; import React, {useContext, useEffect, useMemo, useRef} from 'react'; +import {useOnyx} from 'react-native-onyx'; import HybridAppMiddleware from '@components/HybridAppMiddleware'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; @@ -12,7 +13,9 @@ import Log from '@libs/Log'; import {getPathFromURL} from '@libs/Url'; import {updateLastVisitedPath} from '@userActions/App'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; +import ROUTES from '@src/ROUTES'; import AppNavigator from './AppNavigator'; import getPolicyIDFromState from './getPolicyIDFromState'; import linkingConfig from './linkingConfig'; @@ -77,25 +80,43 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const {isSmallScreenWidth} = useWindowDimensions(); const {setActiveWorkspaceID} = useActiveWorkspace(); - const initialState = useMemo( - () => { - if (!lastVisitedPath) { - return undefined; + const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { + selector: (onboarding) => { + // onboarding is an array for old accounts and accounts created from olddot + if (Array.isArray(onboarding)) { + return true; } + return onboarding?.hasCompletedGuidedSetupFlow; + }, + }); - const path = initialUrl ? getPathFromURL(initialUrl) : null; + const initialState = useMemo(() => { + // If the user haven't completed the flow, we want to always redirect them to the onboarding flow. + if (!hasCompletedGuidedSetupFlow) { + const {adaptedState} = getAdaptedStateFromPath(ROUTES.ONBOARDING_ROOT, linkingConfig.config); + return adaptedState; + } - // For non-nullable paths we don't want to set initial state - if (path) { - return; - } + // If there is no lastVisitedPath, we can do early return. We won't modify the default behavior. + if (!lastVisitedPath) { + return undefined; + } - const {adaptedState} = getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config); - return adaptedState; - }, + const path = initialUrl ? getPathFromURL(initialUrl) : null; + + // 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; + } + + // Otherwise we want to redirect the user to the last visited path. + const {adaptedState} = getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config); + return adaptedState; + + // The initialState value is relevant only on the first render. // eslint-disable-next-line react-hooks/exhaustive-deps - [], - ); + }, []); // https://reactnavigation.org/docs/themes const navigationTheme = useMemo( From ca9dfe9e3df0b8751c081c6247e4b16f84792807 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 2 Jul 2024 17:42:15 +0200 Subject: [PATCH 09/19] improve redirecting to onboarding after logging in --- .../BottomTabBar/index.website.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index fa8114c9913e..9087ca7290cc 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -15,7 +15,9 @@ import * as Session from '@libs/actions/Session'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import Navigation from '@libs/Navigation/Navigation'; +import linkingConfig from '@libs/Navigation/linkingConfig'; +import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; import {isCentralPaneName} from '@libs/NavigationUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; @@ -55,8 +57,8 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps Welcome.isOnboardingFlowCompleted({ onNotCompleted: () => { - Navigation.dismissModal(); - Navigation.navigate(ROUTES.ONBOARDING_PURPOSE); + const {adaptedState} = getAdaptedStateFromPath(ROUTES.ONBOARDING_ROOT, linkingConfig.config); + navigationRef.resetRoot(adaptedState); }, }); // eslint-disable-next-line react-hooks/exhaustive-deps From db1d22ad458153202954ef2d28f59ed193824029 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 2 Jul 2024 17:43:22 +0200 Subject: [PATCH 10/19] prevent deeplinks if user should see onboarding after login --- src/libs/actions/Report.ts | 70 ++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f4599bc3a2ef..1b750ec54f58 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2535,35 +2535,47 @@ function openReportFromDeepLink(url: string) { // Navigate to the report after sign-in/sign-up. InteractionManager.runAfterInteractions(() => { Session.waitForUserSignIn().then(() => { - Navigation.waitForProtectedRoutes().then(() => { - if (route && Session.isAnonymousUser() && !Session.canAnonymousUserAccessRoute(route)) { - Session.signOutAndRedirectToSignIn(true); - return; - } - - // We don't want to navigate to the exitTo route when creating a new workspace from a deep link, - // because we already handle creating the optimistic policy and navigating to it in App.setUpPoliciesAndNavigate, - // which is already called when AuthScreens mounts. - if (new URL(url).searchParams.get('exitTo') === ROUTES.WORKSPACE_NEW) { - return; - } - - const state = navigationRef.getRootState(); - const currentFocusedRoute = findFocusedRoute(state); - if (shouldSkipDeepLinkNavigation(route)) { - return; - } - - if (isOnboardingFlowName(currentFocusedRoute?.name)) { - Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); - return; - } - - if (isAuthenticated) { - return; - } - - Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + Onyx.connect({ + key: ONYXKEYS.NVP_ONBOARDING, + callback: (onboarding) => { + Navigation.waitForProtectedRoutes().then(() => { + if (route && Session.isAnonymousUser() && !Session.canAnonymousUserAccessRoute(route)) { + Session.signOutAndRedirectToSignIn(true); + return; + } + + // We don't want to navigate to the exitTo route when creating a new workspace from a deep link, + // because we already handle creating the optimistic policy and navigating to it in App.setUpPoliciesAndNavigate, + // which is already called when AuthScreens mounts. + if (new URL(url).searchParams.get('exitTo') === ROUTES.WORKSPACE_NEW) { + return; + } + + if (shouldSkipDeepLinkNavigation(route)) { + return; + } + + const state = navigationRef.getRootState(); + const currentFocusedRoute = findFocusedRoute(state); + const hasCompletedGuidedSetupFlow = Array.isArray(onboarding) || onboarding?.hasCompletedGuidedSetupFlow; + + // We need skip deeplinking if the user hasn't completed the guided setup flow. + if (!hasCompletedGuidedSetupFlow) { + return; + } + + if (isOnboardingFlowName(currentFocusedRoute?.name)) { + Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); + return; + } + + if (isAuthenticated) { + return; + } + + Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + }); + }, }); }); }); From 5e6f56a3598df2938066ce7add246adbad916d55 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Thu, 4 Jul 2024 14:00:18 +0200 Subject: [PATCH 11/19] move useDisableModalDismissOnEscape to top level onboarding navigator --- .../AppNavigator/Navigators/OnboardingModalNavigator.tsx | 3 +++ .../BaseOnboardingPersonalDetails.tsx | 3 --- src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 3 --- src/pages/OnboardingWork/BaseOnboardingWork.tsx | 3 --- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx index 29a2205b2e37..a7e4a7da7761 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx @@ -18,6 +18,7 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; +import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; import Overlay from './Overlay'; const Stack = createStackNavigator(); @@ -34,6 +35,8 @@ function OnboardingModalNavigator() { return onboarding?.hasCompletedGuidedSetupFlow; }, }); + + useDisableModalDismissOnEscape(); useEffect(() => { if (!hasCompletedGuidedSetupFlow) { diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index e850eb9207f1..5f4e48545932 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -12,7 +12,6 @@ import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; -import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; import useLocalize from '@hooks/useLocalize'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -40,8 +39,6 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [shouldValidateOnChange, setShouldValidateOnChange] = useState(false); const {accountID} = useSession(); - useDisableModalDismissOnEscape(); - useEffect(() => { Welcome.setOnboardingErrorMessage(''); }, []); diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index acdcaa600396..c7ca45f59847 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -13,7 +13,6 @@ import MenuItemList from '@components/MenuItemList'; import OfflineIndicator from '@components/OfflineIndicator'; import SafeAreaConsumer from '@components/SafeAreaConsumer'; import Text from '@components/Text'; -import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; import useLocalize from '@hooks/useLocalize'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useTheme from '@hooks/useTheme'; @@ -46,8 +45,6 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on const {isSmallScreenWidth, windowHeight} = useWindowDimensions(); const theme = useTheme(); - useDisableModalDismissOnEscape(); - const PurposeFooterInstance = ; useEffect(() => { diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index 9b8824300d30..14f9223f6c67 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -9,7 +9,6 @@ import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import OfflineIndicator from '@components/OfflineIndicator'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; -import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; import useLocalize from '@hooks/useLocalize'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -33,8 +32,6 @@ function BaseOnboardingWork({shouldUseNativeStyles, onboardingPurposeSelected, o const {isSmallScreenWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useOnboardingLayout(); - useDisableModalDismissOnEscape(); - const completeEngagement = useCallback( (values: FormOnyxValues<'onboardingWorkForm'>) => { if (!onboardingPurposeSelected) { From e2598f20d4c5a95b62a429279e86c0e8ece3e840 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 4 Jul 2024 17:57:30 +0200 Subject: [PATCH 12/19] Add confirmed translation --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 7b88388d43a1..3fc513b46cc6 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1465,7 +1465,7 @@ export default { title: '¿Qué quieres hacer hoy?', errorSelection: 'Por favor selecciona una opción para continuar.', errorContinue: 'Por favor, haz click en continuar para configurar tu cuenta.', - errorBackButton: 'Finalizar la integración para poder utilizar la aplicación.', + errorBackButton: 'Por favor, finaliza las preguntas de configuración para empezar a utilizar la aplicación.', [CONST.ONBOARDING_CHOICES.EMPLOYER]: 'Cobrar de mi empresa', [CONST.ONBOARDING_CHOICES.MANAGE_TEAM]: 'Gestionar los gastos de mi equipo', [CONST.ONBOARDING_CHOICES.PERSONAL_SPEND]: 'Controlar y presupuestar gastos', From 4c35ff1ba6181791cda36015457f7e404cb1b283 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 4 Jul 2024 19:12:59 +0200 Subject: [PATCH 13/19] Fix lint --- .../AppNavigator/Navigators/OnboardingModalNavigator.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx index a7e4a7da7761..fa39be1c78de 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx @@ -4,6 +4,7 @@ import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import NoDropZone from '@components/DragAndDrop/NoDropZone'; import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; +import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -18,7 +19,6 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; -import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; import Overlay from './Overlay'; const Stack = createStackNavigator(); @@ -35,7 +35,7 @@ function OnboardingModalNavigator() { return onboarding?.hasCompletedGuidedSetupFlow; }, }); - + useDisableModalDismissOnEscape(); useEffect(() => { From 3079bc7da67418c7831a4d62be2c9c4fdafd72d0 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 5 Jul 2024 13:56:15 +0200 Subject: [PATCH 14/19] Fix jest tests --- tests/ui/UnreadIndicatorsTest.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index b5990ee5d002..5b1685c10efe 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -134,6 +134,10 @@ beforeAll(() => { }); }); +beforeEach(() => { + Onyx.set(ONYXKEYS.NVP_ONBOARDING, {hasCompletedGuidedSetupFlow: true}); +}); + function scrollUpToRevealNewMessagesBadge() { const hintText = Localize.translateLocal('sidebarScreen.listOfChatMessages'); fireEvent.scroll(screen.getByLabelText(hintText), { From 063373d410cdae430216e63c74cf2a4f467030f9 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 5 Jul 2024 15:26:02 +0200 Subject: [PATCH 15/19] apply CR suggestions --- src/CONST.ts | 4 ++++ .../CustomRouter.ts | 3 ++- .../BaseOnboardingPurpose.tsx | 17 +++++-------- src/pages/OnboardingPurpose/types.ts | 24 +++++-------------- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 4e7557e0b4ba..8607839e9e35 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5128,6 +5128,10 @@ const CONST = { DATE: 'date', LIST: 'dropdown', }, + + NAVIGATION_ACTIONS: { + RESET: 'RESET', + }, } as const; type Country = keyof typeof CONST.ALL_COUNTRIES; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index b83c9e59a0c2..dce70b70a059 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -10,6 +10,7 @@ import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedSt import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; import {isCentralPaneName, isOnboardingFlowName} from '@libs/NavigationUtils'; import * as Welcome from '@userActions/Welcome'; +import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; @@ -100,7 +101,7 @@ function compareAndAdaptState(state: StackNavigationState) { } function shouldPreventReset(state: StackNavigationState, action: CommonActions.Action | StackActionType) { - if (action.type !== 'RESET' || !action?.payload) { + if (action.type !== CONST.NAVIGATION_ACTIONS.RESET || !action?.payload) { return false; } const currentFocusedRoute = findFocusedRoute(state); diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index c7ca45f59847..3e8f71455170 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {ScrollView} from 'react-native-gesture-handler'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; @@ -27,7 +27,7 @@ import type {OnboardingPurposeType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {BaseOnboardingPurposeOnyxProps, BaseOnboardingPurposeProps} from './types'; +import type {BaseOnboardingPurposeProps} from './types'; const menuIcons = { [CONST.ONBOARDING_CHOICES.EMPLOYER]: Illustrations.ReceiptUpload, @@ -37,13 +37,15 @@ const menuIcons = { [CONST.ONBOARDING_CHOICES.LOOKING_AROUND]: Illustrations.Binoculars, }; -function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, onboardingPurposeSelected, onboardingErrorMessage}: BaseOnboardingPurposeProps) { +function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight}: BaseOnboardingPurposeProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useOnboardingLayout(); const [selectedPurpose, setSelectedPurpose] = useState(undefined); const {isSmallScreenWidth, windowHeight} = useWindowDimensions(); const theme = useTheme(); + const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); + const [onboardingErrorMessage] = useOnyx(ONYXKEYS.ONBOARDING_ERROR_MESSAGE); const PurposeFooterInstance = ; @@ -161,13 +163,6 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on BaseOnboardingPurpose.displayName = 'BaseOnboardingPurpose'; -export default withOnyx({ - onboardingPurposeSelected: { - key: ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, - }, - onboardingErrorMessage: { - key: ONYXKEYS.ONBOARDING_ERROR_MESSAGE, - }, -})(BaseOnboardingPurpose); +export default BaseOnboardingPurpose; export type {BaseOnboardingPurposeProps}; diff --git a/src/pages/OnboardingPurpose/types.ts b/src/pages/OnboardingPurpose/types.ts index bc658b4d7299..17970dbab9a6 100644 --- a/src/pages/OnboardingPurpose/types.ts +++ b/src/pages/OnboardingPurpose/types.ts @@ -1,23 +1,11 @@ -import type {OnyxEntry} from 'react-native-onyx'; -import type {OnboardingPurposeType} from '@src/CONST'; - type OnboardingPurposeProps = Record; -type BaseOnboardingPurposeOnyxProps = { - /** Saved onboarding purpose selected by the user */ - onboardingPurposeSelected: OnyxEntry; +type BaseOnboardingPurposeProps = OnboardingPurposeProps & { + /* Whether to use native styles tailored for native devices */ + shouldUseNativeStyles: boolean; - /** Saved error message to be displayed */ - onboardingErrorMessage: OnyxEntry; + /** Whether to use the maxHeight (true) or use the 100% of the height (false) */ + shouldEnableMaxHeight: boolean; }; -type BaseOnboardingPurposeProps = OnboardingPurposeProps & - BaseOnboardingPurposeOnyxProps & { - /* Whether to use native styles tailored for native devices */ - shouldUseNativeStyles: boolean; - - /** Whether to use the maxHeight (true) or use the 100% of the height (false) */ - shouldEnableMaxHeight: boolean; - }; - -export type {BaseOnboardingPurposeOnyxProps, BaseOnboardingPurposeProps, OnboardingPurposeProps}; +export type {BaseOnboardingPurposeProps, OnboardingPurposeProps}; From 30344f3caeadb3c6f0281b22d488a2c39bc02672 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Mon, 8 Jul 2024 12:33:52 +0200 Subject: [PATCH 16/19] Fix focus on Purpose page --- src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index 3e8f71455170..7304c1822ae9 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -27,6 +27,7 @@ import type {OnboardingPurposeType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import type {BaseOnboardingPurposeProps} from './types'; const menuIcons = { @@ -44,8 +45,8 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight}: B const [selectedPurpose, setSelectedPurpose] = useState(undefined); const {isSmallScreenWidth, windowHeight} = useWindowDimensions(); const theme = useTheme(); - const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); - const [onboardingErrorMessage] = useOnyx(ONYXKEYS.ONBOARDING_ERROR_MESSAGE); + const [onboardingPurposeSelected, onboardingPurposeSelectedResult] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); + const [onboardingErrorMessage, onboardingErrorMessageResult] = useOnyx(ONYXKEYS.ONBOARDING_ERROR_MESSAGE); const PurposeFooterInstance = ; @@ -117,6 +118,9 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight}: B const onboardingLocalRef = useRef(null); useImperativeHandle(isFocused ? OnboardingRefManager.ref : onboardingLocalRef, () => ({handleOuterClick}), [handleOuterClick]); + if (isLoadingOnyxValue(onboardingPurposeSelectedResult, onboardingErrorMessageResult)) { + return null; + } return ( {({safeAreaPaddingBottomStyle}) => ( From 84075e488b899616d82f64999c29e4063834764b Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 9 Jul 2024 13:58:04 +0200 Subject: [PATCH 17/19] Fix english text --- src/languages/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 80bb4a3b5af2..a1153ec64019 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1462,7 +1462,7 @@ export default { title: 'What do you want to do today?', errorSelection: 'Please make a selection to continue.', errorContinue: 'Please press continue to get set up.', - errorBackButton: 'Finish onboarding so you can use the app.', + errorBackButton: 'Please finish the setup questions to start using the app.', [CONST.ONBOARDING_CHOICES.EMPLOYER]: 'Get paid back by my employer', [CONST.ONBOARDING_CHOICES.MANAGE_TEAM]: "Manage my team's expenses", [CONST.ONBOARDING_CHOICES.PERSONAL_SPEND]: 'Track and budget expenses', From ed75b6960f688a53bdb8863388ed9a04e0061a55 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 9 Jul 2024 14:05:18 +0200 Subject: [PATCH 18/19] Fix comments styling --- .../AppNavigator/createCustomStackNavigator/CustomRouter.ts | 1 + src/libs/actions/Welcome.ts | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index dce70b70a059..5b3cefb63a2d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -106,6 +106,7 @@ function shouldPreventReset(state: StackNavigationState, action: } const currentFocusedRoute = findFocusedRoute(state); const targetFocusedRoute = findFocusedRoute(action?.payload); + // We want to prevent the user from navigating back to a non-onboarding screen if they are currently on an onboarding screen if (isOnboardingFlowName(currentFocusedRoute?.name) && !isOnboardingFlowName(targetFocusedRoute?.name)) { Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 2c62987e6e87..b592424cfcdf 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -103,8 +103,7 @@ function handleHybridAppOnboarding() { } /** - -* Check if report data are loaded + * Check if report data are loaded */ function checkServerDataReady() { if (isLoadingReportData) { From 7316347a162a63db2b92c507bbb26a1215dc0cd3 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 9 Jul 2024 17:27:47 +0200 Subject: [PATCH 19/19] add hasCompletedGuidedSetupFlowSelector --- .../Navigators/OnboardingModalNavigator.tsx | 9 ++------- src/libs/Navigation/NavigationRoot.tsx | 9 ++------- src/libs/actions/Report.ts | 3 ++- src/libs/hasCompletedGuidedSetupFlowSelector.ts | 12 ++++++++++++ 4 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 src/libs/hasCompletedGuidedSetupFlowSelector.ts diff --git a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx index fa39be1c78de..61adcd77da76 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx @@ -8,6 +8,7 @@ import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscap import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector'; import OnboardingModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/OnboardingModalNavigatorScreenOptions'; import Navigation from '@libs/Navigation/Navigation'; import type {OnboardingModalNavigatorParamList} from '@libs/Navigation/types'; @@ -27,13 +28,7 @@ function OnboardingModalNavigator() { const styles = useThemeStyles(); const {shouldUseNarrowLayout} = useOnboardingLayout(); const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { - selector: (onboarding) => { - // onboarding is an array for old accounts and accounts created from olddot - if (Array.isArray(onboarding)) { - return true; - } - return onboarding?.hasCompletedGuidedSetupFlow; - }, + selector: hasCompletedGuidedSetupFlowSelector, }); useDisableModalDismissOnEscape(); diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 62572a3a0301..c197fa702bfb 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -9,6 +9,7 @@ import useCurrentReportID from '@hooks/useCurrentReportID'; import useTheme from '@hooks/useTheme'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {FSPage} from '@libs/Fullstory'; +import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector'; import Log from '@libs/Log'; import {getPathFromURL} from '@libs/Url'; import {updateLastVisitedPath} from '@userActions/App'; @@ -81,13 +82,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N const {setActiveWorkspaceID} = useActiveWorkspace(); const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { - selector: (onboarding) => { - // onboarding is an array for old accounts and accounts created from olddot - if (Array.isArray(onboarding)) { - return true; - } - return onboarding?.hasCompletedGuidedSetupFlow; - }, + selector: hasCompletedGuidedSetupFlowSelector, }); const initialState = useMemo(() => { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 6d1f33963a88..45bdca8e0fc7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -56,6 +56,7 @@ import {prepareDraftComment} from '@libs/DraftCommentUtils'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as Environment from '@libs/Environment/Environment'; import * as ErrorUtils from '@libs/ErrorUtils'; +import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector'; import isPublicScreenRoute from '@libs/isPublicScreenRoute'; import * as Localize from '@libs/Localize'; import Log from '@libs/Log'; @@ -2573,7 +2574,7 @@ function openReportFromDeepLink(url: string) { const state = navigationRef.getRootState(); const currentFocusedRoute = findFocusedRoute(state); - const hasCompletedGuidedSetupFlow = Array.isArray(onboarding) || onboarding?.hasCompletedGuidedSetupFlow; + const hasCompletedGuidedSetupFlow = hasCompletedGuidedSetupFlowSelector(onboarding); // We need skip deeplinking if the user hasn't completed the guided setup flow. if (!hasCompletedGuidedSetupFlow) { diff --git a/src/libs/hasCompletedGuidedSetupFlowSelector.ts b/src/libs/hasCompletedGuidedSetupFlowSelector.ts new file mode 100644 index 000000000000..83cde0a0be8c --- /dev/null +++ b/src/libs/hasCompletedGuidedSetupFlowSelector.ts @@ -0,0 +1,12 @@ +import type {OnyxValue} from 'react-native-onyx'; +import type ONYXKEYS from '@src/ONYXKEYS'; + +function hasCompletedGuidedSetupFlowSelector(onboarding: OnyxValue): boolean { + // onboarding is an array for old accounts and accounts created from olddot + if (Array.isArray(onboarding)) { + return true; + } + return onboarding?.hasCompletedGuidedSetupFlow ?? false; +} + +export default hasCompletedGuidedSetupFlowSelector;