From a2266c8fd5e66f456071fc1e7158be7794769b1f Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 30 Oct 2024 15:19:34 +0100 Subject: [PATCH 1/4] Add hybridAppSettings AppProp --- src/App.tsx | 10 +++++-- src/Expensify.tsx | 2 +- src/ONYXKEYS.ts | 4 --- src/components/InitialURLContextProvider.tsx | 12 ++++---- src/libs/actions/HybridApp/index.ts | 3 ++ src/libs/actions/HybridApp/types.ts | 12 ++++++++ src/libs/actions/Session/index.ts | 29 ++++++-------------- src/types/onyx/HybridApp.ts | 3 ++ 8 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 src/libs/actions/HybridApp/index.ts create mode 100644 src/libs/actions/HybridApp/types.ts diff --git a/src/App.tsx b/src/App.tsx index 177cc00c7dee..64d41f8b70d7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,14 +35,15 @@ import CONFIG from './CONFIG'; import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import {ReportIDsContextProvider} from './hooks/useReportIDs'; +import type HybridAppSettings from './libs/actions/HybridApp'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { - /** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */ url?: Route; + hybridAppSettings?: HybridAppSettings; }; LogBox.ignoreLogs([ @@ -56,14 +57,17 @@ 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(); return ( - + data?.useNewDotSignInPage}); useEffect(() => { if (!account?.needsTwoFactorAuthSetup || account.requiresTwoFactorAuth) { diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 02d6c15fb856..68e9c318eeef 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -448,9 +448,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 */ @@ -1017,7 +1014,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/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index f026f2de53f9..92858cdaebba 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -1,6 +1,7 @@ import React, {createContext, useEffect, useMemo, useState} from 'react'; import type {ReactNode} from 'react'; import {Linking} from 'react-native'; +import type HybridAppSettings from '@libs/actions/HybridApp'; import {signInAfterTransitionFromOldDot} from '@libs/actions/Session'; import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; @@ -18,20 +19,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?: HybridAppSettings; + /** Children passed to the context provider */ children: ReactNode; }; -function InitialURLContextProvider({children, url}: InitialURLContextProviderProps) { +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 +42,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/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts new file mode 100644 index 000000000000..9995ac4ef5d4 --- /dev/null +++ b/src/libs/actions/HybridApp/index.ts @@ -0,0 +1,3 @@ +import type HybridAppSettings from './types'; + +export default HybridAppSettings; 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 96bd7589d288..bb2332c8c99b 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -5,6 +5,7 @@ import {InteractionManager, Linking, NativeModules} from 'react-native'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import type HybridAppSettings from '@libs/actions/HybridApp'; import * as PersistedRequests from '@libs/actions/PersistedRequests'; import * as API from '@libs/API'; import type { @@ -479,21 +480,12 @@ function signUpUser() { API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); } -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; +function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: HybridAppSettings) { + const {initialOnyxValues} = hybridAppSettings; + const {hybridApp} = initialOnyxValues; const clearOnyxBeforeSignIn = () => { - if (useNewDotSignInPage !== 'true') { + if (!hybridApp.useNewDotSignInPage) { return Promise.resolve(); } @@ -501,7 +493,7 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { }; const initAppAfterTransition = () => { - if (useNewDotSignInPage === 'true') { + if (hybridApp.useNewDotSignInPage) { return Promise.resolve(); } @@ -510,18 +502,13 @@ function signInAfterTransitionFromOldDot(transitionURL: string) { 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(() => Onyx.multiSet(initialOnyxValues)) .then(initAppAfterTransition) .catch((error) => { Log.hmmm('[HybridApp] Initialization of HybridApp has failed. Forcing transition', {error}); }) .finally(() => { - resolve(`${route}?singleNewDotEntry=${isSingleNewDotEntry}` as Route); + resolve(route); }); }); diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index e7d4c08f82db..9aeacbb17fda 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -2,6 +2,9 @@ type HybridApp = { /** */ isSigningIn?: boolean; + + /** Stores the information if HybridApp uses NewDot's sign in flow */ + useNewDotSignInPage: boolean; }; export default HybridApp; From dc6651ed9778efa25165da309f163c98918dad2c Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 6 Nov 2024 14:23:54 +0100 Subject: [PATCH 2/4] Unify how we pass data on both platforms --- src/App.tsx | 3 +-- src/components/InitialURLContextProvider.tsx | 3 +-- src/libs/actions/HybridApp/index.ts | 6 +++++- src/libs/actions/Session/index.ts | 7 ++++--- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 64d41f8b70d7..47c09c84f8de 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,7 +35,6 @@ import CONFIG from './CONFIG'; import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import {ReportIDsContextProvider} from './hooks/useReportIDs'; -import type HybridAppSettings from './libs/actions/HybridApp'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; @@ -43,7 +42,7 @@ import {SplashScreenStateContextProvider} from './SplashScreenStateContext'; type AppProps = { url?: Route; - hybridAppSettings?: HybridAppSettings; + hybridAppSettings?: string; }; LogBox.ignoreLogs([ diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index 92858cdaebba..cf5094e0fcc8 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -1,7 +1,6 @@ import React, {createContext, useEffect, useMemo, useState} from 'react'; import type {ReactNode} from 'react'; import {Linking} from 'react-native'; -import type HybridAppSettings from '@libs/actions/HybridApp'; import {signInAfterTransitionFromOldDot} from '@libs/actions/Session'; import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; @@ -21,7 +20,7 @@ const InitialURLContext = createContext({ type InitialURLContextProviderProps = { url?: Route; - hybridAppSettings?: HybridAppSettings; + hybridAppSettings?: string; /** Children passed to the context provider */ children: ReactNode; diff --git a/src/libs/actions/HybridApp/index.ts b/src/libs/actions/HybridApp/index.ts index 9995ac4ef5d4..bb0b61ec9845 100644 --- a/src/libs/actions/HybridApp/index.ts +++ b/src/libs/actions/HybridApp/index.ts @@ -1,3 +1,7 @@ import type HybridAppSettings from './types'; -export default HybridAppSettings; +function parseHybridAppSettings(hybridAppSettings: string): HybridAppSettings { + return JSON.parse(hybridAppSettings) as HybridAppSettings; +} + +export default parseHybridAppSettings; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index bb2332c8c99b..27c530ccb397 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -5,7 +5,7 @@ import {InteractionManager, Linking, NativeModules} from 'react-native'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import type HybridAppSettings from '@libs/actions/HybridApp'; +import parseHybridAppSettings from '@libs/actions/HybridApp'; import * as PersistedRequests from '@libs/actions/PersistedRequests'; import * as API from '@libs/API'; import type { @@ -480,8 +480,9 @@ function signUpUser() { API.write(WRITE_COMMANDS.SIGN_UP_USER, params, {optimisticData, successData, failureData}); } -function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: HybridAppSettings) { - const {initialOnyxValues} = hybridAppSettings; +function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string) { + const parsedHybridAppSettings = parseHybridAppSettings(hybridAppSettings); + const {initialOnyxValues} = parsedHybridAppSettings; const {hybridApp} = initialOnyxValues; const clearOnyxBeforeSignIn = () => { From 2acb28fc234f2bcb46f750a81fb05c790b261dc3 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 12 Nov 2024 18:45:03 +0100 Subject: [PATCH 3/4] fix after merge --- src/libs/actions/Session/index.ts | 2 +- src/types/onyx/HybridApp.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index fd5a10debd59..6ada25e09b90 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -509,7 +509,7 @@ function signInAfterTransitionFromOldDot(route: Route, hybridAppSettings: string .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); diff --git a/src/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index efd3e25ec4f2..3e488f673398 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -23,6 +23,9 @@ type HybridApp = { /** stores infromation if last log out was performed from OldDot */ loggedOutFromOldDot?: boolean; + + /** */ + shouldRemoveDelegatedAccess?: boolean; }; export default HybridApp; From 3d4254be99e72c3b11051fdffcb2e961ed08b355 Mon Sep 17 00:00:00 2001 From: war-in Date: Wed, 13 Nov 2024 11:03:17 +0100 Subject: [PATCH 4/4] fix comments --- src/App.tsx | 3 +++ src/Expensify.tsx | 4 ++-- src/types/onyx/HybridApp.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c1d10199dd34..74fc3c3637e5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,6 +41,9 @@ 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?: Route; hybridAppSettings?: string; diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 24647a6c5d6a..c37a01b4e40b 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -117,12 +117,12 @@ function Expensify() { const isAuthenticated = useMemo(() => !!(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/types/onyx/HybridApp.ts b/src/types/onyx/HybridApp.ts index 3e488f673398..4e39ba1326be 100644 --- a/src/types/onyx/HybridApp.ts +++ b/src/types/onyx/HybridApp.ts @@ -18,7 +18,7 @@ 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 */