Skip to content

Commit

Permalink
Merge pull request #36750 from Expensify/andrew-fix-hybrid
Browse files Browse the repository at this point in the history
Add back some HybridApp logic after TS migration to fix authentication
  • Loading branch information
roryabraham authored Mar 19, 2024
2 parents 6ea693d + 9e5aa9d commit 888aaab
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 45 deletions.
8 changes: 4 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import CustomStatusBarAndBackground from './components/CustomStatusBarAndBackgro
import CustomStatusBarAndBackgroundContextProvider from './components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContextProvider';
import ErrorBoundary from './components/ErrorBoundary';
import HTMLEngineProvider from './components/HTMLEngineProvider';
import InitialURLContextProvider from './components/InitialURLContextProvider';
import {LocaleContextProvider} from './components/LocaleContextProvider';
import OnyxProvider from './components/OnyxProvider';
import PopoverContextProvider from './components/PopoverProvider';
Expand All @@ -30,12 +31,11 @@ import {WindowDimensionsProvider} from './components/withWindowDimensions';
import Expensify from './Expensify';
import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop';
import OnyxUpdateManager from './libs/actions/OnyxUpdateManager';
import InitialUrlContext from './libs/InitialUrlContext';
import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext';
import type {Route} from './ROUTES';

type AppProps = {
/** If we have an authToken this is true */
/** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */
url?: Route;
};

