Skip to content

Commit 0bcd005

Browse files
authored
Merge pull request #42582 from software-mansion-labs/nav/flatten-central-pane
Simplify the RootNavigator structure
2 parents 65709a2 + becff51 commit 0bcd005

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+314
-318
lines changed

src/ROUTES.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {ValueOf} from 'type-fest';
22
import type CONST from './CONST';
33
import type {IOUAction, IOUType} from './CONST';
44
import type {IOURequestType} from './libs/actions/IOU';
5-
import type {CentralPaneNavigatorParamList} from './libs/Navigation/types';
5+
import type {AuthScreensParamList} from './libs/Navigation/types';
66
import type {SearchQuery} from './types/onyx/SearchResults';
77
import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual';
88

@@ -37,7 +37,7 @@ const ROUTES = {
3737

3838
SEARCH: {
3939
route: '/search/:query',
40-
getRoute: (searchQuery: SearchQuery, queryParams?: CentralPaneNavigatorParamList['Search_Central_Pane']) => {
40+
getRoute: (searchQuery: SearchQuery, queryParams?: AuthScreensParamList['Search_Central_Pane']) => {
4141
const {sortBy, sortOrder} = queryParams ?? {};
4242

4343
if (!sortBy && !sortOrder) {

src/components/ScreenWrapper.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import useTackInputFocus from '@hooks/useTackInputFocus';
1515
import useThemeStyles from '@hooks/useThemeStyles';
1616
import useWindowDimensions from '@hooks/useWindowDimensions';
1717
import * as Browser from '@libs/Browser';
18-
import type {CentralPaneNavigatorParamList, RootStackParamList} from '@libs/Navigation/types';
18+
import type {AuthScreensParamList, RootStackParamList} from '@libs/Navigation/types';
1919
import toggleTestToolsModal from '@userActions/TestTool';
2020
import CONST from '@src/CONST';
2121
import CustomDevMenu from './CustomDevMenu';
@@ -95,7 +95,7 @@ type ScreenWrapperProps = {
9595
*
9696
* This is required because transitionEnd event doesn't trigger in the testing environment.
9797
*/
98-
navigation?: StackNavigationProp<RootStackParamList> | StackNavigationProp<CentralPaneNavigatorParamList>;
98+
navigation?: StackNavigationProp<RootStackParamList> | StackNavigationProp<AuthScreensParamList>;
9999

100100
/** Whether to show offline indicator on wide screens */
101101
shouldShowOfflineIndicatorInWideScreen?: boolean;

src/components/Search.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as ReportUtils from '@libs/ReportUtils';
1313
import * as SearchUtils from '@libs/SearchUtils';
1414
import type {SearchColumnType, SortOrder} from '@libs/SearchUtils';
1515
import Navigation from '@navigation/Navigation';
16-
import type {CentralPaneNavigatorParamList} from '@navigation/types';
16+
import type {AuthScreensParamList} from '@navigation/types';
1717
import EmptySearchView from '@pages/Search/EmptySearchView';
1818
import variables from '@styles/variables';
1919
import CONST from '@src/CONST';
@@ -50,7 +50,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) {
5050
const {isOffline} = useNetwork();
5151
const styles = useThemeStyles();
5252
const {isLargeScreenWidth} = useWindowDimensions();
53-
const navigation = useNavigation<StackNavigationProp<CentralPaneNavigatorParamList>>();
53+
const navigation = useNavigation<StackNavigationProp<AuthScreensParamList>>();
5454
const lastSearchResultsRef = useRef<OnyxEntry<SearchResults>>();
5555

5656
const getItemHeight = useCallback(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
2+
import React from 'react';
3+
import getComponentDisplayName from '@libs/getComponentDisplayName';
4+
import FreezeWrapper from '@libs/Navigation/FreezeWrapper';
5+
6+
/**
7+
* This HOC is dependent on the platform. On native platforms, screens that aren't already displayed in the navigation stack should be frozen to prevent unnecessary rendering.
8+
* It's handled this way only on mobile platforms because on the web, more than one screen is displayed in a wide layout, so these screens shouldn't be frozen.
9+
*/
10+
export default function withPrepareCentralPaneScreen<TProps, TRef>(
11+
WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>,
12+
): (props: TProps & React.RefAttributes<TRef>) => React.ReactElement | null {
13+
function WithPrepareCentralPaneScreen(props: TProps, ref: ForwardedRef<TRef>) {
14+
return (
15+
<FreezeWrapper>
16+
<WrappedComponent
17+
// eslint-disable-next-line react/jsx-props-no-spreading
18+
{...props}
19+
ref={ref}
20+
/>
21+
</FreezeWrapper>
22+
);
23+
}
24+
25+
WithPrepareCentralPaneScreen.displayName = `WithPrepareCentralPaneScreen(${getComponentDisplayName(WrappedComponent)})`;
26+
return React.forwardRef(WithPrepareCentralPaneScreen);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type {ComponentType} from 'react';
2+
3+
/**
4+
* This HOC is dependent on the platform. On native platforms, screens that aren't already displayed in the navigation stack should be frozen to prevent unnecessary rendering.
5+
* It's handled this way only on mobile platforms because on the web, more than one screen is displayed in a wide layout, so these screens shouldn't be frozen.
6+
*/
7+
export default function withPrepareCentralPaneScreen(WrappedComponent: ComponentType) {
8+
return WrappedComponent;
9+
}

src/hooks/useActiveRoute.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {useContext} from 'react';
22
import ActiveRouteContext from '@libs/Navigation/AppNavigator/Navigators/ActiveRouteContext';
3-
import type {CentralPaneNavigatorParamList, NavigationPartialRoute} from '@libs/Navigation/types';
3+
import type {AuthScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types';
44

5-
function useActiveRoute(): NavigationPartialRoute<keyof CentralPaneNavigatorParamList> | undefined {
5+
function useActiveRoute(): NavigationPartialRoute<keyof AuthScreensParamList> | undefined {
66
return useContext(ActiveRouteContext);
77
}
88

src/libs/Navigation/AppNavigator/AuthScreens.tsx

+42-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {memo, useEffect, useMemo, useRef} from 'react';
22
import {View} from 'react-native';
33
import type {OnyxEntry} from 'react-native-onyx';
44
import Onyx, {withOnyx} from 'react-native-onyx';
5+
import type {ValueOf} from 'type-fest';
56
import OptionsListContextProvider from '@components/OptionListContextProvider';
67
import useOnboardingLayout from '@hooks/useOnboardingLayout';
78
import useStyleUtils from '@hooks/useStyleUtils';
@@ -14,7 +15,7 @@ import Log from '@libs/Log';
1415
import getCurrentUrl from '@libs/Navigation/currentUrl';
1516
import getOnboardingModalScreenOptions from '@libs/Navigation/getOnboardingModalScreenOptions';
1617
import Navigation from '@libs/Navigation/Navigation';
17-
import type {AuthScreensParamList} from '@libs/Navigation/types';
18+
import type {AuthScreensParamList, CentralPaneName, CentralPaneScreensParamList} from '@libs/Navigation/types';
1819
import NetworkConnection from '@libs/NetworkConnection';
1920
import * as Pusher from '@libs/Pusher/pusher';
2021
import PusherConnectionManager from '@libs/PusherConnectionManager';
@@ -42,11 +43,11 @@ import SCREENS from '@src/SCREENS';
4243
import type * as OnyxTypes from '@src/types/onyx';
4344
import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails';
4445
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
46+
import CENTRAL_PANE_SCREENS from './CENTRAL_PANE_SCREENS';
4547
import createCustomStackNavigator from './createCustomStackNavigator';
4648
import defaultScreenOptions from './defaultScreenOptions';
4749
import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions';
4850
import BottomTabNavigator from './Navigators/BottomTabNavigator';
49-
import CentralPaneNavigator from './Navigators/CentralPaneNavigator';
5051
import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator';
5152
import FullScreenNavigator from './Navigators/FullScreenNavigator';
5253
import LeftModalNavigator from './Navigators/LeftModalNavigator';
@@ -75,6 +76,21 @@ const loadReportAvatar = () => require<ReactComponentModule>('../../../pages/Rep
7576
const loadReceiptView = () => require<ReactComponentModule>('../../../pages/TransactionReceiptPage').default;
7677
const loadWorkspaceJoinUser = () => require<ReactComponentModule>('@pages/workspace/WorkspaceJoinUserPage').default;
7778

79+
function getCentralPaneScreenInitialParams(screenName: CentralPaneName): Partial<ValueOf<CentralPaneScreensParamList>> {
80+
const url = getCurrentUrl();
81+
const openOnAdminRoom = url ? new URL(url).searchParams.get('openOnAdminRoom') : undefined;
82+
83+
if (screenName === SCREENS.SEARCH.CENTRAL_PANE) {
84+
return {sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, sortOrder: CONST.SEARCH.SORT_ORDER.DESC};
85+
}
86+
87+
if (screenName === SCREENS.REPORT && openOnAdminRoom === 'true') {
88+
return {openOnAdminRoom: true};
89+
}
90+
91+
return undefined;
92+
}
93+
7894
let timezone: Timezone | null;
7995
let currentAccountID = -1;
8096
let isLoadingApp = false;
@@ -298,20 +314,26 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
298314
// eslint-disable-next-line react-hooks/exhaustive-deps
299315
}, []);
300316

317+
const CentralPaneScreenOptions = {
318+
headerShown: false,
319+
title: 'New Expensify',
320+
321+
// Prevent unnecessary scrolling
322+
cardStyle: styles.cardStyleNavigator,
323+
};
324+
301325
return (
302326
<OptionsListContextProvider>
303327
<View style={styles.rootNavigatorContainerStyles(isSmallScreenWidth)}>
304-
<RootStack.Navigator isSmallScreenWidth={isSmallScreenWidth}>
328+
<RootStack.Navigator
329+
screenOptions={screenOptions.centralPaneNavigator}
330+
isSmallScreenWidth={isSmallScreenWidth}
331+
>
305332
<RootStack.Screen
306333
name={NAVIGATORS.BOTTOM_TAB_NAVIGATOR}
307334
options={screenOptions.bottomTab}
308335
component={BottomTabNavigator}
309336
/>
310-
<RootStack.Screen
311-
name={NAVIGATORS.CENTRAL_PANE_NAVIGATOR}
312-
options={screenOptions.centralPaneNavigator}
313-
component={CentralPaneNavigator}
314-
/>
315337
<RootStack.Screen
316338
name={SCREENS.VALIDATE_LOGIN}
317339
options={{
@@ -433,6 +455,18 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
433455
options={defaultScreenOptions}
434456
component={ConnectionCompletePage}
435457
/>
458+
{Object.entries(CENTRAL_PANE_SCREENS).map(([screenName, componentGetter]) => {
459+
const centralPaneName = screenName as CentralPaneName;
460+
return (
461+
<RootStack.Screen
462+
key={centralPaneName}
463+
name={centralPaneName}
464+
initialParams={getCentralPaneScreenInitialParams(centralPaneName)}
465+
getComponent={componentGetter}
466+
options={CentralPaneScreenOptions}
467+
/>
468+
);
469+
})}
436470
</RootStack.Navigator>
437471
</View>
438472
</OptionsListContextProvider>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type {CentralPaneName} from '@libs/Navigation/types';
2+
import withPrepareCentralPaneScreen from '@src/components/withPrepareCentralPaneScreen';
3+
import SCREENS from '@src/SCREENS';
4+
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
5+
6+
type Screens = Partial<Record<CentralPaneName, () => React.ComponentType>>;
7+
8+
const CENTRAL_PANE_SCREENS = {
9+
[SCREENS.SETTINGS.WORKSPACES]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/workspace/WorkspacesListPage').default),
10+
[SCREENS.SETTINGS.PREFERENCES.ROOT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/Preferences/PreferencesPage').default),
11+
[SCREENS.SETTINGS.SECURITY]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/Security/SecuritySettingsPage').default),
12+
[SCREENS.SETTINGS.PROFILE.ROOT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/Profile/ProfilePage').default),
13+
[SCREENS.SETTINGS.WALLET.ROOT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/Wallet/WalletPage').default),
14+
[SCREENS.SETTINGS.ABOUT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/AboutPage/AboutPage').default),
15+
[SCREENS.SETTINGS.TROUBLESHOOT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/Troubleshoot/TroubleshootPage').default),
16+
[SCREENS.SETTINGS.SAVE_THE_WORLD]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/TeachersUnite/SaveTheWorldPage').default),
17+
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/settings/Subscription/SubscriptionSettingsPage').default),
18+
[SCREENS.SEARCH.CENTRAL_PANE]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('../../../pages/Search/SearchPage').default),
19+
[SCREENS.REPORT]: () => withPrepareCentralPaneScreen(require<ReactComponentModule>('./ReportScreenWrapper').default),
20+
} satisfies Screens;
21+
22+
export default CENTRAL_PANE_SCREENS;
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
2-
import type {CentralPaneNavigatorParamList, NavigationPartialRoute} from '@libs/Navigation/types';
2+
import type {AuthScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types';
33

4-
const ActiveRouteContext = React.createContext<NavigationPartialRoute<keyof CentralPaneNavigatorParamList> | undefined>(undefined);
4+
const ActiveRouteContext = React.createContext<NavigationPartialRoute<keyof AuthScreensParamList> | undefined>(undefined);
55

66
export default ActiveRouteContext;

src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx

-66
This file was deleted.

src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/index.native.tsx

-13
This file was deleted.

src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/index.tsx

-10
This file was deleted.

src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type {StackScreenProps} from '@react-navigation/stack';
22
import React from 'react';
3-
import type {CentralPaneNavigatorParamList} from '@navigation/types';
3+
import type {AuthScreensParamList} from '@navigation/types';
44
import ReportScreen from '@pages/home/ReportScreen';
55
import type SCREENS from '@src/SCREENS';
66
import ReportScreenIDSetter from './ReportScreenIDSetter';
77

8-
type ReportScreenWrapperProps = StackScreenProps<CentralPaneNavigatorParamList, typeof SCREENS.REPORT>;
8+
type ReportScreenWrapperProps = StackScreenProps<AuthScreensParamList, typeof SCREENS.REPORT>;
99

1010
function ReportScreenWrapper({route, navigation}: ReportScreenWrapperProps) {
1111
// The ReportScreen without the reportID set will display a skeleton

src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'
1717
import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute';
1818
import Navigation from '@libs/Navigation/Navigation';
1919
import type {RootStackParamList, State} from '@libs/Navigation/types';
20+
import isCentralPaneName from '@libs/NavigationUtils';
2021
import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils';
2122
import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar';
2223
import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton';
@@ -47,7 +48,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps
4748
const currentRoute = routes?.[navigationState?.index ?? 0];
4849
// When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method.
4950
// To prevent this, the value of the bottomTabRoute?.name is checked here
50-
if (!!(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) || Session.isAnonymousUser()) {
51+
if (!!(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && !isCentralPaneName(currentRoute.name)) || Session.isAnonymousUser()) {
5152
return;
5253
}
5354

0 commit comments

Comments
 (0)