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] [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; };