Expand All @@ -52,7 +52,7 @@ function App({url}: AppProps) {
useDefaultDragAndDrop();
OnyxUpdateManager();
return (
<InitialUrlContext.Provider value={url}>
<InitialURLContextProvider url={url}>
<GestureHandlerRootView style={fill}>
<ComposeProviders
components={[
Expand Down Expand Up @@ -88,7 +88,7 @@ function App({url}: AppProps) {
</ErrorBoundary>
</ComposeProviders>
</GestureHandlerRootView>
</InitialUrlContext.Provider>
</InitialURLContextProvider>
);
}

Expand Down
33 changes: 33 additions & 0 deletions src/components/InitialURLContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, {createContext, useEffect, useState} from 'react';
import type {ReactNode} from 'react';
import {Linking} from 'react-native';
import type {Route} from '@src/ROUTES';

/** Initial url that will be opened when NewDot is embedded into Hybrid App. */
const InitialURLContext = createContext<Route | undefined>(undefined);

type InitialURLContextProviderProps = {
/** URL passed to our top-level React Native component by HybridApp. Will always be undefined in "pure" NewDot builds. */
url?: Route;

/** Children passed to the context provider */
children: ReactNode;
};

function InitialURLContextProvider({children, url}: InitialURLContextProviderProps) {
const [initialURL, setInitialURL] = useState(url);
useEffect(() => {
if (initialURL) {
return;
}
Linking.getInitialURL().then((initURL) => {
setInitialURL(initURL as Route);
});
}, [initialURL]);
return <InitialURLContext.Provider value={initialURL}>{children}</InitialURLContext.Provider>;
}

InitialURLContextProvider.displayName = 'InitialURLContextProvider';

export default InitialURLContextProvider;
export {InitialURLContext};
7 changes: 0 additions & 7 deletions src/libs/InitialUrlContext/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/libs/Navigation/AppNavigator/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, {useContext, useEffect} from 'react';
import {NativeModules} from 'react-native';
import InitialUrlContext from '@libs/InitialUrlContext';
import {InitialURLContext} from '@components/InitialURLContextProvider';
import Navigation from '@libs/Navigation/Navigation';

type AppNavigatorProps = {
Expand All @@ -9,7 +9,7 @@ type AppNavigatorProps = {
};

function AppNavigator({authenticated}: AppNavigatorProps) {
const initUrl = useContext(InitialUrlContext);
const initUrl = useContext(InitialURLContext);

useEffect(() => {
if (!NativeModules.HybridAppModule || !initUrl) {
Expand Down
86 changes: 54 additions & 32 deletions src/pages/LogOutPreviousUserPage.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useEffect} from 'react';
import {Linking} from 'react-native';
import React, {useContext, useEffect} from 'react';
import {NativeModules} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import Navigation from '@libs/Navigation/Navigation';
import {InitialURLContext} from '@components/InitialURLContextProvider';
import * as SessionUtils from '@libs/SessionUtils';
import Navigation from '@navigation/Navigation';
import type {AuthScreensParamList} from '@navigation/types';
import * as SessionActions from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Route} from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Session} from '@src/types/onyx';

type LogOutPreviousUserPageOnyxProps = {
/** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */
session: OnyxEntry<Session>;

/** Is the account loading? */
isAccountLoading: boolean;
};

type LogOutPreviousUserPageProps = LogOutPreviousUserPageOnyxProps & StackScreenProps<AuthScreensParamList, typeof SCREENS.TRANSITION_BETWEEN_APPS>;
Expand All @@ -25,49 +30,66 @@ type LogOutPreviousUserPageProps = LogOutPreviousUserPageOnyxProps & StackScreen
// out if the transition is for another user.
//
// This component should not do any other navigation as that handled in App.setUpPoliciesAndNavigate
function LogOutPreviousUserPage({session, route}: LogOutPreviousUserPageProps) {
function LogOutPreviousUserPage({session, route, isAccountLoading}: LogOutPreviousUserPageProps) {
const initialURL = useContext(InitialURLContext);
useEffect(() => {
Linking.getInitialURL().then((transitionURL) => {
const sessionEmail = session?.email;
const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL ?? undefined, sessionEmail);
const isSupportalLogin = route.params.authTokenType === CONST.AUTH_TOKEN_TYPES.SUPPORT;
const sessionEmail = session?.email;
const transitionURL = NativeModules.HybridAppModule ? `${CONST.DEEPLINK_BASE_URL}${initialURL ?? ''}` : initialURL;
const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL ?? undefined, sessionEmail);
const isSupportalLogin = route.params.authTokenType === CONST.AUTH_TOKEN_TYPES.SUPPORT;

if (isLoggingInAsNewUser) {
SessionActions.signOutAndRedirectToSignIn(false, isSupportalLogin);
}
if (isLoggingInAsNewUser) {
SessionActions.signOutAndRedirectToSignIn(false, isSupportalLogin);
return;
}

if (isSupportalLogin) {
SessionActions.signInWithSupportAuthToken(route.params.shortLivedAuthToken ?? '');
Navigation.isNavigationReady().then(() => {
// We must call goBack() to remove the /transition route from history
Navigation.goBack();
Navigation.navigate(ROUTES.HOME);
});
return;
}
if (isSupportalLogin) {
SessionActions.signInWithSupportAuthToken(route.params.shortLivedAuthToken ?? '');
Navigation.isNavigationReady().then(() => {
// We must call goBack() to remove the /transition route from history
Navigation.goBack();
Navigation.navigate(ROUTES.HOME);
});
return;
}

// We need to signin and fetch a new authToken, if a user was already authenticated in NewDot, and was redirected to OldDot
// and their authToken stored in Onyx becomes invalid.
// This workflow is triggered while setting up VBBA. User is redirected from NewDot to OldDot to set up 2FA, and then redirected back to NewDot
// On Enabling 2FA, authToken stored in Onyx becomes expired and hence we need to fetch new authToken
const shouldForceLogin = route.params.shouldForceLogin === 'true';
if (shouldForceLogin) {
const email = route.params.email ?? '';
const shortLivedAuthToken = route.params.shortLivedAuthToken ?? '';
SessionActions.signInWithShortLivedAuthToken(email, shortLivedAuthToken);
}
});
// We need to signin and fetch a new authToken, if a user was already authenticated in NewDot, and was redirected to OldDot
// and their authToken stored in Onyx becomes invalid.
// This workflow is triggered while setting up VBBA. User is redirected from NewDot to OldDot to set up 2FA, and then redirected back to NewDot
// On Enabling 2FA, authToken stored in Onyx becomes expired and hence we need to fetch new authToken
const shouldForceLogin = route.params.shouldForceLogin === 'true';
if (shouldForceLogin) {
const email = route.params.email ?? '';
const shortLivedAuthToken = route.params.shortLivedAuthToken ?? '';
SessionActions.signInWithShortLivedAuthToken(email, shortLivedAuthToken);
}
const exitTo = route.params.exitTo as Route | null;
// We don't want to navigate to the exitTo route when creating a new workspace from a deep link,
// because we already handle creating the optimistic policy and navigating to it in App.setUpPoliciesAndNavigate,
// which is already called when AuthScreens mounts.
if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !isAccountLoading && !isLoggingInAsNewUser) {
Navigation.isNavigationReady().then(() => {
// remove this screen and navigate to exit route
const exitUrl = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo;
Navigation.goBack();
Navigation.navigate(exitUrl);
});
}

// We only want to run this effect once on mount (when the page first loads after transitioning from OldDot)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [initialURL, isAccountLoading]);

return <FullScreenLoadingIndicator />;
}

LogOutPreviousUserPage.displayName = 'LogOutPreviousUserPage';

export default withOnyx<LogOutPreviousUserPageProps, LogOutPreviousUserPageOnyxProps>({
isAccountLoading: {
key: ONYXKEYS.ACCOUNT,
selector: (account) => account?.isLoading ?? false,
},
session: {
key: ONYXKEYS.SESSION,
},
Expand Down

0 comments on commit 888aaab

Please sign in to comment.