diff --git a/src/Expensify.tsx b/src/Expensify.tsx
index 62e7839b21f0..f5d4655c4861 100644
--- a/src/Expensify.tsx
+++ b/src/Expensify.tsx
@@ -20,7 +20,6 @@ import {updateLastRoute} from './libs/actions/App';
import * as EmojiPickerAction from './libs/actions/EmojiPickerAction';
import * as Report from './libs/actions/Report';
import * as User from './libs/actions/User';
-import {handleHybridAppOnboarding} from './libs/actions/Welcome';
import * as ActiveClientManager from './libs/ActiveClientManager';
import FS from './libs/Fullstory';
import * as Growl from './libs/Growl';
@@ -99,7 +98,6 @@ function Expensify({
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [lastRoute] = useOnyx(ONYXKEYS.LAST_ROUTE);
- const [tryNewDotData] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT);
const [shouldShowRequire2FAModal, setShouldShowRequire2FAModal] = useState(false);
useEffect(() => {
@@ -118,14 +116,6 @@ function Expensify({
setAttemptedToOpenPublicRoom(true);
}, [isCheckingPublicRoom]);
- useEffect(() => {
- if (splashScreenState !== CONST.BOOT_SPLASH_STATE.HIDDEN || tryNewDotData === undefined) {
- return;
- }
-
- handleHybridAppOnboarding();
- }, [splashScreenState, tryNewDotData]);
-
const isAuthenticated = useMemo(() => !!(session?.authToken ?? null), [session]);
const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]);
diff --git a/src/components/ExplanationModal.tsx b/src/components/ExplanationModal.tsx
index 73290c43d39a..d846dd4d28ba 100644
--- a/src/components/ExplanationModal.tsx
+++ b/src/components/ExplanationModal.tsx
@@ -1,30 +1,12 @@
-import React, {useCallback} from 'react';
+import React from 'react';
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';
function ExplanationModal() {
const {translate} = useLocalize();
- const onClose = useCallback(() => {
- Welcome.completeHybridAppOnboarding();
-
- // We need to check if standard NewDot onboarding is completed.
- Welcome.isOnboardingFlowCompleted({
- onNotCompleted: () => {
- setTimeout(() => {
- Navigation.isNavigationReady().then(() => {
- OnboardingFlow.startOnboardingFlow();
- });
- }, variables.welcomeVideoDelay);
- },
- });
- }, []);
-
return (
);
}
diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx
index 322f28aa246d..b2068bfe1b1b 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.tsx
+++ b/src/components/LHNOptionsList/OptionRowLHN.tsx
@@ -21,7 +21,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import DomUtils from '@libs/DomUtils';
-import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector';
+import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import Parser from '@libs/Parser';
import Performance from '@libs/Performance';
@@ -47,7 +47,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID || -1}`);
const [isFirstTimeNewExpensifyUser] = useOnyx(ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER);
- const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
+ const [isOnboardingCompleted = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
});
const [shouldHideGBRTooltip] = useOnyx(ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP, {initialValue: true});
@@ -171,12 +171,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
>
{
+ if (NativeModules.HybridAppModule) {
+ // When user is transitioning from OldDot to NewDot, we usually show the explanation modal
+ if (isHybridAppOnboardingCompleted === false) {
+ Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT);
+ }
+
+ // But if the hybrid app onboarding is completed, but NewDot onboarding is not completed, we start NewDot onboarding flow
+ // This is a special case when user created an account from NewDot without finishing the onboarding flow and then logged in from OldDot
+ if (isHybridAppOnboardingCompleted === true && isOnboardingCompleted === false) {
+ OnboardingFlow.startOnboardingFlow();
+ }
+ }
+
+ // If the user is not transitioning from OldDot to NewDot, we should start NewDot onboarding flow if it's not completed yet
+ if (!NativeModules.HybridAppModule && isOnboardingCompleted === false) {
+ OnboardingFlow.startOnboardingFlow();
+ }
+ }, [isOnboardingCompleted, isHybridAppOnboardingCompleted]);
+
+ return {isOnboardingCompleted, isHybridAppOnboardingCompleted};
+}
+
+export default useOnboardingFlowRouter;
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
index dee84a4f201f..ccc8290a93c3 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
@@ -9,6 +9,7 @@ import OptionsListContextProvider from '@components/OptionListContextProvider';
import {SearchContextProvider} from '@components/Search/SearchContext';
import SearchRouter from '@components/Search/SearchRouter/SearchRouter';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
+import useOnboardingFlowRouter from '@hooks/useOnboardingFlow';
import usePermissions from '@hooks/usePermissions';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
@@ -237,7 +238,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
);
const modal = useRef({});
const [didPusherInit, setDidPusherInit] = useState(false);
-
+ const {isOnboardingCompleted} = useOnboardingFlowRouter();
let initialReportID: string | undefined;
const isInitialRender = useRef(true);
if (isInitialRender.current) {
@@ -526,17 +527,19 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
options={onboardingModalScreenOptions}
component={WelcomeVideoModalNavigator}
/>
- {
- Modal.setDisableDismissOnEscape(true);
- },
- beforeRemove: () => Modal.setDisableDismissOnEscape(false),
- }}
- />
+ {isOnboardingCompleted === false && (
+ {
+ Modal.setDisableDismissOnEscape(true);
+ },
+ beforeRemove: () => Modal.setDisableDismissOnEscape(false),
+ }}
+ />
+ )}
();
function OnboardingModalNavigator() {
const styles = useThemeStyles();
const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout();
- const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
- selector: hasCompletedGuidedSetupFlowSelector,
- });
- const {shouldUseNarrowLayout} = useResponsiveLayout();
-
- useEffect(() => {
- if (!hasCompletedGuidedSetupFlow) {
- return;
- }
- Navigation.isNavigationReady().then(() => {
- // On small screens, pop all navigation states and go back to HOME.
- // On large screens, need to go back to previous route and then redirect to Concierge,
- // otherwise going back on Concierge will go to onboarding and then redirected to Concierge again
- if (shouldUseNarrowLayout) {
- Navigation.setShouldPopAllStateOnUP(true);
- Navigation.goBack(ROUTES.HOME, true, true);
- } else {
- Navigation.goBack();
- Report.navigateToConciergeChat();
- }
- });
- }, [hasCompletedGuidedSetupFlow, shouldUseNarrowLayout]);
-
const outerViewRef = React.useRef(null);
const handleOuterClick = useCallback(() => {
@@ -58,9 +29,6 @@ function OnboardingModalNavigator() {
useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ESCAPE, handleOuterClick, {shouldBubble: true});
- if (hasCompletedGuidedSetupFlow) {
- return null;
- }
return (
diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
index 5befa446f6f9..d718de95861a 100644
--- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
@@ -1,6 +1,5 @@
-import {useNavigation} from '@react-navigation/native';
import React, {memo, useCallback, useEffect, useState} from 'react';
-import {NativeModules, View} from 'react-native';
+import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
@@ -11,11 +10,9 @@ import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
-import * as Session from '@libs/actions/Session';
import interceptAnonymousUser from '@libs/interceptAnonymousUser';
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList, RootStackParamList, State} from '@libs/Navigation/types';
-import {isCentralPaneName} from '@libs/NavigationUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as SearchUtils from '@libs/SearchUtils';
import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
@@ -24,10 +21,7 @@ import navigationRef from '@navigation/navigationRef';
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';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
@@ -69,9 +63,7 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
- const navigation = useNavigation();
const {activeWorkspaceID} = useActiveWorkspace();
- const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP);
const transactionViolations = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const [chatTabBrickRoad, setChatTabBrickRoad] = useState(getChatTabBrickRoad(activeWorkspaceID));
@@ -79,28 +71,6 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) {
setChatTabBrickRoad(getChatTabBrickRoad(activeWorkspaceID));
}, [activeWorkspaceID, transactionViolations]);
- useEffect(() => {
- const navigationState = navigation.getState() as State | undefined;
- const routes = navigationState?.routes;
- const currentRoute = routes?.[navigationState?.index ?? 0];
- // When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method.
- // To prevent this, the value of the bottomTabRoute?.name is checked here
- if (!!(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && !isCentralPaneName(currentRoute.name)) || Session.isAnonymousUser()) {
- return;
- }
-
- // HybridApp has own entry point when we decide whether to display onboarding and explanation modal.
- if (NativeModules.HybridAppModule) {
- return;
- }
-
- Welcome.isOnboardingFlowCompleted({
- onNotCompleted: () => OnboardingFlow.startOnboardingFlow(),
- });
-
- // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
- }, [isLoadingApp]);
-
const navigateToChats = useCallback(() => {
if (selectedTab === SCREENS.HOME) {
return;
diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx
index a1aa53bc0b7e..39e3dfa697f1 100644
--- a/src/libs/Navigation/NavigationRoot.tsx
+++ b/src/libs/Navigation/NavigationRoot.tsx
@@ -11,8 +11,8 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import Firebase from '@libs/Firebase';
import {FSPage} from '@libs/Fullstory';
-import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector';
import Log from '@libs/Log';
+import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors';
import {getPathFromURL} from '@libs/Url';
import {updateLastVisitedPath} from '@userActions/App';
import {updateOnboardingLastVisitedPath} from '@userActions/Welcome';
@@ -92,7 +92,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh
const {setActiveWorkspaceID} = useActiveWorkspace();
const [user] = useOnyx(ONYXKEYS.USER);
- const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
+ const [isOnboardingCompleted = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
});
@@ -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 (!NativeModules.HybridAppModule && !hasCompletedGuidedSetupFlow && authenticated && !shouldShowRequire2FAModal) {
+ if (!NativeModules.HybridAppModule && !isOnboardingCompleted && authenticated && !shouldShowRequire2FAModal) {
const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(), linkingConfig.config);
return adaptedState;
}
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 6a1c83656686..01d71eb09331 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -3580,6 +3580,11 @@ function completeOnboarding(
key: ONYXKEYS.NVP_INTRO_SELECTED,
value: {choice: engagementChoice},
},
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.NVP_ONBOARDING,
+ value: {hasCompletedGuidedSetupFlow: true},
+ },
);
const successData: OnyxUpdate[] = [...tasksForSuccessData];
@@ -3635,6 +3640,11 @@ function completeOnboarding(
key: ONYXKEYS.NVP_INTRO_SELECTED,
value: {choice: null},
},
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.NVP_ONBOARDING,
+ value: {hasCompletedGuidedSetupFlow: false},
+ },
);
const guidedSetupData: GuidedSetupData = [
diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts
index ab209e9bf928..4d6ba6cfa774 100644
--- a/src/libs/actions/Session/index.ts
+++ b/src/libs/actions/Session/index.ts
@@ -482,7 +482,7 @@ function signUpUser() {
function signInAfterTransitionFromOldDot(transitionURL: string) {
const [route, queryParams] = transitionURL.split('?');
- const {email, authToken, encryptedAuthToken, accountID, autoGeneratedLogin, autoGeneratedPassword, clearOnyxOnStart} = Object.fromEntries(
+ const {email, authToken, encryptedAuthToken, accountID, autoGeneratedLogin, autoGeneratedPassword, clearOnyxOnStart, completedHybridAppOnboarding} = Object.fromEntries(
queryParams.split('&').map((param) => {
const [key, value] = param.split('=');
return [key, value];
@@ -493,6 +493,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) {
Onyx.multiSet({
[ONYXKEYS.SESSION]: {email, authToken, encryptedAuthToken: decodeURIComponent(encryptedAuthToken), accountID: Number(accountID)},
[ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword},
+ [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {completedHybridAppOnboarding: completedHybridAppOnboarding === 'true'}},
}).then(App.openApp);
};
diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts
index fc921b16f4cf..b15c5a6cc39c 100644
--- a/src/libs/actions/Welcome/index.ts
+++ b/src/libs/actions/Welcome/index.ts
@@ -4,12 +4,8 @@ import Onyx from 'react-native-onyx';
import * as API from '@libs/API';
import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types';
import Log from '@libs/Log';
-import Navigation from '@libs/Navigation/Navigation';
-import variables from '@styles/variables';
import type {OnboardingPurposeType} from '@src/CONST';
-import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
import type Onboarding from '@src/types/onyx/Onboarding';
import type TryNewDot from '@src/types/onyx/TryNewDot';
import * as OnboardingFlow from './OnboardingFlow';
@@ -26,11 +22,6 @@ type HasCompletedOnboardingFlowProps = {
onCanceled?: () => void;
};
-type HasOpenedForTheFirstTimeFromHybridAppProps = {
- onFirstTimeInHybridApp?: () => void;
- onSubsequentRuns?: () => void;
-};
-
let resolveIsReadyPromise: (value?: Promise) => void | undefined;
let isServerDataReadyPromise = new Promise((resolve) => {
resolveIsReadyPromise = resolve;
@@ -42,9 +33,6 @@ let isOnboardingFlowStatusKnownPromise = new Promise((resolve) => {
});
let resolveTryNewDotStatus: (value?: Promise) => void | undefined;
-const tryNewDotStatusPromise = new Promise((resolve) => {
- resolveTryNewDotStatus = resolve;
-});
function onServerDataReady(): Promise {
return isServerDataReadyPromise;
@@ -68,49 +56,6 @@ function isOnboardingFlowCompleted({onCompleted, onNotCompleted, onCanceled}: Ha
});
}
-/**
- * Determines whether the application is being launched for the first time by a hybrid app user,
- * and executes corresponding callback functions.
- */
-function isFirstTimeHybridAppUser({onFirstTimeInHybridApp, onSubsequentRuns}: HasOpenedForTheFirstTimeFromHybridAppProps) {
- tryNewDotStatusPromise.then(() => {
- let completedHybridAppOnboarding = tryNewDotData?.classicRedirect?.completedHybridAppOnboarding;
- // Backend might return strings instead of booleans
- if (typeof completedHybridAppOnboarding === 'string') {
- completedHybridAppOnboarding = completedHybridAppOnboarding === 'true';
- }
-
- if (NativeModules.HybridAppModule && !completedHybridAppOnboarding) {
- onFirstTimeInHybridApp?.();
- return;
- }
-
- onSubsequentRuns?.();
- });
-}
-
-/**
- * Handles HybridApp onboarding flow if it's possible and necessary.
- */
-function handleHybridAppOnboarding() {
- if (!NativeModules.HybridAppModule) {
- return;
- }
-
- isFirstTimeHybridAppUser({
- // When user opens New Expensify for the first time from HybridApp we always want to show explanation modal first.
- onFirstTimeInHybridApp: () => Navigation.navigate(ROUTES.EXPLANATION_MODAL_ROOT),
- // In other scenarios we need to check if onboarding was completed.
- onSubsequentRuns: () =>
- isOnboardingFlowCompleted({
- onNotCompleted: () =>
- setTimeout(() => {
- OnboardingFlow.startOnboardingFlow();
- }, variables.explanationModalDelay),
- }),
- });
-}
-
/**
* Check if report data are loaded
*/
@@ -165,6 +110,10 @@ function updateOnboardingLastVisitedPath(path: string) {
}
function completeHybridAppOnboarding() {
+ if (!NativeModules.HybridAppModule) {
+ return;
+ }
+
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
@@ -177,27 +126,15 @@ function completeHybridAppOnboarding() {
},
];
- const failureData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: ONYXKEYS.NVP_TRYNEWDOT,
- value: {
- classicRedirect: {
- completedHybridAppOnboarding: false,
- },
- },
- },
- ];
-
// eslint-disable-next-line rulesdir/no-api-side-effects-method
- API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING, {}, {optimisticData, failureData}).then((response) => {
+ API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING, {}, {optimisticData}).then((response) => {
if (!response) {
return;
}
- // if the call succeeded HybridApp onboarding is finished, otherwise it's not
- Log.info(`[HybridApp] Onboarding status has changed. Propagating new value to OldDot`, true, {completedHybridAppOnboarding: response?.jsonCode === CONST.JSON_CODE.SUCCESS});
- NativeModules.HybridAppModule.completeOnboarding(response?.jsonCode === CONST.JSON_CODE.SUCCESS);
+ // No matter what the response is, we want to mark the onboarding as completed (user saw the explanation modal)
+ Log.info(`[HybridApp] Onboarding status has changed. Propagating new value to OldDot`, true);
+ NativeModules.HybridAppModule.completeOnboarding(true);
});
}
@@ -247,6 +184,5 @@ export {
setOnboardingAdminsChatReportID,
setOnboardingPolicyID,
completeHybridAppOnboarding,
- handleHybridAppOnboarding,
setOnboardingErrorMessage,
};
diff --git a/src/libs/hasCompletedGuidedSetupFlowSelector.ts b/src/libs/hasCompletedGuidedSetupFlowSelector.ts
deleted file mode 100644
index 4934c83d7773..000000000000
--- a/src/libs/hasCompletedGuidedSetupFlowSelector.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-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 ?? true;
-}
-
-export default hasCompletedGuidedSetupFlowSelector;
diff --git a/src/libs/onboardingSelectors.ts b/src/libs/onboardingSelectors.ts
new file mode 100644
index 000000000000..efa67d2aed48
--- /dev/null
+++ b/src/libs/onboardingSelectors.ts
@@ -0,0 +1,38 @@
+import type {OnyxValue} from 'react-native-onyx';
+import type ONYXKEYS from '@src/ONYXKEYS';
+
+/**
+ * Selector to get the value of hasCompletedGuidedSetupFlow from the Onyx store
+ *
+ * `undefined` means the value is not loaded yet
+ * `true` means the user has completed the NewDot onboarding flow
+ * `false` means the user has not completed the NewDot onboarding flow
+ */
+function hasCompletedGuidedSetupFlowSelector(onboarding: OnyxValue): boolean | undefined {
+ // Onboarding is an array for old accounts and accounts created from OldDot
+ if (Array.isArray(onboarding)) {
+ return true;
+ }
+
+ return onboarding?.hasCompletedGuidedSetupFlow;
+}
+
+/**
+ * Selector to get the value of completedHybridAppOnboarding from the Onyx store
+ *
+ * `undefined` means the value is not loaded yet
+ * `true` means the user has completed the hybrid app onboarding flow
+ * `false` means the user has not completed the hybrid app onboarding flow
+ */
+function hasCompletedHybridAppOnboardingFlowSelector(tryNewDotData: OnyxValue): boolean | undefined {
+ let completedHybridAppOnboarding = tryNewDotData?.classicRedirect?.completedHybridAppOnboarding;
+
+ // Backend might return strings instead of booleans
+ if (typeof completedHybridAppOnboarding === 'string') {
+ completedHybridAppOnboarding = completedHybridAppOnboarding === 'true';
+ }
+
+ return completedHybridAppOnboarding;
+}
+
+export {hasCompletedGuidedSetupFlowSelector, hasCompletedHybridAppOnboardingFlowSelector};
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index db2febc11c57..82d443928c7a 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -213,8 +213,6 @@ export default {
restrictedActionIllustrationHeight: 136,
photoUploadPopoverWidth: 335,
onboardingModalWidth: 500,
- welcomeVideoDelay: 1000,
- explanationModalDelay: 2000,
// The height of the empty list is 14px (2px for borders and 12px for vertical padding)
// This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility