Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate AuthScreens to useOnyx #55966

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 43 additions & 46 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {findFocusedRoute, useNavigation} from '@react-navigation/native';
import React, {memo, useEffect, useMemo, useRef, useState} from 'react';
import {NativeModules, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Onyx, {withOnyx} from 'react-native-onyx';
import Onyx, {useOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import ActiveGuidesEventListener from '@components/ActiveGuidesEventListener';
import ComposeProviders from '@components/ComposeProviders';
Expand Down Expand Up @@ -60,6 +60,7 @@ import SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
import beforeRemoveReportOpenedFromSearchRHP from './beforeRemoveReportOpenedFromSearchRHP';
import CENTRAL_PANE_SCREENS from './CENTRAL_PANE_SCREENS';
Expand All @@ -77,17 +78,6 @@ import RightModalNavigator from './Navigators/RightModalNavigator';
import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator';
import useRootNavigatorOptions from './useRootNavigatorOptions';

type AuthScreensProps = {
/** Session of currently logged in user */
session: OnyxEntry<OnyxTypes.Session>;

/** The report ID of the last opened public room as anonymous user */
lastOpenedPublicRoomID: OnyxEntry<string>;

/** The last Onyx update ID was applied to the client */
initialLastUpdateIDAppliedToClient: OnyxEntry<number>;
};

const loadReportAttachments = () => require<ReactComponentModule>('../../../pages/home/report/ReportAttachments').default;
const loadValidateLoginPage = () => require<ReactComponentModule>('../../../pages/ValidateLoginPage').default;
const loadLogOutPreviousUserPage = () => require<ReactComponentModule>('../../../pages/LogOutPreviousUserPage').default;
Expand Down Expand Up @@ -239,7 +229,10 @@ const modalScreenListenersWithCancelSearch = {
},
};

function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDAppliedToClient}: AuthScreensProps) {
function AuthScreens() {
const [session] = useOnyx(ONYXKEYS.SESSION);
const [lastOpenedPublicRoomID, lastOpenedPublicRoomIDStatus] = useOnyx(ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID);
const [initialLastUpdateIDAppliedToClient, initialLastUpdateIDAppliedToClientStatus] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT);
const theme = useTheme();
const styles = useThemeStyles();
const {shouldUseNarrowLayout} = useResponsiveLayout();
Expand All @@ -251,6 +244,10 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie

const modal = useRef<OnyxTypes.Modal>({});
const {isOnboardingCompleted} = useOnboardingFlowRouter();
const isLastOpenedPublicRoomIDLoading = isLoadingOnyxValue(lastOpenedPublicRoomIDStatus);
const isInitialLastUpdateIDAppliedToClientLoading = isLoadingOnyxValue(initialLastUpdateIDAppliedToClientStatus);
const isLastOpenedPublicRoomIDLoadedRef = useRef(false);
const isInitialLastUpdateIDAppliedToClientLoadedRef = useRef(false);
const navigation = useNavigation();

useEffect(() => {
Expand Down Expand Up @@ -284,6 +281,37 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
};
}, [theme]);

useEffect(() => {
if (isLastOpenedPublicRoomIDLoading || isLastOpenedPublicRoomIDLoadedRef.current) {
return;
}

isLastOpenedPublicRoomIDLoadedRef.current = true;
if (lastOpenedPublicRoomID) {
// Re-open the last opened public room if the user logged in from a public room link
Report.openLastOpenedPublicRoom(lastOpenedPublicRoomID);
}
}, [isLastOpenedPublicRoomIDLoading, lastOpenedPublicRoomID]);

useEffect(() => {
if (isInitialLastUpdateIDAppliedToClientLoading || isInitialLastUpdateIDAppliedToClientLoadedRef.current) {
return;
}

isInitialLastUpdateIDAppliedToClientLoadedRef.current = 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);
}
}
}, [initialLastUpdateIDAppliedToClient, isInitialLastUpdateIDAppliedToClientLoading]);

useEffect(() => {
const shortcutsOverviewShortcutConfig = CONST.KEYBOARD_SHORTCUTS.SHORTCUTS;
const searchShortcutConfig = CONST.KEYBOARD_SHORTCUTS.SEARCH;
Expand All @@ -304,28 +332,12 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
PusherConnectionManager.init();
initializePusher();

// 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);
}
}

PriorityMode.autoSwitchToFocusMode();

App.setUpPoliciesAndNavigate(session);

App.redirectThirdPartyDesktopSignIn();

if (lastOpenedPublicRoomID) {
// Re-open the last opened public room if the user logged in from a public room link
Report.openLastOpenedPublicRoom(lastOpenedPublicRoomID);
}
Download.clearDownloads();

