From 5eaa292a55b8e2213dddce2f129a76cb8bab98ab Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Tue, 22 Oct 2024 15:57:34 +0200 Subject: [PATCH 01/41] Implement POC of NewDot login page on HybridApp --- src/Expensify.tsx | 5 +- src/ONYXKEYS.ts | 4 ++ src/components/InitialURLContextProvider.tsx | 7 ++- .../Navigation/AppNavigator/PublicScreens.tsx | 4 +- .../Navigation/AppNavigator/index.native.tsx | 19 ++++-- src/libs/actions/Session/index.ts | 62 +++++++++++++------ .../ValidateCodeForm/BaseValidateCodeForm.tsx | 34 ++++++++-- src/types/modules/react-native.d.ts | 2 + 8 files changed, 102 insertions(+), 35 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index e07b03a6d405..aa374ff935ae 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -96,6 +96,7 @@ function Expensify() { const [screenShareRequest] = useOnyx(ONYXKEYS.SCREEN_SHARE_REQUEST); const [focusModeNotification] = useOnyx(ONYXKEYS.FOCUS_MODE_NOTIFICATION, {initWithStoredValues: false}); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); + const [useNewDotSignInPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); useEffect(() => { if (!account?.needsTwoFactorAuthSetup || account.requiresTwoFactorAuth) { @@ -119,7 +120,9 @@ function Expensify() { const shouldInit = isNavigationReady && hasAttemptedToOpenPublicRoom; const shouldHideSplash = shouldInit && - (NativeModules.HybridAppModule ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && isAuthenticated : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); + (NativeModules.HybridAppModule + ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || useNewDotSignInPage) + : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); const initializeClient = () => { if (!Visibility.isVisible()) { diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d083a46d7760..4e04139e4c18 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -444,6 +444,9 @@ const ONYXKEYS = { /** Company cards custom names */ NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES: 'nvp_expensify_ccCustomNames', + /** Stores the information if HybridApp uses NewDot's sign in flow */ + USE_NEWDOT_SIGN_IN_PAGE: 'useNewDotSignInPage', + /** Collection Keys */ COLLECTION: { DOWNLOAD: 'download_', @@ -1007,6 +1010,7 @@ type OnyxValuesMapping = { [ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean; [ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; + [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: boolean; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping; diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index 85ad54ca6c94..f026f2de53f9 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -31,9 +31,10 @@ function InitialURLContextProvider({children, url}: InitialURLContextProviderPro useEffect(() => { if (url) { - const route = signInAfterTransitionFromOldDot(url); - setInitialURL(route); - setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); + signInAfterTransitionFromOldDot(url).then((route) => { + setInitialURL(route); + setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); + }); return; } Linking.getInitialURL().then((initURL) => { diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.tsx b/src/libs/Navigation/AppNavigator/PublicScreens.tsx index cfd41a4b1fa0..faf6563a0ef3 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.tsx +++ b/src/libs/Navigation/AppNavigator/PublicScreens.tsx @@ -1,9 +1,7 @@ import {createStackNavigator} from '@react-navigation/stack'; import React from 'react'; -import {NativeModules} from 'react-native'; 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'; @@ -24,7 +22,7 @@ function PublicScreens() { { + if (!NativeModules.HybridAppModule) { + return authenticated; + } + + return useNewDotLoginPage === false && authenticated; + }, [authenticated, useNewDotLoginPage]); useEffect(() => { - if (!NativeModules.HybridAppModule || !initialURL) { + if (!NativeModules.HybridAppModule || !initialURL || !shouldShowAuthScreens) { return; } @@ -21,9 +32,9 @@ function AppNavigator({authenticated}: AppNavigatorProps) { Navigation.navigate(Navigation.parseHybridAppUrl(initialURL)); setInitialURL(undefined); }); - }, [initialURL, setInitialURL]); + }, [initialURL, setInitialURL, shouldShowAuthScreens]); - if (authenticated) { + if (shouldShowAuthScreens) { const AuthScreens = require('./AuthScreens').default; // These are the protected screens and only accessible when an authToken is present diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 37488442525d..1972908fcb1e 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -209,7 +209,7 @@ function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSess if (!isAnonymousUser()) { // In the HybridApp, we want the Old Dot to handle the sign out process if (NativeModules.HybridAppModule && killHybridApp) { - NativeModules.HybridAppModule.closeReactNativeApp(true, false); + NativeModules.HybridAppModule.signOutFromOldDot(); return; } // We'll only call signOut if we're not stashing the session and this is not a supportal session, @@ -482,29 +482,51 @@ function signUpUser() { function signInAfterTransitionFromOldDot(transitionURL: string) { const [route, queryParams] = transitionURL.split('?'); + const queryParamsObject = queryParams + ? Object.fromEntries( + queryParams.split('&').map((param) => { + const [key, value] = param.split('='); + return [key, value]; + }), + ) + : {}; + + const {useNewDotSignInPage, isSingleNewDotEntry} = queryParamsObject; + + const clearOnyxBeforeSignIn = () => { + if (useNewDotSignInPage !== 'true') { + return Promise.resolve(); + } - const {email, authToken, encryptedAuthToken, accountID, autoGeneratedLogin, autoGeneratedPassword, clearOnyxOnStart, completedHybridAppOnboarding} = Object.fromEntries( - queryParams.split('&').map((param) => { - const [key, value] = param.split('='); - return [key, value]; - }), - ); - - const setSessionDataAndOpenApp = () => { - 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); + return Onyx.clear(); }; - if (clearOnyxOnStart === 'true') { - Onyx.clear(KEYS_TO_PRESERVE).then(setSessionDataAndOpenApp); - } else { - setSessionDataAndOpenApp(); - } + const initAppAfterTransition = () => { + if (useNewDotSignInPage === 'true') { + return Promise.resolve(); + } + + return App.openApp(); + }; + + const setSessionDataAndOpenApp = new Promise((resolve) => { + clearOnyxBeforeSignIn() + .then(() => + Onyx.multiSet({ + [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: useNewDotSignInPage === 'true', + [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed: 'true'}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands + }), + ) + .then(initAppAfterTransition) + .catch((error) => { + Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); + }) + .finally(() => { + resolve(`${route}?singleNewDotEntry=${isSingleNewDotEntry}` as Route); + }); + }); - return route as Route; + return setSessionDataAndOpenApp; } /** diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index f73c0a1602fb..8b196673d729 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -1,7 +1,7 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; -import {View} from 'react-native'; +import {NativeModules, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; @@ -30,6 +30,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import type ValidateCodeFormProps from './types'; type BaseValidateCodeFormProps = WithToggleVisibilityViewProps & @@ -61,6 +62,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [timeRemaining, setTimeRemaining] = useState(CONST.REQUEST_CODE_DELAY as number); const [recoveryCode, setRecoveryCode] = useState(''); const [needToClearError, setNeedToClearError] = useState(!!account?.errors); + const [tryNewDot, tryNewDotMetadata] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); const prevRequiresTwoFactorAuth = usePrevious(account?.requiresTwoFactorAuth); const prevValidateCode = usePrevious(credentials?.validateCode); @@ -74,6 +76,30 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); + const isLoading = useMemo(() => { + if (!NativeModules.HybridAppModule || !session?.authToken) { + return isValidateCodeFormSubmitting; + } + + if (isLoadingOnyxValue(tryNewDotMetadata)) { + return true; + } + + return (tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true') && !!session.authToken; + }, [isValidateCodeFormSubmitting, session?.authToken, tryNewDot, tryNewDotMetadata]); + + useEffect(() => { + if (!NativeModules.HybridAppModule || isValidateCodeFormSubmitting || !session?.authToken) { + return; + } + + if (tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true') { + if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { + NativeModules.HybridAppModule.signInToOldDot(credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword); + } + } + }, [credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, isValidateCodeFormSubmitting, session?.authToken, tryNewDot?.classicRedirect.dismissed]); + useEffect(() => { if (!(inputValidateCodeRef.current && hasError && (session?.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED || account?.isLoading))) { return; @@ -335,7 +361,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco style={[styles.mt2]} onPress={switchBetween2faAndRecoveryCode} hoverDimmingValue={1} - disabled={isValidateCodeFormSubmitting} + disabled={isLoading} role={CONST.ROLE.BUTTON} accessibilityLabel={isUsingRecoveryCode ? translate('recoveryCodeForm.use2fa') : translate('recoveryCodeForm.useRecoveryCode')} > @@ -394,7 +420,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco large style={[styles.mv3]} text={translate('common.signIn')} - isLoading={isValidateCodeFormSubmitting} + isLoading={isLoading} onPress={validateAndSubmitForm} /> diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 40c5b72ce1e4..6389ed0f50be 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -8,6 +8,8 @@ import type StartupTimer from '@libs/StartupTimer/types'; type HybridAppModule = { closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void; completeOnboarding: (status: boolean) => void; + signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string) => void; + signOutFromOldDot: () => void; exitApp: () => void; }; From f81ec86898ab6cf54a45e3d3191223f845d962e2 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 29 Oct 2024 12:59:42 +0100 Subject: [PATCH 02/41] fix android crash --- src/libs/actions/Session/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 1972908fcb1e..96bd7589d288 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -210,7 +210,6 @@ function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSess // In the HybridApp, we want the Old Dot to handle the sign out process if (NativeModules.HybridAppModule && killHybridApp) { NativeModules.HybridAppModule.signOutFromOldDot(); - return; } // We'll only call signOut if we're not stashing the session and this is not a supportal session, // otherwise we'll call the API to invalidate the autogenerated credentials used for infinite From 290748d4e0318f98fac86053d57547e436ec164a Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 29 Oct 2024 16:25:53 +0100 Subject: [PATCH 03/41] add HybridApp onyx key --- src/ONYXKEYS.ts | 3 +++ src/types/onyx/HybridApp.ts | 7 +++++++ src/types/onyx/index.ts | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 src/types/onyx/HybridApp.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 6c73d8cdee37..02d6c15fb856 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -451,6 +451,8 @@ const ONYXKEYS = { /** Stores the information if HybridApp uses NewDot's sign in flow */ USE_NEWDOT_SIGN_IN_PAGE: 'useNewDotSignInPage', + HYBRID_APP: 'hybridApp', + /** Collection Keys */ COLLECTION: { DOWNLOAD: 'download_', @@ -1016,6 +1018,7 @@ type OnyxValuesMapping = { [ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: boolean; + [ONYXKEYS.HYBRID_APP]: OnyxTypes.HybridApp; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping; diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts new file mode 100644 index 000000000000..e7d4c08f82db --- /dev/null +++ b/src/types/onyx/HybridApp.ts @@ -0,0 +1,7 @@ +/** */ +type HybridApp = { + /** */ + isSigningIn?: boolean; +}; + +export default HybridApp; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index fe559aab3aa9..99d8f33897a1 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -24,6 +24,7 @@ import type ExpensifyCardSettings from './ExpensifyCardSettings'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; import type Fund from './Fund'; +import type HybridApp from './HybridApp'; import type ImportedSpreadsheet from './ImportedSpreadsheet'; import type IntroSelected from './IntroSelected'; import type InvitedEmailsToAccountIDs from './InvitedEmailsToAccountIDs'; @@ -235,4 +236,5 @@ export type { RecentSearchItem, ImportedSpreadsheet, ValidateMagicCodeAction, + HybridApp, }; From 05c4573c1c008eb47c91147af8aff71eb209624f Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 12:03:17 +0100 Subject: [PATCH 04/41] wip - sending native events --- src/App.tsx | 2 ++ src/CONST.ts | 15 +++++++++++++++ src/libs/HybridApp.ts | 19 +++++++++++++++++++ src/libs/actions/HybridApp.ts | 12 ++++++++++++ src/types/onyx/HybridApp.ts | 3 +++ 5 files changed, 51 insertions(+) create mode 100644 src/libs/HybridApp.ts create mode 100644 src/libs/actions/HybridApp.ts diff --git a/src/App.tsx b/src/App.tsx index 177cc00c7dee..4e5f8bb858f8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,6 +36,7 @@ import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import {ReportIDsContextProvider} from './hooks/useReportIDs'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; +import {init} from './libs/HybridApp'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; @@ -59,6 +60,7 @@ const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode function App({url}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); + init(); return ( diff --git a/src/CONST.ts b/src/CONST.ts index 9f7940e17c23..01c195682c75 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4424,6 +4424,9 @@ const CONST = { }, EVENTS: { SCROLLING: 'scrolling', + HYBRID_APP: { + ON_SIGN_IN_FINISHED: 'onSignInFinished', + }, }, CHAT_HEADER_LOADER_HEIGHT: 36, @@ -5973,6 +5976,18 @@ const CONST = { HIDDEN: `hidden`, }, + OLD_DOT_SIGN_IN_STATE: { + NOT_STARTED: 'notStarted', + STARTED: 'started', + FINISHED: 'finished', + }, + + NEW_DOT_SIGN_IN_STATE: { + NOT_STARTED: 'notStarted', + STARTED: 'started', + FINISHED: 'finished', + }, + CSV_IMPORT_COLUMNS: { EMAIL: 'email', NAME: 'name', diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts new file mode 100644 index 000000000000..3d63adf2dc7b --- /dev/null +++ b/src/libs/HybridApp.ts @@ -0,0 +1,19 @@ +import {DeviceEventEmitter, NativeModules} from 'react-native'; +import CONST from '@src/CONST'; +import {setIsSigningIn} from './actions/HybridApp'; +import type {Init} from './ActiveClientManager/types'; +import Log from './Log'; + +const init: Init = () => { + if (!NativeModules.HybridAppModule) { + return; + } + + // Setup event listeners + DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, () => { + Log.info('[HybridApp] `onSignInFinished` event received', true); + setIsSigningIn(false); + }); +}; + +export {init}; diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp.ts new file mode 100644 index 000000000000..940c562cd67e --- /dev/null +++ b/src/libs/actions/HybridApp.ts @@ -0,0 +1,12 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function setIsSigningIn(isSigningIn: boolean) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {isSigningIn}); +} + +function setReadyToShowAuthScreens(readyToShowAuthScreens: boolean) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToShowAuthScreens}); +} + +export {setIsSigningIn, setReadyToShowAuthScreens}; diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index e7d4c08f82db..0b5bf1868e35 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -2,6 +2,9 @@ type HybridApp = { /** */ isSigningIn?: boolean; + + /** */ + readyToShowAuthScreens?: boolean; }; export default HybridApp; From bb05ef91b9c84f0892ea470e62f90237a9e75130 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 12:57:57 +0100 Subject: [PATCH 05/41] js logic --- src/Expensify.tsx | 10 +-- .../Navigation/AppNavigator/index.native.tsx | 28 ++++++- src/libs/actions/Session/index.ts | 8 +- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 80 +++++++++++++++---- 4 files changed, 97 insertions(+), 29 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index aa374ff935ae..cd249b055d07 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -118,11 +118,11 @@ function Expensify() { const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]); const shouldInit = isNavigationReady && hasAttemptedToOpenPublicRoom; - const shouldHideSplash = - shouldInit && - (NativeModules.HybridAppModule - ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || useNewDotSignInPage) - : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); + const shouldHideSplash = true; + // shouldInit && + // (NativeModules.HybridAppModule + // ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || useNewDotSignInPage) + // : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); const initializeClient = () => { if (!Visibility.isVisible()) { diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index 5c5bafebacbd..0038ea8a39da 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -4,6 +4,7 @@ import {useOnyx} from 'react-native-onyx'; import {InitialURLContext} from '@components/InitialURLContextProvider'; import Navigation from '@libs/Navigation/Navigation'; import ONYXKEYS from '@src/ONYXKEYS'; +import {TryNewDot} from '@src/types/onyx'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; type AppNavigatorProps = { @@ -11,17 +12,38 @@ type AppNavigatorProps = { authenticated: boolean; }; +function shouldUseOldApp(tryNewDot?: TryNewDot) { + return tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true'; +} + function AppNavigator({authenticated}: AppNavigatorProps) { const {initialURL, setInitialURL} = useContext(InitialURLContext); - const [useNewDotLoginPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); + const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); + const [useNewDotSignInPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); + /** + * kiedy chcemy pokazać auth screens: + * - logujemy do OD + * - nie chcemy wcale + * - logujemy do ND + * - gdy jesteśmy authenticated (po odpaleniu signInToOldDot) + * - przechodzimy z OD do ND + * - od razu o ile jesteśmy zalogowani, ale powinniśmy być zawsze + */ const shouldShowAuthScreens = useMemo(() => { if (!NativeModules.HybridAppModule) { return authenticated; } + if (shouldUseOldApp(tryNewDot)) { + return false; + } + + console.log('dupa dupa dupa', authenticated, hybridApp?.readyToShowAuthScreens); + return authenticated && (!useNewDotSignInPage || hybridApp?.readyToShowAuthScreens); + }, [authenticated, hybridApp?.readyToShowAuthScreens, tryNewDot, useNewDotSignInPage]); - return useNewDotLoginPage === false && authenticated; - }, [authenticated, useNewDotLoginPage]); + console.log('dupa should show auth screens', shouldShowAuthScreens); useEffect(() => { if (!NativeModules.HybridAppModule || !initialURL || !shouldShowAuthScreens) { diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 96bd7589d288..847d24299c48 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -39,9 +39,10 @@ import * as SessionUtils 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 * as App from '@userActions/App'; +import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import * as Device from '@userActions/Device'; +import {setReadyToShowAuthScreens} from '@userActions/HybridApp'; import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; @@ -494,6 +495,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { const clearOnyxBeforeSignIn = () => { if (useNewDotSignInPage !== 'true') { + setReadyToShowAuthScreens(true); return Promise.resolve(); } @@ -508,7 +510,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return App.openApp(); }; - const setSessionDataAndOpenApp = new Promise((resolve) => { + return new Promise((resolve) => { clearOnyxBeforeSignIn() .then(() => Onyx.multiSet({ @@ -524,8 +526,6 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { resolve(`${route}?singleNewDotEntry=${isSingleNewDotEntry}` as Route); }); }); - - return setSessionDataAndOpenApp; } /** diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 8b196673d729..24aff5360146 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -1,8 +1,9 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; import {NativeModules, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -24,11 +25,13 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; +import {setIsSigningIn, setReadyToShowAuthScreens} from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {TryNewDot} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import type ValidateCodeFormProps from './types'; @@ -47,10 +50,14 @@ type ValidateCodeFormVariant = 'validateCode' | 'twoFactorAuthCode' | 'recoveryC type FormError = Partial>; +function shouldUseOldApp(tryNewDot?: TryNewDot) { + return tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true'; +} + function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingRecoveryCode, isVisible}: BaseValidateCodeFormProps, forwardedRef: ForwardedRef) { const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); - const [session] = useOnyx(ONYXKEYS.SESSION); + const [session, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -62,7 +69,12 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [timeRemaining, setTimeRemaining] = useState(CONST.REQUEST_CODE_DELAY as number); const [recoveryCode, setRecoveryCode] = useState(''); const [needToClearError, setNeedToClearError] = useState(!!account?.errors); - const [tryNewDot, tryNewDotMetadata] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + const [oldDotSignInState, setOldDotSignInState] = useState>(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); + const [newDotSignInState, setNewDotSignInState] = useState>(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); + + const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); + const [useNewDotSignInPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); const prevRequiresTwoFactorAuth = usePrevious(account?.requiresTwoFactorAuth); const prevValidateCode = usePrevious(credentials?.validateCode); @@ -76,29 +88,62 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); - const isLoading = useMemo(() => { - if (!NativeModules.HybridAppModule || !session?.authToken) { - return isValidateCodeFormSubmitting; + /** + * ustawianie stanu logowania ND na finished, jak używamy strony logowania + */ + useEffect(() => { + if (!NativeModules.HybridAppModule || !useNewDotSignInPage) { + return; } - if (isLoadingOnyxValue(tryNewDotMetadata)) { - return true; + if (isLoadingOnyxValue(sessionMetadata)) { + return; } - return (tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true') && !!session.authToken; - }, [isValidateCodeFormSubmitting, session?.authToken, tryNewDot, tryNewDotMetadata]); + if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { + setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.FINISHED); + } + }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, sessionMetadata, useNewDotSignInPage]); + /** + * ustawianie stanu logowania OD na finished, jak używamy strony logowania + */ useEffect(() => { - if (!NativeModules.HybridAppModule || isValidateCodeFormSubmitting || !session?.authToken) { + if (!NativeModules.HybridAppModule || !useNewDotSignInPage) { return; } - if (tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true') { - if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { - NativeModules.HybridAppModule.signInToOldDot(credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword); + if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.STARTED && !hybridApp?.isSigningIn) { + setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.FINISHED); + } + }, [hybridApp?.isSigningIn, oldDotSignInState, useNewDotSignInPage]); + + /** + * główny efekt obsługujacy kilka rzeczy + * - odpalanie OD sign in po skończeniu logowania do ND + * - jak chcemy używać ND to w tym momencie jesteśmy gotowi i pokazujemy auth screens + * - jeśli skońćzyło się logowanie do OD i chcemy używać starej apki to zamykamy RN + */ + useEffect(() => { + if (!NativeModules.HybridAppModule || !useNewDotSignInPage) { + return; + } + + if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.FINISHED && oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED) { + if (shouldUseOldApp(tryNewDot)) { + setIsSigningIn(true); + } else { + setReadyToShowAuthScreens(true); } + + setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.STARTED); + NativeModules.HybridAppModule.signInToOldDot(credentials?.autoGeneratedLogin ?? '', credentials?.autoGeneratedPassword ?? ''); + } + + if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { + NativeModules.HybridAppModule.closeReactNativeApp(false, false); } - }, [credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, isValidateCodeFormSubmitting, session?.authToken, tryNewDot?.classicRedirect.dismissed]); + }, [account?.isLoading, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, newDotSignInState, oldDotSignInState, tryNewDot, useNewDotSignInPage]); useEffect(() => { if (!(inputValidateCodeRef.current && hasError && (session?.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED || account?.isLoading))) { @@ -308,6 +353,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const recoveryCodeOr2faCode = isUsingRecoveryCode ? recoveryCode : twoFactorAuthCode; + setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.STARTED); const accountID = credentials?.accountID; if (accountID) { SessionActions.signInWithValidateCode(accountID, validateCode, recoveryCodeOr2faCode); @@ -361,7 +407,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco style={[styles.mt2]} onPress={switchBetween2faAndRecoveryCode} hoverDimmingValue={1} - disabled={isLoading} + disabled={isValidateCodeFormSubmitting} role={CONST.ROLE.BUTTON} accessibilityLabel={isUsingRecoveryCode ? translate('recoveryCodeForm.use2fa') : translate('recoveryCodeForm.useRecoveryCode')} > @@ -420,7 +466,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco large style={[styles.mv3]} text={translate('common.signIn')} - isLoading={isLoading} + isLoading={shouldUseOldApp(tryNewDot) ? isValidateCodeFormSubmitting || hybridApp?.isSigningIn : isValidateCodeFormSubmitting} onPress={validateAndSubmitForm} /> From 94ebb8fea73d4a9f68e9a76b153570dfba40a3b5 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 13:29:12 +0100 Subject: [PATCH 06/41] fix switching to new experience --- src/libs/Navigation/AppNavigator/index.native.tsx | 3 --- src/libs/actions/Session/index.ts | 9 +++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index 0038ea8a39da..124bd28d3376 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -39,12 +39,9 @@ function AppNavigator({authenticated}: AppNavigatorProps) { return false; } - console.log('dupa dupa dupa', authenticated, hybridApp?.readyToShowAuthScreens); return authenticated && (!useNewDotSignInPage || hybridApp?.readyToShowAuthScreens); }, [authenticated, hybridApp?.readyToShowAuthScreens, tryNewDot, useNewDotSignInPage]); - console.log('dupa should show auth screens', shouldShowAuthScreens); - useEffect(() => { if (!NativeModules.HybridAppModule || !initialURL || !shouldShowAuthScreens) { return; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 847d24299c48..5bf732886679 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -512,12 +512,13 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return new Promise((resolve) => { clearOnyxBeforeSignIn() - .then(() => + .then(() => { + const dismissed = useNewDotSignInPage === 'true' ? 'false' : 'false'; Onyx.multiSet({ [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: useNewDotSignInPage === 'true', - [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed: 'true'}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands - }), - ) + [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands + }); + }) .then(initAppAfterTransition) .catch((error) => { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); From a9ff5866198e48b44b5736a500bf434b8a8851a3 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 14:12:24 +0100 Subject: [PATCH 07/41] block switching when still signing in to OD --- src/libs/HybridApp.ts | 3 ++- src/libs/actions/HybridApp.ts | 6 +++++- src/pages/settings/InitialSettingsPage.tsx | 4 ++++ src/types/onyx/HybridApp.ts | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 3d63adf2dc7b..9b2c51369da0 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -1,6 +1,6 @@ import {DeviceEventEmitter, NativeModules} from 'react-native'; import CONST from '@src/CONST'; -import {setIsSigningIn} from './actions/HybridApp'; +import {setIsSigningIn, setReadyToSwitchToClassicExperience} from './actions/HybridApp'; import type {Init} from './ActiveClientManager/types'; import Log from './Log'; @@ -13,6 +13,7 @@ const init: Init = () => { DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, () => { Log.info('[HybridApp] `onSignInFinished` event received', true); setIsSigningIn(false); + setReadyToSwitchToClassicExperience(true); }); }; diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp.ts index 940c562cd67e..c2a8554486ff 100644 --- a/src/libs/actions/HybridApp.ts +++ b/src/libs/actions/HybridApp.ts @@ -9,4 +9,8 @@ function setReadyToShowAuthScreens(readyToShowAuthScreens: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToShowAuthScreens}); } -export {setIsSigningIn, setReadyToShowAuthScreens}; +function setReadyToSwitchToClassicExperience(readyToSwitchToClassicExperience: boolean) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToSwitchToClassicExperience}); +} + +export {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience}; diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 8c1d68e0a95b..9c6dad757214 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -82,6 +82,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); + const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); const network = useNetwork(); const theme = useTheme(); @@ -236,6 +237,9 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr ...(NativeModules.HybridAppModule ? { action: () => { + if (!hybridApp?.readyToSwitchToClassicExperience) { + return; + } NativeModules.HybridAppModule.closeReactNativeApp(false, true); setInitialURL(undefined); }, diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 0b5bf1868e35..7f58c5664152 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -5,6 +5,9 @@ type HybridApp = { /** */ readyToShowAuthScreens?: boolean; + + /** */ + readyToSwitchToClassicExperience?: boolean; }; export default HybridApp; From ec6315eb5631c3ff6a34aa5eb9e403a029763d22 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 14:16:00 +0100 Subject: [PATCH 08/41] reset onyx values on start --- src/libs/actions/Session/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 5bf732886679..38ec1b736fd0 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -517,6 +517,11 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { Onyx.multiSet({ [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: useNewDotSignInPage === 'true', [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands + [ONYXKEYS.HYBRID_APP]: { + readyToSwitchToClassicExperience: false, + readyToShowAuthScreens: useNewDotSignInPage !== 'true', + isSigningIn: false, + }, }); }) .then(initAppAfterTransition) From e1562bb0d80181d0d8922eb916f0063ebc00172f Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 15:44:52 +0100 Subject: [PATCH 09/41] preserve HYBRID_APP onyx key when signing out --- src/libs/actions/HybridApp.ts | 6 ++++- src/libs/actions/SignInRedirect.ts | 1 + src/pages/settings/InitialSettingsPage.tsx | 4 +++- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 24 +++++++++++++++++-- src/types/onyx/HybridApp.ts | 3 +++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp.ts index c2a8554486ff..2d4fdd93621d 100644 --- a/src/libs/actions/HybridApp.ts +++ b/src/libs/actions/HybridApp.ts @@ -13,4 +13,8 @@ function setReadyToSwitchToClassicExperience(readyToSwitchToClassicExperience: b Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToSwitchToClassicExperience}); } -export {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience}; +function setShouldResetSigningInLogic(shouldResetSigningInLogic: boolean) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {shouldResetSigningInLogic}); +} + +export {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic}; diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index 4d8b60265f29..2996c9939175 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -22,6 +22,7 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { keysToPreserve.push(ONYXKEYS.NVP_PREFERRED_LOCALE); keysToPreserve.push(ONYXKEYS.ACTIVE_CLIENTS); keysToPreserve.push(ONYXKEYS.DEVICE_ID); + keysToPreserve.push(ONYXKEYS.HYBRID_APP); // After signing out, set ourselves as offline if we were offline before logging out and we are not forcing it. // If we are forcing offline, ignore it while signed out, otherwise it would require a refresh because there's no way to toggle the switch to go back online while signed out. diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 9c6dad757214..cb2c0c677e3f 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -37,6 +37,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; +import {setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; @@ -269,12 +270,13 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr translationKey: signOutTranslationKey, icon: Expensicons.Exit, action: () => { + setShouldResetSigningInLogic(true); signOut(false); }, }, ], }; - }, [styles.pt4, signOut, setInitialURL]); + }, [styles.pt4, hybridApp?.readyToSwitchToClassicExperience, setInitialURL, signOut]); /** * Retuns JSX.Element with menu items diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 24aff5360146..0987f223209d 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; import {NativeModules, View} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; @@ -25,7 +25,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; -import {setIsSigningIn, setReadyToShowAuthScreens} from '@userActions/HybridApp'; +import {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; @@ -88,6 +88,23 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); + useEffect(() => { + if (!NativeModules.HybridAppModule) { + return; + } + if (!hybridApp?.shouldResetSigningInLogic) { + return; + } + + setReadyToShowAuthScreens(false); + setReadyToSwitchToClassicExperience(false); + setIsSigningIn(false); + Onyx.merge(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE, true); + setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); + setShouldResetSigningInLogic(false); + }, [hybridApp?.shouldResetSigningInLogic]); + /** * ustawianie stanu logowania ND na finished, jak używamy strony logowania */ @@ -129,6 +146,8 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco return; } + console.log('[HybridApp] newDotSignInState', newDotSignInState); + console.log('[HybridApp] oldDotSignInState', oldDotSignInState); if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.FINISHED && oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED) { if (shouldUseOldApp(tryNewDot)) { setIsSigningIn(true); @@ -136,6 +155,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco setReadyToShowAuthScreens(true); } + console.log('[HybridApp] signInToOldDot'); setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.STARTED); NativeModules.HybridAppModule.signInToOldDot(credentials?.autoGeneratedLogin ?? '', credentials?.autoGeneratedPassword ?? ''); } diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 7f58c5664152..9c239b8bab75 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -8,6 +8,9 @@ type HybridApp = { /** */ readyToSwitchToClassicExperience?: boolean; + + /** */ + shouldResetSigningInLogic?: boolean; }; export default HybridApp; From efde8055c86cac722c997980cf4f6c6bdf7f33d3 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 15:57:25 +0100 Subject: [PATCH 10/41] fix --- src/libs/actions/Session/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 38ec1b736fd0..b34f50b50e96 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -513,12 +513,12 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return new Promise((resolve) => { clearOnyxBeforeSignIn() .then(() => { - const dismissed = useNewDotSignInPage === 'true' ? 'false' : 'false'; + const dismissed = useNewDotSignInPage === 'true' ? 'true' : 'false'; Onyx.multiSet({ [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: useNewDotSignInPage === 'true', [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands [ONYXKEYS.HYBRID_APP]: { - readyToSwitchToClassicExperience: false, + readyToSwitchToClassicExperience: useNewDotSignInPage !== 'true', readyToShowAuthScreens: useNewDotSignInPage !== 'true', isSigningIn: false, }, From a1b9e1a2c5debbcf58eaead078b18171970bd31a Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 31 Oct 2024 18:00:15 +0100 Subject: [PATCH 11/41] move part of resetting logic --- src/Expensify.tsx | 10 +++++----- src/libs/actions/Session/index.ts | 5 ----- src/pages/settings/InitialSettingsPage.tsx | 8 ++++++-- .../signin/ValidateCodeForm/BaseValidateCodeForm.tsx | 4 ---- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index cd249b055d07..aa374ff935ae 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -118,11 +118,11 @@ function Expensify() { const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]); const shouldInit = isNavigationReady && hasAttemptedToOpenPublicRoom; - const shouldHideSplash = true; - // shouldInit && - // (NativeModules.HybridAppModule - // ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || useNewDotSignInPage) - // : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); + const shouldHideSplash = + shouldInit && + (NativeModules.HybridAppModule + ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || useNewDotSignInPage) + : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); const initializeClient = () => { if (!Visibility.isVisible()) { diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index b34f50b50e96..a0e688643de9 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -517,11 +517,6 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { Onyx.multiSet({ [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: useNewDotSignInPage === 'true', [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands - [ONYXKEYS.HYBRID_APP]: { - readyToSwitchToClassicExperience: useNewDotSignInPage !== 'true', - readyToShowAuthScreens: useNewDotSignInPage !== 'true', - isSigningIn: false, - }, }); }) .then(initAppAfterTransition) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index cb2c0c677e3f..58d7310e894d 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, use // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; import {NativeModules, View} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; import AccountSwitcherSkeletonView from '@components/AccountSwitcherSkeletonView'; @@ -37,7 +37,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; @@ -271,6 +271,10 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr icon: Expensicons.Exit, action: () => { setShouldResetSigningInLogic(true); + setReadyToShowAuthScreens(false); + setReadyToSwitchToClassicExperience(false); + setIsSigningIn(false); + Onyx.merge(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE, true); signOut(false); }, }, diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 0987f223209d..83efaac1099c 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -96,10 +96,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco return; } - setReadyToShowAuthScreens(false); - setReadyToSwitchToClassicExperience(false); - setIsSigningIn(false); - Onyx.merge(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE, true); setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); setShouldResetSigningInLogic(false); From 3ad1b62cae38bab09b155fef72c7112a23c3ec69 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 4 Nov 2024 10:05:23 +0100 Subject: [PATCH 12/41] move useNewDotSignInPage to HybridApp key --- src/Expensify.tsx | 4 ++-- src/ONYXKEYS.ts | 4 ---- src/libs/Navigation/AppNavigator/index.native.tsx | 5 ++--- src/libs/actions/Session/index.ts | 2 +- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 13 ++++++------- src/types/onyx/HybridApp.ts | 3 +++ 6 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index aa374ff935ae..c0db92069108 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -96,7 +96,7 @@ function Expensify() { const [screenShareRequest] = useOnyx(ONYXKEYS.SCREEN_SHARE_REQUEST); const [focusModeNotification] = useOnyx(ONYXKEYS.FOCUS_MODE_NOTIFICATION, {initWithStoredValues: false}); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); - const [useNewDotSignInPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); + const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); useEffect(() => { if (!account?.needsTwoFactorAuthSetup || account.requiresTwoFactorAuth) { @@ -121,7 +121,7 @@ function Expensify() { const shouldHideSplash = shouldInit && (NativeModules.HybridAppModule - ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || useNewDotSignInPage) + ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || hybridApp?.useNewDotSignInPage) : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); const initializeClient = () => { diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 2222ace5c411..b94d3afe94f0 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -452,9 +452,6 @@ const ONYXKEYS = { /** Company cards custom names */ NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES: 'nvp_expensify_ccCustomNames', - /** Stores the information if HybridApp uses NewDot's sign in flow */ - USE_NEWDOT_SIGN_IN_PAGE: 'useNewDotSignInPage', - HYBRID_APP: 'hybridApp', /** Collection Keys */ @@ -1026,7 +1023,6 @@ type OnyxValuesMapping = { [ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean; [ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; - [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: boolean; [ONYXKEYS.HYBRID_APP]: OnyxTypes.HybridApp; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping; diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index 124bd28d3376..022b3bffd2cb 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -20,7 +20,6 @@ function AppNavigator({authenticated}: AppNavigatorProps) { const {initialURL, setInitialURL} = useContext(InitialURLContext); const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); - const [useNewDotSignInPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); /** * kiedy chcemy pokazać auth screens: @@ -39,8 +38,8 @@ function AppNavigator({authenticated}: AppNavigatorProps) { return false; } - return authenticated && (!useNewDotSignInPage || hybridApp?.readyToShowAuthScreens); - }, [authenticated, hybridApp?.readyToShowAuthScreens, tryNewDot, useNewDotSignInPage]); + return authenticated && (!hybridApp?.useNewDotSignInPage || hybridApp?.readyToShowAuthScreens); + }, [authenticated, hybridApp?.readyToShowAuthScreens, tryNewDot, hybridApp?.useNewDotSignInPage]); useEffect(() => { if (!NativeModules.HybridAppModule || !initialURL || !shouldShowAuthScreens) { diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index a0e688643de9..6f27144b433d 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -515,7 +515,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { .then(() => { const dismissed = useNewDotSignInPage === 'true' ? 'true' : 'false'; Onyx.multiSet({ - [ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE]: useNewDotSignInPage === 'true', + [ONYXKEYS.HYBRID_APP]: {useNewDotSignInPage: useNewDotSignInPage === 'true'}, [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands }); }) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 83efaac1099c..661deac5f5a7 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -74,7 +74,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); - const [useNewDotSignInPage] = useOnyx(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE); const prevRequiresTwoFactorAuth = usePrevious(account?.requiresTwoFactorAuth); const prevValidateCode = usePrevious(credentials?.validateCode); @@ -105,7 +104,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * ustawianie stanu logowania ND na finished, jak używamy strony logowania */ useEffect(() => { - if (!NativeModules.HybridAppModule || !useNewDotSignInPage) { + if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage) { return; } @@ -116,20 +115,20 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.FINISHED); } - }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, sessionMetadata, useNewDotSignInPage]); + }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, sessionMetadata, hybridApp?.useNewDotSignInPage]); /** * ustawianie stanu logowania OD na finished, jak używamy strony logowania */ useEffect(() => { - if (!NativeModules.HybridAppModule || !useNewDotSignInPage) { + if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage) { return; } if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.STARTED && !hybridApp?.isSigningIn) { setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.FINISHED); } - }, [hybridApp?.isSigningIn, oldDotSignInState, useNewDotSignInPage]); + }, [hybridApp?.isSigningIn, oldDotSignInState, hybridApp?.useNewDotSignInPage]); /** * główny efekt obsługujacy kilka rzeczy @@ -138,7 +137,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * - jeśli skońćzyło się logowanie do OD i chcemy używać starej apki to zamykamy RN */ useEffect(() => { - if (!NativeModules.HybridAppModule || !useNewDotSignInPage) { + if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage) { return; } @@ -159,7 +158,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { NativeModules.HybridAppModule.closeReactNativeApp(false, false); } - }, [account?.isLoading, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, newDotSignInState, oldDotSignInState, tryNewDot, useNewDotSignInPage]); + }, [account?.isLoading, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, newDotSignInState, oldDotSignInState, tryNewDot, hybridApp?.useNewDotSignInPage]); useEffect(() => { if (!(inputValidateCodeRef.current && hasError && (session?.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED || account?.isLoading))) { diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 9c239b8bab75..75a9ba1da97d 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -1,5 +1,8 @@ /** */ type HybridApp = { + /** Stores the information if HybridApp uses NewDot's sign in flow */ + useNewDotSignInPage?: boolean; + /** */ isSigningIn?: boolean; From f9b0e5aa8541321f84a5d141a71257eef661d632 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 4 Nov 2024 10:37:12 +0100 Subject: [PATCH 13/41] fix lint & typecheck --- src/App.tsx | 4 ++-- src/Expensify.tsx | 2 +- src/libs/HybridApp.ts | 2 +- .../Navigation/AppNavigator/index.native.tsx | 2 +- src/libs/actions/HybridApp.ts | 6 +++++- src/libs/actions/Session/index.ts | 16 ++++++---------- src/pages/settings/InitialSettingsPage.tsx | 7 ++++--- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 18 ++++++------------ 8 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4e5f8bb858f8..a308512a40a5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,7 +36,7 @@ import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import {ReportIDsContextProvider} from './hooks/useReportIDs'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; -import {init} from './libs/HybridApp'; +import HybridApp from './libs/HybridApp'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; @@ -60,7 +60,7 @@ const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode function App({url}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); - init(); + HybridApp.init(); return ( diff --git a/src/Expensify.tsx b/src/Expensify.tsx index c0db92069108..5d9ab88f6806 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -121,7 +121,7 @@ function Expensify() { const shouldHideSplash = shouldInit && (NativeModules.HybridAppModule - ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || hybridApp?.useNewDotSignInPage) + ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || !!hybridApp?.useNewDotSignInPage) : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); const initializeClient = () => { diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 9b2c51369da0..79c3608c1fcc 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -17,4 +17,4 @@ const init: Init = () => { }); }; -export {init}; +export default {init}; diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index 022b3bffd2cb..7af3e7305662 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -4,7 +4,7 @@ import {useOnyx} from 'react-native-onyx'; import {InitialURLContext} from '@components/InitialURLContextProvider'; import Navigation from '@libs/Navigation/Navigation'; import ONYXKEYS from '@src/ONYXKEYS'; -import {TryNewDot} from '@src/types/onyx'; +import type {TryNewDot} from '@src/types/onyx'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; type AppNavigatorProps = { diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp.ts index 2d4fdd93621d..0dcd7bf11696 100644 --- a/src/libs/actions/HybridApp.ts +++ b/src/libs/actions/HybridApp.ts @@ -17,4 +17,8 @@ function setShouldResetSigningInLogic(shouldResetSigningInLogic: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {shouldResetSigningInLogic}); } -export {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic}; +function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); +} + +export {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage}; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 6f27144b433d..17f513dc34ce 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -482,16 +482,12 @@ function signUpUser() { function signInAfterTransitionFromOldDot(transitionURL: string) { const [route, queryParams] = transitionURL.split('?'); - const queryParamsObject = queryParams - ? Object.fromEntries( - queryParams.split('&').map((param) => { - const [key, value] = param.split('='); - return [key, value]; - }), - ) - : {}; - - const {useNewDotSignInPage, isSingleNewDotEntry} = queryParamsObject; + const {useNewDotSignInPage, isSingleNewDotEntry} = Object.fromEntries( + queryParams.split('&').map((param) => { + const [key, value] = param.split('='); + return [key, value]; + }), + ); const clearOnyxBeforeSignIn = () => { if (useNewDotSignInPage !== 'true') { diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 58d7310e894d..3c0be0e20eef 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, use // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; import {NativeModules, View} from 'react-native'; -import Onyx, {useOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; import AccountSwitcherSkeletonView from '@components/AccountSwitcherSkeletonView'; @@ -37,7 +37,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; @@ -274,7 +274,8 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr setReadyToShowAuthScreens(false); setReadyToSwitchToClassicExperience(false); setIsSigningIn(false); - Onyx.merge(ONYXKEYS.USE_NEWDOT_SIGN_IN_PAGE, true); + setUseNewDotSignInPage(true); + signOut(false); }, }, diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 661deac5f5a7..31c2ae8d2b1d 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; import {NativeModules, View} from 'react-native'; -import Onyx, {useOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; @@ -25,7 +25,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; -import {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import {setIsSigningIn, setReadyToShowAuthScreens, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; @@ -33,7 +33,6 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {TryNewDot} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import type ValidateCodeFormProps from './types'; type BaseValidateCodeFormProps = WithToggleVisibilityViewProps & @@ -57,7 +56,9 @@ function shouldUseOldApp(tryNewDot?: TryNewDot) { function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingRecoveryCode, isVisible}: BaseValidateCodeFormProps, forwardedRef: ForwardedRef) { const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); - const [session, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -72,9 +73,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [oldDotSignInState, setOldDotSignInState] = useState>(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); const [newDotSignInState, setNewDotSignInState] = useState>(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); - const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); - const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); - const prevRequiresTwoFactorAuth = usePrevious(account?.requiresTwoFactorAuth); const prevValidateCode = usePrevious(credentials?.validateCode); @@ -108,14 +106,10 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco return; } - if (isLoadingOnyxValue(sessionMetadata)) { - return; - } - if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.FINISHED); } - }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, sessionMetadata, hybridApp?.useNewDotSignInPage]); + }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, hybridApp?.useNewDotSignInPage]); /** * ustawianie stanu logowania OD na finished, jak używamy strony logowania From 377b6869707aa98b64b4e9617bb62e54fef273c7 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 4 Nov 2024 11:56:47 +0100 Subject: [PATCH 14/41] correctly set onyx props when switching from OD --- src/libs/actions/Session/index.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 17f513dc34ce..4ca9a2ab2398 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -42,7 +42,7 @@ import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContex import * as App from '@userActions/App'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import * as Device from '@userActions/Device'; -import {setReadyToShowAuthScreens} from '@userActions/HybridApp'; +import {setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience} from '@userActions/HybridApp'; import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; @@ -482,16 +482,19 @@ function signUpUser() { function signInAfterTransitionFromOldDot(transitionURL: string) { const [route, queryParams] = transitionURL.split('?'); - const {useNewDotSignInPage, isSingleNewDotEntry} = Object.fromEntries( - queryParams.split('&').map((param) => { - const [key, value] = param.split('='); - return [key, value]; - }), - ); + const {useNewDotSignInPage, isSingleNewDotEntry} = queryParams + ? Object.fromEntries( + queryParams.split('&').map((param) => { + const [key, value] = param.split('='); + return [key, value]; + }), + ) + : {useNewDotSignInPage: undefined, isSingleNewDotEntry: undefined}; const clearOnyxBeforeSignIn = () => { if (useNewDotSignInPage !== 'true') { setReadyToShowAuthScreens(true); + setReadyToSwitchToClassicExperience(true); return Promise.resolve(); } @@ -509,7 +512,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return new Promise((resolve) => { clearOnyxBeforeSignIn() .then(() => { - const dismissed = useNewDotSignInPage === 'true' ? 'true' : 'false'; + const dismissed = useNewDotSignInPage === 'true' ? 'false' : 'false'; Onyx.multiSet({ [ONYXKEYS.HYBRID_APP]: {useNewDotSignInPage: useNewDotSignInPage === 'true'}, [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands From 90f7c0f8b7215810381871c7e5aa8624b19ffff2 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 4 Nov 2024 13:42:58 +0100 Subject: [PATCH 15/41] don't set HybridApp key in onyx (merge it) --- src/libs/actions/Session/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 4ca9a2ab2398..cbc4ab4dc052 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -42,7 +42,7 @@ import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContex import * as App from '@userActions/App'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import * as Device from '@userActions/Device'; -import {setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience} from '@userActions/HybridApp'; +import {setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setUseNewDotSignInPage} from '@userActions/HybridApp'; import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; @@ -512,9 +512,9 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return new Promise((resolve) => { clearOnyxBeforeSignIn() .then(() => { - const dismissed = useNewDotSignInPage === 'true' ? 'false' : 'false'; + setUseNewDotSignInPage(useNewDotSignInPage === 'true'); + const dismissed = useNewDotSignInPage === 'true' ? 'true' : 'false'; Onyx.multiSet({ - [ONYXKEYS.HYBRID_APP]: {useNewDotSignInPage: useNewDotSignInPage === 'true'}, [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands }); }) From 4b3e539a48974d8d3a91fe3ee7815207e7a83e0b Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 4 Nov 2024 16:56:19 +0100 Subject: [PATCH 16/41] send error message on failed OD login --- src/libs/HybridApp.ts | 9 ++++--- src/libs/actions/HybridApp.ts | 6 ++++- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 25 ++++++++++++++++--- src/types/onyx/HybridApp.ts | 3 +++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 79c3608c1fcc..b500dc1ad1a7 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -1,6 +1,6 @@ import {DeviceEventEmitter, NativeModules} from 'react-native'; import CONST from '@src/CONST'; -import {setIsSigningIn, setReadyToSwitchToClassicExperience} from './actions/HybridApp'; +import {setIsSigningIn, setOldDotSignInError, setReadyToSwitchToClassicExperience} from './actions/HybridApp'; import type {Init} from './ActiveClientManager/types'; import Log from './Log'; @@ -10,9 +10,12 @@ const init: Init = () => { } // Setup event listeners - DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, () => { - Log.info('[HybridApp] `onSignInFinished` event received', true); + DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, (data) => { + Log.info(`[HybridApp] onSignInFinished event received with data: ${data}`, true); + const eventData = JSON.parse(data as string) as {errorMessage: string}; + setIsSigningIn(false); + setOldDotSignInError(eventData.errorMessage); setReadyToSwitchToClassicExperience(true); }); }; diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp.ts index 0dcd7bf11696..9bf7c79694a1 100644 --- a/src/libs/actions/HybridApp.ts +++ b/src/libs/actions/HybridApp.ts @@ -5,6 +5,10 @@ function setIsSigningIn(isSigningIn: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {isSigningIn}); } +function setOldDotSignInError(oldDotSignInError: string) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInError}); +} + function setReadyToShowAuthScreens(readyToShowAuthScreens: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToShowAuthScreens}); } @@ -21,4 +25,4 @@ function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); } -export {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage}; +export {setOldDotSignInError, setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage}; diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 31c2ae8d2b1d..148ad852a85d 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -137,7 +137,12 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco console.log('[HybridApp] newDotSignInState', newDotSignInState); console.log('[HybridApp] oldDotSignInState', oldDotSignInState); - if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.FINISHED && oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED) { + if ( + newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.FINISHED && + oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED && + credentials?.autoGeneratedLogin && + credentials?.autoGeneratedPassword + ) { if (shouldUseOldApp(tryNewDot)) { setIsSigningIn(true); } else { @@ -146,13 +151,26 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco console.log('[HybridApp] signInToOldDot'); setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.STARTED); - NativeModules.HybridAppModule.signInToOldDot(credentials?.autoGeneratedLogin ?? '', credentials?.autoGeneratedPassword ?? ''); + NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); } + console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { + if (hybridApp?.oldDotSignInError) { + return; + } NativeModules.HybridAppModule.closeReactNativeApp(false, false); } - }, [account?.isLoading, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, newDotSignInState, oldDotSignInState, tryNewDot, hybridApp?.useNewDotSignInPage]); + }, [ + account?.isLoading, + credentials?.autoGeneratedLogin, + credentials?.autoGeneratedPassword, + newDotSignInState, + oldDotSignInState, + tryNewDot, + hybridApp?.useNewDotSignInPage, + hybridApp?.oldDotSignInError, + ]); useEffect(() => { if (!(inputValidateCodeRef.current && hasError && (session?.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED || account?.isLoading))) { @@ -444,6 +462,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco testID="validateCode" /> {hasError && } + {!!hybridApp?.oldDotSignInError && } {timeRemaining > 0 && !isOffline ? ( diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 75a9ba1da97d..8e248f4148b4 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -6,6 +6,9 @@ type HybridApp = { /** */ isSigningIn?: boolean; + /** */ + oldDotSignInError?: string | null; + /** */ readyToShowAuthScreens?: boolean; From 026ac7bacad45c7466f116d391ee70b59f1f0d56 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 5 Nov 2024 11:28:13 +0100 Subject: [PATCH 17/41] add useOldDot const --- src/libs/actions/Session/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index cbc4ab4dc052..81d61530bcce 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -513,7 +513,8 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { clearOnyxBeforeSignIn() .then(() => { setUseNewDotSignInPage(useNewDotSignInPage === 'true'); - const dismissed = useNewDotSignInPage === 'true' ? 'true' : 'false'; + const useOldDot = 'true'; + const dismissed = useNewDotSignInPage === 'true' ? useOldDot : 'false'; Onyx.multiSet({ [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands }); From d976148e4bafa8b3fcc5d6a11d280048383e17ad Mon Sep 17 00:00:00 2001 From: Jan Nowakowski <56261019+jnowakow@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:54:19 +0100 Subject: [PATCH 18/41] Bootsplash logic in hybridapp (#132) * Implement ND bootsplash logic * Rename param * Adress review --- src/Expensify.tsx | 6 ++++-- src/libs/actions/HybridApp.ts | 6 +++++- src/libs/actions/Session/index.ts | 7 ++++--- src/types/onyx/HybridApp.ts | 3 +++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 5d9ab88f6806..24647a6c5d6a 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -117,10 +117,12 @@ function Expensify() { const isAuthenticated = useMemo(() => !!(session?.authToken ?? null), [session]); const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]); - const shouldInit = isNavigationReady && hasAttemptedToOpenPublicRoom; + const shouldInit = !!NativeModules.HybridAppModule + ? !hybridApp?.loggedOutFromOldDot && isNavigationReady && hasAttemptedToOpenPublicRoom + : isNavigationReady && hasAttemptedToOpenPublicRoom; const shouldHideSplash = shouldInit && - (NativeModules.HybridAppModule + (!!NativeModules.HybridAppModule ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || !!hybridApp?.useNewDotSignInPage) : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp.ts index 9bf7c79694a1..db9dd1b08584 100644 --- a/src/libs/actions/HybridApp.ts +++ b/src/libs/actions/HybridApp.ts @@ -25,4 +25,8 @@ function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); } -export {setOldDotSignInError, setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage}; +function setLoggedOutFromOldDot(loggedOutFromOldDot: boolean) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {loggedOutFromOldDot}); +} + +export {setOldDotSignInError, setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage, setLoggedOutFromOldDot}; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 81d61530bcce..91e597d10164 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -42,7 +42,7 @@ import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContex import * as App from '@userActions/App'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import * as Device from '@userActions/Device'; -import {setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setUseNewDotSignInPage} from '@userActions/HybridApp'; +import {setLoggedOutFromOldDot, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setUseNewDotSignInPage} from '@userActions/HybridApp'; import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; @@ -482,14 +482,14 @@ function signUpUser() { function signInAfterTransitionFromOldDot(transitionURL: string) { const [route, queryParams] = transitionURL.split('?'); - const {useNewDotSignInPage, isSingleNewDotEntry} = queryParams + const {useNewDotSignInPage, isSingleNewDotEntry, loggedOutFromOldDot} = queryParams ? Object.fromEntries( queryParams.split('&').map((param) => { const [key, value] = param.split('='); return [key, value]; }), ) - : {useNewDotSignInPage: undefined, isSingleNewDotEntry: undefined}; + : {useNewDotSignInPage: undefined, isSingleNewDotEntry: undefined, loggedOutFromOldDot: undefined}; const clearOnyxBeforeSignIn = () => { if (useNewDotSignInPage !== 'true') { @@ -513,6 +513,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { clearOnyxBeforeSignIn() .then(() => { setUseNewDotSignInPage(useNewDotSignInPage === 'true'); + setLoggedOutFromOldDot(loggedOutFromOldDot === 'true'); const useOldDot = 'true'; const dismissed = useNewDotSignInPage === 'true' ? useOldDot : 'false'; Onyx.multiSet({ diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 8e248f4148b4..d77412caaf37 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -17,6 +17,9 @@ type HybridApp = { /** */ shouldResetSigningInLogic?: boolean; + + /** stores infromation if last log out was performed from OldDot */ + loggedOutFromOldDot?: boolean; }; export default HybridApp; From a78141d885ec2b4dad0eccfddb8ff9d750faea16 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 13 Nov 2024 11:23:46 +0100 Subject: [PATCH 19/41] fix clearing onyx --- src/libs/actions/Session/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index ae7fb378943c..c9a9531bc2ba 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -499,7 +499,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return Promise.resolve(); } - return Onyx.clear(KEYS_TO_PRESERVE); + return Onyx.clear(); }; const initAppAfterTransition = () => { From deaba6fca7def8751e341a89c9bbf291e07c8b6a Mon Sep 17 00:00:00 2001 From: Mateusz Rajski <60450261+mateuuszzzzz@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:54:12 +0100 Subject: [PATCH 20/41] [HybridApp] Do not use query params in AppProp URL (#125) * Add hybridAppSettings AppProp * Unify how we pass data on both platforms * fix after merge * fix comments --------- Co-authored-by: war-in --- src/App.tsx | 12 ++++-- src/Expensify.tsx | 4 +- src/ONYXKEYS.ts | 4 -- src/components/InitialURLContextProvider.tsx | 13 ++++--- src/hooks/useOnboardingFlow.ts | 2 +- src/libs/TripReservationUtils.ts | 4 +- .../{HybridApp.ts => HybridApp/index.ts} | 16 +++++++- src/libs/actions/HybridApp/types.ts | 12 ++++++ src/libs/actions/Session/index.ts | 37 +++++++++---------- src/types/onyx/HybridApp.ts | 6 +++ 10 files changed, 71 insertions(+), 39 deletions(-) rename src/libs/actions/{HybridApp.ts => HybridApp/index.ts} (69%) create mode 100644 src/libs/actions/HybridApp/types.ts diff --git a/src/App.tsx b/src/App.tsx index 5132d8390421..74fc3c3637e5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,9 +41,12 @@ import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsCo import type {Route} from './ROUTES'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; +/** + * URL and settings passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. + */ type AppProps = { - /** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */ url?: Route; + hybridAppSettings?: string; }; LogBox.ignoreLogs([ @@ -59,7 +62,7 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; -function App({url}: AppProps) { +function App({url, hybridAppSettings}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); HybridApp.init(); @@ -67,7 +70,10 @@ function App({url}: AppProps) { return ( - + !!(session?.authToken ?? null), [session]); const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]); - const shouldInit = !!NativeModules.HybridAppModule + const shouldInit = NativeModules.HybridAppModule ? !hybridApp?.loggedOutFromOldDot && isNavigationReady && hasAttemptedToOpenPublicRoom : isNavigationReady && hasAttemptedToOpenPublicRoom; const shouldHideSplash = shouldInit && - (!!NativeModules.HybridAppModule + (NativeModules.HybridAppModule ? splashScreenState === CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN && (isAuthenticated || !!hybridApp?.useNewDotSignInPage) : splashScreenState === CONST.BOOT_SPLASH_STATE.VISIBLE); diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index c8b5c83774bc..0693ef26c69b 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -449,9 +449,6 @@ const ONYXKEYS = { /** Stores recently used currencies */ RECENTLY_USED_CURRENCIES: 'nvp_recentlyUsedCurrencies', - /** States whether we transitioned from OldDot to show only certain group of screens. It should be undefined on pure NewDot. */ - IS_SINGLE_NEW_DOT_ENTRY: 'isSingleNewDotEntry', - /** Company cards custom names */ NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES: 'nvp_expensify_ccCustomNames', @@ -1026,7 +1023,6 @@ type OnyxValuesMapping = { [ONYXKEYS.APPROVAL_WORKFLOW]: OnyxTypes.ApprovalWorkflowOnyx; [ONYXKEYS.IMPORTED_SPREADSHEET]: OnyxTypes.ImportedSpreadsheet; [ONYXKEYS.LAST_ROUTE]: string; - [ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY]: boolean | undefined; [ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean; [ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index adf361a2573d..cf5094e0fcc8 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -18,20 +18,21 @@ const InitialURLContext = createContext({ }); type InitialURLContextProviderProps = { - /** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */ url?: Route; + hybridAppSettings?: string; + /** Children passed to the context provider */ children: ReactNode; }; -function InitialURLContextProvider({children, url}: InitialURLContextProviderProps) { - const [initialURL, setInitialURL] = useState(); +function InitialURLContextProvider({children, url, hybridAppSettings}: InitialURLContextProviderProps) { + const [initialURL, setInitialURL] = useState(url); const {setSplashScreenState} = useSplashScreenStateContext(); useEffect(() => { - if (url) { - signInAfterTransitionFromOldDot(url).then((route) => { + if (url && hybridAppSettings) { + signInAfterTransitionFromOldDot(url, hybridAppSettings).then((route) => { setInitialURL(route); setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); }); @@ -40,7 +41,7 @@ function InitialURLContextProvider({children, url}: InitialURLContextProviderPro Linking.getInitialURL().then((initURL) => { setInitialURL(initURL as Route); }); - }, [setSplashScreenState, url]); + }, [hybridAppSettings, setSplashScreenState, url]); const initialUrlContext = useMemo( () => ({ diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 66ef088d0e4f..9a2f57451d37 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -21,7 +21,7 @@ function useOnboardingFlowRouter() { selector: hasCompletedHybridAppOnboardingFlowSelector, }); - const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY); + const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.HYBRID_APP, {selector: (data) => data?.isSingleNewDotEntry}); useEffect(() => { if (isLoadingOnyxValue(isOnboardingCompletedMetadata)) { diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index f2ce5113af81..9c9947644e7f 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -44,9 +44,9 @@ Onyx.connect({ let isSingleNewDotEntry: boolean | undefined; Onyx.connect({ - key: ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY, + key: ONYXKEYS.HYBRID_APP, callback: (val) => { - isSingleNewDotEntry = val; + isSingleNewDotEntry = val?.isSingleNewDotEntry; }, }); diff --git a/src/libs/actions/HybridApp.ts b/src/libs/actions/HybridApp/index.ts similarity index 69% rename from src/libs/actions/HybridApp.ts rename to src/libs/actions/HybridApp/index.ts index db9dd1b08584..42a9468ec1bf 100644 --- a/src/libs/actions/HybridApp.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -1,5 +1,10 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; +import type HybridAppSettings from './types'; + +function parseHybridAppSettings(hybridAppSettings: string): HybridAppSettings { + return JSON.parse(hybridAppSettings) as HybridAppSettings; +} function setIsSigningIn(isSigningIn: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {isSigningIn}); @@ -29,4 +34,13 @@ function setLoggedOutFromOldDot(loggedOutFromOldDot: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {loggedOutFromOldDot}); } -export {setOldDotSignInError, setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage, setLoggedOutFromOldDot}; +export { + parseHybridAppSettings, + setOldDotSignInError, + setIsSigningIn, + setReadyToShowAuthScreens, + setReadyToSwitchToClassicExperience, + setShouldResetSigningInLogic, + setUseNewDotSignInPage, + setLoggedOutFromOldDot, +}; diff --git a/src/libs/actions/HybridApp/types.ts b/src/libs/actions/HybridApp/types.ts new file mode 100644 index 000000000000..dc99313344c8 --- /dev/null +++ b/src/libs/actions/HybridApp/types.ts @@ -0,0 +1,12 @@ +import type ONYXKEYS from '@src/ONYXKEYS'; +import type {TryNewDot} from '@src/types/onyx'; +import type HybridApp from '@src/types/onyx/HybridApp'; + +type HybridAppSettings = { + initialOnyxValues: { + [ONYXKEYS.HYBRID_APP]: HybridApp; + [ONYXKEYS.NVP_TRYNEWDOT]?: TryNewDot; + }; +}; + +export default HybridAppSettings; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index c9a9531bc2ba..b60416268749 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -43,7 +43,7 @@ import * as App from '@userActions/App'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import {KEYS_TO_PRESERVE_DELEGATE_ACCESS} from '@userActions/Delegate'; import * as Device from '@userActions/Device'; -import {setLoggedOutFromOldDot, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setUseNewDotSignInPage} from '@userActions/HybridApp'; +import {parseHybridAppSettings, setLoggedOutFromOldDot, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setUseNewDotSignInPage} from '@userActions/HybridApp'; import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; @@ -481,19 +481,13 @@ function signUpUser() { API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); } -function signInAfterTransitionFromOldDot(transitionURL: string) { - const [route, queryParams] = transitionURL.split('?'); - const {useNewDotSignInPage, isSingleNewDotEntry, loggedOutFromOldDot, shouldRemoveDelegatedAccess} = queryParams - ? Object.fromEntries( - queryParams.split('&').map((param) => { - const [key, value] = param.split('='); - return [key, value]; - }), - ) - : {useNewDotSignInPage: undefined, isSingleNewDotEntry: undefined, loggedOutFromOldDot: undefined, shouldRemoveDelegatedAccess: undefined}; +function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string) { + const parsedHybridAppSettings = parseHybridAppSettings(hybridAppSettings); + const {initialOnyxValues} = parsedHybridAppSettings; + const {hybridApp, ...newDotOnyxValues} = initialOnyxValues; const clearOnyxBeforeSignIn = () => { - if (useNewDotSignInPage !== 'true') { + if (!hybridApp.useNewDotSignInPage) { setReadyToShowAuthScreens(true); setReadyToSwitchToClassicExperience(true); return Promise.resolve(); @@ -503,7 +497,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { }; const initAppAfterTransition = () => { - if (useNewDotSignInPage === 'true') { + if (hybridApp.useNewDotSignInPage) { return Promise.resolve(); } @@ -512,19 +506,22 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { return new Promise((resolve) => { clearOnyxBeforeSignIn() + .then(() => Onyx.merge(ONYXKEYS.HYBRID_APP, hybridApp)) + .then(() => Onyx.multiSet(newDotOnyxValues)) .then(() => { - if (!shouldRemoveDelegatedAccess) { + if (!hybridApp.shouldRemoveDelegatedAccess) { return; } return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS); }) .then(() => { - setUseNewDotSignInPage(useNewDotSignInPage === 'true'); - setLoggedOutFromOldDot(loggedOutFromOldDot === 'true'); + // This data is mocked and should be returned by BeginSignUp/SignInUser API commands + setUseNewDotSignInPage(!!hybridApp.useNewDotSignInPage); + setLoggedOutFromOldDot(!!hybridApp.loggedOutFromOldDot); const useOldDot = 'true'; - const dismissed = useNewDotSignInPage === 'true' ? useOldDot : 'false'; - Onyx.multiSet({ - [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, // This data is mocked and should be returned by BeginSignUp/SignInUser API commands + const dismissed = hybridApp.useNewDotSignInPage ? useOldDot : 'false'; + return Onyx.multiSet({ + [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, }); }) .then(initAppAfterTransition) @@ -532,7 +529,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); }) .finally(() => { - resolve(`${route}?singleNewDotEntry=${isSingleNewDotEntry}` as Route); + resolve(`${route}?singleNewDotEntry=${hybridApp.isSingleNewDotEntry}` as Route); }); }); } diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index d77412caaf37..4e39ba1326be 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -18,8 +18,14 @@ type HybridApp = { /** */ shouldResetSigningInLogic?: boolean; + /** States whether we transitioned from OldDot to show only certain group of screens. It should be undefined on pure NewDot. */ + isSingleNewDotEntry?: boolean; + /** stores infromation if last log out was performed from OldDot */ loggedOutFromOldDot?: boolean; + + /** */ + shouldRemoveDelegatedAccess?: boolean; }; export default HybridApp; From f2e3bb4a24ac799b780e927bec927951142aa271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Warcho=C5=82?= <61014013+war-in@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:22:55 +0100 Subject: [PATCH 21/41] Handle OD sing-in errors (#136) * wip * block switching when error returned * make goBack work correctly * validate code form and SignIn button works (but require further investigation) * show error when unable to switch to OD * adjust new sign-up flow * add comment --------- Co-authored-by: war-in --- src/CONST.ts | 2 + src/libs/HybridApp.ts | 5 +- src/libs/actions/HybridApp/index.ts | 2 +- src/pages/settings/InitialSettingsPage.tsx | 34 ++++++++++--- src/pages/signin/SignUpWelcomeForm.tsx | 6 ++- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 50 ++++++++++++++++--- 6 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 6f4a8c7fefe1..9466ebd667aa 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6139,12 +6139,14 @@ const CONST = { NOT_STARTED: 'notStarted', STARTED: 'started', FINISHED: 'finished', + WAITING_FOR_SIGN_OUT: 'waitingForSignOut', }, NEW_DOT_SIGN_IN_STATE: { NOT_STARTED: 'notStarted', STARTED: 'started', FINISHED: 'finished', + WAITING_FOR_SIGN_OUT: 'waitingForSignOut', }, CSV_IMPORT_COLUMNS: { diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index b500dc1ad1a7..16ff02aff4ad 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -16,7 +16,10 @@ const init: Init = () => { setIsSigningIn(false); setOldDotSignInError(eventData.errorMessage); - setReadyToSwitchToClassicExperience(true); + + if (eventData.errorMessage === null) { + setReadyToSwitchToClassicExperience(true); + } }); }; diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 42a9468ec1bf..b73c5b964969 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -10,7 +10,7 @@ function setIsSigningIn(isSigningIn: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {isSigningIn}); } -function setOldDotSignInError(oldDotSignInError: string) { +function setOldDotSignInError(oldDotSignInError: string | null) { Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInError}); } diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 3c0be0e20eef..ea4db7a20956 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,8 +1,9 @@ import {useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; +import type {ReactNode} from 'react'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; -import {NativeModules, View} from 'react-native'; +import {ActivityIndicator, NativeModules, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; @@ -37,10 +38,11 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setIsSigningIn, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, setUseNewDotSignInPage} from '@userActions/HybridApp'; +import {setIsSigningIn, setOldDotSignInError, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; +import {signOutAndRedirectToSignIn} from '@userActions/Session'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -71,6 +73,8 @@ type MenuData = { iconRight?: IconAsset; badgeText?: string; badgeStyle?: ViewStyle; + shouldShowRightComponent?: boolean; + rightComponent?: ReactNode; }; type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; @@ -84,6 +88,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); + const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); const network = useNetwork(); const theme = useTheme(); @@ -235,10 +240,26 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr { translationKey: 'exitSurvey.goToExpensifyClassic', icon: Expensicons.ExpensifyLogoNew, + shouldShowRightComponent: hybridApp?.isSigningIn, + rightComponent: ( + + ), + brickRoadIndicator: hybridApp?.oldDotSignInError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, ...(NativeModules.HybridAppModule ? { action: () => { if (!hybridApp?.readyToSwitchToClassicExperience) { + if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { + // jeszcze logika handlująca poprawne zalogowanie, jak po takim kliknięciu się zaloguje to zamykaj apkę!!!! + NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + setIsSigningIn(true); + setOldDotSignInError(null); + } else { + signOutAndRedirectToSignIn(undefined, undefined, false); + } return; } NativeModules.HybridAppModule.closeReactNativeApp(false, true); @@ -271,17 +292,12 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr icon: Expensicons.Exit, action: () => { setShouldResetSigningInLogic(true); - setReadyToShowAuthScreens(false); - setReadyToSwitchToClassicExperience(false); - setIsSigningIn(false); - setUseNewDotSignInPage(true); - signOut(false); }, }, ], }; - }, [styles.pt4, hybridApp?.readyToSwitchToClassicExperience, setInitialURL, signOut]); + }, [styles.pt4, hybridApp?.isSigningIn, hybridApp?.oldDotSignInError, hybridApp?.readyToSwitchToClassicExperience, theme.spinner, credentials, setInitialURL, signOut]); /** * Retuns JSX.Element with menu items @@ -344,6 +360,8 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr !!item.routeName && !!(activeCentralPaneRoute.name.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')) } + shouldShowRightComponent={item.shouldShowRightComponent} + rightComponent={item.rightComponent} iconRight={item.iconRight} shouldShowRightIcon={item.shouldShowRightIcon} shouldIconUseAutoWidthStyle diff --git a/src/pages/signin/SignUpWelcomeForm.tsx b/src/pages/signin/SignUpWelcomeForm.tsx index 1f8687c218b7..b83ffe90bfd5 100644 --- a/src/pages/signin/SignUpWelcomeForm.tsx +++ b/src/pages/signin/SignUpWelcomeForm.tsx @@ -7,6 +7,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; +import {setReadyToShowAuthScreens} from '@userActions/HybridApp'; import * as Session from '@userActions/Session'; import ONYXKEYS from '@src/ONYXKEYS'; import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink'; @@ -28,7 +29,10 @@ function SignUpWelcomeForm() { large text={translate('welcomeSignUpForm.join')} isLoading={account?.isLoading} - onPress={() => Session.signUpUser()} + onPress={() => { + Session.signUpUser(); + setReadyToShowAuthScreens(true); + }} pressOnEnter style={[styles.mb2]} /> diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 148ad852a85d..4e40f4535d40 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; import {NativeModules, View} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; @@ -25,8 +25,16 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; -import {setIsSigningIn, setReadyToShowAuthScreens, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import { + setIsSigningIn, + setOldDotSignInError, + setReadyToShowAuthScreens, + setReadyToSwitchToClassicExperience, + setShouldResetSigningInLogic, + setUseNewDotSignInPage, +} from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; +import {signOut} from '@userActions/Session'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -80,7 +88,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const input2FARef = useRef(); const timerRef = useRef(); - const hasError = !!account && !isEmptyObject(account?.errors) && !needToClearError; + const hasError = (!!account && !isEmptyObject(account?.errors) && !needToClearError) || !!hybridApp?.oldDotSignInError; const isLoadingResendValidationForm = account?.loadingForm === CONST.FORMS.RESEND_VALIDATE_CODE_FORM; const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); @@ -92,6 +100,12 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco if (!hybridApp?.shouldResetSigningInLogic) { return; } + console.log('[HybridApp] Resetting sign in flow'); + setReadyToShowAuthScreens(false); + setReadyToSwitchToClassicExperience(false); + setIsSigningIn(false); + setUseNewDotSignInPage(true); + setOldDotSignInError(null); setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); @@ -107,6 +121,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco } if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { + console.log('[HybridApp] session auth token', session?.authToken); setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.FINISHED); } }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, hybridApp?.useNewDotSignInPage]); @@ -251,6 +266,9 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Trigger the reset validate code flow and ensure the 2FA input field is reset to avoid it being permanently hidden */ const resendValidateCode = () => { + setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); + setOldDotSignInError(null); User.resendValidateCode(credentials?.login ?? ''); inputValidateCodeRef.current?.clear(); // Give feedback to the user to let them know the email was sent so that they don't spam the button. @@ -273,8 +291,13 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco */ const clearSignInData = useCallback(() => { clearLocalSignInData(); + if (session?.authToken) { + signOut(); + Onyx.set(ONYXKEYS.SESSION, null); + } SessionActions.clearSignInData(); - }, [clearLocalSignInData]); + setShouldResetSigningInLogic(true); + }, [clearLocalSignInData, session?.authToken]); useImperativeHandle(forwardedRef, () => ({ clearSignInData, @@ -330,6 +353,20 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Check that all the form fields are valid, then trigger the submit callback */ const validateAndSubmitForm = useCallback(() => { + if (session?.authToken) { + setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); + setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); + signOut(); + Onyx.merge(ONYXKEYS.SESSION, { + authToken: null, + }).then(() => { + setIsSigningIn(false); + setOldDotSignInError(null); + setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.STARTED); + }); + } + if (account?.isLoading) { return; } @@ -387,7 +424,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco } else { SessionActions.signIn(validateCode, recoveryCodeOr2faCode); } - }, [account, credentials, twoFactorAuthCode, validateCode, isUsingRecoveryCode, recoveryCode]); + }, [session?.authToken, account?.isLoading, account?.errors, account?.requiresTwoFactorAuth, isUsingRecoveryCode, recoveryCode, twoFactorAuthCode, credentials?.accountID, validateCode]); return ( @@ -461,8 +498,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco key="validateCode" testID="validateCode" /> - {hasError && } - {!!hybridApp?.oldDotSignInError && } + {hasError && } {timeRemaining > 0 && !isOffline ? ( From 8859b6cd6dfa9da18dd583bcc09e1548b28bf5a0 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 13 Nov 2024 13:40:09 +0100 Subject: [PATCH 22/41] add disabled to button --- src/pages/settings/InitialSettingsPage.tsx | 26 ++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index ea4db7a20956..3546559f257d 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -75,6 +75,7 @@ type MenuData = { badgeStyle?: ViewStyle; shouldShowRightComponent?: boolean; rightComponent?: ReactNode; + disabled?: boolean; }; type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; @@ -241,13 +242,15 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr translationKey: 'exitSurvey.goToExpensifyClassic', icon: Expensicons.ExpensifyLogoNew, shouldShowRightComponent: hybridApp?.isSigningIn, + disabled: !!hybridApp?.isSigningIn, rightComponent: ( - + + + ), - brickRoadIndicator: hybridApp?.oldDotSignInError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, ...(NativeModules.HybridAppModule ? { action: () => { @@ -297,7 +300,16 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr }, ], }; - }, [styles.pt4, hybridApp?.isSigningIn, hybridApp?.oldDotSignInError, hybridApp?.readyToSwitchToClassicExperience, theme.spinner, credentials, setInitialURL, signOut]); + }, [ + styles.pt4, + styles.popoverMenuIcon, + theme.spinner, + hybridApp?.readyToSwitchToClassicExperience, + setInitialURL, + credentials?.autoGeneratedLogin, + credentials?.autoGeneratedPassword, + signOut, + ]); /** * Retuns JSX.Element with menu items @@ -334,7 +346,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr title={keyTitle} icon={item.icon} iconType={item.iconType} - disabled={isExecuting} + disabled={isExecuting || item.disabled} onPress={singleExecution(() => { if (item.action) { item.action(); From a3436bc73e25d274f00cd65854ccba2256484855 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 13 Nov 2024 16:33:39 +0100 Subject: [PATCH 23/41] move sign in state to HybridApp key --- src/CONST.ts | 9 +-- src/libs/actions/HybridApp/index.ts | 12 ++++ src/libs/actions/Session/index.ts | 12 +++- src/libs/actions/SignInRedirect.ts | 3 +- src/pages/settings/InitialSettingsPage.tsx | 12 ++-- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 55 ++++++++++--------- src/types/onyx/HybridApp.ts | 9 +++ 7 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 9466ebd667aa..679597cca181 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6135,14 +6135,7 @@ const CONST = { HIDDEN: `hidden`, }, - OLD_DOT_SIGN_IN_STATE: { - NOT_STARTED: 'notStarted', - STARTED: 'started', - FINISHED: 'finished', - WAITING_FOR_SIGN_OUT: 'waitingForSignOut', - }, - - NEW_DOT_SIGN_IN_STATE: { + HYBRID_APP_SIGN_IN_STATE: { NOT_STARTED: 'notStarted', STARTED: 'started', FINISHED: 'finished', diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index b73c5b964969..486b7a0e79d7 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -1,4 +1,6 @@ import Onyx from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type HybridAppSettings from './types'; @@ -34,6 +36,14 @@ function setLoggedOutFromOldDot(loggedOutFromOldDot: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {loggedOutFromOldDot}); } +function setNewDotSignInState(newDotSignInState: ValueOf) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {newDotSignInState}); +} + +function setOldDotSignInState(oldDotSignInState: ValueOf) { + Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInState}); +} + export { parseHybridAppSettings, setOldDotSignInError, @@ -43,4 +53,6 @@ export { setShouldResetSigningInLogic, setUseNewDotSignInPage, setLoggedOutFromOldDot, + setNewDotSignInState, + setOldDotSignInState, }; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index b60416268749..01e96e7e1a4a 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -43,7 +43,15 @@ import * as App from '@userActions/App'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import {KEYS_TO_PRESERVE_DELEGATE_ACCESS} from '@userActions/Delegate'; import * as Device from '@userActions/Device'; -import {parseHybridAppSettings, setLoggedOutFromOldDot, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setUseNewDotSignInPage} from '@userActions/HybridApp'; +import { + parseHybridAppSettings, + setLoggedOutFromOldDot, + setNewDotSignInState, + setOldDotSignInState, + setReadyToShowAuthScreens, + setReadyToSwitchToClassicExperience, + setUseNewDotSignInPage, +} from '@userActions/HybridApp'; import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; @@ -498,6 +506,8 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string const initAppAfterTransition = () => { if (hybridApp.useNewDotSignInPage) { + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); return Promise.resolve(); } diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index 2996c9939175..e612b56aa794 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -2,6 +2,7 @@ import Onyx from 'react-native-onyx'; import * as ErrorUtils from '@libs/ErrorUtils'; import type {OnyxKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; +import {setUseNewDotSignInPage} from './HybridApp'; import * as Policy from './Policy/Policy'; let currentIsOffline: boolean | undefined; @@ -22,7 +23,6 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { keysToPreserve.push(ONYXKEYS.NVP_PREFERRED_LOCALE); keysToPreserve.push(ONYXKEYS.ACTIVE_CLIENTS); keysToPreserve.push(ONYXKEYS.DEVICE_ID); - keysToPreserve.push(ONYXKEYS.HYBRID_APP); // After signing out, set ourselves as offline if we were offline before logging out and we are not forcing it. // If we are forcing offline, ignore it while signed out, otherwise it would require a refresh because there's no way to toggle the switch to go back online while signed out. @@ -38,6 +38,7 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { // `Onyx.clear` reinitializes the Onyx instance with initial values so use `Onyx.merge` instead of `Onyx.set` Onyx.merge(ONYXKEYS.SESSION, {errors: ErrorUtils.getMicroSecondOnyxErrorWithMessage(errorMessage)}); + setUseNewDotSignInPage(true); }); } diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 3546559f257d..95388841638c 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -38,7 +38,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setIsSigningIn, setOldDotSignInError, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import {setIsSigningIn, setNewDotSignInState, setOldDotSignInError, setOldDotSignInState} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; @@ -262,6 +262,8 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr setOldDotSignInError(null); } else { signOutAndRedirectToSignIn(undefined, undefined, false); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); } return; } @@ -293,18 +295,16 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr { translationKey: signOutTranslationKey, icon: Expensicons.Exit, - action: () => { - setShouldResetSigningInLogic(true); - signOut(false); - }, + action: () => signOut(false), }, ], }; }, [ styles.pt4, styles.popoverMenuIcon, - theme.spinner, + hybridApp?.isSigningIn, hybridApp?.readyToSwitchToClassicExperience, + theme.spinner, setInitialURL, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 4e40f4535d40..08b0aebe488e 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -3,7 +3,6 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, import type {ForwardedRef} from 'react'; import {NativeModules, View} from 'react-native'; import Onyx, {useOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -27,7 +26,9 @@ import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; import { setIsSigningIn, + setNewDotSignInState, setOldDotSignInError, + setOldDotSignInState, setReadyToShowAuthScreens, setReadyToSwitchToClassicExperience, setShouldResetSigningInLogic, @@ -66,7 +67,9 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); const [session] = useOnyx(ONYXKEYS.SESSION); const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); - const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); + const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP, { + initialValue: {oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED}, + }); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -78,8 +81,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [timeRemaining, setTimeRemaining] = useState(CONST.REQUEST_CODE_DELAY as number); const [recoveryCode, setRecoveryCode] = useState(''); const [needToClearError, setNeedToClearError] = useState(!!account?.errors); - const [oldDotSignInState, setOldDotSignInState] = useState>(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); - const [newDotSignInState, setNewDotSignInState] = useState>(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); const prevRequiresTwoFactorAuth = usePrevious(account?.requiresTwoFactorAuth); const prevValidateCode = usePrevious(credentials?.validateCode); @@ -107,8 +108,8 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco setUseNewDotSignInPage(true); setOldDotSignInError(null); - setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); setShouldResetSigningInLogic(false); }, [hybridApp?.shouldResetSigningInLogic]); @@ -120,11 +121,11 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco return; } - if (newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { + if (hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { console.log('[HybridApp] session auth token', session?.authToken); - setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.FINISHED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); } - }, [newDotSignInState, isValidateCodeFormSubmitting, session?.authToken, hybridApp?.useNewDotSignInPage]); + }, [isValidateCodeFormSubmitting, session?.authToken, hybridApp?.useNewDotSignInPage, hybridApp?.newDotSignInState]); /** * ustawianie stanu logowania OD na finished, jak używamy strony logowania @@ -134,10 +135,10 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco return; } - if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.STARTED && !hybridApp?.isSigningIn) { - setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.FINISHED); + if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && !hybridApp?.isSigningIn) { + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); } - }, [hybridApp?.isSigningIn, oldDotSignInState, hybridApp?.useNewDotSignInPage]); + }, [hybridApp?.isSigningIn, hybridApp?.useNewDotSignInPage, hybridApp?.oldDotSignInState]); /** * główny efekt obsługujacy kilka rzeczy @@ -150,11 +151,11 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco return; } - console.log('[HybridApp] newDotSignInState', newDotSignInState); - console.log('[HybridApp] oldDotSignInState', oldDotSignInState); + console.log('[HybridApp] newDotSignInState', hybridApp?.newDotSignInState); + console.log('[HybridApp] oldDotSignInState', hybridApp?.oldDotSignInState); if ( - newDotSignInState === CONST.NEW_DOT_SIGN_IN_STATE.FINISHED && - oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED && + hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && + hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED && credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword ) { @@ -165,12 +166,12 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco } console.log('[HybridApp] signInToOldDot'); - setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); } console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); - if (oldDotSignInState === CONST.OLD_DOT_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { + if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { if (hybridApp?.oldDotSignInError) { return; } @@ -180,11 +181,11 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco account?.isLoading, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, - newDotSignInState, - oldDotSignInState, tryNewDot, hybridApp?.useNewDotSignInPage, hybridApp?.oldDotSignInError, + hybridApp?.newDotSignInState, + hybridApp?.oldDotSignInState, ]); useEffect(() => { @@ -266,8 +267,8 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Trigger the reset validate code flow and ensure the 2FA input field is reset to avoid it being permanently hidden */ const resendValidateCode = () => { - setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.NOT_STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); setOldDotSignInError(null); User.resendValidateCode(credentials?.login ?? ''); inputValidateCodeRef.current?.clear(); @@ -354,16 +355,16 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco */ const validateAndSubmitForm = useCallback(() => { if (session?.authToken) { - setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); - setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); signOut(); Onyx.merge(ONYXKEYS.SESSION, { authToken: null, }).then(() => { setIsSigningIn(false); setOldDotSignInError(null); - setOldDotSignInState(CONST.OLD_DOT_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); }); } @@ -417,7 +418,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const recoveryCodeOr2faCode = isUsingRecoveryCode ? recoveryCode : twoFactorAuthCode; - setNewDotSignInState(CONST.NEW_DOT_SIGN_IN_STATE.STARTED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); const accountID = credentials?.accountID; if (accountID) { SessionActions.signInWithValidateCode(accountID, validateCode, recoveryCodeOr2faCode); diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 4e39ba1326be..5eeb84f9a314 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -1,3 +1,6 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + /** */ type HybridApp = { /** Stores the information if HybridApp uses NewDot's sign in flow */ @@ -26,6 +29,12 @@ type HybridApp = { /** */ shouldRemoveDelegatedAccess?: boolean; + + /** */ + newDotSignInState?: ValueOf; + + /** */ + oldDotSignInState?: ValueOf; }; export default HybridApp; From 703c31d1baec03f10c59e64b50d8806495d4b930 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 14 Nov 2024 14:56:42 +0100 Subject: [PATCH 24/41] add loading indicator and error modal --- src/CONST.ts | 3 ++ src/libs/HybridApp.ts | 21 ++++++++- src/pages/settings/InitialSettingsPage.tsx | 44 ++++++++++++------- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 16 +------ 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 679597cca181..0922f57bc3d6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6140,6 +6140,9 @@ const CONST = { STARTED: 'started', FINISHED: 'finished', WAITING_FOR_SIGN_OUT: 'waitingForSignOut', + RETRYING_AFTER_FAILURE: 'retryingAfterFailure', + FAILED_AGAIN: 'failedAgain', + BLOCKED: 'blocked', }, CSV_IMPORT_COLUMNS: { diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 16ff02aff4ad..924eac9130aa 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -1,9 +1,27 @@ import {DeviceEventEmitter, NativeModules} from 'react-native'; +import Onyx, {OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; -import {setIsSigningIn, setOldDotSignInError, setReadyToSwitchToClassicExperience} from './actions/HybridApp'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {HybridApp} from '@src/types/onyx'; +import {setIsSigningIn, setOldDotSignInError, setOldDotSignInState, setReadyToSwitchToClassicExperience} from './actions/HybridApp'; import type {Init} from './ActiveClientManager/types'; import Log from './Log'; +let currentHybridApp: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.HYBRID_APP, + callback: (hybridApp) => { + if (currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED) { + if (hybridApp?.oldDotSignInError) { + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); + } else { + NativeModules.HybridAppModule.closeReactNativeApp(false, true); + } + } + currentHybridApp = hybridApp; + }, +}); + const init: Init = () => { if (!NativeModules.HybridAppModule) { return; @@ -16,6 +34,7 @@ const init: Init = () => { setIsSigningIn(false); setOldDotSignInError(eventData.errorMessage); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); if (eventData.errorMessage === null) { setReadyToSwitchToClassicExperience(true); diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 95388841638c..5ebe39086f8c 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -241,8 +241,8 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr { translationKey: 'exitSurvey.goToExpensifyClassic', icon: Expensicons.ExpensifyLogoNew, - shouldShowRightComponent: hybridApp?.isSigningIn, - disabled: !!hybridApp?.isSigningIn, + shouldShowRightComponent: hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE, + disabled: hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE, rightComponent: ( { - if (!hybridApp?.readyToSwitchToClassicExperience) { - if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { - // jeszcze logika handlująca poprawne zalogowanie, jak po takim kliknięciu się zaloguje to zamykaj apkę!!!! - NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); - setIsSigningIn(true); - setOldDotSignInError(null); - } else { - signOutAndRedirectToSignIn(undefined, undefined, false); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - } + console.log('[HybridApp] ready to switch', hybridApp); + if (hybridApp?.readyToSwitchToClassicExperience) { + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.BLOCKED); + NativeModules.HybridAppModule.closeReactNativeApp(false, true); + setInitialURL(undefined); return; } - NativeModules.HybridAppModule.closeReactNativeApp(false, true); - setInitialURL(undefined); + + if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { + NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + setIsSigningIn(true); + setOldDotSignInError(null); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE); + } else { + signOutAndRedirectToSignIn(undefined, undefined, false); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + } }, } : { @@ -475,6 +478,17 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr onConfirm={() => signOut(true)} onCancel={() => toggleSignoutConfirmModal(false)} /> + { + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); + }} + shouldShowCancelButton={false} + /> ); diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 08b0aebe488e..9a071ab9e305 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -127,19 +127,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco } }, [isValidateCodeFormSubmitting, session?.authToken, hybridApp?.useNewDotSignInPage, hybridApp?.newDotSignInState]); - /** - * ustawianie stanu logowania OD na finished, jak używamy strony logowania - */ - useEffect(() => { - if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage) { - return; - } - - if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && !hybridApp?.isSigningIn) { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); - } - }, [hybridApp?.isSigningIn, hybridApp?.useNewDotSignInPage, hybridApp?.oldDotSignInState]); - /** * główny efekt obsługujacy kilka rzeczy * - odpalanie OD sign in po skończeniu logowania do ND @@ -147,7 +134,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * - jeśli skońćzyło się logowanie do OD i chcemy używać starej apki to zamykamy RN */ useEffect(() => { - if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage) { + if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage || hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.BLOCKED) { return; } @@ -175,6 +162,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco if (hybridApp?.oldDotSignInError) { return; } + console.log('closing!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); NativeModules.HybridAppModule.closeReactNativeApp(false, false); } }, [ From 1f6a0e16c5a155a41f306807453efc7d87c31262 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 14 Nov 2024 17:38:41 +0100 Subject: [PATCH 25/41] move sign in logic to HybridApp.ts --- src/CONST.ts | 1 - src/libs/HybridApp.ts | 95 +++++++++++++++++- src/libs/actions/SignInRedirect.ts | 1 + src/pages/settings/InitialSettingsPage.tsx | 23 ++--- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 99 +------------------ 5 files changed, 103 insertions(+), 116 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 0922f57bc3d6..8ce03a631c3b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6142,7 +6142,6 @@ const CONST = { WAITING_FOR_SIGN_OUT: 'waitingForSignOut', RETRYING_AFTER_FAILURE: 'retryingAfterFailure', FAILED_AGAIN: 'failedAgain', - BLOCKED: 'blocked', }, CSV_IMPORT_COLUMNS: { diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 924eac9130aa..4ebc9563df53 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -1,16 +1,65 @@ import {DeviceEventEmitter, NativeModules} from 'react-native'; -import Onyx, {OnyxEntry} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {HybridApp} from '@src/types/onyx'; -import {setIsSigningIn, setOldDotSignInError, setOldDotSignInState, setReadyToSwitchToClassicExperience} from './actions/HybridApp'; +import type {Credentials, HybridApp, Session, TryNewDot} from '@src/types/onyx'; +import { + setIsSigningIn, + setNewDotSignInState, + setOldDotSignInError, + setOldDotSignInState, + setReadyToShowAuthScreens, + setReadyToSwitchToClassicExperience, + setShouldResetSigningInLogic, + setUseNewDotSignInPage, +} from './actions/HybridApp'; import type {Init} from './ActiveClientManager/types'; import Log from './Log'; +function shouldUseOldApp(tryNewDot?: TryNewDot) { + return tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true'; +} + +let tryNewDot: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.NVP_TRYNEWDOT, + callback: (newTryNewDot) => { + tryNewDot = newTryNewDot; + }, +}); + +let credentials: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.CREDENTIALS, + callback: (newCredentials) => { + credentials = newCredentials; + }, +}); + let currentHybridApp: OnyxEntry; Onyx.connect({ key: ONYXKEYS.HYBRID_APP, callback: (hybridApp) => { + if (!NativeModules.HybridAppModule) { + return; + } + + // reset sign in logic + if (hybridApp?.shouldResetSigningInLogic) { + console.log('[HybridApp] Resetting sign in flow'); + setReadyToShowAuthScreens(false); + setReadyToSwitchToClassicExperience(false); + setIsSigningIn(false); + setUseNewDotSignInPage(true); + setOldDotSignInError(null); + + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setShouldResetSigningInLogic(false); + return; + } + if (currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED) { if (hybridApp?.oldDotSignInError) { setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); @@ -18,10 +67,48 @@ Onyx.connect({ NativeModules.HybridAppModule.closeReactNativeApp(false, true); } } + + console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); + if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { + if (hybridApp?.oldDotSignInError) { + return; + } + console.log('closing!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); + NativeModules.HybridAppModule.closeReactNativeApp(false, false); + } + + if ( + hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && + hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED && + credentials?.autoGeneratedLogin && + credentials?.autoGeneratedPassword + ) { + if (shouldUseOldApp(tryNewDot)) { + setIsSigningIn(true); + } else { + setReadyToShowAuthScreens(true); + } + + console.log('[HybridApp] signInToOldDot'); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); + NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + } + currentHybridApp = hybridApp; }, }); +let currentSession: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (session: OnyxEntry) => { + if (!currentSession?.authToken && session?.authToken && currentHybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED) { + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); + } + currentSession = session; + }, +}); + const init: Init = () => { if (!NativeModules.HybridAppModule) { return; @@ -43,3 +130,5 @@ const init: Init = () => { }; export default {init}; + +export {shouldUseOldApp}; diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index e612b56aa794..3042ddda6845 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -23,6 +23,7 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { keysToPreserve.push(ONYXKEYS.NVP_PREFERRED_LOCALE); keysToPreserve.push(ONYXKEYS.ACTIVE_CLIENTS); keysToPreserve.push(ONYXKEYS.DEVICE_ID); + keysToPreserve.push(ONYXKEYS.HYBRID_APP); // After signing out, set ourselves as offline if we were offline before logging out and we are not forcing it. // If we are forcing offline, ignore it while signed out, otherwise it would require a refresh because there's no way to toggle the switch to go back online while signed out. diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 5ebe39086f8c..e441c6b9c09f 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -38,7 +38,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setIsSigningIn, setNewDotSignInState, setOldDotSignInError, setOldDotSignInState} from '@userActions/HybridApp'; +import {setIsSigningIn, setNewDotSignInState, setOldDotSignInError, setOldDotSignInState, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; @@ -256,7 +256,6 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr action: () => { console.log('[HybridApp] ready to switch', hybridApp); if (hybridApp?.readyToSwitchToClassicExperience) { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.BLOCKED); NativeModules.HybridAppModule.closeReactNativeApp(false, true); setInitialURL(undefined); return; @@ -269,8 +268,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE); } else { signOutAndRedirectToSignIn(undefined, undefined, false); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setShouldResetSigningInLogic(true); } }, } @@ -298,21 +296,14 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr { translationKey: signOutTranslationKey, icon: Expensicons.Exit, - action: () => signOut(false), + action: () => { + signOut(false); + setShouldResetSigningInLogic(true); + }, }, ], }; - }, [ - styles.pt4, - styles.popoverMenuIcon, - hybridApp?.isSigningIn, - hybridApp?.readyToSwitchToClassicExperience, - theme.spinner, - setInitialURL, - credentials?.autoGeneratedLogin, - credentials?.autoGeneratedPassword, - signOut, - ]); + }, [styles.pt4, styles.popoverMenuIcon, hybridApp, theme.spinner, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, setInitialURL, signOut]); /** * Retuns JSX.Element with menu items diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 9a071ab9e305..51ffb03ec81b 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -21,26 +21,17 @@ import useThemeStyles from '@hooks/useThemeStyles'; import AccountUtils from '@libs/AccountUtils'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import * as ErrorUtils from '@libs/ErrorUtils'; +import {shouldUseOldApp} from '@libs/HybridApp'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; -import { - setIsSigningIn, - setNewDotSignInState, - setOldDotSignInError, - setOldDotSignInState, - setReadyToShowAuthScreens, - setReadyToSwitchToClassicExperience, - setShouldResetSigningInLogic, - setUseNewDotSignInPage, -} from '@userActions/HybridApp'; +import {setIsSigningIn, setNewDotSignInState, setOldDotSignInError, setOldDotSignInState, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; import {signOut} from '@userActions/Session'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {TryNewDot} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type ValidateCodeFormProps from './types'; @@ -58,10 +49,6 @@ type ValidateCodeFormVariant = 'validateCode' | 'twoFactorAuthCode' | 'recoveryC type FormError = Partial>; -function shouldUseOldApp(tryNewDot?: TryNewDot) { - return tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true'; -} - function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingRecoveryCode, isVisible}: BaseValidateCodeFormProps, forwardedRef: ForwardedRef) { const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); @@ -94,87 +81,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); - useEffect(() => { - if (!NativeModules.HybridAppModule) { - return; - } - if (!hybridApp?.shouldResetSigningInLogic) { - return; - } - console.log('[HybridApp] Resetting sign in flow'); - setReadyToShowAuthScreens(false); - setReadyToSwitchToClassicExperience(false); - setIsSigningIn(false); - setUseNewDotSignInPage(true); - setOldDotSignInError(null); - - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setShouldResetSigningInLogic(false); - }, [hybridApp?.shouldResetSigningInLogic]); - - /** - * ustawianie stanu logowania ND na finished, jak używamy strony logowania - */ - useEffect(() => { - if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage) { - return; - } - - if (hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && !isValidateCodeFormSubmitting && !!session?.authToken) { - console.log('[HybridApp] session auth token', session?.authToken); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); - } - }, [isValidateCodeFormSubmitting, session?.authToken, hybridApp?.useNewDotSignInPage, hybridApp?.newDotSignInState]); - - /** - * główny efekt obsługujacy kilka rzeczy - * - odpalanie OD sign in po skończeniu logowania do ND - * - jak chcemy używać ND to w tym momencie jesteśmy gotowi i pokazujemy auth screens - * - jeśli skońćzyło się logowanie do OD i chcemy używać starej apki to zamykamy RN - */ - useEffect(() => { - if (!NativeModules.HybridAppModule || !hybridApp?.useNewDotSignInPage || hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.BLOCKED) { - return; - } - - console.log('[HybridApp] newDotSignInState', hybridApp?.newDotSignInState); - console.log('[HybridApp] oldDotSignInState', hybridApp?.oldDotSignInState); - if ( - hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && - hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED && - credentials?.autoGeneratedLogin && - credentials?.autoGeneratedPassword - ) { - if (shouldUseOldApp(tryNewDot)) { - setIsSigningIn(true); - } else { - setReadyToShowAuthScreens(true); - } - - console.log('[HybridApp] signInToOldDot'); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); - NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); - } - - console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); - if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { - if (hybridApp?.oldDotSignInError) { - return; - } - console.log('closing!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); - NativeModules.HybridAppModule.closeReactNativeApp(false, false); - } - }, [ - account?.isLoading, - credentials?.autoGeneratedLogin, - credentials?.autoGeneratedPassword, - tryNewDot, - hybridApp?.useNewDotSignInPage, - hybridApp?.oldDotSignInError, - hybridApp?.newDotSignInState, - hybridApp?.oldDotSignInState, - ]); + console.log('[HybridApp] hybridApp', hybridApp); useEffect(() => { if (!(inputValidateCodeRef.current && hasError && (session?.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED || account?.isLoading))) { From 63a3de843ddb6de4d62cda2142f920cb57063eb1 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 14 Nov 2024 18:18:59 +0100 Subject: [PATCH 26/41] fix lint --- src/pages/settings/InitialSettingsPage.tsx | 2 +- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index e441c6b9c09f..a9924831c022 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -38,7 +38,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setIsSigningIn, setNewDotSignInState, setOldDotSignInError, setOldDotSignInState, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import {setIsSigningIn, setOldDotSignInError, setOldDotSignInState, setShouldResetSigningInLogic} from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 51ffb03ec81b..894ce2b7ac02 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -1,7 +1,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; -import {NativeModules, View} from 'react-native'; +import {View} from 'react-native'; import Onyx, {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; From 1a99fd116608b63139730326787d0d53a4a77326 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 20 Nov 2024 15:25:23 +0100 Subject: [PATCH 27/41] Prevent closing NewDot on first transition with button --- src/libs/HybridApp.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 4ebc9563df53..d5a3d8d96bf7 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -64,12 +64,17 @@ Onyx.connect({ if (hybridApp?.oldDotSignInError) { setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); } else { + console.debug('Closing, but why', {currentHybridApp, hybridApp}); NativeModules.HybridAppModule.closeReactNativeApp(false, true); } } console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); - if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && shouldUseOldApp(tryNewDot)) { + if ( + currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && + hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && + shouldUseOldApp(tryNewDot) + ) { if (hybridApp?.oldDotSignInError) { return; } From 0e601bd55e59833968e955ca2cbc95d8c9f1eeae Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 21 Nov 2024 15:20:50 +0100 Subject: [PATCH 28/41] use "tryNewDot" value to determine which app should be opened --- src/libs/API/types.ts | 4 +- src/libs/HybridApp.ts | 132 ++++++++++++++++-------------- src/libs/actions/Session/index.ts | 17 ++-- src/types/onyx/Response.ts | 3 + src/types/onyx/TryNewDot.ts | 2 +- 5 files changed, 87 insertions(+), 71 deletions(-) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index bd8a58555617..53144d406813 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -91,7 +91,6 @@ const WRITE_COMMANDS = { REQUEST_NEW_VALIDATE_CODE: 'RequestNewValidateCode', SIGN_IN_WITH_APPLE: 'SignInWithApple', SIGN_IN_WITH_GOOGLE: 'SignInWithGoogle', - SIGN_IN_USER: 'SigninUser', SIGN_IN_USER_WITH_LINK: 'SigninUserWithLink', SEARCH: 'Search', REQUEST_UNLINK_VALIDATION_LINK: 'RequestUnlinkValidationLink', @@ -515,7 +514,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.REQUEST_NEW_VALIDATE_CODE]: Parameters.RequestNewValidateCodeParams; [WRITE_COMMANDS.SIGN_IN_WITH_APPLE]: Parameters.BeginAppleSignInParams; [WRITE_COMMANDS.SIGN_IN_WITH_GOOGLE]: Parameters.BeginGoogleSignInParams; - [WRITE_COMMANDS.SIGN_IN_USER]: SignInUserParams; [WRITE_COMMANDS.SIGN_IN_USER_WITH_LINK]: Parameters.SignInUserWithLinkParams; [WRITE_COMMANDS.REQUEST_UNLINK_VALIDATION_LINK]: Parameters.RequestUnlinkValidationLinkParams; [WRITE_COMMANDS.UNLINK_LOGIN]: Parameters.UnlinkLoginParams; @@ -1025,6 +1023,7 @@ const SIDE_EFFECT_REQUEST_COMMANDS = { DISCONNECT_AS_DELEGATE: 'DisconnectAsDelegate', COMPLETE_HYBRID_APP_ONBOARDING: 'CompleteHybridAppOnboarding', CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP: 'ConnectPolicyToQuickbooksDesktop', + SIGN_IN_USER: 'SigninUser', } as const; type SideEffectRequestCommand = ValueOf; @@ -1045,6 +1044,7 @@ type SideEffectRequestCommandParameters = { [SIDE_EFFECT_REQUEST_COMMANDS.DISCONNECT_AS_DELEGATE]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP]: Parameters.ConnectPolicyToQuickBooksDesktopParams; + [SIDE_EFFECT_REQUEST_COMMANDS.SIGN_IN_USER]: SignInUserParams; }; type ApiRequestCommandParameters = WriteCommandParameters & ReadCommandParameters & SideEffectRequestCommandParameters; diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index d5a3d8d96bf7..45f464c25afd 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -18,17 +18,9 @@ import type {Init} from './ActiveClientManager/types'; import Log from './Log'; function shouldUseOldApp(tryNewDot?: TryNewDot) { - return tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true'; + return tryNewDot?.classicRedirect.dismissed === true; } -let tryNewDot: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.NVP_TRYNEWDOT, - callback: (newTryNewDot) => { - tryNewDot = newTryNewDot; - }, -}); - let credentials: OnyxEntry; Onyx.connect({ key: ONYXKEYS.CREDENTIALS, @@ -38,70 +30,86 @@ Onyx.connect({ }); let currentHybridApp: OnyxEntry; +let currentTryNewDot: OnyxEntry; + Onyx.connect({ key: ONYXKEYS.HYBRID_APP, callback: (hybridApp) => { - if (!NativeModules.HybridAppModule) { - return; - } + handleSignInFlow(hybridApp, currentTryNewDot); + }, +}); - // reset sign in logic - if (hybridApp?.shouldResetSigningInLogic) { - console.log('[HybridApp] Resetting sign in flow'); - setReadyToShowAuthScreens(false); - setReadyToSwitchToClassicExperience(false); - setIsSigningIn(false); - setUseNewDotSignInPage(true); - setOldDotSignInError(null); - - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setShouldResetSigningInLogic(false); - return; - } +Onyx.connect({ + key: ONYXKEYS.NVP_TRYNEWDOT, + callback: (tryNewDot) => { + handleSignInFlow(currentHybridApp, tryNewDot); + }, +}); - if (currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED) { - if (hybridApp?.oldDotSignInError) { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); - } else { - console.debug('Closing, but why', {currentHybridApp, hybridApp}); - NativeModules.HybridAppModule.closeReactNativeApp(false, true); - } +function handleSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry) { + if (!NativeModules.HybridAppModule) { + return; + } + + // reset sign in logic + if (hybridApp?.shouldResetSigningInLogic) { + console.log('[HybridApp] Resetting sign in flow'); + setReadyToShowAuthScreens(false); + setReadyToSwitchToClassicExperience(false); + setIsSigningIn(false); + setUseNewDotSignInPage(true); + setOldDotSignInError(null); + + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + setShouldResetSigningInLogic(false); + return; + } + + if (currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED) { + if (hybridApp?.oldDotSignInError) { + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); + } else { + console.debug('Closing, but why', {currentHybridApp, hybridApp}); + NativeModules.HybridAppModule.closeReactNativeApp(false, true); } + } - console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); - if ( - currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && - hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && - shouldUseOldApp(tryNewDot) - ) { - if (hybridApp?.oldDotSignInError) { - return; - } - console.log('closing!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); - NativeModules.HybridAppModule.closeReactNativeApp(false, false); + console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); + if ( + currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && + hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && + tryNewDot !== undefined && + shouldUseOldApp(tryNewDot) + ) { + if (hybridApp?.oldDotSignInError) { + return; } + console.log('closing!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); + NativeModules.HybridAppModule.closeReactNativeApp(false, false); + } - if ( - hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && - hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED && - credentials?.autoGeneratedLogin && - credentials?.autoGeneratedPassword - ) { - if (shouldUseOldApp(tryNewDot)) { - setIsSigningIn(true); - } else { - setReadyToShowAuthScreens(true); - } - - console.log('[HybridApp] signInToOldDot'); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); - NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + if ( + hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && + hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED && + credentials?.autoGeneratedLogin && + credentials?.autoGeneratedPassword && + tryNewDot !== undefined + ) { + if (shouldUseOldApp(tryNewDot)) { + setIsSigningIn(true); + } else { + setReadyToShowAuthScreens(true); } - currentHybridApp = hybridApp; - }, -}); + console.log('[HybridApp] signInToOldDot'); + setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); + NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + } + + currentHybridApp = hybridApp; + currentTryNewDot = tryNewDot; +} let currentSession: OnyxEntry; Onyx.connect({ diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 87de3dd3675d..b05405911133 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -528,11 +528,6 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string // This data is mocked and should be returned by BeginSignUp/SignInUser API commands setUseNewDotSignInPage(!!hybridApp.useNewDotSignInPage); setLoggedOutFromOldDot(!!hybridApp.loggedOutFromOldDot); - const useOldDot = 'true'; - const dismissed = hybridApp.useNewDotSignInPage ? useOldDot : 'false'; - return Onyx.multiSet({ - [ONYXKEYS.NVP_TRYNEWDOT]: {classicRedirect: {dismissed}}, - }); }) .then(initAppAfterTransition) .catch((error) => { @@ -642,7 +637,17 @@ function signIn(validateCode: string, twoFactorAuthCode?: string) { params.validateCode = validateCode || credentials.validateCode; } - API.write(WRITE_COMMANDS.SIGN_IN_USER, params, {optimisticData, successData, failureData}); + // eslint-disable-next-line rulesdir/no-api-side-effects-method + API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.SIGN_IN_USER, params, { + optimisticData, + successData, + failureData, + }).then((response) => { + if (!response) { + return; + } + Onyx.merge(ONYXKEYS.NVP_TRYNEWDOT, {classicRedirect: {dismissed: !response.tryNewDot}}); + }); }); } diff --git a/src/types/onyx/Response.ts b/src/types/onyx/Response.ts index ce9acbaaccc6..ca39aa2563b5 100644 --- a/src/types/onyx/Response.ts +++ b/src/types/onyx/Response.ts @@ -83,6 +83,9 @@ type Response = { /** If there is newer data to load for pagination commands */ hasNewerActions?: boolean; + + /** [HybridApp] Determines which app should be opened, NewDot or OldDot */ + tryNewDot?: boolean; }; export default Response; diff --git a/src/types/onyx/TryNewDot.ts b/src/types/onyx/TryNewDot.ts index 67ea66d255d8..a5261a694da4 100644 --- a/src/types/onyx/TryNewDot.ts +++ b/src/types/onyx/TryNewDot.ts @@ -9,7 +9,7 @@ type TryNewDot = { /** * Indicates if transistion from OldDot to NewDot should happen in HybridApp. */ - dismissed: boolean | string; + dismissed: boolean; /** * Indicates timestamp of an action. */ From 1c0c219a4900a21754452f55b4d9c3f2bd3cf9b6 Mon Sep 17 00:00:00 2001 From: war-in Date: Thu, 21 Nov 2024 16:57:46 +0100 Subject: [PATCH 29/41] first step to fix deeplinks --- src/libs/Navigation/AppNavigator/index.native.tsx | 2 +- src/libs/actions/Session/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index 7af3e7305662..bc95c4531756 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -13,7 +13,7 @@ type AppNavigatorProps = { }; function shouldUseOldApp(tryNewDot?: TryNewDot) { - return tryNewDot?.classicRedirect.dismissed === true || tryNewDot?.classicRedirect.dismissed === 'true'; + return tryNewDot?.classicRedirect.dismissed === true; } function AppNavigator({authenticated}: AppNavigatorProps) { diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index b05405911133..1a61a8adec7c 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -534,7 +534,7 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); }) .finally(() => { - resolve(`${route}?singleNewDotEntry=${hybridApp.isSingleNewDotEntry}` as Route); + resolve(`${route}${hybridApp.isSingleNewDotEntry ? '?singleNewDotEntry=true' : ''}` as Route); }); }); } From 417096876c679955f9354bd971df91f14ff1fb15 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 27 Nov 2024 11:43:46 +0100 Subject: [PATCH 30/41] improve openApp flow --- .../Navigation/AppNavigator/AuthScreens.tsx | 17 +++++++---------- src/libs/actions/Session/index.ts | 15 +-------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 689945da099e..819e8ac29ffe 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -294,16 +294,13 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie setDidPusherInit(true); }); - // In Hybrid App we decide to call one of those method when booting ND and we don't want to duplicate calls - if (!NativeModules.HybridAppModule) { - // 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(). - if (SessionUtils.didUserLogInDuringSession()) { - App.openApp(); - } else { - Log.info('[AuthScreens] Sending ReconnectApp'); - App.reconnectApp(initialLastUpdateIDAppliedToClient); - } + // 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(). + if (SessionUtils.didUserLogInDuringSession()) { + App.openApp(); + } else { + Log.info('[AuthScreens] Sending ReconnectApp'); + App.reconnectApp(initialLastUpdateIDAppliedToClient); } PriorityMode.autoSwitchToFocusMode(); diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index ea106f8c9dda..80ff373d3d72 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -489,15 +489,6 @@ function signUpUser() { API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); } -function getLastUpdateIDAppliedToClient(): Promise { - return new Promise((resolve) => { - Onyx.connect({ - key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, - callback: (value) => resolve(value ?? 0), - }); - }); -} - function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string) { const parsedHybridAppSettings = parseHybridAppSettings(hybridAppSettings); const {initialOnyxValues} = parsedHybridAppSettings; @@ -517,12 +508,8 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string if (hybridApp.useNewDotSignInPage) { setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - return Promise.resolve(); } - - return getLastUpdateIDAppliedToClient().then((lastUpdateId) => { - return App.reconnectApp(lastUpdateId); - }); + return Promise.resolve(); }; return new Promise((resolve) => { From 50c3fede85c436057b9cc6e177337dbb9d9f2d8c Mon Sep 17 00:00:00 2001 From: Mateusz Rajski <60450261+mateuuszzzzz@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:27:35 +0100 Subject: [PATCH 31/41] [HybridApp] Clean code (#143) * Simplify HybridApp.ts * Use early returns * Rearrange HybridApp module * Remove redundant keys * Remove unused import --- src/components/InitialURLContextProvider.tsx | 6 +- src/libs/HybridApp.ts | 118 +++++++----------- src/libs/actions/HybridApp/index.ts | 75 ++++++++--- src/libs/actions/Session/index.ts | 30 +---- src/pages/settings/InitialSettingsPage.tsx | 16 +-- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 29 +++-- src/types/onyx/HybridApp.ts | 21 +--- 7 files changed, 139 insertions(+), 156 deletions(-) diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index a111928ae094..b89a842d1420 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -32,7 +32,7 @@ type InitialURLContextProviderProps = { }; function InitialURLContextProvider({children, url, hybridAppSettings}: InitialURLContextProviderProps) { - const [initialURL, setInitialURL] = useState(url); + const [initialURL, setInitialURL] = useState | undefined>(url); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); const {splashScreenState, setSplashScreenState} = useSplashScreenStateContext(); @@ -66,8 +66,8 @@ function InitialURLContextProvider({children, url, hybridAppSettings}: InitialUR const initialUrlContext = useMemo( () => ({ - initialURL, - setInitialURL, + initialURL: initialURL === CONST.HYBRID_APP.REORDERING_REACT_NATIVE_ACTIVITY_TO_FRONT ? undefined : initialURL, + setInitialURL: setInitialURL as React.Dispatch>, }), [initialURL], ); diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 45f464c25afd..4a5dea653714 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -4,37 +4,17 @@ import type {OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Credentials, HybridApp, Session, TryNewDot} from '@src/types/onyx'; -import { - setIsSigningIn, - setNewDotSignInState, - setOldDotSignInError, - setOldDotSignInState, - setReadyToShowAuthScreens, - setReadyToSwitchToClassicExperience, - setShouldResetSigningInLogic, - setUseNewDotSignInPage, -} from './actions/HybridApp'; +import * as HybridAppActions from './actions/HybridApp'; import type {Init} from './ActiveClientManager/types'; import Log from './Log'; -function shouldUseOldApp(tryNewDot?: TryNewDot) { - return tryNewDot?.classicRedirect.dismissed === true; -} - -let credentials: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.CREDENTIALS, - callback: (newCredentials) => { - credentials = newCredentials; - }, -}); - let currentHybridApp: OnyxEntry; let currentTryNewDot: OnyxEntry; Onyx.connect({ key: ONYXKEYS.HYBRID_APP, callback: (hybridApp) => { + console.debug('Last hybridApp value', {hybridApp}); handleSignInFlow(hybridApp, currentTryNewDot); }, }); @@ -42,40 +22,50 @@ Onyx.connect({ Onyx.connect({ key: ONYXKEYS.NVP_TRYNEWDOT, callback: (tryNewDot) => { + console.debug('Last tryNewDot value', {tryNewDot}); handleSignInFlow(currentHybridApp, tryNewDot); }, }); +let currentSession: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (session: OnyxEntry) => { + if (!currentSession?.authToken && session?.authToken && currentHybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED) { + HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); + } + currentSession = session; + }, +}); + +let credentials: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.CREDENTIALS, + callback: (newCredentials) => { + credentials = newCredentials; + }, +}); + +function shouldUseOldApp(tryNewDot?: TryNewDot) { + return tryNewDot?.classicRedirect.dismissed === true; +} + function handleSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry) { if (!NativeModules.HybridAppModule) { return; } - // reset sign in logic - if (hybridApp?.shouldResetSigningInLogic) { - console.log('[HybridApp] Resetting sign in flow'); - setReadyToShowAuthScreens(false); - setReadyToSwitchToClassicExperience(false); - setIsSigningIn(false); - setUseNewDotSignInPage(true); - setOldDotSignInError(null); - - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setShouldResetSigningInLogic(false); - return; - } - if (currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED) { if (hybridApp?.oldDotSignInError) { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); - } else { - console.debug('Closing, but why', {currentHybridApp, hybridApp}); - NativeModules.HybridAppModule.closeReactNativeApp(false, true); + Log.info('[HybridApp] Unable to open OldDot. Sign-in has failed again'); + HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); + return; } + + Log.info('[HybridApp] Closing NewDot as retrying sign-in to OldDot succeeded'); + NativeModules.HybridAppModule.closeReactNativeApp(false, true); } - console.log('[Hybridapp] should use old app', shouldUseOldApp(tryNewDot)); if ( currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && @@ -83,9 +73,11 @@ function handleSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry< shouldUseOldApp(tryNewDot) ) { if (hybridApp?.oldDotSignInError) { + Log.info('[HybridApp] Unable to open OldDot. Sign-in has failed'); return; } - console.log('closing!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); + + Log.info('[HybridApp] Closing NewDot as sign-in to OldDot succeeded'); NativeModules.HybridAppModule.closeReactNativeApp(false, false); } @@ -96,31 +88,24 @@ function handleSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry< credentials?.autoGeneratedPassword && tryNewDot !== undefined ) { - if (shouldUseOldApp(tryNewDot)) { - setIsSigningIn(true); - } else { - setReadyToShowAuthScreens(true); + if (!shouldUseOldApp(tryNewDot)) { + 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.'); + HybridAppActions.setReadyToShowAuthScreens(true); } - console.log('[HybridApp] signInToOldDot'); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); - NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + Log.info(`[HybridApp] Performing sign-in${shouldUseOldApp(tryNewDot) ? '' : ' (in background)'} on OldDot side`); + HybridAppActions.startOldDotSignIn(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); } currentHybridApp = hybridApp; currentTryNewDot = tryNewDot; } -let currentSession: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.SESSION, - callback: (session: OnyxEntry) => { - if (!currentSession?.authToken && session?.authToken && currentHybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED) { - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); - } - currentSession = session; - }, -}); +function onOldDotSignInFinished(data: string) { + const eventData = JSON.parse(data) as {errorMessage: string}; + Log.info(`[HybridApp] onSignInFinished event received`, true, {eventData}); + HybridAppActions.finishOldDotSignIn(eventData.errorMessage); +} const init: Init = () => { if (!NativeModules.HybridAppModule) { @@ -128,18 +113,7 @@ const init: Init = () => { } // Setup event listeners - DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, (data) => { - Log.info(`[HybridApp] onSignInFinished event received with data: ${data}`, true); - const eventData = JSON.parse(data as string) as {errorMessage: string}; - - setIsSigningIn(false); - setOldDotSignInError(eventData.errorMessage); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); - - if (eventData.errorMessage === null) { - setReadyToSwitchToClassicExperience(true); - } - }); + DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, onOldDotSignInFinished); }; export default {init}; diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 486b7a0e79d7..8ef4dfeca492 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -1,17 +1,15 @@ +import {NativeModules} from 'react-native'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {HybridApp} from '@src/types/onyx'; import type HybridAppSettings from './types'; function parseHybridAppSettings(hybridAppSettings: string): HybridAppSettings { return JSON.parse(hybridAppSettings) as HybridAppSettings; } -function setIsSigningIn(isSigningIn: boolean) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {isSigningIn}); -} - function setOldDotSignInError(oldDotSignInError: string | null) { Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInError}); } @@ -20,14 +18,6 @@ function setReadyToShowAuthScreens(readyToShowAuthScreens: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToShowAuthScreens}); } -function setReadyToSwitchToClassicExperience(readyToSwitchToClassicExperience: boolean) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToSwitchToClassicExperience}); -} - -function setShouldResetSigningInLogic(shouldResetSigningInLogic: boolean) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {shouldResetSigningInLogic}); -} - function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); } @@ -44,15 +34,68 @@ function setOldDotSignInState(oldDotSignInState: ValueOf { if (!hybridApp.useNewDotSignInPage) { - setReadyToShowAuthScreens(true); - setReadyToSwitchToClassicExperience(true); return Promise.resolve(); } return Onyx.clear(); }; - const initAppAfterTransition = () => { - if (hybridApp.useNewDotSignInPage) { - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - } - return Promise.resolve(); - }; - return new Promise((resolve) => { clearOnyxBeforeSignIn() - .then(() => Onyx.merge(ONYXKEYS.HYBRID_APP, hybridApp)) + .then(() => HybridAppActions.prepareHybridAppAfterTransitionToNewDot(hybridApp)) .then(() => Onyx.multiSet(newDotOnyxValues)) .then(() => { if (!hybridApp.shouldRemoveDelegatedAccess) { @@ -523,11 +505,9 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS); }) .then(() => { - // This data is mocked and should be returned by BeginSignUp/SignInUser API commands - setUseNewDotSignInPage(!!hybridApp.useNewDotSignInPage); - setLoggedOutFromOldDot(!!hybridApp.loggedOutFromOldDot); + HybridAppActions.setUseNewDotSignInPage(!!hybridApp.useNewDotSignInPage); + HybridAppActions.setLoggedOutFromOldDot(!!hybridApp.loggedOutFromOldDot); }) - .then(initAppAfterTransition) .catch((error) => { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); }) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 03065d4e6cce..05d9e240e978 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -38,7 +38,7 @@ import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import variables from '@styles/variables'; import * as App from '@userActions/App'; -import {setIsSigningIn, setOldDotSignInError, setOldDotSignInState, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import * as HybridAppActions from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; @@ -256,21 +256,17 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr ...(NativeModules.HybridAppModule ? { action: () => { - console.log('[HybridApp] ready to switch', hybridApp); - if (hybridApp?.readyToSwitchToClassicExperience) { + if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && !hybridApp?.oldDotSignInError) { NativeModules.HybridAppModule.closeReactNativeApp(false, true); setInitialURL(undefined); return; } if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { - NativeModules.HybridAppModule.signInToOldDot(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); - setIsSigningIn(true); - setOldDotSignInError(null); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE); + HybridAppActions.retryOldDotSignInAfterFailure(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); } else { signOutAndRedirectToSignIn(undefined, undefined, false); - setShouldResetSigningInLogic(true); + HybridAppActions.resetHybridAppSignInState(); } }, } @@ -306,7 +302,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr icon: Expensicons.Exit, action: () => { signOut(false); - setShouldResetSigningInLogic(true); + HybridAppActions.resetHybridAppSignInState(); }, }, ], @@ -484,7 +480,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr confirmText={translate('workspace.upgrade.completed.gotIt')} isVisible={hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN} onConfirm={() => { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); + HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); }} shouldShowCancelButton={false} /> diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 894ce2b7ac02..4157c51ce125 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -25,7 +25,7 @@ import {shouldUseOldApp} from '@libs/HybridApp'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; -import {setIsSigningIn, setNewDotSignInState, setOldDotSignInError, setOldDotSignInState, setShouldResetSigningInLogic} from '@userActions/HybridApp'; +import * as HybridAppActions from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; import {signOut} from '@userActions/Session'; import * as User from '@userActions/User'; @@ -81,8 +81,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); - console.log('[HybridApp] hybridApp', hybridApp); - useEffect(() => { if (!(inputValidateCodeRef.current && hasError && (session?.autoAuthState === CONST.AUTO_AUTH_STATE.FAILED || account?.isLoading))) { return; @@ -162,9 +160,9 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Trigger the reset validate code flow and ensure the 2FA input field is reset to avoid it being permanently hidden */ const resendValidateCode = () => { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setOldDotSignInError(null); + HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + HybridAppActions.setOldDotSignInError(null); User.resendValidateCode(credentials?.login ?? ''); inputValidateCodeRef.current?.clear(); // Give feedback to the user to let them know the email was sent so that they don't spam the button. @@ -192,7 +190,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco Onyx.set(ONYXKEYS.SESSION, null); } SessionActions.clearSignInData(); - setShouldResetSigningInLogic(true); + HybridAppActions.resetHybridAppSignInState(); }, [clearLocalSignInData, session?.authToken]); useImperativeHandle(forwardedRef, () => ({ @@ -250,16 +248,15 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco */ const validateAndSubmitForm = useCallback(() => { if (session?.authToken) { - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); + HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); + HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); signOut(); Onyx.merge(ONYXKEYS.SESSION, { authToken: null, }).then(() => { - setIsSigningIn(false); - setOldDotSignInError(null); - setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); + HybridAppActions.setOldDotSignInError(null); + HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); + HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); }); } @@ -313,7 +310,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const recoveryCodeOr2faCode = isUsingRecoveryCode ? recoveryCode : twoFactorAuthCode; - setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); + HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); const accountID = credentials?.accountID; if (accountID) { SessionActions.signInWithValidateCode(accountID, validateCode, recoveryCodeOr2faCode); @@ -426,7 +423,9 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco large style={[styles.mv3]} text={translate('common.signIn')} - isLoading={shouldUseOldApp(tryNewDot) ? isValidateCodeFormSubmitting || hybridApp?.isSigningIn : isValidateCodeFormSubmitting} + isLoading={ + shouldUseOldApp(tryNewDot) ? isValidateCodeFormSubmitting || hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED : isValidateCodeFormSubmitting + } onPress={validateAndSubmitForm} /> diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 5eeb84f9a314..688ac01d05ed 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -3,25 +3,16 @@ import type CONST from '@src/CONST'; /** */ type HybridApp = { - /** Stores the information if HybridApp uses NewDot's sign in flow */ + /** Stores the information if HybridApp uses NewDot's sign-in flow */ useNewDotSignInPage?: boolean; - /** */ - isSigningIn?: boolean; - - /** */ + /** Stores the information about error that occurred on OldDot side during sign-in */ oldDotSignInError?: string | null; - /** */ + /** Tells if we can show AuthScreens */ readyToShowAuthScreens?: boolean; - /** */ - readyToSwitchToClassicExperience?: boolean; - - /** */ - shouldResetSigningInLogic?: boolean; - - /** States whether we transitioned from OldDot to show only certain group of screens. It should be undefined on pure NewDot. */ + /** States whether we transitioned from OldDot to show only certain group of screens */ isSingleNewDotEntry?: boolean; /** stores infromation if last log out was performed from OldDot */ @@ -30,10 +21,10 @@ type HybridApp = { /** */ shouldRemoveDelegatedAccess?: boolean; - /** */ + /** Describes current stage of NewDot sign-in */ newDotSignInState?: ValueOf; - /** */ + /** Describes current stage of OldDot sign-in */ oldDotSignInState?: ValueOf; }; From fb82b65c568e3d8ad0c39d301771b42cb2f8b058 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski <60450261+mateuuszzzzz@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:31:28 +0100 Subject: [PATCH 32/41] [HybridApp] Refactor `SignInPage` PR v2 (#144) * Simplify HybridApp.ts * Use early returns * Rearrange HybridApp module * Remove redundant keys * Remove unused import * Refactor remaining parts of code * Do not init with useEffect * Remove console.debug * Reduce repeating code * Remove unused import * reset sign-in logic on sign out --------- Co-authored-by: war-in --- src/libs/HybridApp.ts | 9 +-- .../Navigation/AppNavigator/AuthScreens.tsx | 2 +- src/libs/actions/HybridApp/index.ts | 73 ++++++++++++++----- src/libs/actions/Session/index.ts | 5 -- src/libs/actions/SignInRedirect.ts | 8 +- src/pages/settings/InitialSettingsPage.tsx | 4 +- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 34 +++------ 7 files changed, 80 insertions(+), 55 deletions(-) diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 4a5dea653714..720698c2d712 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -14,16 +14,14 @@ let currentTryNewDot: OnyxEntry; Onyx.connect({ key: ONYXKEYS.HYBRID_APP, callback: (hybridApp) => { - console.debug('Last hybridApp value', {hybridApp}); - handleSignInFlow(hybridApp, currentTryNewDot); + handleChangeInHybridAppSignInFlow(hybridApp, currentTryNewDot); }, }); Onyx.connect({ key: ONYXKEYS.NVP_TRYNEWDOT, callback: (tryNewDot) => { - console.debug('Last tryNewDot value', {tryNewDot}); - handleSignInFlow(currentHybridApp, tryNewDot); + handleChangeInHybridAppSignInFlow(currentHybridApp, tryNewDot); }, }); @@ -50,7 +48,7 @@ function shouldUseOldApp(tryNewDot?: TryNewDot) { return tryNewDot?.classicRedirect.dismissed === true; } -function handleSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry) { +function handleChangeInHybridAppSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry) { if (!NativeModules.HybridAppModule) { return; } @@ -112,7 +110,6 @@ const init: Init = () => { return; } - // Setup event listeners DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, onOldDotSignInFinished); }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 819e8ac29ffe..0e909b5ca614 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -1,6 +1,6 @@ import {findFocusedRoute} from '@react-navigation/native'; import React, {memo, useEffect, useMemo, useRef, useState} from 'react'; -import {NativeModules, View} from 'react-native'; +import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Onyx, {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 8ef4dfeca492..66e3ef1f5a5a 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -1,49 +1,80 @@ import {NativeModules} from 'react-native'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {HybridApp} from '@src/types/onyx'; import type HybridAppSettings from './types'; +/* + * Parses initial settings passed from OldDot app + */ function parseHybridAppSettings(hybridAppSettings: string): HybridAppSettings { return JSON.parse(hybridAppSettings) as HybridAppSettings; } -function setOldDotSignInError(oldDotSignInError: string | null) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInError}); -} - +/* + * Changes value of `readyToShowAuthScreens` + */ function setReadyToShowAuthScreens(readyToShowAuthScreens: boolean) { Onyx.merge(ONYXKEYS.HYBRID_APP, {readyToShowAuthScreens}); } -function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); -} - -function setLoggedOutFromOldDot(loggedOutFromOldDot: boolean) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {loggedOutFromOldDot}); -} - +/* + * Changes NewDot sign-in state + */ function setNewDotSignInState(newDotSignInState: ValueOf) { Onyx.merge(ONYXKEYS.HYBRID_APP, {newDotSignInState}); } +/* + * Changes OldDot sign-in state + */ function setOldDotSignInState(oldDotSignInState: ValueOf) { Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInState}); } -function resetHybridAppSignInState() { +/* + * Starts HybridApp sign-in flow from the beginning. + * In certain cases, it can perform sign-out if necessary + */ +function resetSignInFlow(signOut = false) { + if (signOut) { + Onyx.merge(ONYXKEYS.HYBRID_APP, { + oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT, + newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT, + }); + + Session.signOut(); + + Onyx.merge(ONYXKEYS.SESSION, null); + } + Onyx.merge(ONYXKEYS.HYBRID_APP, { readyToShowAuthScreens: false, + oldDotSignInError: null, + oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, + newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, + }); +} + +/* + * Sets proper sign-in state after sign-out on NewDot side + */ +function resetStateAfterSignOut() { + Onyx.merge(ONYXKEYS.HYBRID_APP, { useNewDotSignInPage: true, + readyToShowAuthScreens: false, oldDotSignInError: null, oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, }); } +/* + * Starts OldDot sign-in flow + */ function startOldDotSignIn(autoGeneratedLogin: string, autoGeneratedPassword: string) { Onyx.merge(ONYXKEYS.HYBRID_APP, { oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.STARTED, @@ -52,6 +83,9 @@ function startOldDotSignIn(autoGeneratedLogin: string, autoGeneratedPassword: st NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword); } +/* + * Retries sign-in on OldDot side + */ function retryOldDotSignInAfterFailure(autoGeneratedLogin: string, autoGeneratedPassword: string) { Onyx.merge(ONYXKEYS.HYBRID_APP, { oldDotSignInError: null, @@ -61,6 +95,9 @@ function retryOldDotSignInAfterFailure(autoGeneratedLogin: string, autoGenerated NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword); } +/* + * Updates Onyx state after successful OldDot sign-in + */ function finishOldDotSignIn(errorMessage: string | null) { Onyx.merge(ONYXKEYS.HYBRID_APP, { oldDotSignInError: errorMessage, @@ -68,6 +105,9 @@ function finishOldDotSignIn(errorMessage: string | null) { }); } +/* + * 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, { @@ -79,6 +119,7 @@ function prepareHybridAppAfterTransitionToNewDot(hybridApp: HybridApp) { }); } + // 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, @@ -87,15 +128,13 @@ function prepareHybridAppAfterTransitionToNewDot(hybridApp: HybridApp) { export { parseHybridAppSettings, - setOldDotSignInError, setReadyToShowAuthScreens, - setUseNewDotSignInPage, - setLoggedOutFromOldDot, setNewDotSignInState, setOldDotSignInState, - resetHybridAppSignInState, + resetSignInFlow, retryOldDotSignInAfterFailure, finishOldDotSignIn, startOldDotSignIn, prepareHybridAppAfterTransitionToNewDot, + resetStateAfterSignOut, }; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 1f0e0f5a8eec..c89d9fb76ffd 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -39,7 +39,6 @@ import * as SessionUtils from '@libs/SessionUtils'; import {clearSoundAssetsCache} from '@libs/Sound'; import Timers from '@libs/Timers'; import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import * as App from '@userActions/App'; import {KEYS_TO_PRESERVE, openApp} from '@userActions/App'; import {KEYS_TO_PRESERVE_DELEGATE_ACCESS} from '@userActions/Delegate'; import * as Device from '@userActions/Device'; @@ -504,10 +503,6 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string } return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS); }) - .then(() => { - HybridAppActions.setUseNewDotSignInPage(!!hybridApp.useNewDotSignInPage); - HybridAppActions.setLoggedOutFromOldDot(!!hybridApp.loggedOutFromOldDot); - }) .catch((error) => { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); }) diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index 3042ddda6845..48734e79456d 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -1,8 +1,9 @@ +import {NativeModules} from 'react-native'; import Onyx from 'react-native-onyx'; import * as ErrorUtils from '@libs/ErrorUtils'; import type {OnyxKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; -import {setUseNewDotSignInPage} from './HybridApp'; +import * as HybridAppActions from './HybridApp'; import * as Policy from './Policy/Policy'; let currentIsOffline: boolean | undefined; @@ -39,7 +40,10 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { // `Onyx.clear` reinitializes the Onyx instance with initial values so use `Onyx.merge` instead of `Onyx.set` Onyx.merge(ONYXKEYS.SESSION, {errors: ErrorUtils.getMicroSecondOnyxErrorWithMessage(errorMessage)}); - setUseNewDotSignInPage(true); + + if (NativeModules.HybridAppModule) { + HybridAppActions.resetStateAfterSignOut(); + } }); } diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 05d9e240e978..61206e84695d 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -266,7 +266,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr HybridAppActions.retryOldDotSignInAfterFailure(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); } else { signOutAndRedirectToSignIn(undefined, undefined, false); - HybridAppActions.resetHybridAppSignInState(); + HybridAppActions.resetStateAfterSignOut(); } }, } @@ -302,7 +302,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr icon: Expensicons.Exit, action: () => { signOut(false); - HybridAppActions.resetHybridAppSignInState(); + HybridAppActions.resetStateAfterSignOut(); }, }, ], diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 4157c51ce125..9dd5082c08ca 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -1,8 +1,8 @@ import {useIsFocused} from '@react-navigation/native'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; -import {View} from 'react-native'; -import Onyx, {useOnyx} from 'react-native-onyx'; +import {NativeModules, View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import SafariFormWrapper from '@components/Form/SafariFormWrapper'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -27,7 +27,6 @@ import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; import * as HybridAppActions from '@userActions/HybridApp'; import * as SessionActions from '@userActions/Session'; -import {signOut} from '@userActions/Session'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -160,9 +159,10 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Trigger the reset validate code flow and ensure the 2FA input field is reset to avoid it being permanently hidden */ const resendValidateCode = () => { - HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - HybridAppActions.setOldDotSignInError(null); + if (NativeModules.HybridAppModule) { + HybridAppActions.resetSignInFlow(); + } + User.resendValidateCode(credentials?.login ?? ''); inputValidateCodeRef.current?.clear(); // Give feedback to the user to let them know the email was sent so that they don't spam the button. @@ -184,13 +184,12 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Clears local and Onyx sign in states */ const clearSignInData = useCallback(() => { - clearLocalSignInData(); - if (session?.authToken) { - signOut(); - Onyx.set(ONYXKEYS.SESSION, null); + if (NativeModules.HybridAppModule && session?.authToken) { + HybridAppActions.resetSignInFlow(true); } + + clearLocalSignInData(); SessionActions.clearSignInData(); - HybridAppActions.resetHybridAppSignInState(); }, [clearLocalSignInData, session?.authToken]); useImperativeHandle(forwardedRef, () => ({ @@ -247,17 +246,8 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Check that all the form fields are valid, then trigger the submit callback */ const validateAndSubmitForm = useCallback(() => { - if (session?.authToken) { - HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); - HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT); - signOut(); - Onyx.merge(ONYXKEYS.SESSION, { - authToken: null, - }).then(() => { - HybridAppActions.setOldDotSignInError(null); - HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED); - HybridAppActions.setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); - }); + if (NativeModules.HybridAppModule && session?.authToken) { + HybridAppActions.resetSignInFlow(true); } if (account?.isLoading) { From ecb93e274ae6dd3ee313c3cdc0cfa66a1f18936c Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 27 Nov 2024 15:33:07 +0100 Subject: [PATCH 33/41] fix part of signup flow --- src/pages/settings/InitialSettingsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 61206e84695d..8f97b2ebba94 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -265,7 +265,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { HybridAppActions.retryOldDotSignInAfterFailure(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); } else { - signOutAndRedirectToSignIn(undefined, undefined, false); + signOutAndRedirectToSignIn(false, false, false); HybridAppActions.resetStateAfterSignOut(); } }, From 6f926fe1fca95d87f50f05165b2ee9a1762ca171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Warcho=C5=82?= <61014013+war-in@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:43:15 +0100 Subject: [PATCH 34/41] [SignIn Page] New user flow (#145) Co-authored-by: war-in --- src/libs/HybridApp.ts | 2 +- src/libs/actions/HybridApp/index.ts | 14 +++++++-- src/pages/settings/InitialSettingsPage.tsx | 35 +++++++++++++++++----- src/types/modules/react-native.d.ts | 2 +- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 720698c2d712..257191af8eca 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -76,7 +76,7 @@ function handleChangeInHybridAppSignInFlow(hybridApp: OnyxEntry, tryN } Log.info('[HybridApp] Closing NewDot as sign-in to OldDot succeeded'); - NativeModules.HybridAppModule.closeReactNativeApp(false, false); + NativeModules.HybridAppModule.closeReactNativeApp(false, true); } if ( diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 66e3ef1f5a5a..073cf2f3ae8a 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -80,19 +80,27 @@ function startOldDotSignIn(autoGeneratedLogin: string, autoGeneratedPassword: st oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.STARTED, }); - NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword); + NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword, false, '', '', '', ''); } /* * Retries sign-in on OldDot side */ -function retryOldDotSignInAfterFailure(autoGeneratedLogin: string, autoGeneratedPassword: string) { +function retryOldDotSignInAfterFailure( + autoGeneratedLogin: string, + autoGeneratedPassword: string, + isNewUser: boolean, + authToken: string, + encryptedAuthToken: string, + email: string, + policyID: string, +) { Onyx.merge(ONYXKEYS.HYBRID_APP, { oldDotSignInError: null, oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE, }); - NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword); + NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword, isNewUser, authToken, encryptedAuthToken, email, policyID); } /* diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 8f97b2ebba94..e8ca15a45659 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -32,6 +32,7 @@ import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {resetExitSurveyForm} from '@libs/actions/ExitSurvey'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {getCurrentUserEmail} from '@libs/Network/NetworkStore'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as UserUtils from '@libs/UserUtils'; import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; @@ -42,7 +43,6 @@ import * as HybridAppActions from '@userActions/HybridApp'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Session from '@userActions/Session'; -import {signOutAndRedirectToSignIn} from '@userActions/Session'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -91,6 +91,8 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const network = useNetwork(); const theme = useTheme(); @@ -262,12 +264,15 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr return; } - if (credentials?.autoGeneratedLogin && credentials?.autoGeneratedPassword) { - HybridAppActions.retryOldDotSignInAfterFailure(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); - } else { - signOutAndRedirectToSignIn(false, false, false); - HybridAppActions.resetStateAfterSignOut(); - } + HybridAppActions.retryOldDotSignInAfterFailure( + credentials?.autoGeneratedLogin ?? '', + credentials?.autoGeneratedPassword ?? '', + !credentials?.autoGeneratedLogin, + session?.authToken ?? '', + session?.encryptedAuthToken ?? '', + getCurrentUserEmail() ?? '', + activePolicyID ?? '', + ); }, } : { @@ -307,7 +312,21 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr }, ], }; - }, [styles.pt4, styles.popoverMenuIcon, hybridApp, theme.spinner, credentials?.autoGeneratedLogin, credentials?.autoGeneratedPassword, setInitialURL, signOut, shouldOpenBookACall]); + }, [ + styles.pt4, + styles.popoverMenuIcon, + hybridApp?.oldDotSignInState, + hybridApp?.oldDotSignInError, + theme.spinner, + credentials?.autoGeneratedLogin, + credentials?.autoGeneratedPassword, + session?.authToken, + session?.encryptedAuthToken, + activePolicyID, + setInitialURL, + shouldOpenBookACall, + signOut, + ]); /** * Retuns JSX.Element with menu items diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index d2603de1edf3..7d083c07076b 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -9,7 +9,7 @@ type HybridAppModule = { closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void; completeOnboarding: (status: boolean) => void; switchAccount: (newDotCurrentAccount: string) => void; - signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string) => void; + signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, isNewUser: boolean, authToken: string, encryptedAuthToken: string, email: string, policyID: string) => void; signOutFromOldDot: () => void; exitApp: () => void; }; From 90c0954bcb93621a2a29e8978bc86de65e861137 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 2 Dec 2024 13:53:31 +0100 Subject: [PATCH 35/41] fix isSingleNewDotEntry logic (travel feature) --- src/libs/Navigation/AppNavigator/index.native.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index bc95c4531756..16c867afb622 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -21,25 +21,16 @@ function AppNavigator({authenticated}: AppNavigatorProps) { const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); - /** - * kiedy chcemy pokazać auth screens: - * - logujemy do OD - * - nie chcemy wcale - * - logujemy do ND - * - gdy jesteśmy authenticated (po odpaleniu signInToOldDot) - * - przechodzimy z OD do ND - * - od razu o ile jesteśmy zalogowani, ale powinniśmy być zawsze - */ const shouldShowAuthScreens = useMemo(() => { if (!NativeModules.HybridAppModule) { return authenticated; } - if (shouldUseOldApp(tryNewDot)) { + if (shouldUseOldApp(tryNewDot) && !hybridApp?.isSingleNewDotEntry) { return false; } return authenticated && (!hybridApp?.useNewDotSignInPage || hybridApp?.readyToShowAuthScreens); - }, [authenticated, hybridApp?.readyToShowAuthScreens, tryNewDot, hybridApp?.useNewDotSignInPage]); + }, [tryNewDot, hybridApp?.isSingleNewDotEntry, hybridApp?.useNewDotSignInPage, hybridApp?.readyToShowAuthScreens, authenticated]); useEffect(() => { if (!NativeModules.HybridAppModule || !initialURL || !shouldShowAuthScreens) { From 9cfbaf5bb3e2f8c63f65885b08a637978473f2f3 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Thu, 5 Dec 2024 15:54:13 +0100 Subject: [PATCH 36/41] Rename argument --- src/libs/actions/Session/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 9cc89a8d896b..a69fe0a75a88 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -212,12 +212,12 @@ function hasAuthToken(): boolean { return !!session.authToken; } -function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, killHybridApp = true) { +function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean, shouldSignOutFromOldDot = true) { Log.info('Redirecting to Sign In because signOut() was called'); hideContextMenu(false); if (!isAnonymousUser()) { // In the HybridApp, we want the Old Dot to handle the sign out process - if (NativeModules.HybridAppModule && killHybridApp) { + if (NativeModules.HybridAppModule && shouldSignOutFromOldDot) { NativeModules.HybridAppModule.signOutFromOldDot(); } // We'll only call signOut if we're not stashing the session and this is not a supportal session, From 85eddb238e7e5965af19d5d02955ec366f437230 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Thu, 5 Dec 2024 15:56:55 +0100 Subject: [PATCH 37/41] Change function name --- src/components/InitialURLContextProvider.tsx | 4 ++-- src/libs/actions/Session/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index b89a842d1420..61f18de4d034 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -3,7 +3,7 @@ import type {ReactNode} from 'react'; import {Linking} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import {signInAfterTransitionFromOldDot} from '@libs/actions/Session'; +import {setupNewDotAfterTransitionFromOldDot} from '@libs/actions/Session'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -53,7 +53,7 @@ function InitialURLContextProvider({children, url, hybridAppSettings}: InitialUR } if (url && hybridAppSettings) { - signInAfterTransitionFromOldDot(url, hybridAppSettings).then((route) => { + setupNewDotAfterTransitionFromOldDot(url, hybridAppSettings).then((route) => { setInitialURL(route); setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); }); diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index a69fe0a75a88..7c09abc55951 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -488,7 +488,7 @@ function signUpUser() { API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); } -function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string) { +function setupNewDotAfterTransitionFromOldDot(route: Route, hybridAppSettings: string) { const parsedHybridAppSettings = HybridAppActions.parseHybridAppSettings(hybridAppSettings); const {initialOnyxValues} = parsedHybridAppSettings; const {hybridApp, ...newDotOnyxValues} = initialOnyxValues; @@ -1165,5 +1165,5 @@ export { isSupportAuthToken, hasStashedSession, signUpUser, - signInAfterTransitionFromOldDot, + setupNewDotAfterTransitionFromOldDot, }; From 68ed5168bb4eaac39b13ed1fe66cb8c180867884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Warcho=C5=82?= <61014013+war-in@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:41:39 +0100 Subject: [PATCH 38/41] Dont sign in in old dot (#150) * wip * wip * remove comment * clean * remove unused parameters and fix sign up flow * block sign in logic when OD sign-in was performed * simplify blocking logic * fix crash when switching apps * Fix bug where OpenApp is called after opening app from background --------- Co-authored-by: war-in Co-authored-by: Mateusz Rajski --- src/App.tsx | 2 - src/CONST.ts | 3 - src/components/InitialURLContextProvider.tsx | 19 +++- src/libs/API/types.ts | 4 +- src/libs/HybridApp.ts | 103 +++++++----------- .../Navigation/AppNavigator/PublicScreens.tsx | 1 - .../Navigation/AppNavigator/index.native.tsx | 7 +- src/libs/actions/HybridApp/index.ts | 93 +--------------- src/libs/actions/Session/index.ts | 36 ++++-- src/libs/actions/SignInRedirect.ts | 6 - src/pages/settings/InitialSettingsPage.tsx | 72 +----------- src/pages/signin/SignUpWelcomeForm.tsx | 4 +- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 21 +--- src/types/modules/react-native.d.ts | 2 +- src/types/onyx/HybridApp.ts | 8 +- 15 files changed, 105 insertions(+), 276 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0f09216a3fe6..34d4acb6c4d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,7 +36,6 @@ import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import {ReportIDsContextProvider} from './hooks/useReportIDs'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; -import HybridApp from './libs/HybridApp'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; @@ -65,7 +64,6 @@ const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode function App({url, hybridAppSettings}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); - HybridApp.init(); return ( diff --git a/src/CONST.ts b/src/CONST.ts index 6964fd99fd53..5cd9499d0e0f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6299,9 +6299,6 @@ const CONST = { NOT_STARTED: 'notStarted', STARTED: 'started', FINISHED: 'finished', - WAITING_FOR_SIGN_OUT: 'waitingForSignOut', - RETRYING_AFTER_FAILURE: 'retryingAfterFailure', - FAILED_AGAIN: 'failedAgain', }, CSV_IMPORT_COLUMNS: { diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index 61f18de4d034..d6162231c694 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -1,4 +1,4 @@ -import React, {createContext, useEffect, useMemo, useState} from 'react'; +import React, {createContext, useEffect, useMemo, useRef, useState} from 'react'; import type {ReactNode} from 'react'; import {Linking} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -9,6 +9,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import {useSplashScreenStateContext} from '@src/SplashScreenStateContext'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; type InitialUrlContextType = { initialURL: Route | undefined; @@ -35,6 +36,9 @@ function InitialURLContextProvider({children, url, hybridAppSettings}: InitialUR const [initialURL, setInitialURL] = useState | undefined>(url); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); const {splashScreenState, setSplashScreenState} = useSplashScreenStateContext(); + const [tryNewDot, tryNewDotMetadata] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + // We use `setupCalled` ref to guarantee that `signInAfterTransitionFromOldDot` is called once. + const setupCalled = useRef(false); useEffect(() => { if (url !== CONST.HYBRID_APP.REORDERING_REACT_NATIVE_ACTIVITY_TO_FRONT) { @@ -53,16 +57,19 @@ function InitialURLContextProvider({children, url, hybridAppSettings}: InitialUR } if (url && hybridAppSettings) { - setupNewDotAfterTransitionFromOldDot(url, hybridAppSettings).then((route) => { - setInitialURL(route); - setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); - }); + if (!isLoadingOnyxValue(tryNewDotMetadata) && !setupCalled.current) { + setupCalled.current = true; + setupNewDotAfterTransitionFromOldDot(url, hybridAppSettings, tryNewDot).then((route) => { + setInitialURL(route); + setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); + }); + } return; } Linking.getInitialURL().then((initURL) => { setInitialURL(initURL as Route); }); - }, [hybridAppSettings, setSplashScreenState, url]); + }, [hybridAppSettings, setSplashScreenState, tryNewDot, tryNewDotMetadata, url]); const initialUrlContext = useMemo( () => ({ diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index c0485fcbd1b5..a5014c67091d 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -306,7 +306,6 @@ const WRITE_COMMANDS = { TRANSACTION_MERGE: 'Transaction_Merge', RESOLVE_DUPLICATES: 'ResolveDuplicates', UPDATE_SUBSCRIPTION_TYPE: 'UpdateSubscriptionType', - SIGN_UP_USER: 'SignUpUser', UPDATE_SUBSCRIPTION_AUTO_RENEW: 'UpdateSubscriptionAutoRenew', UPDATE_SUBSCRIPTION_ADD_NEW_USERS_AUTOMATICALLY: 'UpdateSubscriptionAddNewUsersAutomatically', UPDATE_SUBSCRIPTION_SIZE: 'UpdateSubscriptionSize', @@ -756,7 +755,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.TRANSACTION_MERGE]: Parameters.TransactionMergeParams; [WRITE_COMMANDS.RESOLVE_DUPLICATES]: Parameters.ResolveDuplicatesParams; [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_TYPE]: Parameters.UpdateSubscriptionTypeParams; - [WRITE_COMMANDS.SIGN_UP_USER]: Parameters.SignUpUserParams; [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_AUTO_RENEW]: Parameters.UpdateSubscriptionAutoRenewParams; [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_ADD_NEW_USERS_AUTOMATICALLY]: Parameters.UpdateSubscriptionAddNewUsersAutomaticallyParams; [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE]: Parameters.UpdateSubscriptionSizeParams; @@ -1030,6 +1028,7 @@ const SIDE_EFFECT_REQUEST_COMMANDS = { COMPLETE_HYBRID_APP_ONBOARDING: 'CompleteHybridAppOnboarding', CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP: 'ConnectPolicyToQuickbooksDesktop', SIGN_IN_USER: 'SigninUser', + SIGN_UP_USER: 'SignUpUser', // PayMoneyRequestOnSearch only works online (pattern C) and we need to play the success sound only when the request is successful PAY_MONEY_REQUEST_ON_SEARCH: 'PayMoneyRequestOnSearch', @@ -1054,6 +1053,7 @@ type SideEffectRequestCommandParameters = { [SIDE_EFFECT_REQUEST_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP]: Parameters.ConnectPolicyToQuickBooksDesktopParams; [SIDE_EFFECT_REQUEST_COMMANDS.SIGN_IN_USER]: SignInUserParams; + [SIDE_EFFECT_REQUEST_COMMANDS.SIGN_UP_USER]: Parameters.SignUpUserParams; [SIDE_EFFECT_REQUEST_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH]: Parameters.PayMoneyRequestOnSearchParams; }; diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 257191af8eca..99843665f1d7 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -1,27 +1,36 @@ -import {DeviceEventEmitter, NativeModules} from 'react-native'; +import {NativeModules} from 'react-native'; import Onyx from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Credentials, HybridApp, Session, TryNewDot} from '@src/types/onyx'; import * as HybridAppActions from './actions/HybridApp'; -import type {Init} from './ActiveClientManager/types'; import Log from './Log'; +import {getCurrentUserEmail} from './Network/NetworkStore'; let currentHybridApp: OnyxEntry; let currentTryNewDot: OnyxEntry; +let currentCredentials: OnyxEntry; Onyx.connect({ key: ONYXKEYS.HYBRID_APP, callback: (hybridApp) => { - handleChangeInHybridAppSignInFlow(hybridApp, currentTryNewDot); + handleChangeInHybridAppSignInFlow(hybridApp, currentTryNewDot, currentCredentials); }, }); Onyx.connect({ key: ONYXKEYS.NVP_TRYNEWDOT, callback: (tryNewDot) => { - handleChangeInHybridAppSignInFlow(currentHybridApp, tryNewDot); + handleChangeInHybridAppSignInFlow(currentHybridApp, tryNewDot, currentCredentials); + }, +}); + +Onyx.connect({ + key: ONYXKEYS.CREDENTIALS, + callback: (credentials) => { + currentCredentials = credentials; + handleChangeInHybridAppSignInFlow(currentHybridApp, currentTryNewDot, credentials); }, }); @@ -36,11 +45,11 @@ Onyx.connect({ }, }); -let credentials: OnyxEntry; +let activePolicyID: OnyxEntry; Onyx.connect({ - key: ONYXKEYS.CREDENTIALS, - callback: (newCredentials) => { - credentials = newCredentials; + key: ONYXKEYS.NVP_ACTIVE_POLICY_ID, + callback: (newActivePolicyID) => { + activePolicyID = newActivePolicyID; }, }); @@ -48,71 +57,39 @@ function shouldUseOldApp(tryNewDot?: TryNewDot) { return tryNewDot?.classicRedirect.dismissed === true; } -function handleChangeInHybridAppSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry) { +function handleChangeInHybridAppSignInFlow(hybridApp: OnyxEntry, tryNewDot: OnyxEntry, credentials: OnyxEntry) { if (!NativeModules.HybridAppModule) { return; } - if (currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE && hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED) { - if (hybridApp?.oldDotSignInError) { - Log.info('[HybridApp] Unable to open OldDot. Sign-in has failed again'); - HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FAILED_AGAIN); - return; - } - - Log.info('[HybridApp] Closing NewDot as retrying sign-in to OldDot succeeded'); - NativeModules.HybridAppModule.closeReactNativeApp(false, true); - } - - if ( - currentHybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED && - hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && - tryNewDot !== undefined && - shouldUseOldApp(tryNewDot) - ) { - if (hybridApp?.oldDotSignInError) { - Log.info('[HybridApp] Unable to open OldDot. Sign-in has failed'); - return; - } - - Log.info('[HybridApp] Closing NewDot as sign-in to OldDot succeeded'); - NativeModules.HybridAppModule.closeReactNativeApp(false, true); + if (!hybridApp?.useNewDotSignInPage) { + currentHybridApp = hybridApp; + currentTryNewDot = tryNewDot; + return; } - if ( - hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && - hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED && - credentials?.autoGeneratedLogin && - credentials?.autoGeneratedPassword && - tryNewDot !== undefined - ) { - if (!shouldUseOldApp(tryNewDot)) { - 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.'); - HybridAppActions.setReadyToShowAuthScreens(true); - } - + if (hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && tryNewDot !== undefined && !!credentials?.autoGeneratedLogin && !!credentials?.autoGeneratedPassword) { Log.info(`[HybridApp] Performing sign-in${shouldUseOldApp(tryNewDot) ? '' : ' (in background)'} on OldDot side`); - HybridAppActions.startOldDotSignIn(credentials.autoGeneratedLogin, credentials.autoGeneratedPassword); + NativeModules.HybridAppModule.signInToOldDot( + credentials.autoGeneratedLogin, + credentials.autoGeneratedPassword, + currentSession?.authToken ?? '', + currentSession?.encryptedAuthToken ?? '', + getCurrentUserEmail() ?? '', + activePolicyID ?? '', + ); + HybridAppActions.setUseNewDotSignInPage(false).then(() => { + if (shouldUseOldApp(tryNewDot)) { + NativeModules.HybridAppModule.closeReactNativeApp(false, 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.'); + HybridAppActions.setReadyToShowAuthScreens(true); + } + }); } currentHybridApp = hybridApp; currentTryNewDot = tryNewDot; } -function onOldDotSignInFinished(data: string) { - const eventData = JSON.parse(data) as {errorMessage: string}; - Log.info(`[HybridApp] onSignInFinished event received`, true, {eventData}); - HybridAppActions.finishOldDotSignIn(eventData.errorMessage); -} - -const init: Init = () => { - if (!NativeModules.HybridAppModule) { - return; - } - - DeviceEventEmitter.addListener(CONST.EVENTS.HYBRID_APP.ON_SIGN_IN_FINISHED, onOldDotSignInFinished); -}; - -export default {init}; - -export {shouldUseOldApp}; +export default {shouldUseOldApp}; diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.tsx b/src/libs/Navigation/AppNavigator/PublicScreens.tsx index fa880a5246f2..3a4e867e8e84 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.tsx +++ b/src/libs/Navigation/AppNavigator/PublicScreens.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import {NativeModules} from 'react-native'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; import type {PublicScreensParamList} from '@navigation/types'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; diff --git a/src/libs/Navigation/AppNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/index.native.tsx index 16c867afb622..48f1ef2f2dd5 100644 --- a/src/libs/Navigation/AppNavigator/index.native.tsx +++ b/src/libs/Navigation/AppNavigator/index.native.tsx @@ -2,6 +2,7 @@ import React, {memo, useContext, useEffect, useMemo} from 'react'; import {NativeModules} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import {InitialURLContext} from '@components/InitialURLContextProvider'; +import HybridApp from '@libs/HybridApp'; import Navigation from '@libs/Navigation/Navigation'; import ONYXKEYS from '@src/ONYXKEYS'; import type {TryNewDot} from '@src/types/onyx'; @@ -12,10 +13,6 @@ type AppNavigatorProps = { authenticated: boolean; }; -function shouldUseOldApp(tryNewDot?: TryNewDot) { - return tryNewDot?.classicRedirect.dismissed === true; -} - function AppNavigator({authenticated}: AppNavigatorProps) { const {initialURL, setInitialURL} = useContext(InitialURLContext); const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); @@ -25,7 +22,7 @@ function AppNavigator({authenticated}: AppNavigatorProps) { if (!NativeModules.HybridAppModule) { return authenticated; } - if (shouldUseOldApp(tryNewDot) && !hybridApp?.isSingleNewDotEntry) { + if (HybridApp.shouldUseOldApp(tryNewDot) && !hybridApp?.isSingleNewDotEntry) { return false; } diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 073cf2f3ae8a..0bcda22b8b2c 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -1,7 +1,5 @@ -import {NativeModules} from 'react-native'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {HybridApp} from '@src/types/onyx'; @@ -28,88 +26,18 @@ function setNewDotSignInState(newDotSignInState: ValueOf) { - Onyx.merge(ONYXKEYS.HYBRID_APP, {oldDotSignInState}); +function setUseNewDotSignInPage(useNewDotSignInPage: boolean) { + return Onyx.merge(ONYXKEYS.HYBRID_APP, {useNewDotSignInPage}); } /* * Starts HybridApp sign-in flow from the beginning. - * In certain cases, it can perform sign-out if necessary */ -function resetSignInFlow(signOut = false) { - if (signOut) { - Onyx.merge(ONYXKEYS.HYBRID_APP, { - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT, - newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.WAITING_FOR_SIGN_OUT, - }); - - Session.signOut(); - - Onyx.merge(ONYXKEYS.SESSION, null); - } - +function resetSignInFlow() { Onyx.merge(ONYXKEYS.HYBRID_APP, { readyToShowAuthScreens: false, - oldDotSignInError: null, - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, - }); -} - -/* - * Sets proper sign-in state after sign-out on NewDot side - */ -function resetStateAfterSignOut() { - Onyx.merge(ONYXKEYS.HYBRID_APP, { useNewDotSignInPage: true, - readyToShowAuthScreens: false, - oldDotSignInError: null, - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, - newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, - }); -} - -/* - * Starts OldDot sign-in flow - */ -function startOldDotSignIn(autoGeneratedLogin: string, autoGeneratedPassword: string) { - Onyx.merge(ONYXKEYS.HYBRID_APP, { - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.STARTED, - }); - - NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword, false, '', '', '', ''); -} - -/* - * Retries sign-in on OldDot side - */ -function retryOldDotSignInAfterFailure( - autoGeneratedLogin: string, - autoGeneratedPassword: string, - isNewUser: boolean, - authToken: string, - encryptedAuthToken: string, - email: string, - policyID: string, -) { - Onyx.merge(ONYXKEYS.HYBRID_APP, { - oldDotSignInError: null, - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE, - }); - - NativeModules.HybridAppModule.signInToOldDot(autoGeneratedLogin, autoGeneratedPassword, isNewUser, authToken, encryptedAuthToken, email, policyID); -} - -/* - * Updates Onyx state after successful OldDot sign-in - */ -function finishOldDotSignIn(errorMessage: string | null) { - Onyx.merge(ONYXKEYS.HYBRID_APP, { - oldDotSignInError: errorMessage, - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED, }); } @@ -121,8 +49,6 @@ function prepareHybridAppAfterTransitionToNewDot(hybridApp: HybridApp) { return Onyx.merge(ONYXKEYS.HYBRID_APP, { ...hybridApp, readyToShowAuthScreens: false, - oldDotSignInError: null, - oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, }); } @@ -134,15 +60,4 @@ function prepareHybridAppAfterTransitionToNewDot(hybridApp: HybridApp) { }); } -export { - parseHybridAppSettings, - setReadyToShowAuthScreens, - setNewDotSignInState, - setOldDotSignInState, - resetSignInFlow, - retryOldDotSignInAfterFailure, - finishOldDotSignIn, - startOldDotSignIn, - prepareHybridAppAfterTransitionToNewDot, - resetStateAfterSignOut, -}; +export {parseHybridAppSettings, setReadyToShowAuthScreens, setNewDotSignInState, resetSignInFlow, prepareHybridAppAfterTransitionToNewDot, setUseNewDotSignInPage}; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 7c09abc55951..5c71e562c1cb 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -37,6 +37,7 @@ import NetworkConnection from '@libs/NetworkConnection'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportUtils 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'; @@ -54,6 +55,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {HybridAppRoute, 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 Session from '@src/types/onyx/Session'; import type {AutoAuthState} from '@src/types/onyx/Session'; @@ -485,10 +487,16 @@ function signUpUser() { const params: SignUpUserParams = {email: credentials.login, preferredLocale}; - API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); + // eslint-disable-next-line rulesdir/no-api-side-effects-method + API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}).then((response) => { + if (!response) { + return; + } + Onyx.merge(ONYXKEYS.NVP_TRYNEWDOT, {classicRedirect: {dismissed: !response.tryNewDot}}); + }); } -function setupNewDotAfterTransitionFromOldDot(route: Route, hybridAppSettings: string) { +function setupNewDotAfterTransitionFromOldDot(route: Route, hybridAppSettings: string, tryNewDot: TryNewDot | undefined) { const parsedHybridAppSettings = HybridAppActions.parseHybridAppSettings(hybridAppSettings); const {initialOnyxValues} = parsedHybridAppSettings; const {hybridApp, ...newDotOnyxValues} = initialOnyxValues; @@ -501,16 +509,28 @@ function setupNewDotAfterTransitionFromOldDot(route: Route, hybridAppSettings: s return Onyx.clear(); }; + const resetDidUserLoginDuringSessionIfNeeded = () => { + if (newDotOnyxValues.nvp_tryNewDot === undefined || tryNewDot?.classicRedirect.dismissed !== true) { + return Promise.resolve(); + } + + Log.info("[HybridApp] OpenApp hasn't been called yet. Calling `resetDidUserLogInDuringSession`"); + resetDidUserLogInDuringSession(); + }; + + const handleDelegateAccess = () => { + if (!hybridApp.shouldRemoveDelegatedAccess) { + return; + } + return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS); + }; + return new Promise((resolve) => { clearOnyxBeforeSignIn() .then(() => HybridAppActions.prepareHybridAppAfterTransitionToNewDot(hybridApp)) + .then(resetDidUserLoginDuringSessionIfNeeded) .then(() => Onyx.multiSet(newDotOnyxValues)) - .then(() => { - if (!hybridApp.shouldRemoveDelegatedAccess) { - return; - } - return Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS); - }) + .then(handleDelegateAccess) .catch((error) => { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); }) diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index ca4cf7529d47..2c81fe01fb0f 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -1,9 +1,7 @@ -import {NativeModules} from 'react-native'; import Onyx from 'react-native-onyx'; import * as ErrorUtils from '@libs/ErrorUtils'; import type {OnyxKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; -import * as HybridAppActions from './HybridApp'; import * as Policy from './Policy/Policy'; let currentIsOffline: boolean | undefined; @@ -41,10 +39,6 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { // `Onyx.clear` reinitializes the Onyx instance with initial values so use `Onyx.merge` instead of `Onyx.set` Onyx.merge(ONYXKEYS.SESSION, {errors: ErrorUtils.getMicroSecondOnyxErrorWithMessage(errorMessage)}); - - if (NativeModules.HybridAppModule) { - HybridAppActions.resetStateAfterSignOut(); - } }); } diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 6d1a557890fe..07071736cd10 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,9 +1,8 @@ import {useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; -import type {ReactNode} from 'react'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; -import {ActivityIndicator, NativeModules, View} from 'react-native'; +import {NativeModules, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; @@ -33,7 +32,6 @@ import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {resetExitSurveyForm} from '@libs/actions/ExitSurvey'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getCurrentUserEmail} from '@libs/Network/NetworkStore'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as UserUtils from '@libs/UserUtils'; import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils'; @@ -74,9 +72,6 @@ type MenuData = { iconRight?: IconAsset; badgeText?: string; badgeStyle?: ViewStyle; - shouldShowRightComponent?: boolean; - rightComponent?: ReactNode; - disabled?: boolean; }; type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; @@ -89,11 +84,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); - const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP); - const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); - const [session] = useOnyx(ONYXKEYS.SESSION); - const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => !!account?.delegatedAccess?.delegate}); @@ -250,34 +241,11 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr { translationKey: 'exitSurvey.goToExpensifyClassic', icon: Expensicons.ExpensifyLogoNew, - shouldShowRightComponent: hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE, - disabled: hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.RETRYING_AFTER_FAILURE, - rightComponent: ( - - - - ), ...(NativeModules.HybridAppModule ? { action: () => { - if (hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && !hybridApp?.oldDotSignInError) { - NativeModules.HybridAppModule.closeReactNativeApp(false, true); - setInitialURL(undefined); - return; - } - - HybridAppActions.retryOldDotSignInAfterFailure( - credentials?.autoGeneratedLogin ?? '', - credentials?.autoGeneratedPassword ?? '', - !credentials?.autoGeneratedLogin, - session?.authToken ?? '', - session?.encryptedAuthToken ?? '', - getCurrentUserEmail() ?? '', - activePolicyID ?? '', - ); + NativeModules.HybridAppModule.closeReactNativeApp(false, true); + setInitialURL(undefined); }, } : { @@ -316,27 +284,12 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr icon: Expensicons.Exit, action: () => { signOut(false); - HybridAppActions.resetStateAfterSignOut(); + HybridAppActions.resetSignInFlow(); }, }, ], }; - }, [ - styles.pt4, - styles.popoverMenuIcon, - hybridApp?.oldDotSignInState, - hybridApp?.oldDotSignInError, - theme.spinner, - credentials?.autoGeneratedLogin, - credentials?.autoGeneratedPassword, - session?.authToken, - session?.encryptedAuthToken, - activePolicyID, - setInitialURL, - shouldOpenBookACall, - signOut, - isActingAsDelegate - ]); + }, [styles.pt4, setInitialURL, shouldOpenBookACall, signOut, isActingAsDelegate]); /** * Retuns JSX.Element with menu items @@ -373,7 +326,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr title={keyTitle} icon={item.icon} iconType={item.iconType} - disabled={isExecuting || item.disabled} + disabled={isExecuting} onPress={singleExecution(() => { if (item.action) { item.action(); @@ -399,8 +352,6 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr !!item.routeName && !!(activeCentralPaneRoute.name.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')) } - shouldShowRightComponent={item.shouldShowRightComponent} - rightComponent={item.rightComponent} iconRight={item.iconRight} shouldShowRightIcon={item.shouldShowRightIcon} shouldIconUseAutoWidthStyle @@ -503,17 +454,6 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr onConfirm={() => signOut(true)} onCancel={() => toggleSignoutConfirmModal(false)} /> - { - HybridAppActions.setOldDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED); - }} - shouldShowCancelButton={false} - /> { + setNewDotSignInState(CONST.HYBRID_APP_SIGN_IN_STATE.STARTED); Session.signUpUser(); setReadyToShowAuthScreens(true); }} diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 9dd5082c08ca..64b4d83284a6 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -21,7 +21,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import AccountUtils from '@libs/AccountUtils'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import * as ErrorUtils from '@libs/ErrorUtils'; -import {shouldUseOldApp} from '@libs/HybridApp'; import * as ValidationUtils from '@libs/ValidationUtils'; import ChangeExpensifyLoginLink from '@pages/signin/ChangeExpensifyLoginLink'; import Terms from '@pages/signin/Terms'; @@ -52,10 +51,6 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS); const [session] = useOnyx(ONYXKEYS.SESSION); - const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); - const [hybridApp] = useOnyx(ONYXKEYS.HYBRID_APP, { - initialValue: {oldDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED, newDotSignInState: CONST.HYBRID_APP_SIGN_IN_STATE.NOT_STARTED}, - }); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -75,7 +70,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco const input2FARef = useRef(); const timerRef = useRef(); - const hasError = (!!account && !isEmptyObject(account?.errors) && !needToClearError) || !!hybridApp?.oldDotSignInError; + const hasError = !!account && !isEmptyObject(account?.errors) && !needToClearError; const isLoadingResendValidationForm = account?.loadingForm === CONST.FORMS.RESEND_VALIDATE_CODE_FORM; const shouldDisableResendValidateCode = isOffline ?? account?.isLoading; const isValidateCodeFormSubmitting = AccountUtils.isValidateCodeFormSubmitting(account); @@ -184,13 +179,9 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco * Clears local and Onyx sign in states */ const clearSignInData = useCallback(() => { - if (NativeModules.HybridAppModule && session?.authToken) { - HybridAppActions.resetSignInFlow(true); - } - clearLocalSignInData(); SessionActions.clearSignInData(); - }, [clearLocalSignInData, session?.authToken]); + }, [clearLocalSignInData]); useImperativeHandle(forwardedRef, () => ({ clearSignInData, @@ -247,7 +238,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco */ const validateAndSubmitForm = useCallback(() => { if (NativeModules.HybridAppModule && session?.authToken) { - HybridAppActions.resetSignInFlow(true); + HybridAppActions.resetSignInFlow(); } if (account?.isLoading) { @@ -381,7 +372,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco key="validateCode" testID="validateCode" /> - {hasError && } + {hasError && } {timeRemaining > 0 && !isOffline ? ( @@ -413,9 +404,7 @@ function BaseValidateCodeForm({autoComplete, isUsingRecoveryCode, setIsUsingReco large style={[styles.mv3]} text={translate('common.signIn')} - isLoading={ - shouldUseOldApp(tryNewDot) ? isValidateCodeFormSubmitting || hybridApp?.oldDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.STARTED : isValidateCodeFormSubmitting - } + isLoading={isValidateCodeFormSubmitting} onPress={validateAndSubmitForm} /> diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 7d083c07076b..a1b2b0c587b8 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -9,7 +9,7 @@ type HybridAppModule = { closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void; completeOnboarding: (status: boolean) => void; switchAccount: (newDotCurrentAccount: string) => void; - signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, isNewUser: boolean, authToken: string, encryptedAuthToken: string, email: string, policyID: string) => void; + signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, authToken: string, encryptedAuthToken: string, email: string, policyID: string) => void; signOutFromOldDot: () => void; exitApp: () => void; }; diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 688ac01d05ed..d7ab1b0cf61f 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -6,16 +6,13 @@ type HybridApp = { /** Stores the information if HybridApp uses NewDot's sign-in flow */ useNewDotSignInPage?: boolean; - /** Stores the information about error that occurred on OldDot side during sign-in */ - oldDotSignInError?: string | null; - /** Tells if we can show AuthScreens */ readyToShowAuthScreens?: boolean; /** States whether we transitioned from OldDot to show only certain group of screens */ isSingleNewDotEntry?: boolean; - /** stores infromation if last log out was performed from OldDot */ + /** Stores information if last log out was performed from OldDot */ loggedOutFromOldDot?: boolean; /** */ @@ -23,9 +20,6 @@ type HybridApp = { /** Describes current stage of NewDot sign-in */ newDotSignInState?: ValueOf; - - /** Describes current stage of OldDot sign-in */ - oldDotSignInState?: ValueOf; }; export default HybridApp; From 9450d21ef6794b33b2ffa843a6281ffad05ea696 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 9 Dec 2024 15:04:44 +0100 Subject: [PATCH 39/41] clear onyx but leave same data as when signing out --- src/libs/actions/Session/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 5c71e562c1cb..3323e5a5270a 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -506,7 +506,7 @@ function setupNewDotAfterTransitionFromOldDot(route: Route, hybridAppSettings: s return Promise.resolve(); } - return Onyx.clear(); + return redirectToSignIn(); }; const resetDidUserLoginDuringSessionIfNeeded = () => { From 62b861ccdb396f235e3f6c42e2381a5d30485bd2 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Tue, 10 Dec 2024 14:24:17 +0100 Subject: [PATCH 40/41] Improve data fetching on OldDot side --- src/libs/HybridApp.ts | 1 - src/types/modules/react-native.d.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 99843665f1d7..60e6debeec96 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -74,7 +74,6 @@ function handleChangeInHybridAppSignInFlow(hybridApp: OnyxEntry, tryN credentials.autoGeneratedLogin, credentials.autoGeneratedPassword, currentSession?.authToken ?? '', - currentSession?.encryptedAuthToken ?? '', getCurrentUserEmail() ?? '', activePolicyID ?? '', ); diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index a1b2b0c587b8..cfab342c5c26 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -9,7 +9,7 @@ type HybridAppModule = { closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void; completeOnboarding: (status: boolean) => void; switchAccount: (newDotCurrentAccount: string) => void; - signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, authToken: string, encryptedAuthToken: string, email: string, policyID: string) => void; + signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, authToken: string, email: string, policyID: string) => void; signOutFromOldDot: () => void; exitApp: () => void; }; From 892dec22757fa20e7b0b2ec6776480a468a0a926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Warcho=C5=82?= <61014013+war-in@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:50:49 +0100 Subject: [PATCH 41/41] Add named params in HybridAppModule methods (#153) * make HybridAppModule methods readable * improve another method --------- Co-authored-by: war-in --- src/components/ScreenWrapper.tsx | 2 +- src/libs/HybridApp.ts | 16 ++++++++-------- src/libs/TripReservationUtils.ts | 2 +- src/libs/actions/Delegate.ts | 4 ++-- src/libs/actions/Welcome/index.ts | 2 +- src/pages/ErrorPage/SessionExpiredPage.tsx | 2 +- src/pages/settings/InitialSettingsPage.tsx | 2 +- src/types/modules/react-native.d.ts | 8 ++++---- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 09315bfb8a8e..1e3df2a34817 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -177,7 +177,7 @@ function ScreenWrapper( }, [route?.params]); UNSTABLE_usePreventRemove(shouldReturnToOldDot, () => { - NativeModules.HybridAppModule?.closeReactNativeApp(false, false); + NativeModules.HybridAppModule?.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); }); const panResponder = useRef( diff --git a/src/libs/HybridApp.ts b/src/libs/HybridApp.ts index 60e6debeec96..85b137bf6d50 100644 --- a/src/libs/HybridApp.ts +++ b/src/libs/HybridApp.ts @@ -70,16 +70,16 @@ function handleChangeInHybridAppSignInFlow(hybridApp: OnyxEntry, tryN if (hybridApp?.newDotSignInState === CONST.HYBRID_APP_SIGN_IN_STATE.FINISHED && tryNewDot !== undefined && !!credentials?.autoGeneratedLogin && !!credentials?.autoGeneratedPassword) { Log.info(`[HybridApp] Performing sign-in${shouldUseOldApp(tryNewDot) ? '' : ' (in background)'} on OldDot side`); - NativeModules.HybridAppModule.signInToOldDot( - credentials.autoGeneratedLogin, - credentials.autoGeneratedPassword, - currentSession?.authToken ?? '', - getCurrentUserEmail() ?? '', - activePolicyID ?? '', - ); + NativeModules.HybridAppModule.signInToOldDot({ + autoGeneratedLogin: credentials.autoGeneratedLogin, + autoGeneratedPassword: credentials.autoGeneratedPassword, + authToken: currentSession?.authToken ?? '', + email: getCurrentUserEmail() ?? '', + policyID: activePolicyID ?? '', + }); HybridAppActions.setUseNewDotSignInPage(false).then(() => { if (shouldUseOldApp(tryNewDot)) { - NativeModules.HybridAppModule.closeReactNativeApp(false, false); + NativeModules.HybridAppModule.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.'); HybridAppActions.setReadyToShowAuthScreens(true); diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index 9c9947644e7f..275a0a965c24 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -108,7 +108,7 @@ function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessag } Log.info('[HybridApp] Returning to OldDot after opening TravelDot'); - NativeModules.HybridAppModule.closeReactNativeApp(false, false); + NativeModules.HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: false}); }) ?.catch(() => { setCtaErrorMessage(translate('travel.errorMessage')); diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index f99400d87d3e..3901c63ad8dd 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -140,7 +140,7 @@ function connect(email: string) { confirmReadyToOpenApp(); openApp(); - NativeModules.HybridAppModule.switchAccount(email); + NativeModules.HybridAppModule.switchAccount({newDotCurrentAccount: email}); }); }) .catch((error) => { @@ -210,7 +210,7 @@ function disconnect() { confirmReadyToOpenApp(); openApp(); - NativeModules.HybridAppModule.switchAccount(getCurrentUserEmail() ?? ''); + NativeModules.HybridAppModule.switchAccount({newDotCurrentAccount: getCurrentUserEmail() ?? ''}); }); }) .catch((error) => { diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index f91ab98f7b1d..6febdf5f3e60 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -145,7 +145,7 @@ function completeHybridAppOnboarding() { // 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); + NativeModules.HybridAppModule.completeOnboarding({status: true}); }); } diff --git a/src/pages/ErrorPage/SessionExpiredPage.tsx b/src/pages/ErrorPage/SessionExpiredPage.tsx index 5ccf70c40ab6..906c32dbb102 100644 --- a/src/pages/ErrorPage/SessionExpiredPage.tsx +++ b/src/pages/ErrorPage/SessionExpiredPage.tsx @@ -37,7 +37,7 @@ function SessionExpiredPage() { Navigation.goBack(); return; } - NativeModules.HybridAppModule.closeReactNativeApp(true, false); + NativeModules.HybridAppModule.closeReactNativeApp({shouldSignOut: true, shouldSetNVP: false}); }} > {translate('deeplinkWrapper.signIn')} diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 07071736cd10..0bc86426455a 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -244,7 +244,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr ...(NativeModules.HybridAppModule ? { action: () => { - NativeModules.HybridAppModule.closeReactNativeApp(false, true); + NativeModules.HybridAppModule.closeReactNativeApp({shouldSignOut: false, shouldSetNVP: true}); setInitialURL(undefined); }, } diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index cfab342c5c26..d3a17c73fced 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -6,10 +6,10 @@ import type {ShortcutManagerModule} from '@libs/ShortcutManager'; import type StartupTimer from '@libs/StartupTimer/types'; type HybridAppModule = { - closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void; - completeOnboarding: (status: boolean) => void; - switchAccount: (newDotCurrentAccount: string) => void; - signInToOldDot: (autoGeneratedLogin: string, autoGeneratedPassword: string, authToken: string, email: string, policyID: string) => void; + closeReactNativeApp: ({shouldSignOut: boolean, shouldSetNVP: boolean}) => void; + completeOnboarding: ({status: boolean}) => void; + switchAccount: ({newDotCurrentAccount: string}) => void; + signInToOldDot: ({autoGeneratedLogin: string, autoGeneratedPassword: string, authToken: string, email: string, policyID: string}) => void; signOutFromOldDot: () => void; exitApp: () => void; };