From de662352e365d69a3c90d337836c03f5380b52c7 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 6 Aug 2025 11:18:52 +0200 Subject: [PATCH 1/2] revert dialog fix --- src/Expensify.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 032d416e79142..86eecfb6c9f50 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -130,13 +130,13 @@ function Expensify() { // This effect is closing OldDot sign out modal based on splash screen state useEffect(() => { - if (!isSplashReadyToBeHidden || !isNavigationReady || !hasAttemptedToOpenPublicRoom || !hybridApp?.loggedOutFromOldDot) { + if (!isSplashReadyToBeHidden || !shouldInit || !hybridApp?.loggedOutFromOldDot) { return; } setSplashScreenState(CONST.BOOT_SPLASH_STATE.HIDDEN); HybridAppModule.clearOldDotAfterSignOut(); - }, [hasAttemptedToOpenPublicRoom, hybridApp?.loggedOutFromOldDot, isNavigationReady, isSplashReadyToBeHidden, setSplashScreenState]); + }, [hybridApp?.loggedOutFromOldDot, isSplashReadyToBeHidden, setSplashScreenState, shouldInit, splashScreenState]); const initializeClient = () => { if (!Visibility.isVisible()) { From 723357bba64705e34ea129fb8d0a6addc9b1eaa4 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 6 Aug 2025 11:19:23 +0200 Subject: [PATCH 2/2] revert SignInPage PR --- __mocks__/@ua/react-native-airship.ts | 9 +- .../ReactNativeHybridApp.kt | 18 -- .../hybrid-app/ios/ReactNativeHybridApp.mm | 12 -- .../src/NativeReactNativeHybridApp.ts | 3 - modules/hybrid-app/src/index.native.ts | 9 - modules/hybrid-app/src/index.ts | 12 -- modules/hybrid-app/src/types.ts | 3 - src/App.tsx | 91 ++++----- src/CONFIG.ts | 11 -- src/Expensify.tsx | 19 +- src/HybridAppHandler.tsx | 20 +- src/components/ScreenWrapper/index.tsx | 2 +- .../GoogleSignIn/index.native.tsx | 20 +- src/libs/HybridApp.ts | 112 ----------- .../Navigation/AppNavigator/AuthScreens.tsx | 33 ++-- .../Navigation/AppNavigator/PublicScreens.tsx | 4 +- .../Navigation/AppNavigator/index.native.tsx | 17 +- .../PushNotification/index.native.ts | 23 +-- src/libs/actions/App.ts | 1 - src/libs/actions/Delegate.ts | 1 - src/libs/actions/HybridApp/index.ts | 71 +------ src/libs/actions/HybridApp/types.ts | 11 -- src/libs/actions/QueuedOnyxUpdates.ts | 1 - src/libs/actions/Session/index.ts | 180 +++++++++++------- src/libs/actions/SignInRedirect.ts | 5 - src/libs/actions/User.ts | 5 - src/pages/ErrorPage/SessionExpiredPage.tsx | 2 +- .../BaseOnboardingAccounting.tsx | 2 +- .../BaseOnboardingInterestedFeatures.tsx | 2 +- .../FloatingActionButtonAndPopover.tsx | 2 +- src/pages/settings/InitialSettingsPage.tsx | 3 +- .../MergeAccounts/MergeResultPage.tsx | 2 +- src/pages/signin/SignUpWelcomeForm.tsx | 16 +- src/types/onyx/HybridApp.ts | 33 ---- src/types/onyx/TryNewDot.ts | 6 +- 35 files changed, 208 insertions(+), 553 deletions(-) delete mode 100644 src/libs/HybridApp.ts delete mode 100644 src/libs/actions/HybridApp/types.ts diff --git a/__mocks__/@ua/react-native-airship.ts b/__mocks__/@ua/react-native-airship.ts index 08ec1b781fc21..a60053df475e0 100644 --- a/__mocks__/@ua/react-native-airship.ts +++ b/__mocks__/@ua/react-native-airship.ts @@ -6,13 +6,6 @@ enum EventType { PushReceived = 'com.airship.push_received', } -// eslint-disable-next-line no-restricted-syntax -enum PermissionStatus { - Granted = 'granted', - Denied = 'denied', - NotDetermined = 'not_determined', -} - // eslint-disable-next-line @typescript-eslint/no-namespace namespace iOS { /** @@ -78,4 +71,4 @@ const Airship: Partial = { export default Airship; -export {EventType, iOS, PermissionStatus}; +export {EventType, iOS}; diff --git a/modules/hybrid-app/android/src/main/java/com/expensify/reactnativehybridapp/ReactNativeHybridApp.kt b/modules/hybrid-app/android/src/main/java/com/expensify/reactnativehybridapp/ReactNativeHybridApp.kt index d366c6c732948..8c836ec97c427 100644 --- a/modules/hybrid-app/android/src/main/java/com/expensify/reactnativehybridapp/ReactNativeHybridApp.kt +++ b/modules/hybrid-app/android/src/main/java/com/expensify/reactnativehybridapp/ReactNativeHybridApp.kt @@ -51,22 +51,4 @@ class ReactNativeHybridApp(reactContext: ReactApplicationContext) : override fun onURLListenerAdded() { Log.d(NAME, "`onURLListenerAdded` should never be called in standalone `New Expensify` app") } - - override fun signInToOldDot( - autoGeneratedLogin: String, - autoGeneratedPassword: String, - authToken: String, - email: String, - policyID: String - ) { - Log.d(NAME, "`signInToOldDot` should never be called in standalone `New Expensify` app") - } - - override fun signOutFromOldDot() { - Log.d(NAME, "`signOutFromOldDot` should never be called in standalone `New Expensify` app") - } - - override fun clearOldDotAfterSignOut() { - Log.d(NAME, "`clearOldDotAfterSignOut` should never be called in standalone `New Expensify` app") - } } diff --git a/modules/hybrid-app/ios/ReactNativeHybridApp.mm b/modules/hybrid-app/ios/ReactNativeHybridApp.mm index 6dfd968f01333..3935f40eb9a7e 100644 --- a/modules/hybrid-app/ios/ReactNativeHybridApp.mm +++ b/modules/hybrid-app/ios/ReactNativeHybridApp.mm @@ -43,18 +43,6 @@ - (void)onURLListenerAdded { NSLog(@"[ReactNativeHybridApp] `onURLListenerAdded` should never be called in standalone `New Expensify` app"); } -- (void)signInToOldDot:(NSString *)autoGeneratedLogin autoGeneratedPassword:(NSString *)autoGeneratedPassword authToken:(NSString *)authToken email:(NSString *)email policyID:(NSString *)policyID { - NSLog(@"[ReactNativeHybridApp] `signInToOldDot` should never be called in standalone `New Expensify` app"); -} - -- (void)signOutFromOldDot { - NSLog(@"[ReactNativeHybridApp] `signOutFromOldDot` should never be called in standalone `New Expensify` app"); -} - -- (void)clearOldDotAfterSignOut { - NSLog(@"[ReactNativeHybridApp] `clearOldDotAfterSignOut` should never be called in standalone `New Expensify` app"); -} - - (std::shared_ptr)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params { diff --git a/modules/hybrid-app/src/NativeReactNativeHybridApp.ts b/modules/hybrid-app/src/NativeReactNativeHybridApp.ts index d88aa84b5b606..87bd482faf284 100644 --- a/modules/hybrid-app/src/NativeReactNativeHybridApp.ts +++ b/modules/hybrid-app/src/NativeReactNativeHybridApp.ts @@ -12,9 +12,6 @@ export interface Spec extends TurboModule { getHybridAppSettings: () => Promise; getInitialURL(): Promise; onURLListenerAdded: () => void; - signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, authToken: string, email: string, policyID: string) => void; - signOutFromOldDot: () => void; - clearOldDotAfterSignOut: () => void; } export default TurboModuleRegistry.getEnforcing('ReactNativeHybridApp'); diff --git a/modules/hybrid-app/src/index.native.ts b/modules/hybrid-app/src/index.native.ts index 0d5770c6aa0d9..2fa0c8463c549 100644 --- a/modules/hybrid-app/src/index.native.ts +++ b/modules/hybrid-app/src/index.native.ts @@ -29,15 +29,6 @@ const HybridAppModule: HybridAppModuleType = { onURLListenerAdded() { ReactNativeHybridApp.onURLListenerAdded(); }, - signInToOldDot({autoGeneratedLogin, autoGeneratedPassword, authToken, email, policyID}) { - ReactNativeHybridApp.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword, authToken, email, policyID); - }, - signOutFromOldDot() { - ReactNativeHybridApp.signOutFromOldDot(); - }, - clearOldDotAfterSignOut() { - ReactNativeHybridApp.clearOldDotAfterSignOut(); - }, }; export default HybridAppModule; diff --git a/modules/hybrid-app/src/index.ts b/modules/hybrid-app/src/index.ts index 6e0534d37d4b6..813fe7af880f7 100644 --- a/modules/hybrid-app/src/index.ts +++ b/modules/hybrid-app/src/index.ts @@ -38,18 +38,6 @@ const HybridAppModule: HybridAppModuleType = { // eslint-disable-next-line no-console console.warn('HybridAppModule: `onURLListenerAdded` should never be called on web'); }, - signInToOldDot() { - // eslint-disable-next-line no-console - console.warn('HybridAppModule: `signInToOldDot` should never be called on web'); - }, - signOutFromOldDot() { - // eslint-disable-next-line no-console - console.warn('HybridAppModule: `signOutFromOldDot` should never be called on web'); - }, - clearOldDotAfterSignOut() { - // eslint-disable-next-line no-console - console.warn('HybridAppModule: `clearOldDotAfterSignOut` should never be called on web'); - }, }; export default HybridAppModule; diff --git a/modules/hybrid-app/src/types.ts b/modules/hybrid-app/src/types.ts index 508091e2c82e1..ed227123a9b9a 100644 --- a/modules/hybrid-app/src/types.ts +++ b/modules/hybrid-app/src/types.ts @@ -8,9 +8,6 @@ type HybridAppModuleType = { getHybridAppSettings: () => Promise; getInitialURL(): Promise; onURLListenerAdded: () => void; - signInToOldDot: (args: {autoGeneratedLogin: string; autoGeneratedPassword: string; authToken: string; email: string; policyID: string}) => void; - signOutFromOldDot: () => void; - clearOldDotAfterSignOut: () => void; }; export default HybridAppModuleType; diff --git a/src/App.tsx b/src/App.tsx index f42ac14638825..a7510270bdb90 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,7 +41,6 @@ import {CurrentReportIDContextProvider} from './hooks/useCurrentReportID'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import HybridAppHandler from './HybridAppHandler'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; -import './libs/HybridApp'; import {AttachmentModalContextProvider} from './pages/media/AttachmentModalScreen/AttachmentModalContext'; import './setup/backgroundTask'; import './setup/hybridApp'; @@ -70,57 +69,47 @@ function App() { - {/* Initialize metrics early to ensure the UI renders even when NewDot is hidden. - This is necessary for iOS HybridApp's SignInPage to appear correctly without the bootsplash. - See: https://github.com/Expensify/App/pull/65178#issuecomment-3139026551 - */} - - - - - - - - - - - + + + + + + + + diff --git a/src/CONFIG.ts b/src/CONFIG.ts index aa7b3d3963a13..aa6e3a325d499 100644 --- a/src/CONFIG.ts +++ b/src/CONFIG.ts @@ -95,18 +95,7 @@ export default { GOOGLE_SIGN_IN: { // cspell:disable-next-line WEB_CLIENT_ID: '921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com', - // cspell:disable-next-line IOS_CLIENT_ID: '921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com', - HYBRID_APP: { - // cspell:disable-next-line - IOS_CLIENT_ID: '1008697809946-sh04nqq0hea396s1qdqqbj6ia649odb2.apps.googleusercontent.com', - WEB_CLIENT_ID: { - // cspell:disable-next-line - IOS: '1008697809946-5e095eqem3o6ugtpc2rjf7v880tcp28p.apps.googleusercontent.com', - // cspell:disable-next-line - ANDROID: '240677659774-86pov3adub93cv4b8uj13g7varolmk2l.apps.googleusercontent.com', - }, - }, }, GCP_GEOLOCATION_API_KEY: googleGeolocationAPIKey, FIREBASE_WEB_CONFIG: { diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 86eecfb6c9f50..ee719dc938070 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -105,7 +105,6 @@ function Expensify() { const [currentOnboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, {canBeMissing: true}); const [currentOnboardingCompanySize] = useOnyx(ONYXKEYS.ONBOARDING_COMPANY_SIZE, {canBeMissing: true}); const [onboardingInitialPath] = useOnyx(ONYXKEYS.ONBOARDING_LAST_VISITED_PATH, {canBeMissing: true}); - const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP, {canBeMissing: true}); useDebugShortcut(); usePriorityMode(); @@ -122,22 +121,10 @@ function Expensify() { const isAuthenticated = useIsAuthenticated(); const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]); - const isSplashReadyToBeHidden = splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN; - const isSplashVisible = splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE; - const shouldInit = isNavigationReady && hasAttemptedToOpenPublicRoom && !!preferredLocale; - const shouldHideSplash = (isSplashReadyToBeHidden || isSplashVisible) && shouldInit && !hybridApp?.loggedOutFromOldDot; - - // This effect is closing OldDot sign out modal based on splash screen state - useEffect(() => { - if (!isSplashReadyToBeHidden || !shouldInit || !hybridApp?.loggedOutFromOldDot) { - return; - } - - setSplashScreenState(CONST.BOOT_SPLASH_STATE.HIDDEN); - HybridAppModule.clearOldDotAfterSignOut(); - }, [hybridApp?.loggedOutFromOldDot, isSplashReadyToBeHidden, setSplashScreenState, shouldInit, splashScreenState]); - + const isSplashVisible = splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE; + const isHybridAppReady = splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && isAuthenticated; + const shouldHideSplash = shouldInit && (CONFIG.IS_HYBRID_APP ? isHybridAppReady : isSplashVisible); const initializeClient = () => { if (!Visibility.isVisible()) { return; diff --git a/src/HybridAppHandler.tsx b/src/HybridAppHandler.tsx index a73c93e3c868a..2b425b34072d2 100644 --- a/src/HybridAppHandler.tsx +++ b/src/HybridAppHandler.tsx @@ -2,22 +2,15 @@ import HybridAppModule from '@expensify/react-native-hybrid-app'; import {useContext, useEffect} from 'react'; import CONFIG from './CONFIG'; import CONST from './CONST'; -import useOnyx from './hooks/useOnyx'; -import {parseHybridAppSettings} from './libs/actions/HybridApp'; -import {setupNewDotAfterTransitionFromOldDot} from './libs/actions/Session'; +import {signInAfterTransitionFromOldDot} from './libs/actions/Session'; import Log from './libs/Log'; -import ONYXKEYS from './ONYXKEYS'; import SplashScreenStateContext from './SplashScreenStateContext'; -import isLoadingOnyxValue from './types/utils/isLoadingOnyxValue'; function HybridAppHandler() { - const {splashScreenState, setSplashScreenState} = useContext(SplashScreenStateContext); - const [tryNewDot, tryNewDotMetadata] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT, {canBeMissing: true}); - - const isLoading = isLoadingOnyxValue(tryNewDotMetadata); + const {setSplashScreenState} = useContext(SplashScreenStateContext); useEffect(() => { - if (!CONFIG.IS_HYBRID_APP || isLoading) { + if (!CONFIG.IS_HYBRID_APP) { return; } @@ -28,14 +21,11 @@ function HybridAppHandler() { return; } - setupNewDotAfterTransitionFromOldDot(parseHybridAppSettings(hybridAppSettings), tryNewDot).then(() => { - if (splashScreenState !== CONST.BOOT_SPLASH_STATE.VISIBLE) { - return; - } + signInAfterTransitionFromOldDot(hybridAppSettings).then(() => { setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); }); }); - }, [isLoading, setSplashScreenState, splashScreenState, tryNewDot]); + }, [setSplashScreenState]); return null; } diff --git a/src/components/ScreenWrapper/index.tsx b/src/components/ScreenWrapper/index.tsx index f745e615c3cba..a2d6474b21c0a 100644 --- a/src/components/ScreenWrapper/index.tsx +++ b/src/components/ScreenWrapper/index.tsx @@ -20,7 +20,7 @@ import NarrowPaneContext from '@libs/Navigation/AppNavigator/Navigators/NarrowPa import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList, RootNavigatorParamList} from '@libs/Navigation/types'; -import {closeReactNativeApp} from '@userActions/HybridApp'; +import closeReactNativeApp from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/components/SignInButtons/GoogleSignIn/index.native.tsx b/src/components/SignInButtons/GoogleSignIn/index.native.tsx index 70144444d282f..4a70a10ef0171 100644 --- a/src/components/SignInButtons/GoogleSignIn/index.native.tsx +++ b/src/components/SignInButtons/GoogleSignIn/index.native.tsx @@ -1,32 +1,20 @@ import {GoogleSignin, statusCodes} from '@react-native-google-signin/google-signin'; import React from 'react'; import IconButton from '@components/SignInButtons/IconButton'; -import getPlatform from '@libs/getPlatform'; import Log from '@libs/Log'; -import {beginGoogleSignIn} from '@userActions/Session'; +import * as Session from '@userActions/Session'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import type {GoogleSignInProps} from '.'; import type GoogleError from './types'; -/** - * Helper function returning webClientId based on a platform used - */ -function getWebClientId() { - if (!CONFIG.IS_HYBRID_APP) { - return CONFIG.GOOGLE_SIGN_IN.WEB_CLIENT_ID; - } - - return getPlatform() === CONST.PLATFORM.ANDROID ? CONFIG.GOOGLE_SIGN_IN.HYBRID_APP.WEB_CLIENT_ID.ANDROID : CONFIG.GOOGLE_SIGN_IN.HYBRID_APP.WEB_CLIENT_ID.IOS; -} - /** * Google Sign In method for iOS and android that returns identityToken. */ function googleSignInRequest() { GoogleSignin.configure({ - webClientId: getWebClientId(), - iosClientId: CONFIG.IS_HYBRID_APP ? CONFIG.GOOGLE_SIGN_IN.HYBRID_APP.IOS_CLIENT_ID : CONFIG.GOOGLE_SIGN_IN.IOS_CLIENT_ID, + webClientId: CONFIG.GOOGLE_SIGN_IN.WEB_CLIENT_ID, + iosClientId: CONFIG.GOOGLE_SIGN_IN.IOS_CLIENT_ID, offlineAccess: false, }); @@ -37,7 +25,7 @@ function googleSignInRequest() { GoogleSignin.signIn() .then((response) => response.idToken) - .then((token) => beginGoogleSignIn(token)) + .then((token) => Session.beginGoogleSignIn(token)) .catch((error: GoogleError | undefined) => { // Handle unexpected error shape if (error?.code === undefined) { diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts deleted file mode 100644 index e1579b673fb37..0000000000000 --- a/src/libs/HybridApp.ts +++ /dev/null @@ -1,112 +0,0 @@ -import HybridAppModule from '@expensify/react-native-hybrid-app'; -import Onyx from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; -import CONFIG from '@src/CONFIG'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Credentials, HybridApp, Session, TryNewDot} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {closeReactNativeApp, setReadyToShowAuthScreens, setUseNewDotSignInPage} from './actions/HybridApp'; -import {isAnonymousUser} from './actions/Session'; -import Log from './Log'; -import {getCurrentUserEmail} from './Network/NetworkStore'; - -let currentHybridApp: OnyxEntry; -let currentTryNewDot: OnyxEntry; -let currentCredentials: OnyxEntry; -let currentSession: OnyxEntry; - -Onyx.connect({ - key: ONYXKEYS.HYBRID_APP, - callback: (hybridApp) => { - currentHybridApp = hybridApp; - handleChangeInHybridAppSignInFlow(hybridApp, currentTryNewDot, currentCredentials, currentSession); - }, -}); - -Onyx.connect({ - key: ONYXKEYS.NVP_TRY_NEW_DOT, - callback: (tryNewDot) => { - currentTryNewDot = tryNewDot; - handleChangeInHybridAppSignInFlow(currentHybridApp, tryNewDot, currentCredentials, currentSession); - }, -}); - -Onyx.connect({ - key: ONYXKEYS.CREDENTIALS, - callback: (credentials) => { - currentCredentials = credentials; - handleChangeInHybridAppSignInFlow(currentHybridApp, currentTryNewDot, credentials, currentSession); - }, -}); - -Onyx.connect({ - key: ONYXKEYS.SESSION, - callback: (session: OnyxEntry) => { - if (!currentSession?.authToken && session?.authToken) { - handleChangeInHybridAppSignInFlow(currentHybridApp, currentTryNewDot, currentCredentials, session); - } else if (isAnonymousUser(currentSession) && !isAnonymousUser(session)) { - handleChangeInHybridAppSignInFlow(currentHybridApp, currentTryNewDot, currentCredentials, session, true); - } - currentSession = session; - }, -}); - -let activePolicyID: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.NVP_ACTIVE_POLICY_ID, - callback: (newActivePolicyID) => { - activePolicyID = newActivePolicyID; - }, -}); - -function shouldUseOldApp(tryNewDot: TryNewDot) { - if (isEmptyObject(tryNewDot) || isEmptyObject(tryNewDot.classicRedirect)) { - return true; - } - return tryNewDot.classicRedirect.dismissed; -} - -function handleChangeInHybridAppSignInFlow( - hybridApp: OnyxEntry, - tryNewDot: OnyxEntry, - credentials: OnyxEntry, - session: OnyxEntry, - usingSignInModal = false, -) { - if (!CONFIG.IS_HYBRID_APP) { - return; - } - - if (!session?.authToken || (!hybridApp?.useNewDotSignInPage && !usingSignInModal)) { - return; - } - - if (isAnonymousUser()) { - setUseNewDotSignInPage(false).then(() => { - setReadyToShowAuthScreens(true); - }); - return; - } - - if (tryNewDot !== undefined && !!credentials?.autoGeneratedLogin && !!credentials?.autoGeneratedPassword) { - // It's better to not pass function directly to Log.info to avoid bugs with evaluation - const shouldUseOD = shouldUseOldApp(tryNewDot); - Log.info(`[HybridApp] Performing sign-in${shouldUseOD ? '' : ' (in background)'} on OldDot side`); - HybridAppModule.signInToOldDot({ - autoGeneratedLogin: credentials.autoGeneratedLogin, - autoGeneratedPassword: credentials.autoGeneratedPassword, - authToken: session.authToken, - email: getCurrentUserEmail() ?? '', - // eslint-disable-next-line rulesdir/no-default-id-values - policyID: activePolicyID ?? '', - }); - setUseNewDotSignInPage(false).then(() => { - if (shouldUseOD) { - closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); - } else { - Log.info('[HybridApp] The user should see NewDot. There is no need to block the user on the `SignInPage` until the sign-in process is completed on the OldDot side.'); - setReadyToShowAuthScreens(true); - } - }); - } -} diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 7c7fbba7b2e52..afe54338d961d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -308,23 +308,26 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie init(); } - // If we are on this screen then we are "logged in", but the user might not have "just logged in". They could be reopening the app - // or returning from background. If so, we'll assume they have some app data already and we can call reconnectApp() instead of openApp() and connect() for delegator from OldDot. - if (SessionUtils.didUserLogInDuringSession() || delegatorEmail) { - if (delegatorEmail) { - connect(delegatorEmail, true) - ?.then((success) => { - App.setAppLoading(!!success); - }) - .finally(() => { - setIsDelegatorFromOldDotIsReady(true); - }); + // In Hybrid App we decide to call one of those method when booting ND and we don't want to duplicate calls + if (!CONFIG.IS_HYBRID_APP) { + // If we are on this screen then we are "logged in", but the user might not have "just logged in". They could be reopening the app + // or returning from background. If so, we'll assume they have some app data already and we can call reconnectApp() instead of openApp() and connect() for delegator from OldDot. + if (SessionUtils.didUserLogInDuringSession() || delegatorEmail) { + if (delegatorEmail) { + connect(delegatorEmail, true) + ?.then((success) => { + App.setAppLoading(!!success); + }) + .finally(() => { + setIsDelegatorFromOldDotIsReady(true); + }); + } else { + App.openApp(); + } } else { - App.openApp(); + Log.info('[AuthScreens] Sending ReconnectApp'); + App.reconnectApp(initialLastUpdateIDAppliedToClient); } - } else { - Log.info('[AuthScreens] Sending ReconnectApp'); - App.reconnectApp(initialLastUpdateIDAppliedToClient); } App.setUpPoliciesAndNavigate(session); diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.tsx b/src/libs/Navigation/AppNavigator/PublicScreens.tsx index e5ea03e14387d..993117174ba69 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.tsx +++ b/src/libs/Navigation/AppNavigator/PublicScreens.tsx @@ -5,6 +5,7 @@ import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigati import {InternalPlatformAnimations} from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation'; import type {PublicScreensParamList} from '@navigation/types'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; +import SessionExpiredPage from '@pages/ErrorPage/SessionExpiredPage'; import LogInWithShortLivedAuthTokenPage from '@pages/LogInWithShortLivedAuthTokenPage'; import AppleSignInDesktopPage from '@pages/signin/AppleSignInDesktopPage'; import GoogleSignInDesktopPage from '@pages/signin/GoogleSignInDesktopPage'; @@ -12,6 +13,7 @@ import SAMLSignInPage from '@pages/signin/SAMLSignInPage'; import SignInPage from '@pages/signin/SignInPage'; import UnlinkLoginPage from '@pages/UnlinkLoginPage'; import ValidateLoginPage from '@pages/ValidateLoginPage'; +import CONFIG from '@src/CONFIG'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import defaultScreenOptions from './defaultScreenOptions'; @@ -31,7 +33,7 @@ function PublicScreens() { { - if (!CONFIG.IS_HYBRID_APP) { - return authenticated; - } - - return authenticated && hybridApp?.readyToShowAuthScreens; - }, [hybridApp?.readyToShowAuthScreens, authenticated]); - - if (shouldShowAuthScreens) { + if (authenticated) { const AuthScreens = require('./AuthScreens').default; // These are the protected screens and only accessible when an authToken is present diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts index 854aeba8074f2..a38e9d98a8e45 100644 --- a/src/libs/Notification/PushNotification/index.native.ts +++ b/src/libs/Notification/PushNotification/index.native.ts @@ -1,8 +1,7 @@ import type {PushPayload} from '@ua/react-native-airship'; -import Airship, {EventType, PermissionStatus} from '@ua/react-native-airship'; +import Airship, {EventType} from '@ua/react-native-airship'; import Log from '@libs/Log'; import ShortcutManager from '@libs/ShortcutManager'; -import CONFIG from '@src/CONFIG'; import ForegroundNotifications from './ForegroundNotifications'; import type {NotificationDataMap, NotificationTypes} from './NotificationType'; import NotificationType from './NotificationType'; @@ -80,28 +79,18 @@ const register: Register = (notificationID) => { Airship.contact .getNamedUserId() .then((userID) => { - // In the HybridApp, the contact identity is set on the YAPL side after sign-in. - // Since the Airship instance is shared between NewDot and OldDot, - // NewDot users won't see the push notification permission prompt as we return early in this case. - // Therefore, we cannot handle the HybridApp scenario here. - if (!CONFIG.IS_HYBRID_APP && userID === notificationID.toString()) { + if (userID === notificationID.toString()) { // No need to register again for this notificationID. return; } - // Get permissions to display push notifications if not determined (prompts user on iOS, but not Android) - Airship.push.getNotificationStatus().then(({notificationPermissionStatus}) => { - if (notificationPermissionStatus !== PermissionStatus.NotDetermined) { + // Get permissions to display push notifications (prompts user on iOS, but not Android) + Airship.push.enableUserNotifications().then((isEnabled) => { + if (isEnabled) { return; } - Airship.push.enableUserNotifications().then((isEnabled) => { - if (isEnabled) { - return; - } - - Log.info('[PushNotification] User has disabled visible push notifications for this app.'); - }); + Log.info('[PushNotification] User has disabled visible push notifications for this app.'); }); // Register this device as a named user in AirshipAPI. diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index eed52c57f4590..b18bb62c2c23c 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -136,7 +136,6 @@ const KEYS_TO_PRESERVE: OnyxKey[] = [ ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.CREDENTIALS, ONYXKEYS.PRESERVED_USER_SESSION, - ONYXKEYS.HYBRID_APP, ]; Onyx.connect({ diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index 0a06a3627c820..f60b5b1dfd881 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -70,7 +70,6 @@ const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ ONYXKEYS.IS_LOADING_APP, ONYXKEYS.HAS_LOADED_APP, ONYXKEYS.STASHED_CREDENTIALS, - ONYXKEYS.HYBRID_APP, // We need to preserve the sidebar loaded state since we never unmount the sidebar when connecting as a delegate // This allows the report screen to load correctly when the delegate token expires and the delegate is returned to their original account. diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 9c9342305a364..941d91ada9655 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -2,8 +2,6 @@ import HybridAppModule from '@expensify/react-native-hybrid-app'; import Onyx from 'react-native-onyx'; import CONFIG from '@src/CONFIG'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {HybridApp} from '@src/types/onyx'; -import type HybridAppSettings from './types'; function closeReactNativeApp({shouldSignOut, shouldSetNVP}: {shouldSignOut: boolean; shouldSetNVP: boolean}) { if (CONFIG.IS_HYBRID_APP) { @@ -13,71 +11,4 @@ function closeReactNativeApp({shouldSignOut, shouldSetNVP}: {shouldSignOut: bool HybridAppModule.closeReactNativeApp({shouldSignOut, shouldSetNVP}); } -/* - * Parses initial settings passed from OldDot app - */ -function parseHybridAppSettings(hybridAppSettings: string): HybridAppSettings { - return JSON.parse(hybridAppSettings) as HybridAppSettings; -} - -/* - * Changes value of `readyToShowAuthScreens` - */ -function setReadyToShowAuthScreens(readyToShowAuthScreens: boolean) { - // This value is only relevant for HybridApp, so we can skip it in other environments. - if (!CONFIG.IS_HYBRID_APP) { - return; - } - Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToShowAuthScreens}); -} - -function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { - // This value is only relevant for HybridApp, so we can skip it in other environments. - if (!CONFIG.IS_HYBRID_APP) { - return Promise.resolve(); - } - return Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); -} - -function setClosingReactNativeApp(closingReactNativeApp: boolean) { - // This value is only relevant for HybridApp, so we can skip it in other environments. - if (!CONFIG.IS_HYBRID_APP) { - return; - } - Onyx.merge(ONYXKEYS.HYBRID_APP, {closingReactNativeApp}); -} - -/* - * Starts HybridApp sign-in flow from the beginning. - */ -function resetSignInFlow() { - // This value is only relevant for HybridApp, so we can skip it in other environments. - if (!CONFIG.IS_HYBRID_APP) { - return; - } - - Onyx.merge(ONYXKEYS.HYBRID_APP, { - readyToShowAuthScreens: false, - useNewDotSignInPage: true, - }); -} - -/* - * Updates Onyx state after start of React Native runtime based on initial `useNewDotSignInPage` value - */ -function prepareHybridAppAfterTransitionToNewDot(hybridApp: HybridApp) { - if (hybridApp?.useNewDotSignInPage) { - return Onyx.merge(ONYXKEYS.HYBRID_APP, { - ...hybridApp, - readyToShowAuthScreens: !(hybridApp?.useNewDotSignInPage ?? false), - }); - } - - // When we transition with useNewDotSignInPage === false, it means that we're already authenticated on NewDot side. - return Onyx.merge(ONYXKEYS.HYBRID_APP, { - ...hybridApp, - readyToShowAuthScreens: true, - }); -} - -export {parseHybridAppSettings, setReadyToShowAuthScreens, resetSignInFlow, prepareHybridAppAfterTransitionToNewDot, setUseNewDotSignInPage, setClosingReactNativeApp, closeReactNativeApp}; +export default closeReactNativeApp; diff --git a/src/libs/actions/HybridApp/types.ts b/src/libs/actions/HybridApp/types.ts deleted file mode 100644 index 5d4ab1431cbc7..0000000000000 --- a/src/libs/actions/HybridApp/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type ONYXKEYS from '@src/ONYXKEYS'; -import type {TryNewDot} from '@src/types/onyx'; -import type HybridApp from '@src/types/onyx/HybridApp'; - -type HybridAppSettings = { - [ONYXKEYS.HYBRID_APP]: HybridApp; - [ONYXKEYS.NVP_TRY_NEW_DOT]?: TryNewDot; - [ONYXKEYS.ACCOUNT]?: {shouldUseStagingServer: boolean}; -}; - -export default HybridAppSettings; diff --git a/src/libs/actions/QueuedOnyxUpdates.ts b/src/libs/actions/QueuedOnyxUpdates.ts index e419c759950f1..ef3f2c9c8ad81 100644 --- a/src/libs/actions/QueuedOnyxUpdates.ts +++ b/src/libs/actions/QueuedOnyxUpdates.ts @@ -32,7 +32,6 @@ function flushQueue(): Promise { if (!currentAccountID && !CONFIG.IS_TEST_ENV && !CONFIG.E2E_TESTING) { const preservedKeys: OnyxKey[] = [ - ONYXKEYS.NVP_TRY_NEW_DOT, ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 1df837bbc4f6d..a6b662dd40a6d 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -3,7 +3,7 @@ import throttle from 'lodash/throttle'; import type {ChannelAuthorizationData} from 'pusher-js/types/src/core/auth/options'; import type {ChannelAuthorizationCallback} from 'pusher-js/with-encryption'; import {InteractionManager, Linking} from 'react-native'; -import type {OnyxEntry, OnyxKey, OnyxUpdate} from 'react-native-onyx'; +import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as PersistedRequests from '@libs/actions/PersistedRequests'; import * as API from '@libs/API'; @@ -41,15 +41,13 @@ import NetworkConnection from '@libs/NetworkConnection'; import Pusher from '@libs/Pusher'; import {getReportIDFromLink, parseReportRouteParams as parseReportRouteParamsReportUtils} from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; -import {resetDidUserLogInDuringSession} from '@libs/SessionUtils'; import {clearSoundAssetsCache} from '@libs/Sound'; import Timers from '@libs/Timers'; import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; +import {KEYS_TO_PRESERVE, openApp, reconnectApp} from '@userActions/App'; import {KEYS_TO_PRESERVE_DELEGATE_ACCESS} from '@userActions/Delegate'; import * as Device from '@userActions/Device'; -import * as HybridAppActions from '@userActions/HybridApp'; -import type HybridAppSettings from '@userActions/HybridApp/types'; +import closeReactNativeApp from '@userActions/HybridApp'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; import * as Welcome from '@userActions/Welcome'; @@ -59,7 +57,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import type {TryNewDot} from '@src/types/onyx'; import type Credentials from '@src/types/onyx/Credentials'; import type Locale from '@src/types/onyx/Locale'; import type Response from '@src/types/onyx/Response'; @@ -73,7 +70,6 @@ const INVALID_TOKEN = 'pizza'; let session: Session = {}; let authPromiseResolver: ((value: boolean) => void) | null = null; -let isHybridAppSetupFinished = false; let hasSwitchedAccountInHybridMode = false; Onyx.connect({ @@ -87,7 +83,7 @@ Onyx.connect({ authPromiseResolver(true); authPromiseResolver = null; } - if (CONFIG.IS_HYBRID_APP && isHybridAppSetupFinished && session.authToken && session.authToken !== INVALID_TOKEN && !isAnonymousUser(value)) { + if (CONFIG.IS_HYBRID_APP && session.authToken && session.authToken !== INVALID_TOKEN) { HybridAppModule.sendAuthToken({authToken: session.authToken}); } }, @@ -248,7 +244,7 @@ function isExpiredSession(sessionCreationDate: number): boolean { return new Date().getTime() - sessionCreationDate >= CONST.SESSION_EXPIRATION_TIME_MS; } -function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, shouldSignOutFromOldDot = true, shouldForceUseStashedSession?: boolean) { +function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, shouldKillHybridApp = true, shouldForceUseStashedSession?: boolean) { Log.info('Redirecting to Sign In because signOut() was called'); hideContextMenu(false); @@ -268,9 +264,10 @@ function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSess return; } - // When signing out from the HybridApp, we need to sign out from the oldDot app as well - if (CONFIG.IS_HYBRID_APP && shouldSignOutFromOldDot) { - HybridAppModule.signOutFromOldDot(); + // In the HybridApp, we want the Old Dot to handle the sign out process + if (CONFIG.IS_HYBRID_APP && shouldKillHybridApp) { + closeReactNativeApp({shouldSignOut: true, shouldSetNVP: false}); + return; } const isSupportal = isSupportAuthToken(); @@ -458,7 +455,6 @@ function beginSignIn(email: string) { const params: BeginSignInParams = {email}; - // eslint-disable-next-line rulesdir/no-api-side-effects-method API.read(READ_COMMANDS.BEGIN_SIGNIN, params, {optimisticData, successData, failureData}); } @@ -521,76 +517,122 @@ function signUpUser() { API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); } -function setupNewDotAfterTransitionFromOldDot(hybridAppSettings: HybridAppSettings, tryNewDot?: TryNewDot) { - const {hybridApp, ...newDotOnyxValues} = hybridAppSettings; - - const clearOnyxBeforeSignIn = () => { - if (!hybridApp.useNewDotSignInPage) { - return Promise.resolve(); - } +function getLastUpdateIDAppliedToClient(): Promise { + return new Promise((resolve) => { + Onyx.connect({ + key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, + callback: (value) => resolve(value ?? 0), + }); + }); +} - return redirectToSignIn(); - }; +type HybridAppSettings = { + email: string; + authToken: string; + accountID: number; + autoGeneratedLogin: string; + autoGeneratedPassword: string; + clearOnyxOnStart: boolean; + completedHybridAppOnboarding: boolean; + isSingleNewDotEntry: boolean; + isStaging: boolean; + primaryLogin: string; + encryptedAuthToken: string; + nudgeMigrationTimestamp?: string; + oldDotOriginalAccountEmail?: string; + stashedAuthToken?: string; + stashedAccountID?: string; + requiresTwoFactorAuth: boolean; + needsTwoFactorAuthSetup: boolean; +}; - const resetDidUserLoginDuringSessionIfNeeded = () => { - if (newDotOnyxValues.nvp_tryNewDot === undefined || tryNewDot?.classicRedirect?.dismissed !== true) { +function signInAfterTransitionFromOldDot(hybridAppSettings: string) { + const { + email, + authToken, + encryptedAuthToken, + accountID, + autoGeneratedLogin, + autoGeneratedPassword, + clearOnyxOnStart, + completedHybridAppOnboarding, + nudgeMigrationTimestamp, + isSingleNewDotEntry, + isStaging, + primaryLogin, + oldDotOriginalAccountEmail, + stashedAuthToken, + stashedAccountID, + requiresTwoFactorAuth, + needsTwoFactorAuthSetup, + } = JSON.parse(hybridAppSettings) as HybridAppSettings; + + const clearOnyxForNewAccount = () => { + if (!clearOnyxOnStart) { return Promise.resolve(); } - Log.info("[HybridApp] OpenApp hasn't been called yet. Calling `resetDidUserLogInDuringSession`"); - resetDidUserLogInDuringSession(); + // We also need to reset: + // - IS_LOADING_APP after sign in to ensure the condition to show ExplanationModal runs once + // https://github.com/Expensify/App/issues/57575#issuecomment-2780189425 + return Onyx.clear(KEYS_TO_PRESERVE) + .then(() => Onyx.merge(ONYXKEYS.ACCOUNT, {delegatedAccess: null})) + .then(() => Onyx.merge(ONYXKEYS.IS_LOADING_APP, null)); }; - return clearOnyxBeforeSignIn() + return clearOnyxForNewAccount() .then(() => { // This section controls copilot changes const currentUserEmail = getCurrentUserEmail(); - // If ND and OD account are the same - do nothing - if (hybridApp?.delegateAccessData?.oldDotCurrentUserEmail === currentUserEmail) { + // If OD is in copilot, stash the original account data + if (oldDotOriginalAccountEmail && oldDotOriginalAccountEmail !== email) { + return Onyx.multiSet({ + [ONYXKEYS.STASHED_SESSION]: {email: oldDotOriginalAccountEmail, authToken: stashedAuthToken, accountID: Number(stashedAccountID)}, + [ONYXKEYS.STASHED_CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword}, + }); + } + + // If OD and ND account are the same - do nothing + if (email === currentUserEmail) { return; } - const stashedData = hybridApp?.delegateAccessData?.isDelegateAccess - ? { - [ONYXKEYS.STASHED_CREDENTIALS]: credentials, - [ONYXKEYS.STASHED_SESSION]: session, - } - : { - [ONYXKEYS.STASHED_CREDENTIALS]: {}, - [ONYXKEYS.STASHED_SESSION]: {}, - }; - - // Account was changed on OD side - clear onyx and apply data - return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS).then(() => - Onyx.multiSet({ - ...stashedData, - [ONYXKEYS.SESSION]: { - email: hybridApp?.delegateAccessData?.oldDotCurrentUserEmail, - authToken: hybridApp?.delegateAccessData?.oldDotCurrentAuthToken, - encryptedAuthToken: decodeURIComponent(hybridApp?.delegateAccessData?.oldDotCurrentEncryptedAuthToken ?? ''), - accountID: hybridApp?.delegateAccessData?.oldDotCurrentAccountID, - }, - [ONYXKEYS.CREDENTIALS]: { - autoGeneratedLogin: credentials?.autoGeneratedLogin, - autoGeneratedPassword: credentials?.autoGeneratedPassword, - }, - }) - .then(() => Onyx.merge(ONYXKEYS.ACCOUNT, {primaryLogin: hybridApp?.delegateAccessData?.oldDotCurrentUserEmail})) - .then(() => openApp()), - ); + // If account was changed to original one on OD side - clear onyx + if (!oldDotOriginalAccountEmail) { + return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS); + } + + // If we're already logged in - do nothing, data will be set in next step + if (currentUserEmail) { + return; + } + + // If we're not logged in - set stashed data + return Onyx.multiSet({ + [ONYXKEYS.STASHED_CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword}, + }); }) .then(() => - HybridAppActions.prepareHybridAppAfterTransitionToNewDot({ - ...hybridApp, - closingReactNativeApp: false, - }), + Onyx.multiSet({ + [ONYXKEYS.SESSION]: {email, authToken, encryptedAuthToken: decodeURIComponent(encryptedAuthToken), accountID: Number(accountID)}, + [ONYXKEYS.CREDENTIALS]: {autoGeneratedLogin, autoGeneratedPassword}, + [ONYXKEYS.NVP_TRY_NEW_DOT]: { + classicRedirect: {completedHybridAppOnboarding}, + nudgeMigration: nudgeMigrationTimestamp ? {timestamp: new Date(nudgeMigrationTimestamp)} : undefined, + }, + [ONYXKEYS.ACCOUNT]: {shouldUseStagingServer: isStaging}, + }) + .then(() => Onyx.merge(ONYXKEYS.ACCOUNT, {primaryLogin, requiresTwoFactorAuth, needsTwoFactorAuthSetup})) + .then(() => Onyx.merge(ONYXKEYS.HYBRID_APP, {isSingleNewDotEntry, closingReactNativeApp: false})), ) - .then(resetDidUserLoginDuringSessionIfNeeded) - .then(() => Promise.all(Object.entries(newDotOnyxValues).map(([key, value]) => Onyx.merge(key as OnyxKey, value ?? {})))) .then(() => { - isHybridAppSetupFinished = true; - return Promise.resolve(); + if (clearOnyxOnStart) { + return openApp(); + } + return getLastUpdateIDAppliedToClient().then((lastUpdateId) => { + return reconnectApp(lastUpdateId); + }); }) .catch((error) => { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); @@ -695,11 +737,7 @@ function signIn(validateCode: string, twoFactorAuthCode?: string) { params.validateCode = validateCode || credentials.validateCode; } - API.write(WRITE_COMMANDS.SIGN_IN_USER, params, { - optimisticData, - successData, - failureData, - }); + API.write(WRITE_COMMANDS.SIGN_IN_USER, params, {optimisticData, successData, failureData}); }); } @@ -1439,7 +1477,7 @@ export { isSupportAuthToken, hasStashedSession, signUpUser, - setupNewDotAfterTransitionFromOldDot, + signInAfterTransitionFromOldDot, AddWorkEmail, MergeIntoAccountAndLogin, resetSMSDeliveryFailureStatus, diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index da39079dc5123..626641927b687 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -1,10 +1,8 @@ import Onyx from 'react-native-onyx'; import {getMicroSecondOnyxErrorWithMessage} from '@libs/ErrorUtils'; import {clearSessionStorage} from '@libs/Navigation/helpers/lastVisitedTabPathUtils'; -import CONFIG from '@src/CONFIG'; import type {OnyxKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; -import {resetSignInFlow} from './HybridApp'; import {clearAllPolicies} from './Policy/Policy'; let currentIsOffline: boolean | undefined; @@ -35,9 +33,6 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { } return Onyx.clear(keysToPreserve).then(() => { - if (CONFIG.IS_HYBRID_APP) { - resetSignInFlow(); - } clearAllPolicies(); if (!errorMessage) { return; diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index d486c27391a7c..c0d7a21ccf26e 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -117,11 +117,6 @@ function closeAccount(reason: string) { optimisticData, failureData, }); - - // On HybridApp, we need to sign out from the oldDot app as well to keep state of both apps in sync - if (CONFIG.IS_HYBRID_APP) { - HybridAppModule.signOutFromOldDot(); - } } /** diff --git a/src/pages/ErrorPage/SessionExpiredPage.tsx b/src/pages/ErrorPage/SessionExpiredPage.tsx index 0ef53fe11ecf1..4972b8e6459e6 100644 --- a/src/pages/ErrorPage/SessionExpiredPage.tsx +++ b/src/pages/ErrorPage/SessionExpiredPage.tsx @@ -9,7 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import {closeReactNativeApp} from '@userActions/HybridApp'; +import closeReactNativeApp from '@userActions/HybridApp'; import {clearSignInData} from '@userActions/Session'; import CONFIG from '@src/CONFIG'; diff --git a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx index c99db4de674dd..0680533b7d146 100644 --- a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx +++ b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx @@ -21,6 +21,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import closeReactNativeApp from '@libs/actions/HybridApp'; import {openOldDotLink} from '@libs/actions/Link'; import {setOnboardingAdminsChatReportID, setOnboardingPolicyID, setOnboardingUserReportedIntegration} from '@libs/actions/Welcome'; import Navigation from '@libs/Navigation/Navigation'; @@ -28,7 +29,6 @@ import {waitForIdle} from '@libs/Network/SequentialQueue'; import {shouldOnboardingRedirectToOldDot} from '@libs/OnboardingUtils'; import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import variables from '@styles/variables'; -import {closeReactNativeApp} from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import type {OnboardingAccounting} from '@src/CONST'; import CONST from '@src/CONST'; diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index 6236aeb2c9a08..c5e6a5d32ed0e 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -31,7 +31,7 @@ import Navigation from '@libs/Navigation/Navigation'; import {waitForIdle} from '@libs/Network/SequentialQueue'; import {shouldOnboardingRedirectToOldDot} from '@libs/OnboardingUtils'; import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; -import {closeReactNativeApp} from '@userActions/HybridApp'; +import closeReactNativeApp from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index ccf75fe922fda..0cf834958bfc4 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -46,7 +46,7 @@ import {getQuickActionIcon, getQuickActionTitle, isQuickActionAllowed} from '@li import {generateReportID, getDisplayNameForParticipant, getIcons, getReportName, getWorkspaceChats, isPolicyExpenseChat} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import variables from '@styles/variables'; -import {closeReactNativeApp} from '@userActions/HybridApp'; +import closeReactNativeApp from '@userActions/HybridApp'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 2f2c581f81072..f35fcb559a57f 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -29,7 +29,6 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import '@libs/actions/Delegate'; import {resetExitSurveyForm} from '@libs/actions/ExitSurvey'; import {checkIfFeedConnectionIsBroken} from '@libs/CardUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; @@ -41,7 +40,7 @@ import type SETTINGS_TO_RHP from '@navigation/linkingConfig/RELATIONS/SETTINGS_T import {showContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import {confirmReadyToOpenApp} from '@userActions/App'; -import {closeReactNativeApp} from '@userActions/HybridApp'; +import closeReactNativeApp from '@userActions/HybridApp'; import {openExternalLink} from '@userActions/Link'; import {hasPaymentMethodError} from '@userActions/PaymentMethods'; import {isSupportAuthToken, signOutAndRedirectToSignIn} from '@userActions/Session'; diff --git a/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx b/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx index 9a0e29bf933a1..233811f8cf8be 100644 --- a/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx +++ b/src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx @@ -17,7 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import {closeReactNativeApp} from '@userActions/HybridApp'; +import closeReactNativeApp from '@userActions/HybridApp'; import {openOldDotLink} from '@userActions/Link'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; diff --git a/src/pages/signin/SignUpWelcomeForm.tsx b/src/pages/signin/SignUpWelcomeForm.tsx index 20ae01b52d7c4..203e799770f05 100644 --- a/src/pages/signin/SignUpWelcomeForm.tsx +++ b/src/pages/signin/SignUpWelcomeForm.tsx @@ -6,9 +6,8 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getLatestErrorMessage} from '@libs/ErrorUtils'; -import {setReadyToShowAuthScreens} from '@userActions/HybridApp'; -import {clearSignInData, signUpUser} from '@userActions/Session'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import * as Session from '@userActions/Session'; import ONYXKEYS from '@src/ONYXKEYS'; import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink'; import Terms from './Terms'; @@ -17,8 +16,8 @@ function SignUpWelcomeForm() { const network = useNetwork(); const styles = useThemeStyles(); const {translate} = useLocalize(); - const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); - const serverErrorText = useMemo(() => (account ? getLatestErrorMessage(account) : ''), [account]); + const [account] = useOnyx(ONYXKEYS.ACCOUNT); + const serverErrorText = useMemo(() => (account ? ErrorUtils.getLatestErrorMessage(account) : ''), [account]); return ( <> @@ -29,10 +28,7 @@ function SignUpWelcomeForm() { large text={translate('welcomeSignUpForm.join')} isLoading={account?.isLoading} - onPress={() => { - signUpUser(); - setReadyToShowAuthScreens(true); - }} + onPress={() => Session.signUpUser()} pressOnEnter style={[styles.mb2]} /> @@ -42,7 +38,7 @@ function SignUpWelcomeForm() { message={serverErrorText} /> )} - clearSignInData()} /> + Session.clearSignInData()} /> diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 46a86e701c50a..1cdac26938d11 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -1,41 +1,8 @@ -/** Data structure holding user's OldDot access information */ -type HybridAppDelegateAccessData = { - /** Indicates if OldDot is accessed in a delegate mode */ - isDelegateAccess?: boolean; - - /** Email address through which the user is currently authenticated in OldDot */ - oldDotCurrentUserEmail?: string; - - /** Authentication token used in OldDot */ - oldDotCurrentAuthToken?: string; - - /** Encrypted authentication token used in OldDot */ - oldDotCurrentEncryptedAuthToken?: string; - - /** Account ID for the user in OldDot */ - oldDotCurrentAccountID?: number; -}; - /** State and configuration of a HybridApp */ type HybridApp = { - /** Stores the information if HybridApp uses NewDot's sign-in flow */ - useNewDotSignInPage?: boolean; - - /** Determines if the AuthScreens are ready to be displayed */ - readyToShowAuthScreens?: boolean; - /** Specifies if the transition from OldDot was made to display a specific subset of screens in NewDot */ isSingleNewDotEntry?: boolean; - /** Indicates if the last sign out action was performed from OldDot */ - loggedOutFromOldDot?: boolean; - - /** Determines whether to remove delegated access */ - shouldRemoveDelegatedAccess?: boolean; - - /** Holds delegate access information */ - delegateAccessData?: HybridAppDelegateAccessData; - /** Indicates if NewDot is being closed */ closingReactNativeApp?: boolean; }; diff --git a/src/types/onyx/TryNewDot.ts b/src/types/onyx/TryNewDot.ts index 4d79b0f36c49b..8d34b2efa18c2 100644 --- a/src/types/onyx/TryNewDot.ts +++ b/src/types/onyx/TryNewDot.ts @@ -5,11 +5,11 @@ type TryNewDot = { /** * This key is mostly used on OldDot. In NewDot, we only use `completedHybridAppOnboarding`. */ - classicRedirect?: { + classicRedirect: { /** * Indicates if transition from OldDot to NewDot should happen in HybridApp. */ - dismissed: boolean; + dismissed: boolean | string; /** * Indicates timestamp of an action. */ @@ -23,7 +23,7 @@ type TryNewDot = { /** * This key is added when user is migrated from OldDot to NewDot with nudge migration as part of a cohort. */ - nudgeMigration?: { + nudgeMigration: { /** Indicates timestamp of an action. */ timestamp: Date; };