const unsubscribeOnyxModal = onyxSubscribe({
Expand Down Expand Up @@ -651,20 +663,5 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie

AuthScreens.displayName = 'AuthScreens';

const AuthScreensMemoized = memo(AuthScreens, () => true);

// Migration to useOnyx cause re-login if logout from deeplinked report in desktop app
// Further analysis required and more details can be seen here:
// https://github.com/Expensify/App/issues/50560
// eslint-disable-next-line
export default withOnyx<AuthScreensProps, AuthScreensProps>({
session: {
key: ONYXKEYS.SESSION,
},
lastOpenedPublicRoomID: {
key: ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID,
},
initialLastUpdateIDAppliedToClient: {
key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
},
})(AuthScreensMemoized);
// This memo is needed because <AuthScreens> is one of the main components in the app and navigation root and is relevant for the app performance.
export default memo(AuthScreens);
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {isCentralPaneName, isOnboardingFlowName} from '@libs/NavigationUtils';
import * as Welcome from '@userActions/Welcome';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import syncBrowserHistory from './syncBrowserHistory';

Expand Down Expand Up @@ -40,13 +41,8 @@ function insertRootRoute(state: State<RootStackParamList>, routeToInsert: Naviga
}

function compareAndAdaptState(state: StackNavigationState<RootStackParamList>) {
// If the state of the last path is not defined the getPathFromState won't work correctly.
if (!state?.routes.at(-1)?.state) {
return;
}

// We need to be sure that the bottom tab state is defined.
const topmostBottomTabRoute = getTopmostBottomTabRoute(state);
const topmostBottomTabRoute = getTopmostBottomTabRoute(state) ?? {name: SCREENS.HOME};
const isNarrowLayout = getIsNarrowLayout();

// This solutions is heuristics and will work for our cases. We may need to improve it in the future if we will have more cases to handle.
Expand All @@ -61,7 +57,9 @@ function compareAndAdaptState(state: StackNavigationState<RootStackParamList>) {
// We will generate a template state and compare the current state with it.
// If there is a difference in the screens that should be visible under the overlay, we will add the screen from templateState to the current state.
const pathFromCurrentState = getPathFromState(state, linkingConfig.config);
const {adaptedState: templateState} = getAdaptedStateFromPath(pathFromCurrentState, linkingConfig.config);
// If the state of the bottom tab has not been fully initialized, the generated path must be adjusted
const adjustedPath = pathFromCurrentState === `/${ROUTES.ROOT}` ? `/${ROUTES.REPORT}` : pathFromCurrentState;
const {adaptedState: templateState} = getAdaptedStateFromPath(adjustedPath, linkingConfig.config);

if (!templateState) {
return;
Expand Down
14 changes: 13 additions & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,19 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.REPORT_AVATAR]: ROUTES.REPORT_AVATAR.route,
[SCREENS.TRANSACTION_RECEIPT]: ROUTES.TRANSACTION_RECEIPT.route,
[SCREENS.WORKSPACE_JOIN_USER]: ROUTES.WORKSPACE_JOIN_USER.route,
[SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route,
[SCREENS.REPORT]: {
path: ROUTES.REPORT_WITH_ID.route,
// If params are defined, but reportID is explicitly undefined, we will get the url /r/undefined.
// We want to avoid that situation, so we will return an empty string instead.
parse: {
// eslint-disable-next-line
reportID: (reportID: string | undefined) => reportID ?? '',
},
stringify: {
// eslint-disable-next-line
reportID: (reportID: string | undefined) => reportID ?? '',
},
},
[SCREENS.SETTINGS.PROFILE.ROOT]: {
path: ROUTES.SETTINGS_PROFILE,
exact: true,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ function setUpPoliciesAndNavigate(session: OnyxEntry<OnyxTypes.Session>) {
if (!isLoggingInAsNewUser && exitTo) {
Navigation.waitForProtectedRoutes()
.then(() => {
Navigation.navigate(exitTo);
Navigation.navigate(exitTo, CONST.NAVIGATION.TYPE.UP);
})
.then(endSignOnTransition);
} else {
Expand Down
17 changes: 4 additions & 13 deletions src/libs/actions/Session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {HybridAppRoute, Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import type Credentials from '@src/types/onyx/Credentials';
import type Session from '@src/types/onyx/Session';
import type {AutoAuthState} from '@src/types/onyx/Session';
Expand Down Expand Up @@ -826,19 +825,11 @@ function clearSignInData() {
}

/**
* Reset all current params of the Home route
* Reset navigation state after logout
*/
function resetHomeRouteParams() {
function resetNavigationState() {
Navigation.isNavigationReady().then(() => {
const routes = navigationRef.current?.getState()?.routes;
const homeRoute = routes?.find((route) => route.name === SCREENS.HOME);

const emptyParams: Record<string, undefined> = {};
Object.keys(homeRoute?.params ?? {}).forEach((paramKey) => {
emptyParams[paramKey] = undefined;
});

Navigation.setParams(emptyParams, homeRoute?.key ?? '');
navigationRef.resetRoot(navigationRef.getRootState());
Onyx.set(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, false);
});
}
Expand All @@ -858,7 +849,7 @@ function cleanupSession() {
PersistedRequests.clear();
NetworkConnection.clearReconnectionCallbacks();
SessionUtils.resetDidUserLogInDuringSession();
resetHomeRouteParams();
resetNavigationState();
clearCache().then(() => {
Log.info('Cleared all cache data', true, {}, true);
});
Expand Down
Loading