From 51989d345ac97ae224c7bfbfb2f8cd8cecf27cb3 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 18 Jun 2024 09:54:01 +0200 Subject: [PATCH 01/34] Replace FullScreenNavigator with SplitNavigator --- .../Navigators/FullScreenNavigator.tsx | 49 ++++++-------- .../index.native.tsx | 36 ----------- .../createCustomFullScreenNavigator/types.ts | 8 --- .../SplitRouter.ts} | 29 ++++----- .../createSplitNavigator/index.tsx | 64 +++++++++++++++++++ .../createSplitNavigator/types.ts | 11 ++++ .../useNavigationReset/index.native.ts | 1 + .../useNavigationReset/index.ts | 14 ++++ .../usePrepareSplitNavigatorChildren.ts | 23 +++++++ 9 files changed, 144 insertions(+), 91 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.native.tsx delete mode 100644 src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/types.ts rename src/libs/Navigation/AppNavigator/{createCustomFullScreenNavigator/CustomFullScreenRouter.tsx => createSplitNavigator/SplitRouter.ts} (74%) create mode 100644 src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx create mode 100644 src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts create mode 100644 src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.native.ts create mode 100644 src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.ts create mode 100644 src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 8c06d5b458d1..5b1954b606cd 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -1,18 +1,12 @@ import React from 'react'; -import {View} from 'react-native'; -import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import createCustomFullScreenNavigator from '@libs/Navigation/AppNavigator/createCustomFullScreenNavigator'; -import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; +import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default; -const RootStack = createCustomFullScreenNavigator(); +const RootStack = createSplitNavigator(); type Screens = Partial React.ComponentType>>; @@ -38,30 +32,23 @@ const CENTRAL_PANE_WORKSPACE_SCREENS = { } satisfies Screens; function FullScreenNavigator() { - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils); - return ( - - - - - {Object.entries(CENTRAL_PANE_WORKSPACE_SCREENS).map(([screenName, componentGetter]) => ( - - ))} - - - + + + {Object.entries(CENTRAL_PANE_WORKSPACE_SCREENS).map(([screenName, componentGetter]) => ( + + ))} + ); } diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.native.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.native.tsx deleted file mode 100644 index 2f61f1519df0..000000000000 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.native.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type {ParamListBase, StackActionHelpers, StackNavigationState} from '@react-navigation/native'; -import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/native'; -import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; -import {StackView} from '@react-navigation/stack'; -import CustomFullScreenRouter from './CustomFullScreenRouter'; -import type {FullScreenNavigatorProps, FullScreenNavigatorRouterOptions} from './types'; - -function CustomFullScreenNavigator(props: FullScreenNavigatorProps) { - const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< - StackNavigationState, - FullScreenNavigatorRouterOptions, - StackActionHelpers, - StackNavigationOptions, - StackNavigationEventMap - >(CustomFullScreenRouter, { - children: props.children, - screenOptions: props.screenOptions, - initialRouteName: props.initialRouteName, - }); - - return ( - - - - ); -} - -CustomFullScreenNavigator.displayName = 'CustomFullScreenNavigator'; - -export default createNavigatorFactory, StackNavigationOptions, StackNavigationEventMap, typeof CustomFullScreenNavigator>(CustomFullScreenNavigator); diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/types.ts b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/types.ts deleted file mode 100644 index 7e7808c003d7..000000000000 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; -import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; - -type FullScreenNavigatorRouterOptions = StackRouterOptions; - -type FullScreenNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap>; - -export type {FullScreenNavigatorProps, FullScreenNavigatorRouterOptions}; diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts similarity index 74% rename from src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx rename to src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts index 27e976d9be0c..de830725ad3c 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts @@ -1,19 +1,17 @@ import type {ParamListBase, PartialState, RouterConfigOptions, StackNavigationState} from '@react-navigation/native'; import {StackRouter} from '@react-navigation/native'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import SCREENS from '@src/SCREENS'; -import type {FullScreenNavigatorRouterOptions} from './types'; +import type {SplitNavigatorRouterOptions} from './types'; type StackState = StackNavigationState | PartialState>; const isAtLeastOneInState = (state: StackState, screenName: string): boolean => state.routes.some((route) => route.name === screenName); -function adaptStateIfNecessary(state: StackState) { +function adaptStateIfNecessary(state: StackState, sidebarScreen: keyof ParamListBase, initialCentralPaneScreen: keyof ParamListBase) { const isNarrowLayout = getIsNarrowLayout(); const workspaceCentralPane = state.routes.at(-1); - - // There should always be WORKSPACE.INITIAL screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. - if (!isAtLeastOneInState(state, SCREENS.WORKSPACE.INITIAL)) { + // There should always be sidebarScreen screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. + if (!isAtLeastOneInState(state, sidebarScreen)) { // @ts-expect-error Updating read only property // noinspection JSConstantReassignment state.stale = true; // eslint-disable-line @@ -22,24 +20,24 @@ function adaptStateIfNecessary(state: StackState) { if (state.stale === true) { // Unshift the root screen to fill left pane. state.routes.unshift({ - name: SCREENS.WORKSPACE.INITIAL, + name: sidebarScreen, params: workspaceCentralPane?.params, }); } } // If the screen is wide, there should be at least two screens inside: - // - WORKSPACE.INITIAL to cover left pane. - // - WORKSPACE.PROFILE (first workspace settings screen) to cover central pane. + // - sidebarScreen to cover left pane. + // - initialCentralPaneScreen to cover central pane. if (!isNarrowLayout) { - if (state.routes.length === 1 && state.routes[0].name === SCREENS.WORKSPACE.INITIAL) { + if (state.routes.length === 1 && state.routes[0].name === sidebarScreen) { // @ts-expect-error Updating read only property // noinspection JSConstantReassignment state.stale = true; // eslint-disable-line // Push the default settings central pane screen. if (state.stale === true) { state.routes.push({ - name: SCREENS.WORKSPACE.PROFILE, + name: initialCentralPaneScreen, params: state.routes[0]?.params, }); } @@ -49,14 +47,13 @@ function adaptStateIfNecessary(state: StackState) { } } -function CustomFullScreenRouter(options: FullScreenNavigatorRouterOptions) { +function SplitRouter(options: SplitNavigatorRouterOptions) { const stackRouter = StackRouter(options); - return { ...stackRouter, getInitialState({routeNames, routeParamList, routeGetIdList}: RouterConfigOptions) { const initialState = stackRouter.getInitialState({routeNames, routeParamList, routeGetIdList}); - adaptStateIfNecessary(initialState); + adaptStateIfNecessary(initialState, options.sidebarScreen, options.initialCentralPaneScreen); // If we needed to modify the state we need to rehydrate it to get keys for new routes. if (initialState.stale) { @@ -66,11 +63,11 @@ function CustomFullScreenRouter(options: FullScreenNavigatorRouterOptions) { return initialState; }, getRehydratedState(partialState: StackState, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions): StackNavigationState { - adaptStateIfNecessary(partialState); + adaptStateIfNecessary(partialState, options.sidebarScreen, options.initialCentralPaneScreen); const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); return state; }, }; } -export default CustomFullScreenRouter; +export default SplitRouter; diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx new file mode 100644 index 000000000000..17ab209cfae8 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx @@ -0,0 +1,64 @@ +import type {ParamListBase, StackActionHelpers, StackNavigationState} from '@react-navigation/native'; +import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/native'; +import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; +import {StackView} from '@react-navigation/stack'; +import React from 'react'; +import {View} from 'react-native'; +import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; +import SplitRouter from './SplitRouter'; +import type {SplitNavigatorProps, SplitNavigatorRouterOptions} from './types'; +import useNavigationReset from './useNavigationReset'; +import usePrepareSplitNavigatorChildren from './usePrepareSplitNavigatorChildren'; + +function SplitNavigator(props: SplitNavigatorProps) { + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); + const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils); + + const children = usePrepareSplitNavigatorChildren(props.children, props.sidebarScreen, screenOptions.homeScreen); + + const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< + StackNavigationState, + SplitNavigatorRouterOptions, + StackActionHelpers, + StackNavigationOptions, + StackNavigationEventMap + >(SplitRouter, { + children, + screenOptions: screenOptions.centralPaneNavigator, + initialRouteName: props.initialRouteName, + sidebarScreen: props.sidebarScreen, + initialCentralPaneScreen: props.initialCentralPaneScreen, + }); + + useNavigationReset(navigation, shouldUseNarrowLayout); + + return ( + + + + + + + + ); +} + +SplitNavigator.displayName = 'SplitNavigator'; + +export default function () { + return createNavigatorFactory, StackNavigationOptions, StackNavigationEventMap, React.ComponentType>>( + SplitNavigator, + )(); +} diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts b/src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts new file mode 100644 index 000000000000..3b41f6f7a96d --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts @@ -0,0 +1,11 @@ +import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; +import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; + +type SplitNavigatorRouterOptions = StackRouterOptions & {initialCentralPaneScreen: string; sidebarScreen: string}; + +type SplitNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap> & { + initialCentralPaneScreen: Extract; + sidebarScreen: Extract; +}; + +export type {SplitNavigatorProps, SplitNavigatorRouterOptions}; diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.native.ts b/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.native.ts new file mode 100644 index 000000000000..5d5d30356781 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.native.ts @@ -0,0 +1 @@ +export default function useNavigationReset() {} diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.ts b/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.ts new file mode 100644 index 000000000000..238fc1ca2928 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.ts @@ -0,0 +1,14 @@ +import type {NavigationHelpers, ParamListBase} from '@react-navigation/native'; +import {useEffect} from 'react'; +import navigationRef from '@libs/Navigation/navigationRef'; + +export default function useNavigationReset(navigation: NavigationHelpers, isSmallScreenWidth: boolean) { + useEffect(() => { + if (!navigationRef.isReady()) { + return; + } + // We need to separately reset state of this navigator to trigger getRehydratedState. + navigation.reset(navigation.getState()); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [isSmallScreenWidth]); +} diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts b/src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts new file mode 100644 index 000000000000..ad05d1079328 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts @@ -0,0 +1,23 @@ +import type {StackNavigationOptions} from '@react-navigation/stack'; +import {Children, isValidElement, useMemo} from 'react'; +import type {ReactNode} from 'react'; + +export default function usePrepareSplitNavigatorChildren(screensNode: ReactNode, sidebarScreenName: string, sidebarScreenOptions: StackNavigationOptions) { + return useMemo( + () => + Children.toArray(screensNode).map((screen: ReactNode) => { + if (isValidElement(screen) && screen?.props?.name === sidebarScreenName) { + // If we found the element we wanted, clone it with the provided prop changes. + return { + ...screen, + props: { + ...screen.props, + options: sidebarScreenOptions, + }, + }; + } + return screen; + }), + [screensNode, sidebarScreenName, sidebarScreenOptions], + ); +} From 1799ae5f29bc48f102200105228891bc104da5ad Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 18 Jun 2024 09:56:25 +0200 Subject: [PATCH 02/34] Fix types in adaptStateIfNecessary --- .../Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts b/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts index de830725ad3c..a9540b89e469 100644 --- a/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts +++ b/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts @@ -7,7 +7,7 @@ type StackState = StackNavigationState | PartialState state.routes.some((route) => route.name === screenName); -function adaptStateIfNecessary(state: StackState, sidebarScreen: keyof ParamListBase, initialCentralPaneScreen: keyof ParamListBase) { +function adaptStateIfNecessary(state: StackState, sidebarScreen: string, initialCentralPaneScreen: string) { const isNarrowLayout = getIsNarrowLayout(); const workspaceCentralPane = state.routes.at(-1); // There should always be sidebarScreen screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. From 415258afdb52ccb3f54d5d82808ddd20b6c13ffc Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 19 Jun 2024 11:49:28 +0200 Subject: [PATCH 03/34] Rename Split to SplitStack --- .../Navigators/FullScreenNavigator.tsx | 4 ++-- .../createSplitNavigator/types.ts | 11 ---------- .../SplitStackRouter.ts} | 6 +++--- .../index.tsx | 20 +++++++++---------- .../createSplitStackNavigator/types.ts | 16 +++++++++++++++ .../useNavigationReset/index.native.ts | 0 .../useNavigationReset/index.ts | 0 .../usePrepareSplitStackNavigatorChildren.ts} | 2 +- 8 files changed, 32 insertions(+), 27 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts rename src/libs/Navigation/AppNavigator/{createSplitNavigator/SplitRouter.ts => createSplitStackNavigator/SplitStackRouter.ts} (95%) rename src/libs/Navigation/AppNavigator/{createSplitNavigator => createSplitStackNavigator}/index.tsx (78%) create mode 100644 src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts rename src/libs/Navigation/AppNavigator/{createSplitNavigator => createSplitStackNavigator}/useNavigationReset/index.native.ts (100%) rename src/libs/Navigation/AppNavigator/{createSplitNavigator => createSplitStackNavigator}/useNavigationReset/index.ts (100%) rename src/libs/Navigation/AppNavigator/{createSplitNavigator/usePrepareSplitNavigatorChildren.ts => createSplitStackNavigator/usePrepareSplitStackNavigatorChildren.ts} (84%) diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 5b1954b606cd..2e47a997bbcf 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; +import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default; -const RootStack = createSplitNavigator(); +const RootStack = createSplitStackNavigator(); type Screens = Partial React.ComponentType>>; diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts b/src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts deleted file mode 100644 index 3b41f6f7a96d..000000000000 --- a/src/libs/Navigation/AppNavigator/createSplitNavigator/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; -import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; - -type SplitNavigatorRouterOptions = StackRouterOptions & {initialCentralPaneScreen: string; sidebarScreen: string}; - -type SplitNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap> & { - initialCentralPaneScreen: Extract; - sidebarScreen: Extract; -}; - -export type {SplitNavigatorProps, SplitNavigatorRouterOptions}; diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts similarity index 95% rename from src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts rename to src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts index a9540b89e469..0b99fb608b76 100644 --- a/src/libs/Navigation/AppNavigator/createSplitNavigator/SplitRouter.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts @@ -1,7 +1,7 @@ import type {ParamListBase, PartialState, RouterConfigOptions, StackNavigationState} from '@react-navigation/native'; import {StackRouter} from '@react-navigation/native'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import type {SplitNavigatorRouterOptions} from './types'; +import type {SplitStackNavigatorRouterOptions} from './types'; type StackState = StackNavigationState | PartialState>; @@ -47,7 +47,7 @@ function adaptStateIfNecessary(state: StackState, sidebarScreen: string, initial } } -function SplitRouter(options: SplitNavigatorRouterOptions) { +function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); return { ...stackRouter, @@ -70,4 +70,4 @@ function SplitRouter(options: SplitNavigatorRouterOptions) { }; } -export default SplitRouter; +export default SplitStackRouter; diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx similarity index 78% rename from src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx rename to src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx index 17ab209cfae8..11a621b4d098 100644 --- a/src/libs/Navigation/AppNavigator/createSplitNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx @@ -9,26 +9,26 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; -import SplitRouter from './SplitRouter'; -import type {SplitNavigatorProps, SplitNavigatorRouterOptions} from './types'; +import SplitStackRouter from './SplitStackRouter'; +import type {SplitStackNavigatorProps, SplitStackNavigatorRouterOptions} from './types'; import useNavigationReset from './useNavigationReset'; -import usePrepareSplitNavigatorChildren from './usePrepareSplitNavigatorChildren'; +import usePrepareSplitStackNavigatorChildren from './usePrepareSplitStackNavigatorChildren'; -function SplitNavigator(props: SplitNavigatorProps) { +function SplitStackNavigator(props: SplitStackNavigatorProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils); - const children = usePrepareSplitNavigatorChildren(props.children, props.sidebarScreen, screenOptions.homeScreen); + const children = usePrepareSplitStackNavigatorChildren(props.children, props.sidebarScreen, screenOptions.homeScreen); const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< StackNavigationState, - SplitNavigatorRouterOptions, + SplitStackNavigatorRouterOptions, StackActionHelpers, StackNavigationOptions, StackNavigationEventMap - >(SplitRouter, { + >(SplitStackRouter, { children, screenOptions: screenOptions.centralPaneNavigator, initialRouteName: props.initialRouteName, @@ -55,10 +55,10 @@ function SplitNavigator(props: SplitNavigatorPr ); } -SplitNavigator.displayName = 'SplitNavigator'; +SplitStackNavigator.displayName = 'SplitStackNavigator'; export default function () { - return createNavigatorFactory, StackNavigationOptions, StackNavigationEventMap, React.ComponentType>>( - SplitNavigator, + return createNavigatorFactory, StackNavigationOptions, StackNavigationEventMap, React.ComponentType>>( + SplitStackNavigator, )(); } diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts new file mode 100644 index 000000000000..e721ca6301ed --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts @@ -0,0 +1,16 @@ +import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; +import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; + +type SplitStackNavigatorRouterOptions = StackRouterOptions & {initialCentralPaneScreen: string; sidebarScreen: string}; + +type SplitStackNavigatorProps = DefaultNavigatorOptions< + ParamListBase, + StackNavigationState, + StackNavigationOptions, + StackNavigationEventMap +> & { + initialCentralPaneScreen: Extract; + sidebarScreen: Extract; +}; + +export type {SplitStackNavigatorProps, SplitStackNavigatorRouterOptions}; diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.native.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.native.ts similarity index 100% rename from src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.native.ts rename to src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.native.ts diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.ts similarity index 100% rename from src/libs/Navigation/AppNavigator/createSplitNavigator/useNavigationReset/index.ts rename to src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.ts diff --git a/src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/usePrepareSplitStackNavigatorChildren.ts similarity index 84% rename from src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts rename to src/libs/Navigation/AppNavigator/createSplitStackNavigator/usePrepareSplitStackNavigatorChildren.ts index ad05d1079328..d32e6b30f876 100644 --- a/src/libs/Navigation/AppNavigator/createSplitNavigator/usePrepareSplitNavigatorChildren.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/usePrepareSplitStackNavigatorChildren.ts @@ -2,7 +2,7 @@ import type {StackNavigationOptions} from '@react-navigation/stack'; import {Children, isValidElement, useMemo} from 'react'; import type {ReactNode} from 'react'; -export default function usePrepareSplitNavigatorChildren(screensNode: ReactNode, sidebarScreenName: string, sidebarScreenOptions: StackNavigationOptions) { +export default function usePrepareSplitStackNavigatorChildren(screensNode: ReactNode, sidebarScreenName: string, sidebarScreenOptions: StackNavigationOptions) { return useMemo( () => Children.toArray(screensNode).map((screen: ReactNode) => { From 2b06edf05ed24c52cd1c015d660a919dea0d1126 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 19 Jun 2024 11:55:32 +0200 Subject: [PATCH 04/34] Rename useNavigationReset to useHandleScreenResize --- .../AppNavigator/createSplitStackNavigator/index.tsx | 4 ++-- .../useHandleScreenResize/index.native.ts | 1 + .../{useNavigationReset => useHandleScreenResize}/index.ts | 7 +++++-- .../useNavigationReset/index.native.ts | 1 - 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.native.ts rename src/libs/Navigation/AppNavigator/createSplitStackNavigator/{useNavigationReset => useHandleScreenResize}/index.ts (67%) delete mode 100644 src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.native.ts diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx index 11a621b4d098..d8914dcd0b80 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx @@ -11,7 +11,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; import SplitStackRouter from './SplitStackRouter'; import type {SplitStackNavigatorProps, SplitStackNavigatorRouterOptions} from './types'; -import useNavigationReset from './useNavigationReset'; +import useHandleScreenResize from './useHandleScreenResize'; import usePrepareSplitStackNavigatorChildren from './usePrepareSplitStackNavigatorChildren'; function SplitStackNavigator(props: SplitStackNavigatorProps) { @@ -36,7 +36,7 @@ function SplitStackNavigator(props: SplitStackN initialCentralPaneScreen: props.initialCentralPaneScreen, }); - useNavigationReset(navigation, shouldUseNarrowLayout); + useHandleScreenResize(navigation); return ( diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.native.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.native.ts new file mode 100644 index 000000000000..4ac0a1ae5595 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.native.ts @@ -0,0 +1 @@ +export default function useHandleScreenResize() {} diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.ts similarity index 67% rename from src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.ts rename to src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.ts index 238fc1ca2928..e6ae505cb560 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useNavigationReset/index.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/useHandleScreenResize/index.ts @@ -1,8 +1,11 @@ import type {NavigationHelpers, ParamListBase} from '@react-navigation/native'; import {useEffect} from 'react'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import navigationRef from '@libs/Navigation/navigationRef'; -export default function useNavigationReset(navigation: NavigationHelpers, isSmallScreenWidth: boolean) { +export default function useHandleScreenResize(navigation: NavigationHelpers) { + const {shouldUseNarrowLayout} = useResponsiveLayout(); + useEffect(() => { if (!navigationRef.isReady()) { return; @@ -10,5 +13,5 @@ export default function useNavigationReset(navigation: NavigationHelpers Date: Wed, 19 Jun 2024 12:22:48 +0200 Subject: [PATCH 05/34] Rename initialCentralPaneScreen to defaultCentralScreen --- .../AppNavigator/Navigators/FullScreenNavigator.tsx | 2 +- .../createSplitStackNavigator/SplitStackRouter.ts | 10 +++++----- .../AppNavigator/createSplitStackNavigator/index.tsx | 2 +- .../AppNavigator/createSplitStackNavigator/types.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 2e47a997bbcf..5953a3e34c92 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -35,7 +35,7 @@ function FullScreenNavigator() { return ( | PartialState state.routes.some((route) => route.name === screenName); -function adaptStateIfNecessary(state: StackState, sidebarScreen: string, initialCentralPaneScreen: string) { +function adaptStateIfNecessary(state: StackState, sidebarScreen: string, defaultCentralScreen: string) { const isNarrowLayout = getIsNarrowLayout(); const workspaceCentralPane = state.routes.at(-1); // There should always be sidebarScreen screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. @@ -28,7 +28,7 @@ function adaptStateIfNecessary(state: StackState, sidebarScreen: string, initial // If the screen is wide, there should be at least two screens inside: // - sidebarScreen to cover left pane. - // - initialCentralPaneScreen to cover central pane. + // - defaultCentralScreen to cover central pane. if (!isNarrowLayout) { if (state.routes.length === 1 && state.routes[0].name === sidebarScreen) { // @ts-expect-error Updating read only property @@ -37,7 +37,7 @@ function adaptStateIfNecessary(state: StackState, sidebarScreen: string, initial // Push the default settings central pane screen. if (state.stale === true) { state.routes.push({ - name: initialCentralPaneScreen, + name: defaultCentralScreen, params: state.routes[0]?.params, }); } @@ -53,7 +53,7 @@ function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { ...stackRouter, getInitialState({routeNames, routeParamList, routeGetIdList}: RouterConfigOptions) { const initialState = stackRouter.getInitialState({routeNames, routeParamList, routeGetIdList}); - adaptStateIfNecessary(initialState, options.sidebarScreen, options.initialCentralPaneScreen); + adaptStateIfNecessary(initialState, options.sidebarScreen, options.defaultCentralScreen); // If we needed to modify the state we need to rehydrate it to get keys for new routes. if (initialState.stale) { @@ -63,7 +63,7 @@ function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { return initialState; }, getRehydratedState(partialState: StackState, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions): StackNavigationState { - adaptStateIfNecessary(partialState, options.sidebarScreen, options.initialCentralPaneScreen); + adaptStateIfNecessary(partialState, options.sidebarScreen, options.defaultCentralScreen); const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); return state; }, diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx index d8914dcd0b80..ea3c8ecad21b 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx @@ -33,7 +33,7 @@ function SplitStackNavigator(props: SplitStackN screenOptions: screenOptions.centralPaneNavigator, initialRouteName: props.initialRouteName, sidebarScreen: props.sidebarScreen, - initialCentralPaneScreen: props.initialCentralPaneScreen, + defaultCentralScreen: props.defaultCentralScreen, }); useHandleScreenResize(navigation); diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts index e721ca6301ed..5e62b4e710a6 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts @@ -1,7 +1,7 @@ import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; -type SplitStackNavigatorRouterOptions = StackRouterOptions & {initialCentralPaneScreen: string; sidebarScreen: string}; +type SplitStackNavigatorRouterOptions = StackRouterOptions & {defaultCentralScreen: string; sidebarScreen: string}; type SplitStackNavigatorProps = DefaultNavigatorOptions< ParamListBase, @@ -9,7 +9,7 @@ type SplitStackNavigatorProps = DefaultNavigato StackNavigationOptions, StackNavigationEventMap > & { - initialCentralPaneScreen: Extract; + defaultCentralScreen: Extract; sidebarScreen: Extract; }; From 3484a8447b82392dd164338fc97ff521d4701466 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 19 Jun 2024 13:45:01 +0200 Subject: [PATCH 06/34] Rename FullScreenNavigator to WorkspaceNavigator --- src/NAVIGATORS.ts | 1 + .../Navigation/AppNavigator/AuthScreens.tsx | 6 +-- ...enNavigator.tsx => WorkspaceNavigator.tsx} | 12 +++--- .../CustomRouter.ts | 4 +- .../AppNavigator/getActionsFromPartialDiff.ts | 2 +- .../AppNavigator/getPartialStateDiff.ts | 8 ++-- src/libs/Navigation/Navigation.ts | 2 +- src/libs/Navigation/dismissModal.ts | 2 +- src/libs/Navigation/dismissModalWithReport.ts | 2 +- .../Navigation/getTopmostFullScreenRoute.ts | 10 ++--- src/libs/Navigation/linkTo/index.ts | 12 +++--- ....ts => WORKSPACE_SCREEN_TO_RHP_MAPPING.ts} | 6 +-- src/libs/Navigation/linkingConfig/config.ts | 2 +- .../linkingConfig/getAdaptedStateFromPath.ts | 40 +++++++++---------- .../getMatchingBottomTabRouteForState.ts | 4 +- src/libs/Navigation/types.ts | 10 ++--- src/pages/workspace/WorkspaceInitialPage.tsx | 4 +- src/pages/workspace/WorkspaceMembersPage.tsx | 4 +- .../workspace/WorkspaceMoreFeaturesPage.tsx | 4 +- .../workspace/bills/WorkspaceBillsPage.tsx | 4 +- .../workspace/card/WorkspaceCardPage.tsx | 4 +- .../categories/WorkspaceCategoriesPage.tsx | 4 +- .../distanceRates/PolicyDistanceRatesPage.tsx | 4 +- .../invoices/WorkspaceInvoicesPage.tsx | 4 +- .../reimburse/WorkspaceReimbursePage.tsx | 4 +- .../workspace/tags/WorkspaceTagsPage.tsx | 4 +- .../workspace/taxes/WorkspaceTaxesPage.tsx | 4 +- .../workspace/travel/WorkspaceTravelPage.tsx | 4 +- src/pages/workspace/withPolicy.tsx | 4 +- .../WorkspaceAutoReportingFrequencyPage.tsx | 4 +- ...orkspaceAutoReportingMonthlyOffsetPage.tsx | 4 +- .../workflows/WorkspaceWorkflowsPage.tsx | 4 +- 32 files changed, 94 insertions(+), 93 deletions(-) rename src/libs/Navigation/AppNavigator/Navigators/{FullScreenNavigator.tsx => WorkspaceNavigator.tsx} (90%) rename src/libs/Navigation/linkingConfig/{FULL_SCREEN_TO_RHP_MAPPING.ts => WORKSPACE_SCREEN_TO_RHP_MAPPING.ts} (98%) diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index 0b4a86c99247..cb829856da7f 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -12,4 +12,5 @@ export default { WELCOME_VIDEO_MODAL_NAVIGATOR: 'WelcomeVideoModalNavigator', EXPLANATION_MODAL_NAVIGATOR: 'ExplanationModalNavigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', + WORKSPACE_NAVIGATOR: 'WorkspaceNavigator', } as const; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index f2461f400678..d9df7750401d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -55,11 +55,11 @@ import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; import BottomTabNavigator from './Navigators/BottomTabNavigator'; import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator'; import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator'; -import FullScreenNavigator from './Navigators/FullScreenNavigator'; import LeftModalNavigator from './Navigators/LeftModalNavigator'; import OnboardingModalNavigator from './Navigators/OnboardingModalNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator'; +import WorkspaceNavigator from './Navigators/WorkspaceNavigator'; type AuthScreensProps = { /** Session of currently logged in user */ @@ -480,9 +480,9 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie listeners={modalScreenListenersWithCancelSearch} /> require('../../../../pages/workspace/WorkspaceInitialPage').default; -const RootStack = createSplitStackNavigator(); +const RootStack = createSplitStackNavigator(); -type Screens = Partial React.ComponentType>>; +type Screens = Partial React.ComponentType>>; const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.PROFILE]: () => require('../../../../pages/workspace/WorkspaceProfilePage').default, @@ -31,7 +31,7 @@ const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.RULES]: () => require('../../../../pages/workspace/rules/PolicyRulesPage').default, } satisfies Screens; -function FullScreenNavigator() { +function WorkspaceNavigator() { return ( ) { // 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. if (topmostBottomTabRoute && !isNarrowLayout) { - const fullScreenRoute = state.routes.find((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR); + const fullScreenRoute = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); // If there is fullScreenRoute we don't need to add anything. if (fullScreenRoute) { @@ -62,7 +62,7 @@ function compareAndAdaptState(state: StackNavigationState) { return; } - const templateFullScreenRoute = templateState.routes.find((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR); + const templateFullScreenRoute = templateState.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); // If templateFullScreenRoute is defined, and full screen route is not in the state, we need to add it. if (templateFullScreenRoute) { diff --git a/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts b/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts index 9685f4fc0339..d66eb313cbb1 100644 --- a/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts +++ b/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts @@ -13,7 +13,7 @@ function getActionsFromPartialDiff(diff: GetPartialStateDiffReturnType): Navigat const bottomTabDiff = diff[NAVIGATORS.BOTTOM_TAB_NAVIGATOR]; const centralPaneDiff = diff[NAVIGATORS.CENTRAL_PANE_NAVIGATOR]; - const fullScreenDiff = diff[NAVIGATORS.FULL_SCREEN_NAVIGATOR]; + const fullScreenDiff = diff[NAVIGATORS.WORKSPACE_NAVIGATOR]; // There is only one bottom tab navigator so we can just push this route. if (bottomTabDiff) { diff --git a/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts b/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts index 17a8ee158219..cac65b833bde 100644 --- a/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts +++ b/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts @@ -9,7 +9,7 @@ import NAVIGATORS from '@src/NAVIGATORS'; type GetPartialStateDiffReturnType = { [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]?: NavigationPartialRoute; [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]?: NavigationPartialRoute; - [NAVIGATORS.FULL_SCREEN_NAVIGATOR]?: NavigationPartialRoute; + [NAVIGATORS.WORKSPACE_NAVIGATOR]?: NavigationPartialRoute; }; /** @@ -62,10 +62,10 @@ function getPartialStateDiff(state: State, templateState: St // This one is heuristic and may need to be improved if we will be able to navigate from modal screen with full screen in background to another modal screen with full screen in background. // For now this simple check is enough. - if (metainfo.isFullScreenNavigatorMandatory) { + if (metainfo.isWorkspaceNavigatorMandatory) { const stateTopmostFullScreen = getTopmostFullScreenRoute(state); const templateStateTopmostFullScreen = getTopmostFullScreenRoute(templateState); - const fullScreenDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR).at(-1) as NavigationPartialRoute; + const fullScreenDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1) as NavigationPartialRoute; if ( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -75,7 +75,7 @@ function getPartialStateDiff(state: State, templateState: St (stateTopmostFullScreen.name !== templateStateTopmostFullScreen.name || !shallowCompare(stateTopmostFullScreen.params as Record | undefined, templateStateTopmostFullScreen.params as Record | undefined))) ) { - diff[NAVIGATORS.FULL_SCREEN_NAVIGATOR] = fullScreenDiff; + diff[NAVIGATORS.WORKSPACE_NAVIGATOR] = fullScreenDiff; } } diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 4c61b953f572..4fa7fda3210d 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -89,7 +89,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number if ( 'name' in stateOrRoute && - (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) + (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.WORKSPACE_NAVIGATOR) ) { return 0; } diff --git a/src/libs/Navigation/dismissModal.ts b/src/libs/Navigation/dismissModal.ts index dd0e512ea33d..ea00a8646c85 100644 --- a/src/libs/Navigation/dismissModal.ts +++ b/src/libs/Navigation/dismissModal.ts @@ -18,7 +18,7 @@ function dismissModal(navigationRef: NavigationContainerRef) const state = navigationRef.getState(); const lastRoute = state.routes.at(-1); switch (lastRoute?.name) { - case NAVIGATORS.FULL_SCREEN_NAVIGATOR: + case NAVIGATORS.WORKSPACE_NAVIGATOR: case NAVIGATORS.LEFT_MODAL_NAVIGATOR: case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR: diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index 1579a0565726..99f23a25ad3a 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -34,7 +34,7 @@ function dismissModalWithReport(targetReport: OnyxEntry, navigationRef: const state = navigationRef.getState(); const lastRoute = state.routes.at(-1); switch (lastRoute?.name) { - case NAVIGATORS.FULL_SCREEN_NAVIGATOR: + case NAVIGATORS.WORKSPACE_NAVIGATOR: case NAVIGATORS.LEFT_MODAL_NAVIGATOR: case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case SCREENS.NOT_FOUND: diff --git a/src/libs/Navigation/getTopmostFullScreenRoute.ts b/src/libs/Navigation/getTopmostFullScreenRoute.ts index fcc28ce76926..ae17b0094f42 100644 --- a/src/libs/Navigation/getTopmostFullScreenRoute.ts +++ b/src/libs/Navigation/getTopmostFullScreenRoute.ts @@ -1,13 +1,13 @@ import NAVIGATORS from '@src/NAVIGATORS'; -import type {FullScreenName, NavigationPartialRoute, RootStackParamList, State} from './types'; +import type {NavigationPartialRoute, RootStackParamList, State, WorkspaceScreenName} from './types'; // Get the name of topmost fullscreen route in the navigation stack. -function getTopmostFullScreenRoute(state: State): NavigationPartialRoute | undefined { +function getTopmostFullScreenRoute(state: State): NavigationPartialRoute | undefined { if (!state) { return; } - const topmostFullScreenRoute = state.routes.filter((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR).at(-1); + const topmostFullScreenRoute = state.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1); if (!topmostFullScreenRoute) { return; @@ -15,13 +15,13 @@ function getTopmostFullScreenRoute(state: State): Navigation if (topmostFullScreenRoute.state) { // There will be at least one route in the fullscreen navigator. - const {name, params} = topmostFullScreenRoute.state.routes.at(-1) as NavigationPartialRoute; + const {name, params} = topmostFullScreenRoute.state.routes.at(-1) as NavigationPartialRoute; return {name, params}; } if (!!topmostFullScreenRoute.params && 'screen' in topmostFullScreenRoute.params) { - return {name: topmostFullScreenRoute.params.screen as FullScreenName, params: topmostFullScreenRoute.params.params}; + return {name: topmostFullScreenRoute.params.screen as WorkspaceScreenName, params: topmostFullScreenRoute.params.params}; } } diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 1fc99c771ca5..0a393100e375 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -57,10 +57,10 @@ export default function linkTo(navigation: NavigationContainerRef, metainfo); const diffActions = getActionsFromPartialDiff(diff); for (const diffAction of diffActions) { @@ -142,7 +142,7 @@ export default function linkTo(navigation: NavigationContainerRef> = { +const WORKSPACE_SCREEN_TO_RHP_MAPPING: Partial> = { [SCREENS.WORKSPACE.PROFILE]: [SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.ADDRESS, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE], [SCREENS.WORKSPACE.REIMBURSE]: [SCREENS.WORKSPACE.RATE_AND_UNIT, SCREENS.WORKSPACE.RATE_AND_UNIT_RATE, SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT], [SCREENS.WORKSPACE.MEMBERS]: [ @@ -204,4 +204,4 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { ], }; -export default FULL_SCREEN_TO_RHP_MAPPING; +export default WORKSPACE_SCREEN_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 87656c5997db..82c034373b5b 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1149,7 +1149,7 @@ const config: LinkingOptions['config'] = { }, }, - [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { + [NAVIGATORS.WORKSPACE_NAVIGATOR]: { screens: { [SCREENS.WORKSPACE.INITIAL]: { path: ROUTES.WORKSPACE_INITIAL.route, diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 2c96e5796309..67987404084b 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -4,7 +4,7 @@ import pick from 'lodash/pick'; import type {TupleToUnion} from 'type-fest'; import {isAnonymousUser} from '@libs/actions/Session'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import type {BottomTabName, CentralPaneName, FullScreenName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import type {BottomTabName, CentralPaneName, NavigationPartialRoute, RootStackParamList, WorkspaceScreenName} from '@libs/Navigation/types'; import {isCentralPaneName} from '@libs/NavigationUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import * as ReportConnection from '@libs/ReportConnection'; @@ -16,10 +16,10 @@ import type {Screen} from '@src/SCREENS'; import SCREENS from '@src/SCREENS'; import CENTRAL_PANE_TO_RHP_MAPPING from './CENTRAL_PANE_TO_RHP_MAPPING'; import config, {normalizedConfigs} from './config'; -import FULL_SCREEN_TO_RHP_MAPPING from './FULL_SCREEN_TO_RHP_MAPPING'; import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState'; import getMatchingCentralPaneRouteForState from './getMatchingCentralPaneRouteForState'; import replacePathInNestedState from './replacePathInNestedState'; +import WORKSPACE_SCREEN_TO_RHP_MAPPING from './WORKSPACE_SCREEN_TO_RHP_MAPPING'; const RHP_SCREENS_OPENED_FROM_LHN = [ SCREENS.SETTINGS.SHARE_CODE, @@ -39,7 +39,7 @@ type Metainfo = { // If the screens in the bottom tab and central pane are not mandatory for this state, we want to have this information. // It will help us later with creating proper diff betwen current and desired state. isCentralPaneAndBottomTabMandatory: boolean; - isFullScreenNavigatorMandatory: boolean; + isWorkspaceNavigatorMandatory: boolean; }; type GetAdaptedStateReturnType = { @@ -78,12 +78,12 @@ function createBottomTabNavigator(route: NavigationPartialRoute, }; } -function createFullScreenNavigator(route?: NavigationPartialRoute): NavigationPartialRoute { +function createWorkspaceNavigator(route?: NavigationPartialRoute): NavigationPartialRoute { const routes = []; const policyID = route?.params && 'policyID' in route.params ? route.params.policyID : undefined; - // Both routes in FullScreenNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in FullScreenNavigator + // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator routes.push({ name: SCREENS.WORKSPACE.INITIAL, params: { @@ -95,7 +95,7 @@ function createFullScreenNavigator(route?: NavigationPartialRoute | undefined { +function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | undefined { // Check for backTo param. One screen with different backTo value may need diferent screens visible under the overlay. if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { const stateForBackTo = getStateFromPath(route.params.backTo, config); @@ -119,7 +119,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat const centralPaneOrFullScreenNavigator = stateForBackTo.routes.find( // eslint-disable-next-line @typescript-eslint/no-shadow - (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR, + (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.WORKSPACE_NAVIGATOR, ); // If there is rhpNavigator in the state generated for backTo url, we want to get root route matching to this rhp screen. @@ -129,7 +129,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat // If we know that backTo targets the root route (central pane or full screen) we want to use it. if (centralPaneOrFullScreenNavigator && centralPaneOrFullScreenNavigator.state) { - return centralPaneOrFullScreenNavigator as NavigationPartialRoute; + return centralPaneOrFullScreenNavigator as NavigationPartialRoute; } } } @@ -144,11 +144,11 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat } // Check for FullScreenNavigator - for (const [fullScreenName, RHPNames] of Object.entries(FULL_SCREEN_TO_RHP_MAPPING)) { + for (const [workspaceScreenName, RHPNames] of Object.entries(WORKSPACE_SCREEN_TO_RHP_MAPPING)) { if (RHPNames.includes(route.name)) { - const paramsFromRoute = getParamsFromRoute(fullScreenName); + const paramsFromRoute = getParamsFromRoute(workspaceScreenName); - return createFullScreenNavigator({name: fullScreenName as FullScreenName, params: pick(route.params, paramsFromRoute)}); + return createWorkspaceNavigator({name: workspaceScreenName as WorkspaceScreenName, params: pick(route.params, paramsFromRoute)}); } } @@ -164,13 +164,13 @@ function getAdaptedState(state: PartialState const isNarrowLayout = getIsNarrowLayout(); const metainfo = { isCentralPaneAndBottomTabMandatory: true, - isFullScreenNavigatorMandatory: true, + isWorkspaceNavigatorMandatory: true, }; // We need to check what is defined to know what we need to add. const bottomTabNavigator = state.routes.find((route) => route.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR); const centralPaneNavigator = state.routes.find((route) => isCentralPaneName(route.name)); - const fullScreenNavigator = state.routes.find((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR); + const WorkspaceNavigator = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); const rhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); const lhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR); const onboardingModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR); @@ -194,17 +194,17 @@ function getAdaptedState(state: PartialState // This may happen if this RHP doens't have a route that should be under the overlay defined. if (!matchingRootRoute || isRHPScreenOpenedFromLHN) { metainfo.isCentralPaneAndBottomTabMandatory = false; - metainfo.isFullScreenNavigatorMandatory = false; + metainfo.isWorkspaceNavigatorMandatory = false; // If matchingRootRoute is undefined and it's a narrow layout, don't add a report screen under the RHP. matchingRootRoute = matchingRootRoute ?? (!isNarrowLayout ? {name: SCREENS.REPORT} : undefined); } - // If the root route is type of FullScreenNavigator, the default bottom tab will be added. + // If the root route is type of WorkspaceNavigator, the default bottom tab will be added. const matchingBottomTabRoute = getMatchingBottomTabRouteForState({routes: matchingRootRoute ? [matchingRootRoute] : []}); routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); // When we open a screen in RHP from FullScreenNavigator, we need to add the appropriate screen in CentralPane. // Then, when we close FullScreenNavigator, we will be redirected to the correct page in CentralPane. - if (matchingRootRoute?.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) { + if (matchingRootRoute?.name === NAVIGATORS.WORKSPACE_NAVIGATOR) { routes.push({name: SCREENS.SETTINGS.WORKSPACES}); } @@ -227,7 +227,7 @@ function getAdaptedState(state: PartialState // There is no screen in these navigators that would have mandatory central pane, bottom tab or fullscreen navigator. metainfo.isCentralPaneAndBottomTabMandatory = false; - metainfo.isFullScreenNavigatorMandatory = false; + metainfo.isWorkspaceNavigatorMandatory = false; const routes = []; routes.push( createBottomTabNavigator( @@ -265,7 +265,7 @@ function getAdaptedState(state: PartialState metainfo, }; } - if (fullScreenNavigator) { + if (WorkspaceNavigator) { // Routes // - default bottom tab // - default central pane on desktop layout @@ -285,7 +285,7 @@ function getAdaptedState(state: PartialState name: SCREENS.SETTINGS.WORKSPACES, }); - routes.push(fullScreenNavigator); + routes.push(WorkspaceNavigator); return { adaptedState: getRoutesWithIndex(routes), diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts index 7b213fdfeb6e..1db77f9917ff 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts @@ -8,9 +8,9 @@ import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING'; function getMatchingBottomTabRouteForState(state: State, policyID?: string): NavigationPartialRoute { const paramsWithPolicyID = policyID ? {policyID} : undefined; const defaultRoute = {name: SCREENS.HOME, params: paramsWithPolicyID}; - const isFullScreenNavigatorOpened = state.routes.some((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR); + const isWorkspaceNavigatorOpened = state.routes.some((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); - if (isFullScreenNavigatorOpened) { + if (isWorkspaceNavigatorOpened) { return {name: SCREENS.SETTINGS.ROOT, params: paramsWithPolicyID}; } diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 3dc7c708f0b6..4b6b97c0d749 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1172,7 +1172,7 @@ type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; }; -type FullScreenNavigatorParamList = { +type WorkspaceNavigatorParamList = { [SCREENS.WORKSPACE.INITIAL]: { policyID: string; backTo?: string; @@ -1364,7 +1364,7 @@ type AuthScreensParamList = CentralPaneScreensParamList & [SCREENS.NOT_FOUND]: undefined; [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.WORKSPACE_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams; @@ -1405,7 +1405,7 @@ type RootStackParamList = PublicScreensParamList & AuthScreensParamList & LeftMo type BottomTabName = keyof BottomTabNavigatorParamList; -type FullScreenName = keyof FullScreenNavigatorParamList; +type WorkspaceScreenName = keyof WorkspaceNavigatorParamList; type CentralPaneName = keyof CentralPaneScreensParamList; @@ -1431,8 +1431,8 @@ export type { EnablePaymentsNavigatorParamList, ExplanationModalNavigatorParamList, FlagCommentNavigatorParamList, - FullScreenName, - FullScreenNavigatorParamList, + WorkspaceScreenName, + WorkspaceNavigatorParamList, LeftModalNavigatorParamList, MoneyRequestNavigatorParamList, NavigationPartialRoute, diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 3c2bbe15a2d0..588d4f7e63d2 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -26,7 +26,7 @@ import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import * as Policy from '@userActions/Policy/Policy'; import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; @@ -75,7 +75,7 @@ type WorkspaceInitialPageOnyxProps = { policyCategories: OnyxEntry; }; -type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps; +type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps; type PolicyFeatureStates = Record; diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index e686ec0bf513..221bd93a4324 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -31,7 +31,7 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -49,7 +49,7 @@ import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscree import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; import WorkspacePageWithSections from './WorkspacePageWithSections'; -type WorkspaceMembersPageProps = WithPolicyAndFullscreenLoadingProps & WithCurrentUserPersonalDetailsProps & StackScreenProps; +type WorkspaceMembersPageProps = WithPolicyAndFullscreenLoadingProps & WithCurrentUserPersonalDetailsProps & StackScreenProps; /** * Inverts an object, equivalent of _.invert diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 0182f1ea8827..9a4628f21918 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -17,7 +17,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import {isControlPolicy} from '@libs/PolicyUtils'; import * as Category from '@userActions/Policy/Category'; import * as DistanceRate from '@userActions/Policy/DistanceRate'; @@ -37,7 +37,7 @@ import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscree import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; import ToggleSettingOptionRow from './workflows/ToggleSettingsOptionRow'; -type WorkspaceMoreFeaturesPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; +type WorkspaceMoreFeaturesPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; type Item = { icon: IconAsset; diff --git a/src/pages/workspace/bills/WorkspaceBillsPage.tsx b/src/pages/workspace/bills/WorkspaceBillsPage.tsx index 195cf51a710b..e98e179479d6 100644 --- a/src/pages/workspace/bills/WorkspaceBillsPage.tsx +++ b/src/pages/workspace/bills/WorkspaceBillsPage.tsx @@ -4,14 +4,14 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import WorkspaceBillsNoVBAView from './WorkspaceBillsNoVBAView'; import WorkspaceBillsVBAView from './WorkspaceBillsVBAView'; -type WorkspaceBillsPageProps = StackScreenProps; +type WorkspaceBillsPageProps = StackScreenProps; function WorkspaceBillsPage({route}: WorkspaceBillsPageProps) { const {translate} = useLocalize(); diff --git a/src/pages/workspace/card/WorkspaceCardPage.tsx b/src/pages/workspace/card/WorkspaceCardPage.tsx index af00d3ad437a..14b51956daec 100644 --- a/src/pages/workspace/card/WorkspaceCardPage.tsx +++ b/src/pages/workspace/card/WorkspaceCardPage.tsx @@ -4,7 +4,7 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; @@ -12,7 +12,7 @@ import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView'; import WorkspaceCardVBANoECardView from './WorkspaceCardVBANoECardView'; import WorkspaceCardVBAWithECardView from './WorkspaceCardVBAWithECardView'; -type WorkspaceCardPageProps = StackScreenProps; +type WorkspaceCardPageProps = StackScreenProps; function WorkspaceCardPage({route}: WorkspaceCardPageProps) { const {translate} = useLocalize(); diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 267b8ec52019..bceff1a70173 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -33,7 +33,7 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import * as Modal from '@userActions/Modal'; @@ -51,7 +51,7 @@ type PolicyOption = ListItem & { keyForList: string; }; -type WorkspaceCategoriesPageProps = StackScreenProps; +type WorkspaceCategoriesPageProps = StackScreenProps; function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const {shouldUseNarrowLayout} = useResponsiveLayout(); diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index 78a22c9a0cce..82818585ecec 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -25,7 +25,7 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import * as DistanceRate from '@userActions/Policy/DistanceRate'; import ButtonWithDropdownMenu from '@src/components/ButtonWithDropdownMenu'; @@ -36,7 +36,7 @@ import type {CustomUnit, Rate} from '@src/types/onyx/Policy'; type RateForList = ListItem & {value: string}; -type PolicyDistanceRatesPageProps = StackScreenProps; +type PolicyDistanceRatesPageProps = StackScreenProps; function PolicyDistanceRatesPage({ route: { diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx b/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx index 40a8239b9ab1..435b8dfd8b2e 100644 --- a/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx +++ b/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx @@ -4,14 +4,14 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import WorkspaceInvoicesNoVBAView from './WorkspaceInvoicesNoVBAView'; import WorkspaceInvoicesVBAView from './WorkspaceInvoicesVBAView'; -type WorkspaceInvoicesPageProps = StackScreenProps; +type WorkspaceInvoicesPageProps = StackScreenProps; function WorkspaceInvoicesPage({route}: WorkspaceInvoicesPageProps) { const {translate} = useLocalize(); diff --git a/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx index d929ec10748a..79b62dc25d3c 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimbursePage.tsx @@ -1,7 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import useLocalize from '@hooks/useLocalize'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; @@ -9,7 +9,7 @@ import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import WorkspaceReimburseView from './WorkspaceReimburseView'; -type WorkspaceReimbursePageProps = WithPolicyProps & StackScreenProps; +type WorkspaceReimbursePageProps = WithPolicyProps & StackScreenProps; function WorkspaceReimbursePage({route, policy}: WorkspaceReimbursePageProps) { const {translate} = useLocalize(); diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index 9eb7a594bdb5..41184d30ae85 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -31,7 +31,7 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import * as Tag from '@userActions/Policy/Tag'; @@ -43,7 +43,7 @@ import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {PolicyTag, PolicyTagList, TagListItem} from './types'; -type WorkspaceTagsPageProps = StackScreenProps; +type WorkspaceTagsPageProps = StackScreenProps; function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { const {shouldUseNarrowLayout} = useResponsiveLayout(); diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index 7730924a6522..dfb2c11cfa0d 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -29,7 +29,7 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -39,7 +39,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {TaxRate} from '@src/types/onyx'; -type WorkspaceTaxesPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; +type WorkspaceTaxesPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; function WorkspaceTaxesPage({ policy, diff --git a/src/pages/workspace/travel/WorkspaceTravelPage.tsx b/src/pages/workspace/travel/WorkspaceTravelPage.tsx index 36f7deaea0a1..7f13ab96f94f 100644 --- a/src/pages/workspace/travel/WorkspaceTravelPage.tsx +++ b/src/pages/workspace/travel/WorkspaceTravelPage.tsx @@ -4,14 +4,14 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import WorkspaceTravelNoVBAView from './WorkspaceTravelNoVBAView'; import WorkspaceTravelVBAView from './WorkspaceTravelVBAView'; -type WorkspaceTravelPageProps = StackScreenProps; +type WorkspaceTravelPageProps = StackScreenProps; function WorkspaceTravelPage({route}: WorkspaceTravelPageProps) { const {translate} = useLocalize(); diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx index c706e18c688d..cb4c9fabec5c 100644 --- a/src/pages/workspace/withPolicy.tsx +++ b/src/pages/workspace/withPolicy.tsx @@ -3,14 +3,14 @@ import type {ComponentType, ForwardedRef, RefAttributes} from 'react'; import React, {forwardRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; -import type {AuthScreensParamList, BottomTabNavigatorParamList, FullScreenNavigatorParamList, ReimbursementAccountNavigatorParamList, SettingsNavigatorParamList} from '@navigation/types'; +import type {AuthScreensParamList, BottomTabNavigatorParamList, ReimbursementAccountNavigatorParamList, SettingsNavigatorParamList, WorkspaceNavigatorParamList} from '@navigation/types'; import * as Policy from '@userActions/Policy/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -type NavigatorsParamList = BottomTabNavigatorParamList & AuthScreensParamList & SettingsNavigatorParamList & ReimbursementAccountNavigatorParamList & FullScreenNavigatorParamList; +type NavigatorsParamList = BottomTabNavigatorParamList & AuthScreensParamList & SettingsNavigatorParamList & ReimbursementAccountNavigatorParamList & WorkspaceNavigatorParamList; type PolicyRoute = RouteProp< NavigatorsParamList, diff --git a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx index d026c218910f..177e2f78467c 100644 --- a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx @@ -13,7 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as Localize from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicy from '@pages/workspace/withPolicy'; @@ -27,7 +27,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AutoReportingFrequencyKey = Exclude, 'instant'>; type Locale = ValueOf; -type WorkspaceAutoReportingFrequencyPageProps = WithPolicyOnyxProps & StackScreenProps; +type WorkspaceAutoReportingFrequencyPageProps = WithPolicyOnyxProps & StackScreenProps; type WorkspaceAutoReportingFrequencyPageItem = { text: string; diff --git a/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx b/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx index a9f96061ec0a..86b91088c030 100644 --- a/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx @@ -8,7 +8,7 @@ import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicy from '@pages/workspace/withPolicy'; @@ -21,7 +21,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; const DAYS_OF_MONTH = 28; -type WorkspaceAutoReportingMonthlyOffsetProps = WithPolicyOnyxProps & StackScreenProps; +type WorkspaceAutoReportingMonthlyOffsetProps = WithPolicyOnyxProps & StackScreenProps; type AutoReportingOffsetKeys = ValueOf; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index cc80705534b2..dc3fc9144753 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -25,7 +25,7 @@ import {getPaymentMethodDescription} from '@libs/PaymentUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import {convertPolicyEmployeesToApprovalWorkflows, INITIAL_APPROVAL_WORKFLOW} from '@libs/WorkflowUtils'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; @@ -42,7 +42,7 @@ import type {ToggleSettingOptionRowProps} from './ToggleSettingsOptionRow'; import {getAutoReportingFrequencyDisplayNames} from './WorkspaceAutoReportingFrequencyPage'; import type {AutoReportingFrequencyKey} from './WorkspaceAutoReportingFrequencyPage'; -type WorkspaceWorkflowsPageProps = WithPolicyProps & StackScreenProps; +type WorkspaceWorkflowsPageProps = WithPolicyProps & StackScreenProps; function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { const {translate, preferredLocale} = useLocalize(); From d39e496c368d891028ed9d9f16ae0ff00529dd3c Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 7 Aug 2024 08:45:46 +0200 Subject: [PATCH 07/34] Replace FullScreenNavigatorParamList with WorkspaceNavigatorParamList --- src/pages/workspace/WorkspaceProfilePage.tsx | 4 ++-- .../workspace/expensifyCard/WorkspaceCardsListLabel.tsx | 8 ++++---- .../expensifyCard/WorkspaceExpensifyCardListPage.tsx | 4 ++-- .../expensifyCard/WorkspaceExpensifyCardPage.tsx | 4 ++-- .../WorkspaceExpensifyCardPageEmptyState.tsx | 4 ++-- .../workspace/reportFields/WorkspaceReportFieldsPage.tsx | 4 ++-- .../approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx | 4 ++-- .../approvals/WorkspaceWorkflowsApprovalsCreatePage.tsx | 4 ++-- .../approvals/WorkspaceWorkflowsApprovalsEditPage.tsx | 4 ++-- .../WorkspaceWorkflowsApprovalsExpensesFromPage.tsx | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index cbd43fd17529..e99afe7ba408 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -23,7 +23,7 @@ import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import Parser from '@libs/Parser'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -45,7 +45,7 @@ type WorkspaceProfilePageOnyxProps = { currencyList: OnyxEntry; }; -type WorkspaceProfilePageProps = WithPolicyProps & WorkspaceProfilePageOnyxProps & StackScreenProps; +type WorkspaceProfilePageProps = WithPolicyProps & WorkspaceProfilePageOnyxProps & StackScreenProps; function WorkspaceProfilePage({policyDraft, policy: policyProp, currencyList = {}, route}: WorkspaceProfilePageProps) { const styles = useThemeStyles(); diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index f1075d85cfd8..685fa91ce9d0 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -1,8 +1,8 @@ import type {RouteProp} from '@react-navigation/native'; import {useRoute} from '@react-navigation/native'; -import React, {useEffect, useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; +import {useEffect, useMemo, useRef, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; @@ -20,7 +20,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import * as PolicyUtils from '@libs/PolicyUtils'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; @@ -40,7 +40,7 @@ type WorkspaceCardsListLabelProps = { }; function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelProps) { - const route = useRoute>(); + const route = useRoute>(); const policy = usePolicy(route.params.policyID); const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx index 25de151bbb6d..f2574223df48 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx @@ -17,7 +17,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import Navigation from '@navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@navigation/types'; +import type {WorkspaceNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -30,7 +30,7 @@ import WorkspaceCardListRow from './WorkspaceCardListRow'; type WorkspaceExpensifyCardListPageProps = { /** Route from navigation */ - route: RouteProp; + route: RouteProp; /** List of Expensify cards */ cardsList: OnyxEntry; diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx index b0b517a0aee0..8362c0eebd96 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx @@ -6,7 +6,7 @@ import {useOnyx} from 'react-native-onyx'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy/Policy'; @@ -16,7 +16,7 @@ import type SCREENS from '@src/SCREENS'; import WorkspaceExpensifyCardListPage from './WorkspaceExpensifyCardListPage'; import WorkspaceExpensifyCardPageEmptyState from './WorkspaceExpensifyCardPageEmptyState'; -type WorkspaceExpensifyCardPageProps = StackScreenProps; +type WorkspaceExpensifyCardPageProps = StackScreenProps; function WorkspaceExpensifyCardPage({route}: WorkspaceExpensifyCardPageProps) { const policyID = route.params.policyID ?? '-1'; diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx index 7dc6293e23ea..732f9c047589 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPageEmptyState.tsx @@ -12,7 +12,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import Navigation from '@navigation/Navigation'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -40,7 +40,7 @@ const expensifyCardFeatures: FeatureListItem[] = [ ]; type WorkspaceExpensifyCardPageEmptyStateProps = { - route: StackScreenProps['route']; + route: StackScreenProps['route']; } & WithPolicyAndFullscreenLoadingProps; function WorkspaceExpensifyCardPageEmptyState({route, policy}: WorkspaceExpensifyCardPageEmptyStateProps) { diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx index d04dcfd2d0cc..380e1c7fbe13 100644 --- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx +++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx @@ -31,7 +31,7 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as WorkspaceReportFieldUtils from '@libs/WorkspaceReportFieldUtils'; @@ -49,7 +49,7 @@ type ReportFieldForList = ListItem & { orderWeight?: number; }; -type WorkspaceReportFieldsPageProps = StackScreenProps; +type WorkspaceReportFieldsPageProps = StackScreenProps; function WorkspaceReportFieldsPage({ route: { diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx index 6125ac842537..a1a6a58142b8 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx @@ -19,7 +19,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -35,7 +35,7 @@ import type {Icon} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WorkspaceWorkflowsApprovalsApproverPageProps = WithPolicyAndFullscreenLoadingProps & - StackScreenProps; + StackScreenProps; type SelectionListApprover = { text: string; diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsCreatePage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsCreatePage.tsx index dc0f067f60df..90f10ce0cd63 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsCreatePage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsCreatePage.tsx @@ -11,7 +11,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -23,7 +23,7 @@ import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ApprovalWorkflowEditor from './ApprovalWorkflowEditor'; -type WorkspaceWorkflowsApprovalsCreatePageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; +type WorkspaceWorkflowsApprovalsCreatePageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; function WorkspaceWorkflowsApprovalsCreatePage({policy, isLoadingReportData = true, route}: WorkspaceWorkflowsApprovalsCreatePageProps) { const styles = useThemeStyles(); diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsEditPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsEditPage.tsx index 21c92f809d22..cb815b99d813 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsEditPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsEditPage.tsx @@ -12,7 +12,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import {convertPolicyEmployeesToApprovalWorkflows} from '@libs/WorkflowUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -26,7 +26,7 @@ import type ApprovalWorkflow from '@src/types/onyx/ApprovalWorkflow'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import ApprovalWorkflowEditor from './ApprovalWorkflowEditor'; -type WorkspaceWorkflowsApprovalsEditPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; +type WorkspaceWorkflowsApprovalsEditPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; function WorkspaceWorkflowsApprovalsEditPage({policy, isLoadingReportData = true, route}: WorkspaceWorkflowsApprovalsEditPageProps) { const styles = useThemeStyles(); diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx index 2c69f07ef83b..9d76dc6430e6 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx @@ -19,7 +19,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; +import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -48,7 +48,7 @@ type SelectionListMember = { type MembersSection = SectionListData>; type WorkspaceWorkflowsApprovalsExpensesFromPageProps = WithPolicyAndFullscreenLoadingProps & - StackScreenProps; + StackScreenProps; function WorkspaceWorkflowsApprovalsExpensesFromPage({policy, isLoadingReportData = true, route}: WorkspaceWorkflowsApprovalsExpensesFromPageProps) { const styles = useThemeStyles(); From b260d44921c79f15bf3c7c0562440fcbd135de79 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 7 Aug 2024 09:09:02 +0200 Subject: [PATCH 08/34] Refactor old naming, replace FullScreenNavigator with WorkspaceNavigator --- .../AppNavigator/getPartialStateDiff.ts | 23 ++++++++------- .../Navigation/getTopmostFullScreenRoute.ts | 28 ------------------- .../Navigation/getTopmostWorkspaceRoute.ts | 28 +++++++++++++++++++ .../linkingConfig/getAdaptedStateFromPath.ts | 18 ++++++------ 4 files changed, 50 insertions(+), 47 deletions(-) delete mode 100644 src/libs/Navigation/getTopmostFullScreenRoute.ts create mode 100644 src/libs/Navigation/getTopmostWorkspaceRoute.ts diff --git a/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts b/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts index cac65b833bde..974f98dccccb 100644 --- a/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts +++ b/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts @@ -1,6 +1,6 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import getTopmostFullScreenRoute from '@libs/Navigation/getTopmostFullScreenRoute'; +import getTopmostWorkspaceRoute from '@libs/Navigation/getTopmostWorkspaceRoute'; import type {Metainfo} from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; import shallowCompare from '@libs/ObjectUtils'; @@ -63,19 +63,22 @@ function getPartialStateDiff(state: State, templateState: St // This one is heuristic and may need to be improved if we will be able to navigate from modal screen with full screen in background to another modal screen with full screen in background. // For now this simple check is enough. if (metainfo.isWorkspaceNavigatorMandatory) { - const stateTopmostFullScreen = getTopmostFullScreenRoute(state); - const templateStateTopmostFullScreen = getTopmostFullScreenRoute(templateState); - const fullScreenDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1) as NavigationPartialRoute; + const stateTopmostWorkspaceRoute = getTopmostWorkspaceRoute(state); + const templateStateTopmostWorkspaceRoute = getTopmostWorkspaceRoute(templateState); + const workspaceNavDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1) as NavigationPartialRoute; if ( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (!stateTopmostFullScreen && templateStateTopmostFullScreen) || - (stateTopmostFullScreen && - templateStateTopmostFullScreen && - (stateTopmostFullScreen.name !== templateStateTopmostFullScreen.name || - !shallowCompare(stateTopmostFullScreen.params as Record | undefined, templateStateTopmostFullScreen.params as Record | undefined))) + (!stateTopmostWorkspaceRoute && templateStateTopmostWorkspaceRoute) || + (stateTopmostWorkspaceRoute && + templateStateTopmostWorkspaceRoute && + (stateTopmostWorkspaceRoute.name !== templateStateTopmostWorkspaceRoute.name || + !shallowCompare( + stateTopmostWorkspaceRoute.params as Record | undefined, + templateStateTopmostWorkspaceRoute.params as Record | undefined, + ))) ) { - diff[NAVIGATORS.WORKSPACE_NAVIGATOR] = fullScreenDiff; + diff[NAVIGATORS.WORKSPACE_NAVIGATOR] = workspaceNavDiff; } } diff --git a/src/libs/Navigation/getTopmostFullScreenRoute.ts b/src/libs/Navigation/getTopmostFullScreenRoute.ts deleted file mode 100644 index ae17b0094f42..000000000000 --- a/src/libs/Navigation/getTopmostFullScreenRoute.ts +++ /dev/null @@ -1,28 +0,0 @@ -import NAVIGATORS from '@src/NAVIGATORS'; -import type {NavigationPartialRoute, RootStackParamList, State, WorkspaceScreenName} from './types'; - -// Get the name of topmost fullscreen route in the navigation stack. -function getTopmostFullScreenRoute(state: State): NavigationPartialRoute | undefined { - if (!state) { - return; - } - - const topmostFullScreenRoute = state.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1); - - if (!topmostFullScreenRoute) { - return; - } - - if (topmostFullScreenRoute.state) { - // There will be at least one route in the fullscreen navigator. - const {name, params} = topmostFullScreenRoute.state.routes.at(-1) as NavigationPartialRoute; - - return {name, params}; - } - - if (!!topmostFullScreenRoute.params && 'screen' in topmostFullScreenRoute.params) { - return {name: topmostFullScreenRoute.params.screen as WorkspaceScreenName, params: topmostFullScreenRoute.params.params}; - } -} - -export default getTopmostFullScreenRoute; diff --git a/src/libs/Navigation/getTopmostWorkspaceRoute.ts b/src/libs/Navigation/getTopmostWorkspaceRoute.ts new file mode 100644 index 000000000000..4e9c5c607c5e --- /dev/null +++ b/src/libs/Navigation/getTopmostWorkspaceRoute.ts @@ -0,0 +1,28 @@ +import NAVIGATORS from '@src/NAVIGATORS'; +import type {NavigationPartialRoute, RootStackParamList, State, WorkspaceScreenName} from './types'; + +// Get the name of topmost workspace navigator route in the navigation stack. +function getTopmostWorkspaceRoute(state: State): NavigationPartialRoute | undefined { + if (!state) { + return; + } + + const topmostWorkspaceRoute = state.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1); + + if (!topmostWorkspaceRoute) { + return; + } + + if (topmostWorkspaceRoute.state) { + // There will be at least one route in the workspace navigator. + const {name, params} = topmostWorkspaceRoute.state.routes.at(-1) as NavigationPartialRoute; + + return {name, params}; + } + + if (!!topmostWorkspaceRoute.params && 'screen' in topmostWorkspaceRoute.params) { + return {name: topmostWorkspaceRoute.params.screen as WorkspaceScreenName, params: topmostWorkspaceRoute.params.params}; + } +} + +export default getTopmostWorkspaceRoute; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 67987404084b..a21ae3202549 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -108,7 +108,7 @@ function getParamsFromRoute(screenName: string): string[] { return route.match(/(?<=[:?&])(\w+)(?=[/=?&]|$)/g) ?? []; } -// This function will return CentralPaneNavigator route or FullScreenNavigator route. +// This function will return CentralPaneNavigator route or WorkspaceNavigator route. function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | undefined { // Check for backTo param. One screen with different backTo value may need diferent screens visible under the overlay. if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { @@ -117,7 +117,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat // eslint-disable-next-line @typescript-eslint/no-shadow const rhpNavigator = stateForBackTo.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); - const centralPaneOrFullScreenNavigator = stateForBackTo.routes.find( + const centralPaneOrWorkspaceNavigator = stateForBackTo.routes.find( // eslint-disable-next-line @typescript-eslint/no-shadow (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.WORKSPACE_NAVIGATOR, ); @@ -128,8 +128,8 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat } // If we know that backTo targets the root route (central pane or full screen) we want to use it. - if (centralPaneOrFullScreenNavigator && centralPaneOrFullScreenNavigator.state) { - return centralPaneOrFullScreenNavigator as NavigationPartialRoute; + if (centralPaneOrWorkspaceNavigator && centralPaneOrWorkspaceNavigator.state) { + return centralPaneOrWorkspaceNavigator as NavigationPartialRoute; } } } @@ -143,7 +143,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat } } - // Check for FullScreenNavigator + // Check for WorkspaceNavigator for (const [workspaceScreenName, RHPNames] of Object.entries(WORKSPACE_SCREEN_TO_RHP_MAPPING)) { if (RHPNames.includes(route.name)) { const paramsFromRoute = getParamsFromRoute(workspaceScreenName); @@ -202,8 +202,8 @@ function getAdaptedState(state: PartialState // If the root route is type of WorkspaceNavigator, the default bottom tab will be added. const matchingBottomTabRoute = getMatchingBottomTabRouteForState({routes: matchingRootRoute ? [matchingRootRoute] : []}); routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); - // When we open a screen in RHP from FullScreenNavigator, we need to add the appropriate screen in CentralPane. - // Then, when we close FullScreenNavigator, we will be redirected to the correct page in CentralPane. + // When we open a screen in RHP from WorkspaceNavigator, we need to add the appropriate screen in CentralPane. + // Then, when we close WorkspaceNavigator, we will be redirected to the correct page in CentralPane. if (matchingRootRoute?.name === NAVIGATORS.WORKSPACE_NAVIGATOR) { routes.push({name: SCREENS.SETTINGS.WORKSPACES}); } @@ -225,7 +225,7 @@ function getAdaptedState(state: PartialState // - default central pane on desktop layout // - found lhp / onboardingModalNavigator - // There is no screen in these navigators that would have mandatory central pane, bottom tab or fullscreen navigator. + // There is no screen in these navigators that would have mandatory central pane, bottom tab or workspace navigator. metainfo.isCentralPaneAndBottomTabMandatory = false; metainfo.isWorkspaceNavigatorMandatory = false; const routes = []; @@ -269,7 +269,7 @@ function getAdaptedState(state: PartialState // Routes // - default bottom tab // - default central pane on desktop layout - // - found fullscreen + // - found workspace navigator const routes = []; routes.push( From d51e458084b7f6550944e894bc2b7a7bd8d8c6f3 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 25 Jul 2024 12:02:04 +0200 Subject: [PATCH 09/34] Add BottomTabBar to WorkspaceInitialPage and SettingsWorkspaces --- .../Navigation/AppNavigator/AuthScreens.tsx | 8 ++++- .../Navigators/WorkspaceNavigator.tsx | 31 ++++++++++--------- src/pages/home/sidebar/BottomTabAvatar.tsx | 12 ++++++- src/pages/workspace/WorkspaceInitialPage.tsx | 4 ++- src/pages/workspace/WorkspacesListPage.tsx | 3 ++ 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index d9df7750401d..a242ae11dfa6 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -547,13 +547,19 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie /> {Object.entries(CENTRAL_PANE_SCREENS).map(([screenName, componentGetter]) => { const centralPaneName = screenName as CentralPaneName; + const options = {...CentralPaneScreenOptions}; + + if (centralPaneName === SCREENS.SETTINGS.WORKSPACES) { + options.animationEnabled = false; + } + return ( ); })} diff --git a/src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx index 844814d38bf1..3e9464e18ff9 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator'; import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; @@ -33,22 +34,24 @@ const CENTRAL_PANE_WORKSPACE_SCREENS = { function WorkspaceNavigator() { return ( - - - {Object.entries(CENTRAL_PANE_WORKSPACE_SCREENS).map(([screenName, componentGetter]) => ( + + - ))} - + {Object.entries(CENTRAL_PANE_WORKSPACE_SCREENS).map(([screenName, componentGetter]) => ( + + ))} + + ); } diff --git a/src/pages/home/sidebar/BottomTabAvatar.tsx b/src/pages/home/sidebar/BottomTabAvatar.tsx index 0a798389517b..7d1ad31e1277 100644 --- a/src/pages/home/sidebar/BottomTabAvatar.tsx +++ b/src/pages/home/sidebar/BottomTabAvatar.tsx @@ -1,15 +1,18 @@ +import {useRoute} from '@react-navigation/native'; import React, {useCallback} from 'react'; import {useOnyx} from 'react-native-onyx'; import {PressableWithFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import AvatarWithDelegateAvatar from './AvatarWithDelegateAvatar'; import AvatarWithOptionalStatus from './AvatarWithOptionalStatus'; import ProfileAvatarWithIndicator from './ProfileAvatarWithIndicator'; @@ -27,8 +30,10 @@ function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomT const {translate} = useLocalize(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const delegateEmail = account?.delegatedAccess?.delegate ?? ''; + const route = useRoute(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const emojiStatus = currentUserPersonalDetails?.status?.emojiCode ?? ''; + const {shouldUseNarrowLayout} = useResponsiveLayout(); const showSettingsPage = useCallback(() => { if (isCreateMenuOpen) { @@ -36,8 +41,13 @@ function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomT return; } + if (([SCREENS.SETTINGS.WORKSPACES, SCREENS.WORKSPACE.INITIAL] as string[]).includes(route.name) && shouldUseNarrowLayout) { + Navigation.goBack(ROUTES.SETTINGS); + return; + } + interceptAnonymousUser(() => Navigation.navigate(ROUTES.SETTINGS)); - }, [isCreateMenuOpen]); + }, [isCreateMenuOpen, shouldUseNarrowLayout, route.name]); let children; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 588d4f7e63d2..ece2311b2d58 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -22,6 +22,7 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {isConnectionInProgress} from '@libs/actions/connections'; +import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -354,7 +355,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyCategories return ( + ); diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index c2a47e536d43..ef5ed12273b3 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -28,6 +28,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {isConnectionInProgress} from '@libs/actions/connections'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import localeCompare from '@libs/LocaleCompare'; +import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -38,6 +39,7 @@ import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type {Policy as PolicyType, ReimbursementAccount, Report, Session as SessionType} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -446,6 +448,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: cancelText={translate('common.cancel')} danger /> + {isSmallScreenWidth && } ); } From 531577f740a1dfab484a5db210cb2e44b3abea82 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 25 Jul 2024 14:50:48 +0200 Subject: [PATCH 10/34] Fix pressing settings tab when navigating deeper into workspaces --- src/pages/home/sidebar/BottomTabAvatar.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/BottomTabAvatar.tsx b/src/pages/home/sidebar/BottomTabAvatar.tsx index 7d1ad31e1277..f7f601960627 100644 --- a/src/pages/home/sidebar/BottomTabAvatar.tsx +++ b/src/pages/home/sidebar/BottomTabAvatar.tsx @@ -41,7 +41,13 @@ function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomT return; } - if (([SCREENS.SETTINGS.WORKSPACES, SCREENS.WORKSPACE.INITIAL] as string[]).includes(route.name) && shouldUseNarrowLayout) { + if (route.name === SCREENS.SETTINGS.WORKSPACES && shouldUseNarrowLayout) { + Navigation.goBack(ROUTES.SETTINGS); + return; + } + + if (route.name === SCREENS.WORKSPACE.INITIAL) { + Navigation.dismissModal(); Navigation.goBack(ROUTES.SETTINGS); return; } From 4b0d5489b947adf0d4476ee6c0f40953a05abb54 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 25 Jul 2024 15:01:32 +0200 Subject: [PATCH 11/34] Turn off animation for FullScreenNavigator --- .../Navigation/AppNavigator/getRootNavigatorScreenOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts index e09f85936385..a59a339c0c6a 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts @@ -107,7 +107,7 @@ const getRootNavigatorScreenOptions: GetRootNavigatorScreenOptions = (isSmallScr }, // We need to turn off animation for the full screen to avoid delay when closing screens. - animationEnabled: isSmallScreenWidth, + animationEnabled: false, }, centralPaneNavigator: { From 24dda4db86a7291c8bb28f346b0ec6a6858ccd97 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 6 Aug 2024 18:17:55 +0200 Subject: [PATCH 12/34] use shouldUseNarrowLayout instead of isSmallScreenWidth --- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index ef5ed12273b3..2aa85f295294 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -448,7 +448,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: cancelText={translate('common.cancel')} danger /> - {isSmallScreenWidth && } + {shouldUseNarrowLayout && } ); } From a84d53acfe65c3a59ab078fe966954c261c6e2b4 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 6 Aug 2024 18:18:37 +0200 Subject: [PATCH 13/34] fix pressing on settings tab while in workspace navigator --- src/pages/home/sidebar/BottomTabAvatar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/sidebar/BottomTabAvatar.tsx b/src/pages/home/sidebar/BottomTabAvatar.tsx index f7f601960627..8e2f4822aabc 100644 --- a/src/pages/home/sidebar/BottomTabAvatar.tsx +++ b/src/pages/home/sidebar/BottomTabAvatar.tsx @@ -48,7 +48,6 @@ function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomT if (route.name === SCREENS.WORKSPACE.INITIAL) { Navigation.dismissModal(); - Navigation.goBack(ROUTES.SETTINGS); return; } From fb08eb76e291e8e1ea16da011314009506a37db4 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 3 Sep 2024 12:33:23 +0200 Subject: [PATCH 14/34] Add todo comments for new navigation logic, adjust split navigators, adjust getAdaptedState --- ...on+stack+6.3.29+002+dontDetachScreen.patch | 2 +- src/NAVIGATORS.ts | 4 +- src/components/Search/index.tsx | 5 +- .../Navigation/AppNavigator/AuthScreens.tsx | 32 ++- .../AppNavigator/CENTRAL_PANE_SCREENS.tsx | 22 -- .../Navigators/ReportsSplitNavigator.tsx | 67 +++++ .../Navigators/SettingsSplitNavigator.tsx | 53 ++++ ...igator.tsx => WorkspaceSplitNavigator.tsx} | 16 +- .../CustomRouter.ts | 234 ++++++++++------- .../createCustomStackNavigator/index.tsx | 90 +------ .../SplitStackRouter.ts | 21 +- src/libs/Navigation/Navigation.ts | 3 +- .../CENTRAL_PANE_TO_RHP_MAPPING.ts | 6 +- .../TAB_TO_CENTRAL_PANE_MAPPING.ts | 27 +- src/libs/Navigation/linkingConfig/config.ts | 131 +++++++--- .../linkingConfig/getAdaptedStateFromPath.ts | 212 ++++++++-------- src/libs/Navigation/linkingConfig/index.ts | 2 +- .../getActionForBottomTabNavigator.ts | 50 ++++ .../Navigation/newLinkTo/getMinimalAction.ts | 42 +++ src/libs/Navigation/newLinkTo/index.ts | 239 ++++++++++++++++++ src/libs/Navigation/newLinkTo/types.ts | 11 + src/libs/Navigation/types.ts | 63 ++++- src/libs/ObjectUtils.ts | 13 +- src/libs/actions/Session/index.ts | 1 + src/pages/Search/SearchPage.tsx | 51 +++- src/pages/WorkspaceSwitcherPage/index.tsx | 7 +- .../SidebarScreen/BaseSidebarScreen.tsx | 3 + .../home/sidebar/SidebarScreen/index.tsx | 9 +- src/pages/settings/InitialSettingsPage.tsx | 3 + src/styles/index.ts | 13 + 30 files changed, 1021 insertions(+), 411 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/CENTRAL_PANE_SCREENS.tsx create mode 100644 src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx create mode 100644 src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx rename src/libs/Navigation/AppNavigator/Navigators/{WorkspaceNavigator.tsx => WorkspaceSplitNavigator.tsx} (94%) create mode 100644 src/libs/Navigation/newLinkTo/getActionForBottomTabNavigator.ts create mode 100644 src/libs/Navigation/newLinkTo/getMinimalAction.ts create mode 100644 src/libs/Navigation/newLinkTo/index.ts create mode 100644 src/libs/Navigation/newLinkTo/types.ts diff --git a/patches/@react-navigation+stack+6.3.29+002+dontDetachScreen.patch b/patches/@react-navigation+stack+6.3.29+002+dontDetachScreen.patch index c65ebbb98007..529e9aab0221 100644 --- a/patches/@react-navigation+stack+6.3.29+002+dontDetachScreen.patch +++ b/patches/@react-navigation+stack+6.3.29+002+dontDetachScreen.patch @@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644 }) : STATE_TRANSITIONING_OR_BELOW_TOP; } + -+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial') && isScreenActive !== STATE_ON_TOP; ++ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial' || route.name === 'Home' || route.name === 'Search_Bottom_Tab' || route.name === 'Settings_Root') && isScreenActive !== STATE_ON_TOP; + const { headerShown = true, diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index cb829856da7f..c6f014f24e72 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -12,5 +12,7 @@ export default { WELCOME_VIDEO_MODAL_NAVIGATOR: 'WelcomeVideoModalNavigator', EXPLANATION_MODAL_NAVIGATOR: 'ExplanationModalNavigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', - WORKSPACE_NAVIGATOR: 'WorkspaceNavigator', + REPORTS_SPLIT_NAVIGATOR: 'ReportsSplitNavigator', + SETTINGS_SPLIT_NAVIGATOR: 'SettingsSplitNavigator', + WORKSPACE_SPLIT_NAVIGATOR: 'WorkspaceSplitNavigator', } as const; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4e23c0883f84..766d4c2445ee 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,6 +1,7 @@ import {useNavigation} from '@react-navigation/native'; import type {StackNavigationProp} from '@react-navigation/stack'; import React, {useCallback, useEffect, useRef, useState} from 'react'; +import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; @@ -338,7 +339,7 @@ function Search({queryJSON}: SearchProps) { const shouldShowSorting = sortableSearchStatuses.includes(status); return ( - <> + setDownloadErrorModalVisible(false)} /> - + ); } diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index a242ae11dfa6..ba323f9fd31f 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -28,6 +28,7 @@ import {buildSearchQueryString} from '@libs/SearchUtils'; import * as SessionUtils from '@libs/SessionUtils'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import SearchPage from '@pages/Search/SearchPage'; import DesktopSignInRedirectPage from '@pages/signin/DesktopSignInRedirectPage'; import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; @@ -48,18 +49,18 @@ import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; -import CENTRAL_PANE_SCREENS from './CENTRAL_PANE_SCREENS'; import createCustomStackNavigator from './createCustomStackNavigator'; import defaultScreenOptions from './defaultScreenOptions'; import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; -import BottomTabNavigator from './Navigators/BottomTabNavigator'; import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator'; import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator'; import LeftModalNavigator from './Navigators/LeftModalNavigator'; import OnboardingModalNavigator from './Navigators/OnboardingModalNavigator'; +import ReportsSplitNavigator from './Navigators/ReportsSplitNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; +import SettingsSplitNavigator from './Navigators/SettingsSplitNavigator'; import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator'; -import WorkspaceNavigator from './Navigators/WorkspaceNavigator'; +import WorkspaceSplitNavigator from './Navigators/WorkspaceSplitNavigator'; type AuthScreensProps = { /** Session of currently logged in user */ @@ -398,10 +399,21 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie screenOptions={screenOptions.centralPaneNavigator} isSmallScreenWidth={isSmallScreenWidth} > + {/* This have to be the first navigator in auth screens. */} + + - {Object.entries(CENTRAL_PANE_SCREENS).map(([screenName, componentGetter]) => { + {/* {Object.entries(CENTRAL_PANE_SCREENS).map(([screenName, componentGetter]) => { const centralPaneName = screenName as CentralPaneName; const options = {...CentralPaneScreenOptions}; @@ -562,7 +574,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie options={options} /> ); - })} + })} */} diff --git a/src/libs/Navigation/AppNavigator/CENTRAL_PANE_SCREENS.tsx b/src/libs/Navigation/AppNavigator/CENTRAL_PANE_SCREENS.tsx deleted file mode 100644 index 5bbe2046040a..000000000000 --- a/src/libs/Navigation/AppNavigator/CENTRAL_PANE_SCREENS.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type {CentralPaneName} from '@libs/Navigation/types'; -import withPrepareCentralPaneScreen from '@src/components/withPrepareCentralPaneScreen'; -import SCREENS from '@src/SCREENS'; -import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; - -type Screens = Partial React.ComponentType>>; - -const CENTRAL_PANE_SCREENS = { - [SCREENS.SETTINGS.WORKSPACES]: withPrepareCentralPaneScreen(() => require('../../../pages/workspace/WorkspacesListPage').default), - [SCREENS.SETTINGS.PREFERENCES.ROOT]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/Preferences/PreferencesPage').default), - [SCREENS.SETTINGS.SECURITY]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/Security/SecuritySettingsPage').default), - [SCREENS.SETTINGS.PROFILE.ROOT]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/Profile/ProfilePage').default), - [SCREENS.SETTINGS.WALLET.ROOT]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/Wallet/WalletPage').default), - [SCREENS.SETTINGS.ABOUT]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/AboutPage/AboutPage').default), - [SCREENS.SETTINGS.TROUBLESHOOT]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/Troubleshoot/TroubleshootPage').default), - [SCREENS.SETTINGS.SAVE_THE_WORLD]: withPrepareCentralPaneScreen(() => require('../../../pages/TeachersUnite/SaveTheWorldPage').default), - [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: withPrepareCentralPaneScreen(() => require('../../../pages/settings/Subscription/SubscriptionSettingsPage').default), - [SCREENS.SEARCH.CENTRAL_PANE]: withPrepareCentralPaneScreen(() => require('../../../pages/Search/SearchPage').default), - [SCREENS.REPORT]: withPrepareCentralPaneScreen(() => require('../../../pages/home/ReportScreen').default), -} satisfies Screens; - -export default CENTRAL_PANE_SCREENS; diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx new file mode 100644 index 000000000000..2ad1d7a480c9 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -0,0 +1,67 @@ +import React, {useRef} from 'react'; +import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; +import withPrepareCentralPaneScreen from '@components/withPrepareCentralPaneScreen'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; +import usePermissions from '@hooks/usePermissions'; +import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator'; +import getCurrentUrl from '@libs/Navigation/currentUrl'; +import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types'; +import * as ReportUtils from '@libs/ReportUtils'; +import SidebarScreen from '@pages/home/sidebar/SidebarScreen'; +import CONST from '@src/CONST'; +import SCREENS from '@src/SCREENS'; +import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; + +const loadReportScreen = withPrepareCentralPaneScreen(() => require('../../../../pages/home/ReportScreen').default); + +const Stack = createSplitStackNavigator(); + +function shouldOpenOnAdminRoom() { + const url = getCurrentUrl(); + return url ? new URL(url).searchParams.get('openOnAdminRoom') === 'true' : false; +} + +function ReportsSplitNavigator() { + const {canUseDefaultRooms} = usePermissions(); + const {activeWorkspaceID} = useActiveWorkspace(); + + let initialReportID: string | undefined; + const isInitialRender = useRef(true); + + if (isInitialRender.current) { + const currentURL = getCurrentUrl(); + if (currentURL) { + initialReportID = new URL(currentURL).pathname.match(CONST.REGEX.REPORT_ID_FROM_PATH)?.at(1); + } + + if (!initialReportID) { + const initialReport = ReportUtils.findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom(), activeWorkspaceID); + initialReportID = initialReport?.reportID ?? ''; + } + + isInitialRender.current = false; + } + + return ( + + + + + + + ); +} + +ReportsSplitNavigator.displayName = 'ReportsSplitNavigator'; + +export default ReportsSplitNavigator; diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx new file mode 100644 index 000000000000..62a1af45f10b --- /dev/null +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; +import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator'; +import type {SettingsSplitNavigatorParamList} from '@libs/Navigation/types'; +import withPrepareCentralPaneScreen from '@src/components/withPrepareCentralPaneScreen'; +import SCREENS from '@src/SCREENS'; +import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; + +const loadInitialSettingsPage = () => require('../../../../pages/settings/InitialSettingsPage').default; + +type Screens = Partial React.ComponentType>>; + +const CENTRAL_PANE_SETTINGS_SCREENS = { + [SCREENS.SETTINGS.WORKSPACES]: withPrepareCentralPaneScreen(() => require('../../../../pages/workspace/WorkspacesListPage').default), + [SCREENS.SETTINGS.PREFERENCES.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Preferences/PreferencesPage').default), + [SCREENS.SETTINGS.SECURITY]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Security/SecuritySettingsPage').default), + [SCREENS.SETTINGS.PROFILE.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Profile/ProfilePage').default), + [SCREENS.SETTINGS.WALLET.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Wallet/WalletPage').default), + [SCREENS.SETTINGS.ABOUT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/AboutPage/AboutPage').default), + [SCREENS.SETTINGS.TROUBLESHOOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Troubleshoot/TroubleshootPage').default), + [SCREENS.SETTINGS.SAVE_THE_WORLD]: withPrepareCentralPaneScreen(() => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default), + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Subscription/SubscriptionSettingsPage').default), +} satisfies Screens; + +const Stack = createSplitStackNavigator(); + +function SettingsSplitNavigator() { + return ( + + + + {Object.entries(CENTRAL_PANE_SETTINGS_SCREENS).map(([screenName, componentGetter]) => ( + + ))} + + + ); +} + +SettingsSplitNavigator.displayName = 'SettingsSplitNavigator'; + +export {CENTRAL_PANE_SETTINGS_SCREENS}; +export default SettingsSplitNavigator; diff --git a/src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/WorkspaceSplitNavigator.tsx similarity index 94% rename from src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx rename to src/libs/Navigation/AppNavigator/Navigators/WorkspaceSplitNavigator.tsx index 3e9464e18ff9..78ac25fabe2d 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/WorkspaceNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/WorkspaceSplitNavigator.tsx @@ -5,12 +5,10 @@ import type {WorkspaceNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; -const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default; - -const RootStack = createSplitStackNavigator(); - type Screens = Partial React.ComponentType>>; +const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default; + const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.PROFILE]: () => require('../../../../pages/workspace/WorkspaceProfilePage').default, [SCREENS.WORKSPACE.CARD]: () => require('../../../../pages/workspace/card/WorkspaceCardPage').default, @@ -32,25 +30,27 @@ const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.RULES]: () => require('../../../../pages/workspace/rules/PolicyRulesPage').default, } satisfies Screens; +const Stack = createSplitStackNavigator(); + function WorkspaceNavigator() { return ( - - {Object.entries(CENTRAL_PANE_WORKSPACE_SCREENS).map(([screenName, componentGetter]) => ( - ))} - + ); } diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index ff65b3caf990..62517376798b 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -1,105 +1,18 @@ import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigationState} from '@react-navigation/native'; import {findFocusedRoute, getPathFromState, StackRouter} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/routers'; -import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import * as Localize from '@libs/Localize'; -import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; -import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; +import isSideModalNavigator from '@libs/Navigation/isSideModalNavigator'; import linkingConfig from '@libs/Navigation/linkingConfig'; -import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; -import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; -import {isCentralPaneName, isOnboardingFlowName} from '@libs/NavigationUtils'; +import {RootStackParamList} from '@libs/Navigation/types'; +import {isOnboardingFlowName} from '@libs/NavigationUtils'; +import * as SearchUtils from '@libs/SearchUtils'; import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; -function insertRootRoute(state: State, routeToInsert: NavigationPartialRoute) { - const nonModalRoutes = state.routes.filter((route) => route.name !== NAVIGATORS.RIGHT_MODAL_NAVIGATOR && route.name !== NAVIGATORS.LEFT_MODAL_NAVIGATOR); - const modalRoutes = state.routes.filter((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || route.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR); - - // It's safe to modify this state before returning in getRehydratedState. - - // @ts-expect-error Updating read only property - // noinspection JSConstantReassignment - state.routes = [...nonModalRoutes, routeToInsert, ...modalRoutes]; // eslint-disable-line - - // @ts-expect-error Updating read only property - // noinspection JSConstantReassignment - state.index = state.routes.length - 1; // eslint-disable-line - - // @ts-expect-error Updating read only property - // noinspection JSConstantReassignment - state.stale = true; // eslint-disable-line -} - -function compareAndAdaptState(state: StackNavigationState) { - // 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 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. - if (topmostBottomTabRoute && !isNarrowLayout) { - const fullScreenRoute = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); - - // If there is fullScreenRoute we don't need to add anything. - if (fullScreenRoute) { - return; - } - - // 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 (!templateState) { - return; - } - - const templateFullScreenRoute = templateState.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); - - // If templateFullScreenRoute is defined, and full screen route is not in the state, we need to add it. - if (templateFullScreenRoute) { - insertRootRoute(state, templateFullScreenRoute); - return; - } - - const topmostCentralPaneRoute = state.routes.filter((route) => isCentralPaneName(route.name)).at(-1); - const templateCentralPaneRoute = templateState.routes.find((route) => isCentralPaneName(route.name)); - - const topmostCentralPaneRouteExtracted = getTopmostCentralPaneRoute(state); - const templateCentralPaneRouteExtracted = getTopmostCentralPaneRoute(templateState as State); - - // If there is no templateCentralPaneRoute, we don't have anything to add. - if (!templateCentralPaneRoute) { - return; - } - - // If there is no topmostCentralPaneRoute in the state and template state has one, we need to add it. - if (!topmostCentralPaneRoute) { - insertRootRoute(state, templateCentralPaneRoute); - return; - } - - // If there is central pane route in state and template state has one, we need to check if they are the same. - if (topmostCentralPaneRouteExtracted && templateCentralPaneRouteExtracted && topmostCentralPaneRouteExtracted.name !== templateCentralPaneRouteExtracted.name) { - // Not every RHP screen has matching central pane defined. In that case we use the REPORT screen as default for initial screen. - // But we don't want to override the central pane for those screens as they may be opened with different central panes under the overlay. - // e.g. i-know-a-teacher may be opened with different central panes under the overlay - if (templateCentralPaneRouteExtracted.name === SCREENS.REPORT) { - return; - } - insertRootRoute(state, templateCentralPaneRoute); - } - } -} - function shouldPreventReset(state: StackNavigationState, action: CommonActions.Action | StackActionType) { if (action.type !== CONST.NAVIGATION_ACTIONS.RESET || !action?.payload) { return false; @@ -117,23 +30,150 @@ function shouldPreventReset(state: StackNavigationState, action: } } +function shouldDismissSideModalNavigator(state: StackNavigationState, action: CommonActions.Action | StackActionType) { + if (action.type !== CONST.NAVIGATION.ACTION_TYPE.PUSH) { + return false; + } + + const lastRoute = state.routes.at(-1); + + // If the last route is a side modal navigator and the generated minimal action want's to push a new side modal navigator that means they are different ones. + // We want to dismiss the one that is currently on the top. + if (isSideModalNavigator(lastRoute?.name) && isSideModalNavigator(action.payload.name)) { + return true; + } + + return false; +} + +const SCREENS_WITH_WORKSPACE_SWITCHER: string[] = [SCREENS.SEARCH.CENTRAL_PANE, NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]; + +// function isPushingScreenWithWorkspaceSwitcher(action: CommonActions.Action | StackActionType): action is StackActionType | {type: 'PUSH'; payload: any} { +// if (action.type !== CONST.NAVIGATION.ACTION_TYPE.PUSH) { +// return false; +// } + +// // Only these screens have the workspace switcher. +// if (SCREENS_WITH_WORKSPACE_SWITCHER.includes(action.payload.name)) { +// return true; +// } + +// return false; +// } + +// eslint-disable-next-line rulesdir/no-inline-named-export +type CustomRootStackActionType = { + type: 'SWITCH_POLICY_ID'; + payload: {policyID: string}; +}; + +// function extractPolicyIDFromState(state: StackNavigationState) { +// for (let i = state.routes.length - 1; i >= 0; i--) { +// const route = state.routes[i]; +// if (route.name === SCREENS.SEARCH.CENTRAL_PANE) { +// return extractPolicyIDFromQuery(route as NavigationPartialRoute); +// } +// if (route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { +// const params = route.params ? (route.params as RootStackParamList[typeof NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]) : undefined; +// return params?.policyID; +// } +// } +// } + function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); return { ...stackRouter, - getRehydratedState(partialState: StackNavigationState, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions): StackNavigationState { - compareAndAdaptState(partialState); - const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); - return state; - }, - getStateForAction(state: StackNavigationState, action: CommonActions.Action | StackActionType, configOptions: RouterConfigOptions) { + getStateForAction(state: StackNavigationState, action: CommonActions.Action | StackActionType | CustomRootStackActionType, configOptions: RouterConfigOptions) { + // TODO: Put this into const; + if (action.type === 'SWITCH_POLICY_ID') { + const lastRoute = state.routes.at(-1); + if (lastRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { + const currentParams = lastRoute.params as RootStackParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; + const queryJSON = SearchUtils.buildSearchQueryJSON(currentParams.q); + let newParams = null; + + if (!queryJSON) { + return; + } + + if (action.payload.policyID) { + queryJSON.policyID = action.payload.policyID; + newParams = {...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON)}; + } else { + delete queryJSON.policyID; + newParams = {...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON)}; + } + + const newRoutes = [...state.routes, {...lastRoute, params: newParams}]; + return {...state, routes: newRoutes, index: newRoutes.length - 1}; + } + if (lastRoute?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { + // TODO: handle change policy id of reports navigator. + return; + } + // In other cases, do nothing. + return null; + } + + // Don't let the user navigate back to a non-onboarding screen if they are currently on an onboarding screen and it's not finished. if (shouldPreventReset(state, action)) { return state; } + + // TODO: I don't remember if the code below makes sense Wojtek :D but it's possible. + // One part seems redundant to the lines 102-108 above + // + // Copy existing policyID to the new screen. + // TODO: Can't figure out the types for + // if (action.type === 'PUSH' && isPushingScreenWithWorkspaceSwitcher(action)) { + // const policyID = extractPolicyIDFromState(state); + + // // If there is no policyID, continue with the default behavior. + // if (!policyID) { + // return stackRouter.getStateForAction(state, action, configOptions); + // } + + // // If there is, we need to add it to the new screen. + // // + // const modifiedAction = {...action}; + + // if (action.payload.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { + // modifiedAction.payload.params = {...action.payload.params, policyID}; + // } + + // if (action.payload.name === SCREENS.SEARCH.CENTRAL_PANE) { + // if (policyID) { + // const queryJSON = SearchUtils.buildSearchQueryJSON(params.q); + // if (queryJSON) { + // queryJSON.policyID = policyID; + // params.q = SearchUtils.buildSearchQueryString(queryJSON); + // } + // } else { + // const queryJSON = SearchUtils.buildSearchQueryJSON(params.q); + // if (queryJSON) { + // delete queryJSON.policyID; + // params.q = SearchUtils.buildSearchQueryString(queryJSON); + // } + // } + // action.payload.params = {...action.payload.params, policyID: extractPolicyIDFromState(state)}; + // } + + // // This shouldn't happen, but if it does, we don't want to break the app. + // console.error('Unhandled screen with workspace switcher:', action.payload.name); + // return; + // } + + if (shouldDismissSideModalNavigator(state, action)) { + const modifiedState = {...state, routes: state.routes.slice(0, -1), index: state.index !== 0 ? state.index - 1 : 0}; + return stackRouter.getStateForAction(modifiedState, action, configOptions); + } + return stackRouter.getStateForAction(state, action, configOptions); }, }; } export default CustomRouter; +export type {CustomRootStackActionType}; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index a37a102ff8a3..af4407af6dc2 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -1,44 +1,13 @@ -import type {ParamListBase, RouteProp, StackActionHelpers, StackNavigationState} from '@react-navigation/native'; +import type {ParamListBase, StackActionHelpers, StackNavigationState} from '@react-navigation/native'; import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/native'; import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; import {StackView} from '@react-navigation/stack'; -import React, {useEffect, useMemo} from 'react'; -import {View} from 'react-native'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useThemeStyles from '@hooks/useThemeStyles'; -import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import navigationRef from '@libs/Navigation/navigationRef'; -import type {RootStackParamList, State} from '@libs/Navigation/types'; -import {isCentralPaneName} from '@libs/NavigationUtils'; -import SCREENS from '@src/SCREENS'; +import React, {useMemo} from 'react'; import CustomRouter from './CustomRouter'; import type {ResponsiveStackNavigatorProps, ResponsiveStackNavigatorRouterOptions} from './types'; -type Routes = StackNavigationState['routes']; -function reduceCentralPaneRoutes(routes: Routes): Routes { - const result: Routes = []; - let count = 0; - const reverseRoutes = [...routes].reverse(); - - reverseRoutes.forEach((route) => { - if (isCentralPaneName(route.name)) { - // Remove all central pane routes except the last 3. This will improve performance. - if (count < 3) { - result.push(route); - count++; - } - } else { - result.push(route); - } - }); - - return result.reverse(); -} - function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const styles = useThemeStyles(); - + // TODO: This part needs some TS magic to work. const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< StackNavigationState, ResponsiveStackNavigatorRouterOptions, @@ -51,54 +20,14 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { initialRouteName: props.initialRouteName, }); - useEffect(() => { - if (!navigationRef.isReady()) { - return; - } - navigationRef.resetRoot(navigationRef.getRootState()); - }, [shouldUseNarrowLayout]); - - const {stateToRender, searchRoute} = useMemo(() => { - const routes = reduceCentralPaneRoutes(state.routes); - - if (shouldUseNarrowLayout) { - const isSearchCentralPane = (route: RouteProp) => getTopmostCentralPaneRoute({routes: [route]} as State)?.name === SCREENS.SEARCH.CENTRAL_PANE; - - const lastRoute = routes[routes.length - 1]; - const lastSearchCentralPane = isSearchCentralPane(lastRoute) ? lastRoute : undefined; - const filteredRoutes = routes.filter((route) => !isSearchCentralPane(route)); - - // On narrow layout, if we are on /search route we want to hide all central pane routes and show only the bottom tab navigator. - if (lastSearchCentralPane) { - return { - stateToRender: { - ...state, - index: 0, - routes: [filteredRoutes[0]], - }, - searchRoute: lastSearchCentralPane, - }; - } - - return { - stateToRender: { - ...state, - index: filteredRoutes.length - 1, - routes: filteredRoutes, - }, - searchRoute: undefined, - }; - } - + const stateToRender = useMemo(() => { + const routes = state.routes.slice(-3); return { - stateToRender: { - ...state, - index: routes.length - 1, - routes: [...routes], - }, - searchRoute: undefined, + ...state, + routes, + index: routes.length - 1, }; - }, [state, shouldUseNarrowLayout]); + }, [state]); return ( @@ -109,7 +38,6 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { descriptors={descriptors} navigation={navigation} /> - {searchRoute && {descriptors[searchRoute.key].render()}} ); } diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts index 7b0e5bbef79f..dbb9980acede 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts @@ -1,4 +1,4 @@ -import type {ParamListBase, PartialState, RouterConfigOptions, StackNavigationState} from '@react-navigation/native'; +import type {CommonActions, ParamListBase, PartialState, RouterConfigOptions, StackActionType, StackNavigationState} from '@react-navigation/native'; import {StackRouter} from '@react-navigation/native'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import type {SplitStackNavigatorRouterOptions} from './types'; @@ -47,10 +47,29 @@ function adaptStateIfNecessary(state: StackState, sidebarScreen: string, default } } +function isPushingSidebarOnCentralPane(state: StackState, action: CommonActions.Action | StackActionType, options: SplitStackNavigatorRouterOptions) { + if (action.type === 'PUSH' && action.payload.name === options.sidebarScreen && state.routes.length > 1) { + return true; + } + return false; +} + function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); return { ...stackRouter, + getStateForAction(state: StackNavigationState, action: CommonActions.Action | StackActionType, configOptions: RouterConfigOptions) { + if (isPushingSidebarOnCentralPane(state, action, options)) { + if (getIsNarrowLayout()) { + // TODO: It's possible that it's better to push whole new SplitNavigator in such case. Not sure yet. + // Pop to top on narrow layout. + return {...state, routes: [state.routes.at(0)], index: 0}; + } + // On wide screen do nothing as we want to keep the central pane screen and the sidebar is visible. + return state; + } + return stackRouter.getStateForAction(state, action, configOptions); + }, getInitialState({routeNames, routeParamList, routeGetIdList}: RouterConfigOptions) { const initialState = stackRouter.getInitialState({routeNames, routeParamList, routeGetIdList}); adaptStateIfNecessary(initialState, options.sidebarScreen, options.defaultCentralScreen); diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 4fa7fda3210d..1ea5822a1333 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -22,8 +22,8 @@ import originalGetTopmostReportActionId from './getTopmostReportActionID'; import originalGetTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; import getMatchingBottomTabRouteForState from './linkingConfig/getMatchingBottomTabRouteForState'; -import linkTo from './linkTo'; import navigationRef from './navigationRef'; +import linkTo from './newLinkTo'; import setNavigationActionToMicrotaskQueue from './setNavigationActionToMicrotaskQueue'; import switchPolicyID from './switchPolicyID'; import type {NavigationStateRoute, RootStackParamList, State, StateOrRoute, SwitchPolicyIDParams} from './types'; @@ -186,6 +186,7 @@ function navigate(route: Route = ROUTES.HOME, type?: string) { pendingRoute = route; return; } + // linkTo(navigationRef.current, route, type, isActiveRoute(route)); linkTo(navigationRef.current, route, type, isActiveRoute(route)); } diff --git a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts index e679631dfbbc..80c588b72365 100755 --- a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts @@ -1,7 +1,8 @@ -import type {CentralPaneName} from '@libs/Navigation/types'; +import type {SplitNavigatorScreenName} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; +import WORKSPACE_SCREEN_TO_RHP_MAPPING from './WORKSPACE_SCREEN_TO_RHP_MAPPING'; -const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = { +const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = { [SCREENS.SETTINGS.PROFILE.ROOT]: [ SCREENS.SETTINGS.PROFILE.DISPLAY_NAME, SCREENS.SETTINGS.PROFILE.CONTACT_METHODS, @@ -77,6 +78,7 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY, SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_PAYMENT_CURRENCY, ], + ...WORKSPACE_SCREEN_TO_RHP_MAPPING, }; export default CENTRAL_PANE_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts index a68959ae7d0f..653c26e06e1c 100755 --- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -1,9 +1,8 @@ -import type {BottomTabName, CentralPaneName} from '@navigation/types'; +import type {BottomTabName, CentralPaneName, SplitNavigatorScreenName} from '@navigation/types'; import SCREENS from '@src/SCREENS'; -const TAB_TO_CENTRAL_PANE_MAPPING: Record = { +const TAB_TO_CENTRAL_PANE_MAPPING: Record = { [SCREENS.HOME]: [SCREENS.REPORT], - [SCREENS.SEARCH.BOTTOM_TAB]: [SCREENS.SEARCH.CENTRAL_PANE], [SCREENS.SETTINGS.ROOT]: [ SCREENS.SETTINGS.PROFILE.ROOT, SCREENS.SETTINGS.PREFERENCES.ROOT, @@ -15,19 +14,33 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = { SCREENS.SETTINGS.TROUBLESHOOT, SCREENS.SETTINGS.SUBSCRIPTION.ROOT, ], + [SCREENS.WORKSPACE.INITIAL]: [ + SCREENS.WORKSPACE.PROFILE, + SCREENS.WORKSPACE.REIMBURSE, + SCREENS.WORKSPACE.MEMBERS, + SCREENS.WORKSPACE.WORKFLOWS, + SCREENS.WORKSPACE.ACCOUNTING.ROOT, + SCREENS.WORKSPACE.TAXES, + SCREENS.WORKSPACE.TAGS, + SCREENS.WORKSPACE.CATEGORIES, + SCREENS.WORKSPACE.DISTANCE_RATES, + SCREENS.WORKSPACE.REPORT_FIELDS, + SCREENS.WORKSPACE.INVOICES, + SCREENS.WORKSPACE.EXPENSIFY_CARD, + ], }; -const generateCentralPaneToTabMapping = (): Record => { - const mapping: Record = {} as Record; +const generateCentralPaneToTabMapping = (): Record => { + const mapping: Record = {} as Record; for (const [tabName, CentralPaneNames] of Object.entries(TAB_TO_CENTRAL_PANE_MAPPING)) { for (const CentralPaneName of CentralPaneNames) { - mapping[CentralPaneName] = tabName as BottomTabName; + mapping[CentralPaneName] = tabName as SplitNavigatorScreenName; } } return mapping; }; -const CENTRAL_PANE_TO_TAB_MAPPING: Record = generateCentralPaneToTabMapping(); +const CENTRAL_PANE_TO_TAB_MAPPING: Record = generateCentralPaneToTabMapping(); export {CENTRAL_PANE_TO_TAB_MAPPING}; export default TAB_TO_CENTRAL_PANE_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 82c034373b5b..43f9dfdd8c77 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -9,7 +9,8 @@ import createNormalizedConfigs from './createNormalizedConfigs'; // Moved to a separate file to avoid cyclic dependencies. const config: LinkingOptions['config'] = { - initialRouteName: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, + // initialRouteName: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, + initialRouteName: NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, screens: { // Main Routes [SCREENS.VALIDATE_LOGIN]: ROUTES.VALIDATE_LOGIN, @@ -29,49 +30,49 @@ const config: LinkingOptions['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.SETTINGS.PROFILE.ROOT]: { - path: ROUTES.SETTINGS_PROFILE, - exact: true, - }, - [SCREENS.SETTINGS.PREFERENCES.ROOT]: { - path: ROUTES.SETTINGS_PREFERENCES, - exact: true, - }, - [SCREENS.SETTINGS.SECURITY]: { - path: ROUTES.SETTINGS_SECURITY, - exact: true, - }, - [SCREENS.SETTINGS.WALLET.ROOT]: { - path: ROUTES.SETTINGS_WALLET, - exact: true, - }, - [SCREENS.SETTINGS.ABOUT]: { - path: ROUTES.SETTINGS_ABOUT, - exact: true, - }, - [SCREENS.SETTINGS.TROUBLESHOOT]: { - path: ROUTES.SETTINGS_TROUBLESHOOT, - exact: true, - }, - [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES, + // [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route, + // [SCREENS.SETTINGS.PROFILE.ROOT]: { + // path: ROUTES.SETTINGS_PROFILE, + // exact: true, + // }, + // [SCREENS.SETTINGS.PREFERENCES.ROOT]: { + // path: ROUTES.SETTINGS_PREFERENCES, + // // exact: true, + // }, + // [SCREENS.SETTINGS.SECURITY]: { + // path: ROUTES.SETTINGS_SECURITY, + // exact: true, + // }, + // [SCREENS.SETTINGS.WALLET.ROOT]: { + // path: ROUTES.SETTINGS_WALLET, + // exact: true, + // }, + // [SCREENS.SETTINGS.ABOUT]: { + // path: ROUTES.SETTINGS_ABOUT, + // exact: true, + // }, + // [SCREENS.SETTINGS.TROUBLESHOOT]: { + // path: ROUTES.SETTINGS_TROUBLESHOOT, + // exact: true, + // }, + // [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES, [SCREENS.SEARCH.CENTRAL_PANE]: { path: ROUTES.SEARCH_CENTRAL_PANE.route, }, - [SCREENS.SETTINGS.SAVE_THE_WORLD]: ROUTES.SETTINGS_SAVE_THE_WORLD, - [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: ROUTES.SETTINGS_SUBSCRIPTION, + // [SCREENS.SETTINGS.SAVE_THE_WORLD]: ROUTES.SETTINGS_SAVE_THE_WORLD, + // [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: ROUTES.SETTINGS_SUBSCRIPTION, // Sidebar - [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]: { - path: ROUTES.ROOT, - initialRouteName: SCREENS.HOME, - screens: { - [SCREENS.HOME]: ROUTES.HOME, - [SCREENS.SETTINGS.ROOT]: { - path: ROUTES.SETTINGS, - }, - }, - }, + // [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]: { + // path: ROUTES.ROOT, + // initialRouteName: SCREENS.HOME, + // screens: { + // [SCREENS.HOME]: ROUTES.HOME, + // // [SCREENS.SETTINGS.ROOT]: { + // // path: ROUTES.SETTINGS, + // // }, + // }, + // }, [SCREENS.NOT_FOUND]: '*', [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: { @@ -1149,7 +1150,57 @@ const config: LinkingOptions['config'] = { }, }, - [NAVIGATORS.WORKSPACE_NAVIGATOR]: { + [NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: { + path: ROUTES.ROOT, + screens: { + [SCREENS.HOME]: { + path: ROUTES.HOME, + exact: true, + }, + [SCREENS.REPORT]: { + path: ROUTES.REPORT_WITH_ID.route, + exact: true, + }, + }, + }, + + [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: { + screens: { + [SCREENS.SETTINGS.ROOT]: ROUTES.SETTINGS, + [SCREENS.SETTINGS.WORKSPACES]: { + path: ROUTES.SETTINGS_WORKSPACES, + exact: true, + }, + [SCREENS.SETTINGS.PROFILE.ROOT]: { + path: ROUTES.SETTINGS_PROFILE, + exact: true, + }, + [SCREENS.SETTINGS.SECURITY]: { + path: ROUTES.SETTINGS_SECURITY, + exact: true, + }, + [SCREENS.SETTINGS.WALLET.ROOT]: { + path: ROUTES.SETTINGS_WALLET, + exact: true, + }, + [SCREENS.SETTINGS.ABOUT]: { + path: ROUTES.SETTINGS_ABOUT, + exact: true, + }, + [SCREENS.SETTINGS.TROUBLESHOOT]: { + path: ROUTES.SETTINGS_TROUBLESHOOT, + exact: true, + }, + [SCREENS.SETTINGS.SAVE_THE_WORLD]: ROUTES.SETTINGS_SAVE_THE_WORLD, + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: ROUTES.SETTINGS_SUBSCRIPTION, + [SCREENS.SETTINGS.PREFERENCES.ROOT]: { + path: ROUTES.SETTINGS_PREFERENCES, + // exact: true, + }, + }, + }, + + [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]: { screens: { [SCREENS.WORKSPACE.INITIAL]: { path: ROUTES.WORKSPACE_INITIAL.route, diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index a21ae3202549..a6dfa775f8a8 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -4,7 +4,7 @@ import pick from 'lodash/pick'; import type {TupleToUnion} from 'type-fest'; import {isAnonymousUser} from '@libs/actions/Session'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import type {BottomTabName, CentralPaneName, NavigationPartialRoute, RootStackParamList, WorkspaceScreenName} from '@libs/Navigation/types'; +import type {BottomTabName, CentralPaneName, NavigationPartialRoute, RootStackParamList, SplitNavigatorScreenName} from '@libs/Navigation/types'; import {isCentralPaneName} from '@libs/NavigationUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import * as ReportConnection from '@libs/ReportConnection'; @@ -17,9 +17,8 @@ import SCREENS from '@src/SCREENS'; import CENTRAL_PANE_TO_RHP_MAPPING from './CENTRAL_PANE_TO_RHP_MAPPING'; import config, {normalizedConfigs} from './config'; import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState'; -import getMatchingCentralPaneRouteForState from './getMatchingCentralPaneRouteForState'; import replacePathInNestedState from './replacePathInNestedState'; -import WORKSPACE_SCREEN_TO_RHP_MAPPING from './WORKSPACE_SCREEN_TO_RHP_MAPPING'; +import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING'; const RHP_SCREENS_OPENED_FROM_LHN = [ SCREENS.SETTINGS.SHARE_CODE, @@ -31,6 +30,12 @@ const RHP_SCREENS_OPENED_FROM_LHN = [ SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM, ] satisfies Screen[]; +const mapLhnToSplitNavigatorName = { + [SCREENS.SETTINGS.ROOT]: NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, + [SCREENS.HOME]: NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, + [SCREENS.WORKSPACE.INITIAL]: NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, +}; + type RHPScreenOpenedFromLHN = TupleToUnion; type Metainfo = { @@ -44,11 +49,14 @@ type Metainfo = { type GetAdaptedStateReturnType = { adaptedState: ReturnType; - metainfo: Metainfo; + // metainfo: Metainfo; }; type GetAdaptedStateFromPath = (...args: [...Parameters, shouldReplacePathInNestedState?: boolean]) => GetAdaptedStateReturnType; +type SplitNavigatorLHNScreen = keyof typeof mapLhnToSplitNavigatorName; +type SplitNavigator = (typeof mapLhnToSplitNavigatorName)[SplitNavigatorLHNScreen]; + // The function getPathFromState that we are using in some places isn't working correctly without defined index. const getRoutesWithIndex = (routes: NavigationPartialRoute[]): PartialState => ({routes, index: routes.length - 1}); @@ -78,14 +86,44 @@ function createBottomTabNavigator(route: NavigationPartialRoute, }; } -function createWorkspaceNavigator(route?: NavigationPartialRoute): NavigationPartialRoute { +// function createWorkspaceNavigator(route?: NavigationPartialRoute): NavigationPartialRoute { +// const routes = []; + +// const policyID = route?.params && 'policyID' in route.params ? route.params.policyID : undefined; + +// // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator +// routes.push({ +// name: SCREENS.WORKSPACE.INITIAL, +// params: { +// policyID, +// }, +// }); + +// if (route) { +// routes.push(route); +// } +// return { +// name: NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, +// state: getRoutesWithIndex(routes), +// }; +// } + +function getParamsFromRoute(screenName: string): string[] { + const routeConfig = normalizedConfigs[screenName as Screen]; + + const route = routeConfig.pattern; + + return route.match(/(?<=[:?&])(\w+)(?=[/=?&]|$)/g) ?? []; +} + +function createSplitNavigator(splitNavigatorLHNScreen: SplitNavigatorLHNScreen, route?: NavigationPartialRoute): NavigationPartialRoute { const routes = []; const policyID = route?.params && 'policyID' in route.params ? route.params.policyID : undefined; // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator routes.push({ - name: SCREENS.WORKSPACE.INITIAL, + name: splitNavigatorLHNScreen, params: { policyID, }, @@ -95,21 +133,13 @@ function createWorkspaceNavigator(route?: NavigationPartialRoute | undefined { +function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | undefined { // Check for backTo param. One screen with different backTo value may need diferent screens visible under the overlay. if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { const stateForBackTo = getStateFromPath(route.params.backTo, config); @@ -119,7 +149,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat const centralPaneOrWorkspaceNavigator = stateForBackTo.routes.find( // eslint-disable-next-line @typescript-eslint/no-shadow - (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.WORKSPACE_NAVIGATOR, + (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, ); // If there is rhpNavigator in the state generated for backTo url, we want to get root route matching to this rhp screen. @@ -129,7 +159,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat // If we know that backTo targets the root route (central pane or full screen) we want to use it. if (centralPaneOrWorkspaceNavigator && centralPaneOrWorkspaceNavigator.state) { - return centralPaneOrWorkspaceNavigator as NavigationPartialRoute; + return centralPaneOrWorkspaceNavigator as NavigationPartialRoute; } } } @@ -138,19 +168,21 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat for (const [centralPaneName, RHPNames] of Object.entries(CENTRAL_PANE_TO_RHP_MAPPING)) { if (RHPNames.includes(route.name)) { const paramsFromRoute = getParamsFromRoute(centralPaneName); - - return {name: centralPaneName as CentralPaneName, params: pick(route.params, paramsFromRoute)}; + return createSplitNavigator(CENTRAL_PANE_TO_TAB_MAPPING[centralPaneName] as SplitNavigatorLHNScreen, { + name: centralPaneName as SplitNavigatorScreenName, + params: pick(route.params, paramsFromRoute), + }); } } // Check for WorkspaceNavigator - for (const [workspaceScreenName, RHPNames] of Object.entries(WORKSPACE_SCREEN_TO_RHP_MAPPING)) { - if (RHPNames.includes(route.name)) { - const paramsFromRoute = getParamsFromRoute(workspaceScreenName); + // for (const [workspaceScreenName, RHPNames] of Object.entries(WORKSPACE_SCREEN_TO_RHP_MAPPING)) { + // if (RHPNames.includes(route.name)) { + // const paramsFromRoute = getParamsFromRoute(workspaceScreenName); - return createWorkspaceNavigator({name: workspaceScreenName as WorkspaceScreenName, params: pick(route.params, paramsFromRoute)}); - } - } + // return createWorkspaceNavigator({name: workspaceScreenName as WorkspaceScreenName, params: pick(route.params, paramsFromRoute)}); + // } + // } // check for valid reportID in the route params // if the reportID is valid, we should navigate back to screen report in CPN @@ -162,15 +194,13 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat function getAdaptedState(state: PartialState>, policyID?: string): GetAdaptedStateReturnType { const isNarrowLayout = getIsNarrowLayout(); - const metainfo = { - isCentralPaneAndBottomTabMandatory: true, - isWorkspaceNavigatorMandatory: true, - }; + // const metainfo = { + // isCentralPaneAndBottomTabMandatory: true, + // isWorkspaceNavigatorMandatory: true, + // }; // We need to check what is defined to know what we need to add. - const bottomTabNavigator = state.routes.find((route) => route.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR); - const centralPaneNavigator = state.routes.find((route) => isCentralPaneName(route.name)); - const WorkspaceNavigator = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); + const WorkspaceNavigator = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR); const rhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); const lhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR); const onboardingModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR); @@ -193,19 +223,16 @@ function getAdaptedState(state: PartialState const isRHPScreenOpenedFromLHN = focusedRHPRoute?.name && RHP_SCREENS_OPENED_FROM_LHN.includes(focusedRHPRoute?.name as RHPScreenOpenedFromLHN); // This may happen if this RHP doens't have a route that should be under the overlay defined. if (!matchingRootRoute || isRHPScreenOpenedFromLHN) { - metainfo.isCentralPaneAndBottomTabMandatory = false; - metainfo.isWorkspaceNavigatorMandatory = false; + // metainfo.isCentralPaneAndBottomTabMandatory = false; + // metainfo.isWorkspaceNavigatorMandatory = false; // If matchingRootRoute is undefined and it's a narrow layout, don't add a report screen under the RHP. matchingRootRoute = matchingRootRoute ?? (!isNarrowLayout ? {name: SCREENS.REPORT} : undefined); } - // If the root route is type of WorkspaceNavigator, the default bottom tab will be added. - const matchingBottomTabRoute = getMatchingBottomTabRouteForState({routes: matchingRootRoute ? [matchingRootRoute] : []}); - routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); // When we open a screen in RHP from WorkspaceNavigator, we need to add the appropriate screen in CentralPane. // Then, when we close WorkspaceNavigator, we will be redirected to the correct page in CentralPane. - if (matchingRootRoute?.name === NAVIGATORS.WORKSPACE_NAVIGATOR) { - routes.push({name: SCREENS.SETTINGS.WORKSPACES}); + if (matchingRootRoute?.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR) { + routes.push(createSplitNavigator(SCREENS.SETTINGS.ROOT, {name: SCREENS.SETTINGS.WORKSPACES})); } if (matchingRootRoute && (!isNarrowLayout || !isRHPScreenOpenedFromLHN)) { @@ -216,7 +243,7 @@ function getAdaptedState(state: PartialState routes.push(rhpNavigator); return { adaptedState: getRoutesWithIndex(routes), - metainfo, + // metainfo, }; } if (lhpNavigator ?? onboardingModalNavigator ?? welcomeVideoModalNavigator ?? featureTrainingModalNavigator) { @@ -226,22 +253,17 @@ function getAdaptedState(state: PartialState // - found lhp / onboardingModalNavigator // There is no screen in these navigators that would have mandatory central pane, bottom tab or workspace navigator. - metainfo.isCentralPaneAndBottomTabMandatory = false; - metainfo.isWorkspaceNavigatorMandatory = false; + // metainfo.isCentralPaneAndBottomTabMandatory = false; + // metainfo.isWorkspaceNavigatorMandatory = false; const routes = []; - routes.push( - createBottomTabNavigator( - { - name: SCREENS.HOME, - }, - policyID, - ), - ); - if (!isNarrowLayout) { - routes.push({ - name: SCREENS.REPORT, - }); - } + + const splitNavigatorMainScreen = !isNarrowLayout + ? { + name: SCREENS.REPORT, + } + : undefined; + + routes.push(createSplitNavigator(SCREENS.HOME, splitNavigatorMainScreen)); // Separate ifs are necessary for typescript to see that we are not pushing undefined to the array. if (lhpNavigator) { @@ -262,7 +284,7 @@ function getAdaptedState(state: PartialState return { adaptedState: getRoutesWithIndex(routes), - metainfo, + // metainfo, }; } if (WorkspaceNavigator) { @@ -273,39 +295,35 @@ function getAdaptedState(state: PartialState const routes = []; routes.push( - createBottomTabNavigator( - { - name: SCREENS.SETTINGS.ROOT, + createSplitNavigator(SCREENS.SETTINGS.ROOT, { + name: SCREENS.SETTINGS.WORKSPACES, + params: { + policyID, }, - policyID, - ), + }), ); - routes.push({ - name: SCREENS.SETTINGS.WORKSPACES, - }); - routes.push(WorkspaceNavigator); return { adaptedState: getRoutesWithIndex(routes), - metainfo, - }; - } - if (centralPaneNavigator) { - // Routes - // - matching bottom tab - // - found central pane - const routes = []; - const matchingBottomTabRoute = getMatchingBottomTabRouteForState(state); - routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); - routes.push(centralPaneNavigator); - - return { - adaptedState: getRoutesWithIndex(routes), - metainfo, + // metainfo, }; } + // if (centralPaneNavigator) { + // // Routes + // // - matching bottom tab + // // - found central pane + // const routes = []; + // const matchingBottomTabRoute = getMatchingBottomTabRouteForState(state); + // routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); + // routes.push(centralPaneNavigator); + + // return { + // adaptedState: getRoutesWithIndex(routes), + // // metainfo, + // }; + // } if (attachmentsScreen) { // Routes // - matching bottom tab @@ -324,44 +342,16 @@ function getAdaptedState(state: PartialState return { adaptedState: getRoutesWithIndex(routes), - metainfo, + // metainfo, }; } } // We need to make sure that this if only handles states where we deeplink to the bottom tab directly - if (bottomTabNavigator && bottomTabNavigator.state) { - // Routes - // - found bottom tab - // - matching central pane on desktop layout - - // We want to make sure that the bottom tab search page is always pushed with matching central pane page. Even on the narrow layout. - if (isNarrowLayout && bottomTabNavigator.state?.routes[0].name !== SCREENS.SEARCH.BOTTOM_TAB) { - return { - adaptedState: state, - metainfo, - }; - } - - const routes = [...state.routes]; - const matchingCentralPaneRoute = getMatchingCentralPaneRouteForState(state); - if (matchingCentralPaneRoute) { - routes.push(matchingCentralPaneRoute); - } else { - // If there is no matching central pane, we want to add the default one. - metainfo.isCentralPaneAndBottomTabMandatory = false; - routes.push({name: SCREENS.REPORT}); - } - - return { - adaptedState: getRoutesWithIndex(routes), - metainfo, - }; - } return { adaptedState: state, - metainfo, + // metainfo, }; } diff --git a/src/libs/Navigation/linkingConfig/index.ts b/src/libs/Navigation/linkingConfig/index.ts index 1f556aa67809..7bb5e8ae5b5a 100644 --- a/src/libs/Navigation/linkingConfig/index.ts +++ b/src/libs/Navigation/linkingConfig/index.ts @@ -15,7 +15,7 @@ const linkingConfig: LinkingOptions = { return adaptedState; }, subscribe, - getPathFromState: customGetPathFromState, + // getPathFromState: customGetPathFromState, prefixes, config, }; diff --git a/src/libs/Navigation/newLinkTo/getActionForBottomTabNavigator.ts b/src/libs/Navigation/newLinkTo/getActionForBottomTabNavigator.ts new file mode 100644 index 000000000000..85580d068ad7 --- /dev/null +++ b/src/libs/Navigation/newLinkTo/getActionForBottomTabNavigator.ts @@ -0,0 +1,50 @@ +import type {NavigationAction, NavigationState} from '@react-navigation/native'; +import type {Writable} from 'type-fest'; +import type {RootStackParamList, StackNavigationAction} from '@libs/Navigation/types'; +import getTopmostBottomTabRoute from '@navigation/getTopmostBottomTabRoute'; +import CONST from '@src/CONST'; +import type {ActionPayloadParams} from './types'; + +// Because we need to change the type to push, we also need to set target for this action to the bottom tab navigator. +function getActionForBottomTabNavigator( + action: StackNavigationAction, + state: NavigationState, + policyID?: string, + shouldNavigate?: boolean, +): Writable | undefined { + const bottomTabNavigatorRoute = state.routes.at(0); + if (!bottomTabNavigatorRoute || bottomTabNavigatorRoute.state === undefined || !action || action.type !== CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { + return; + } + + const params = action.payload.params as ActionPayloadParams; + let payloadParams = params.params as Record; + const screen = params.screen; + + if (policyID && !payloadParams?.policyID) { + payloadParams = {...payloadParams, policyID}; + } else if (!policyID) { + delete payloadParams?.policyID; + } + + // Check if the current bottom tab is the same as the one we want to navigate to. If it is, we don't need to do anything. + const bottomTabCurrentTab = getTopmostBottomTabRoute(state); + const bottomTabParams = bottomTabCurrentTab?.params as Record; + + // Verify if the policyID is different than the one we are currently on. If it is, we need to navigate to the new policyID. + const isNewPolicy = bottomTabParams?.policyID !== payloadParams?.policyID; + if (bottomTabCurrentTab?.name === screen && !shouldNavigate && !isNewPolicy) { + return; + } + + return { + type: CONST.NAVIGATION.ACTION_TYPE.PUSH, + payload: { + name: screen, + params: payloadParams, + }, + target: bottomTabNavigatorRoute.state.key, + }; +} + +export default getActionForBottomTabNavigator; diff --git a/src/libs/Navigation/newLinkTo/getMinimalAction.ts b/src/libs/Navigation/newLinkTo/getMinimalAction.ts new file mode 100644 index 000000000000..ff01b3b8333b --- /dev/null +++ b/src/libs/Navigation/newLinkTo/getMinimalAction.ts @@ -0,0 +1,42 @@ +import type {NavigationAction, NavigationState} from '@react-navigation/native'; +import type {Writable} from 'type-fest'; +import type {State} from '@navigation/types'; +import type {ActionPayload} from './types'; + +/** + * Motivation for this function is described in NAVIGATION.md + * + * @param action action generated by getActionFromState + * @param state The root state + * @returns minimalAction minimal action is the action that we should dispatch + */ +function getMinimalAction(action: NavigationAction, state: NavigationState): Writable { + let currentAction: NavigationAction = action; + let currentState: State | undefined = state; + let currentTargetKey: string | undefined; + + while (currentAction.payload && 'name' in currentAction.payload && currentState?.routes[currentState.index ?? -1].name === currentAction.payload.name) { + if (!currentState?.routes[currentState.index ?? -1].state) { + break; + } + + currentState = currentState?.routes[currentState.index ?? -1].state; + currentTargetKey = currentState?.key; + + const payload = currentAction.payload as ActionPayload; + + // Creating new smaller action + currentAction = { + type: currentAction.type, + payload: { + name: payload?.params?.screen, + params: payload?.params?.params, + path: payload?.params?.path, + }, + target: currentTargetKey, + }; + } + return currentAction; +} + +export default getMinimalAction; diff --git a/src/libs/Navigation/newLinkTo/index.ts b/src/libs/Navigation/newLinkTo/index.ts new file mode 100644 index 000000000000..0f278a9dfc24 --- /dev/null +++ b/src/libs/Navigation/newLinkTo/index.ts @@ -0,0 +1,239 @@ +import {getActionFromState} from '@react-navigation/core'; +import type {NavigationContainerRef, NavigationState, PartialState} from '@react-navigation/native'; +import {findFocusedRoute} from '@react-navigation/native'; +import {shallowCompare} from '@libs/ObjectUtils'; +import {getPathWithoutPolicyID} from '@libs/PolicyUtils'; +import getStateFromPath from '@navigation/getStateFromPath'; +import linkingConfig from '@navigation/linkingConfig'; +import type {RootStackParamList, StackNavigationAction} from '@navigation/types'; +import CONST from '@src/CONST'; +import type {Route} from '@src/ROUTES'; +import getMinimalAction from './getMinimalAction'; + +function shouldDispatchAction(currentState: NavigationState, stateFromPath: PartialState>) { + const currentFocusedRoute = findFocusedRoute(currentState); + const targetFocusedRoute = findFocusedRoute(stateFromPath); + + const areNamesEqual = currentFocusedRoute?.name === targetFocusedRoute?.name; + const areParamsEqual = shallowCompare(currentFocusedRoute?.params as Record | undefined, targetFocusedRoute?.params as Record | undefined); + + if (areNamesEqual && areParamsEqual) { + return false; + } + + return true; +} + +export default function linkTo(navigation: NavigationContainerRef | null, path: Route, type?: string, isActiveRoute?: boolean) { + if (!navigation) { + throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); + } + + const pathWithoutPolicyID = getPathWithoutPolicyID(`/${path}`) as Route; + + // This is the state generated with the default getStateFromPath function. + // It won't include the whole state that will be generated for this path but the focused route will be correct. + // It is necessary because getActionFromState will generate RESET action for whole state generated with our custom getStateFromPath function. + const stateFromPath = getStateFromPath(pathWithoutPolicyID) as PartialState>; + const currentState = navigation.getRootState() as NavigationState; + const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config); + + // We don't want to dispatch action to push/replace with exactly the same route that is already focused. + if (!shouldDispatchAction(currentState, stateFromPath)) { + return; + } + + // If there is no action, just reset the whole state. + if (!action) { + navigation.resetRoot(stateFromPath); + return; + } + + if (action.type === CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { + // We want to PUSH by default to add entries to the browser history. + action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; + } + + if (!shouldDispatchAction(currentState, stateFromPath)) { + return; + } + + const minimalAction = getMinimalAction(action, navigation.getRootState()); + navigation.dispatch(minimalAction); + + // const pathWithoutPolicyID = getPathWithoutPolicyID(`/${path}`) as Route; + // const rootState = navigation.getRootState() as NavigationState; + // const stateFromPath = getStateFromPath(pathWithoutPolicyID) as PartialState>; + // // Creating path with /w/ included if necessary. + // const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState); + // const policyIDs = !!topmostCentralPaneRoute?.params && 'policyIDs' in topmostCentralPaneRoute.params ? (topmostCentralPaneRoute?.params?.policyIDs as string) : ''; + // const extractedPolicyID = extractPolicyIDFromPath(`/${path}`); + // const policyIDFromState = getPolicyIDFromState(rootState); + // const policyID = extractedPolicyID ?? policyIDFromState ?? policyIDs; + // const lastRoute = rootState?.routes?.at(-1); + + // const isNarrowLayout = getIsNarrowLayout(); + + // const isWorkspaceScreenOnTop = lastRoute?.name === NAVIGATORS.WORKSPACE_NAVIGATOR; + + // // policyIDs is present only on SCREENS.SEARCH.CENTRAL_PANE and it's displayed in the url as a query param, on the other pages this parameter is called policyID and it's shown in the url in the format: /w/:policyID + // if (policyID && !isWorkspaceScreenOnTop && !policyIDs) { + // // The stateFromPath doesn't include proper path if there is a policy passed with /w/id. + // // We need to replace the path in the state with the proper one. + // // To avoid this hacky solution we may want to create custom getActionFromState function in the future. + // replacePathInNestedState(stateFromPath, `/w/${policyID}${pathWithoutPolicyID}`); + // } + + // const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config); + + // const isReportInRhpOpened = isReportOpenInRHP(rootState); + + // // If action type is different than NAVIGATE we can't change it to the PUSH safely + // if (action?.type === CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { + // const actionPayloadParams = action.payload.params as ActionPayloadParams; + + // const topRouteName = lastRoute?.name; + + // // CentralPane screens aren't nested in any navigator, if actionPayloadParams?.screen is undefined, it means the screen name and parameters have to be read directly from action.payload + // const targetName = actionPayloadParams?.screen ?? action.payload.name; + // const targetParams = actionPayloadParams?.params ?? actionPayloadParams; + // const isTargetNavigatorOnTop = topRouteName === action.payload.name; + + // const isTargetScreenDifferentThanCurrent = !!(!topmostCentralPaneRoute || topmostCentralPaneRoute.name !== targetName); + // const areParamsDifferent = + // targetName === SCREENS.REPORT + // ? getTopmostReportId(rootState) !== getTopmostReportId(stateFromPath) + // : !shallowCompare( + // omitBy(topmostCentralPaneRoute?.params as Record | undefined, (value) => value === undefined), + // omitBy(targetParams as Record | undefined, (value) => value === undefined), + // ); + + // // If this action is navigating to the report screen and the top most navigator is different from the one we want to navigate - PUSH the new screen to the top of the stack by default + // if (isCentralPaneName(action.payload.name) && (isTargetScreenDifferentThanCurrent || areParamsDifferent)) { + // // We need to push a tab if the tab doesn't match the central pane route that we are going to push. + // const topmostBottomTabRoute = getTopmostBottomTabRoute(rootState); + // const policyIDsFromState = extractPolicyIDsFromState(stateFromPath); + // const matchingBottomTabRoute = getMatchingBottomTabRouteForState(stateFromPath, policyID || policyIDsFromState); + // const isOpeningSearch = matchingBottomTabRoute.name === SCREENS.SEARCH.BOTTOM_TAB; + // const isNewPolicyID = + // ((topmostBottomTabRoute?.params as Record)?.policyID ?? '') !== + // ((matchingBottomTabRoute?.params as Record)?.policyID ?? ''); + + // if (topmostBottomTabRoute && (topmostBottomTabRoute.name !== matchingBottomTabRoute.name || isNewPolicyID || isOpeningSearch)) { + // root.dispatch({ + // type: CONST.NAVIGATION.ACTION_TYPE.PUSH, + // payload: matchingBottomTabRoute, + // }); + // } + + // if (type === CONST.NAVIGATION.TYPE.UP) { + // action.type = CONST.NAVIGATION.ACTION_TYPE.REPLACE; + // } else { + // action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; + // } + + // // If we navigate to SCREENS.SEARCH.CENTRAL_PANE, it's necessary to pass the current policyID, but we have to remember that this param is called policyIDs on this page + // if (targetName === SCREENS.SEARCH.CENTRAL_PANE && targetParams && policyID) { + // (targetParams as Record).policyIDs = policyID; + // } + + // // If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow + // // and at the same time we want the back button to go to the page we were before the deeplink + // } else if (type === CONST.NAVIGATION.TYPE.UP) { + // action.type = CONST.NAVIGATION.ACTION_TYPE.REPLACE; + + // // If this action is navigating to ModalNavigator or WorkspaceNavigator and the last route on the root navigator is not already opened Navigator then push + // } else if ((action.payload.name === NAVIGATORS.WORKSPACE_NAVIGATOR || isSideModalNavigator(action.payload.name)) && !isTargetNavigatorOnTop) { + // if (isSideModalNavigator(topRouteName)) { + // dismissModal(navigation); + // } + + // // If this RHP has mandatory central pane and bottom tab screens defined we need to push them. + // const {adaptedState, metainfo} = getAdaptedStateFromPath(path, linkingConfig.config); + // if (adaptedState && (metainfo.isCentralPaneAndBottomTabMandatory || metainfo.isWorkspaceNavigatorMandatory)) { + // const diff = getPartialStateDiff(rootState, adaptedState as State, metainfo); + // const diffActions = getActionsFromPartialDiff(diff); + // for (const diffAction of diffActions) { + // root.dispatch(diffAction); + // } + // } + // // All actions related to FullScreenNavigator on wide screen are pushed when comparing differences between rootState and adaptedState. + // if (action.payload.name === NAVIGATORS.WORKSPACE_NAVIGATOR) { + // return; + // } + // action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; + + // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // } else if (action.payload.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR) { + // // If path contains a policyID, we should invoke the navigate function + // const shouldNavigate = !!extractedPolicyID; + // const actionForBottomTabNavigator = getActionForBottomTabNavigator(action, rootState, policyID, shouldNavigate); + + // if (!actionForBottomTabNavigator) { + // return; + // } + + // root.dispatch(actionForBottomTabNavigator); + + // // If the layout is wide we need to push matching central pane route to the stack. + // if (!isNarrowLayout) { + // // stateFromPath should always include bottom tab navigator state, so getMatchingCentralPaneRouteForState will be always defined. + // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // const matchingCentralPaneRoute = getMatchingCentralPaneRouteForState(stateFromPath, rootState)!; + // if (matchingCentralPaneRoute && 'name' in matchingCentralPaneRoute) { + // root.dispatch({ + // type: CONST.NAVIGATION.ACTION_TYPE.PUSH, + // payload: { + // name: matchingCentralPaneRoute.name, + // params: matchingCentralPaneRoute.params, + // }, + // }); + // } + // } else { + // // If the layout is small we need to pop everything from the central pane so the bottom tab navigator is visible. + // root.dispatch({ + // type: 'POP_TO_TOP', + // target: rootState.key, + // }); + // } + // return; + // } + // } + + // if (action && 'payload' in action && action.payload && 'name' in action.payload && isSideModalNavigator(action.payload.name)) { + // // Information about the state may be in the params. + // const currentFocusedRoute = findFocusedRoute(extrapolateStateFromParams(rootState)); + // const targetFocusedRoute = findFocusedRoute(stateFromPath); + + // // If the current focused route is the same as the target focused route, we don't want to navigate. + // if ( + // currentFocusedRoute?.name === targetFocusedRoute?.name && + // shallowCompare(currentFocusedRoute?.params as Record, targetFocusedRoute?.params as Record) + // ) { + // return; + // } + + // const minimalAction = getMinimalAction(action, navigation.getRootState()); + // if (minimalAction) { + // // There are situations where a route already exists on the current navigation stack + // // But we want to push the same route instead of going back in the stack + // // Which would break the user navigation history + // if (!isActiveRoute && type === CONST.NAVIGATION.ACTION_TYPE.PUSH) { + // minimalAction.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; + // } + // root.dispatch(minimalAction); + // return; + // } + // } + + // // When we navigate from the ReportScreen opened in RHP, this page shouldn't be removed from the navigation state to allow users to go back to it. + // if (isReportInRhpOpened && action) { + // action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; + // } + + // if (action !== undefined) { + // root.dispatch(action); + // } else { + // root.reset(stateFromPath); + // } +} diff --git a/src/libs/Navigation/newLinkTo/types.ts b/src/libs/Navigation/newLinkTo/types.ts new file mode 100644 index 000000000000..254a4cdef2a5 --- /dev/null +++ b/src/libs/Navigation/newLinkTo/types.ts @@ -0,0 +1,11 @@ +type ActionPayloadParams = { + screen?: string; + params?: unknown; + path?: string; +}; + +type ActionPayload = { + params?: ActionPayloadParams; +}; + +export type {ActionPayload, ActionPayloadParams}; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 4b6b97c0d749..d3834112262b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -57,18 +57,18 @@ type CentralPaneScreensParamList = { openOnAdminRoom?: boolean; referrer?: string; }; - [SCREENS.SETTINGS.PROFILE.ROOT]: undefined; + // [SCREENS.SETTINGS.WORKSPACES]: undefined; [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined; + // [SCREENS.SETTINGS.PROFILE.ROOT]: undefined; [SCREENS.SETTINGS.SECURITY]: undefined; - [SCREENS.SETTINGS.WALLET.ROOT]: undefined; + // [SCREENS.SETTINGS.WALLET.ROOT]: undefined; [SCREENS.SETTINGS.ABOUT]: undefined; [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; - [SCREENS.SETTINGS.WORKSPACES]: undefined; [SCREENS.SEARCH.CENTRAL_PANE]: { q: SearchQueryString; }; - [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; + // [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined; }; @@ -107,17 +107,16 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD]: { backTo: Routes; }; - [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined; + // [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined; [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined; [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: undefined; [SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: undefined; [SCREENS.SETTINGS.PREFERENCES.THEME]: undefined; [SCREENS.SETTINGS.CLOSE]: undefined; - [SCREENS.SETTINGS.SECURITY]: undefined; - [SCREENS.SETTINGS.ABOUT]: undefined; - [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; + // [SCREENS.SETTINGS.SECURITY]: undefined; + // [SCREENS.SETTINGS.ABOUT]: undefined; [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: undefined; - [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; + // [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; [SCREENS.SETTINGS.CONSOLE]: { backTo: Routes; }; @@ -1172,7 +1171,30 @@ type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; }; -type WorkspaceNavigatorParamList = { +type ReportsSplitNavigatorParamList = { + [SCREENS.HOME]: {policyID?: string}; + [SCREENS.REPORT]: { + reportActionID: string; + reportID: string; + openOnAdminRoom?: boolean; + referrer?: string; + }; +}; + +type SettingsSplitNavigatorParamList = { + [SCREENS.SETTINGS.ROOT]: {policyID?: string}; + [SCREENS.SETTINGS.WORKSPACES]: undefined; + [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined; + [SCREENS.SETTINGS.SECURITY]: undefined; + [SCREENS.SETTINGS.PROFILE.ROOT]: undefined; + [SCREENS.SETTINGS.WALLET.ROOT]: undefined; + [SCREENS.SETTINGS.ABOUT]: undefined; + [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; + [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined; +}; + +type WorkspaceSplitNavigatorParamList = { [SCREENS.WORKSPACE.INITIAL]: { policyID: string; backTo?: string; @@ -1362,9 +1384,11 @@ type AuthScreensParamList = CentralPaneScreensParamList & policyID?: string; }; [SCREENS.NOT_FOUND]: undefined; + [NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: NavigatorScreenParams & {policyID?: string}; + [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; - [NAVIGATORS.WORKSPACE_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams; @@ -1405,18 +1429,28 @@ type RootStackParamList = PublicScreensParamList & AuthScreensParamList & LeftMo type BottomTabName = keyof BottomTabNavigatorParamList; -type WorkspaceScreenName = keyof WorkspaceNavigatorParamList; +type WorkspaceScreenName = keyof WorkspaceSplitNavigatorParamList; type CentralPaneName = keyof CentralPaneScreensParamList; type OnboardingFlowName = keyof OnboardingModalNavigatorParamList; +type SplitNavigatorScreenName = keyof WorkspaceSplitNavigatorParamList & keyof SettingsSplitNavigatorParamList & keyof ReportsSplitNavigatorParamList; + type SwitchPolicyIDParams = { policyID?: string; route?: Routes; isPolicyAdmin?: boolean; }; +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace ReactNavigation { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-empty-interface + interface RootParamList extends RootStackParamList {} + } +} + export type { AddPersonalBankAccountNavigatorParamList, AuthScreensParamList, @@ -1432,7 +1466,7 @@ export type { ExplanationModalNavigatorParamList, FlagCommentNavigatorParamList, WorkspaceScreenName, - WorkspaceNavigatorParamList, + WorkspaceSplitNavigatorParamList, LeftModalNavigatorParamList, MoneyRequestNavigatorParamList, NavigationPartialRoute, @@ -1452,10 +1486,12 @@ export type { ReportDescriptionNavigatorParamList, ReportDetailsNavigatorParamList, ReportSettingsNavigatorParamList, + ReportsSplitNavigatorParamList, RightModalNavigatorParamList, RoomMembersNavigatorParamList, RootStackParamList, SettingsNavigatorParamList, + SettingsSplitNavigatorParamList, SignInNavigatorParamList, FeatureTrainingNavigatorParamList, SplitDetailsNavigatorParamList, @@ -1473,4 +1509,5 @@ export type { SearchAdvancedFiltersParamList, RestrictedActionParamList, MissingPersonalDetailsParamList, + SplitNavigatorScreenName, }; diff --git a/src/libs/ObjectUtils.ts b/src/libs/ObjectUtils.ts index 644fe1c7596e..9e5a4fc5d8d7 100644 --- a/src/libs/ObjectUtils.ts +++ b/src/libs/ObjectUtils.ts @@ -1,13 +1,20 @@ +const getDefinedKeys = (obj: Record): string[] => { + return Object.entries(obj) + .filter(([, value]) => value !== undefined) + .map(([key]) => key); +}; + const shallowCompare = (obj1?: Record, obj2?: Record): boolean => { if (!obj1 && !obj2) { return true; } if (obj1 && obj2) { - const keys1 = Object.keys(obj1); - const keys2 = Object.keys(obj2); + const keys1 = getDefinedKeys(obj1); + const keys2 = getDefinedKeys(obj2); return keys1.length === keys2.length && keys1.every((key) => obj1[key] === obj2[key]); } return false; }; -export default shallowCompare; +// eslint-disable-next-line import/prefer-default-export +export {shallowCompare}; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index ab209e9bf928..d45c5f291ae3 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -756,6 +756,7 @@ function cleanupSession() { PersistedRequests.clear(); NetworkConnection.clearReconnectionCallbacks(); SessionUtils.resetDidUserLogInDuringSession(); + // TODO: Check if this breaks something resetHomeRouteParams(); clearCache().then(() => { Log.info('Cleared all cache data', true, {}, true); diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index c3607d4a5947..79183909c21f 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -1,25 +1,43 @@ +// eslint-disable-next-line import/extensions, prettier/prettier import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo} from 'react'; +import {View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; +import {useSearchContext} from '@components/Search/SearchContext'; +import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; +import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; +import TopBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import * as SearchUtils from '@libs/SearchUtils'; import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; +import SCREENS from '@src/SCREENS'; +import SearchTypeMenu from './SearchTypeMenu'; type SearchPageProps = StackScreenProps; function SearchPage({route}: SearchPageProps) { + const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); + const {q} = route.params; - const queryJSON = useMemo(() => SearchUtils.buildSearchQueryJSON(q), [q]); + const {queryJSON, policyID} = useMemo(() => { + const parsedQuery = SearchUtils.buildSearchQueryJSON(q); + const extractedPolicyID = parsedQuery && SearchUtils.getPolicyIDFromSearchQuery(parsedQuery); + + return {queryJSON: parsedQuery, policyID: extractedPolicyID}; + }, [q]); + const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()})); + const {clearSelectedTransactions} = useSearchContext(); // On small screens this page is not displayed, the configuration is in the file: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx // To avoid calling hooks in the Search component when this page isn't visible, we return null here. @@ -39,12 +57,39 @@ function SearchPage({route}: SearchPageProps) { onBackButtonPress={handleOnBackButtonPress} shouldShowLink={false} > - {queryJSON && } + {queryJSON && ( + + + {/* {!selectionMode?.isEnabled && queryJSON ? ( */} + {queryJSON ? ( + + + + + ) : ( + { + clearSelectedTransactions(); + turnOffMobileSelectionMode(); + }} + /> + )} + + + + + )} ); } SearchPage.displayName = 'SearchPage'; +SearchPage.whyDidYouRender = true; export default SearchPage; diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index 6dc5ecce075b..51f76c811557 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import {sortWorkspacesBySelected} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -90,10 +90,11 @@ function WorkspaceSwitcherPage() { const {policyID} = option; - setActiveWorkspaceID(policyID); + // setActiveWorkspaceID(policyID); Navigation.goBack(); if (policyID !== activeWorkspaceID) { - Navigation.navigateWithSwitchPolicyID({policyID}); + // Navigation.navigateWithSwitchPolicyID({policyID}); + navigationRef.dispatch({type: 'SWITCH_POLICY_ID', payload: {policyID}}); } }, [activeWorkspaceID, setActiveWorkspaceID], diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index edc8dfb3cb3a..b82865dd13c7 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -7,6 +7,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {updateLastAccessedWorkspace} from '@libs/actions/Policy/Policy'; import * as Browser from '@libs/Browser'; +import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; import TopBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; @@ -14,6 +15,7 @@ import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; /** * Function called when a pinned chat is selected. @@ -63,6 +65,7 @@ function BaseSidebarScreen() { insets={insets} /> + )} diff --git a/src/pages/home/sidebar/SidebarScreen/index.tsx b/src/pages/home/sidebar/SidebarScreen/index.tsx index 625491674cd8..3190c7b7aa71 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.tsx +++ b/src/pages/home/sidebar/SidebarScreen/index.tsx @@ -1,12 +1,13 @@ import React from 'react'; -import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; +// import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; import BaseSidebarScreen from './BaseSidebarScreen'; +// TODO-SPLITS: Figure out how to handle the FreezeWrapper component. function SidebarScreen() { return ( - - - + // + + // ); } diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 30f4cf010f70..e84788bea7a5 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -30,6 +30,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import * as CurrencyUtils from '@libs/CurrencyUtils'; +import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; import Navigation from '@libs/Navigation/Navigation'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -45,6 +46,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Icon as TIcon} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -455,6 +457,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms onCancel={() => toggleSignoutConfirmModal(false)} /> + ); } diff --git a/src/styles/index.ts b/src/styles/index.ts index 67455ceedf91..94eed38b4990 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1550,6 +1550,19 @@ const styles = (theme: ThemeColors) => ...wordBreak.breakWord, }, + searchSplitContainer: { + flex: 1, + flexDirection: 'row', + }, + + searchSidebar: { + width: variables.sideBarWidth, + height: '100%', + justifyContent: 'space-between', + borderRightWidth: 1, + borderColor: theme.border, + }, + // Sidebar Styles sidebar: { backgroundColor: theme.sidebar, From f85d681752d7fcb1d3f784d0584ab0903b3efe0c Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 3 Sep 2024 14:20:38 +0200 Subject: [PATCH 15/34] Remove ActiveCentralPaneRouteContext --- src/hooks/useActiveCentralPaneRoute.ts | 9 ----- .../ActiveCentralPaneRouteContext.ts | 6 ---- .../Navigators/BottomTabNavigator.tsx | 36 ++++++++----------- src/pages/Search/SearchPage.tsx | 8 ++++- src/pages/Search/SearchPageBottomTab.tsx | 29 +++++---------- src/pages/settings/InitialSettingsPage.tsx | 15 ++++---- 6 files changed, 37 insertions(+), 66 deletions(-) delete mode 100644 src/hooks/useActiveCentralPaneRoute.ts delete mode 100644 src/libs/Navigation/AppNavigator/Navigators/ActiveCentralPaneRouteContext.ts diff --git a/src/hooks/useActiveCentralPaneRoute.ts b/src/hooks/useActiveCentralPaneRoute.ts deleted file mode 100644 index 05354e810c3d..000000000000 --- a/src/hooks/useActiveCentralPaneRoute.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {useContext} from 'react'; -import ActiveCentralPaneRouteContext from '@libs/Navigation/AppNavigator/Navigators/ActiveCentralPaneRouteContext'; -import type {AuthScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types'; - -function useActiveCentralPaneRoute(): NavigationPartialRoute | undefined { - return useContext(ActiveCentralPaneRouteContext); -} - -export default useActiveCentralPaneRoute; diff --git a/src/libs/Navigation/AppNavigator/Navigators/ActiveCentralPaneRouteContext.ts b/src/libs/Navigation/AppNavigator/Navigators/ActiveCentralPaneRouteContext.ts deleted file mode 100644 index 6f37126584a2..000000000000 --- a/src/libs/Navigation/AppNavigator/Navigators/ActiveCentralPaneRouteContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import type {AuthScreensParamList, NavigationPartialRoute} from '@libs/Navigation/types'; - -const ActiveCentralPaneRouteContext = React.createContext | undefined>(undefined); - -export default ActiveCentralPaneRouteContext; diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx index 30e8d4c668c6..c045e01ee2dd 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx @@ -1,14 +1,11 @@ -import {useNavigationState} from '@react-navigation/native'; import type {StackNavigationOptions} from '@react-navigation/stack'; import React from 'react'; import createCustomBottomTabNavigator from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator'; -import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import type {BottomTabNavigatorParamList, CentralPaneName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import type {BottomTabNavigatorParamList} from '@libs/Navigation/types'; import SidebarScreen from '@pages/home/sidebar/SidebarScreen'; import SearchPageBottomTab from '@pages/Search/SearchPageBottomTab'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; -import ActiveCentralPaneRouteContext from './ActiveCentralPaneRouteContext'; const loadInitialSettingsPage = () => require('../../../../pages/settings/InitialSettingsPage').default; const Tab = createCustomBottomTabNavigator(); @@ -19,24 +16,21 @@ const screenOptions: StackNavigationOptions = { }; function BottomTabNavigator() { - const activeRoute = useNavigationState | undefined>(getTopmostCentralPaneRoute); return ( - - - - - - - + + + + + ); } diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 79183909c21f..a06979184ab8 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -18,6 +18,7 @@ import type {AuthScreensParamList} from '@libs/Navigation/types'; import * as SearchUtils from '@libs/SearchUtils'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import SearchPageBottomTab from './SearchPageBottomTab'; import SearchTypeMenu from './SearchTypeMenu'; type SearchPageProps = StackScreenProps; @@ -42,7 +43,12 @@ function SearchPage({route}: SearchPageProps) { // On small screens this page is not displayed, the configuration is in the file: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx // To avoid calling hooks in the Search component when this page isn't visible, we return null here. if (shouldUseNarrowLayout) { - return null; + return ( + + ); } return ( diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 9c46588bb68e..c35382ec5188 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -1,46 +1,35 @@ -import React, {useMemo} from 'react'; +import React from 'react'; import {useOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; import {useSearchContext} from '@components/Search/SearchContext'; -import useActiveCentralPaneRoute from '@hooks/useActiveCentralPaneRoute'; +import type {SearchQueryJSON} from '@components/Search/types'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import Navigation from '@libs/Navigation/Navigation'; -import type {AuthScreensParamList} from '@libs/Navigation/types'; import * as SearchUtils from '@libs/SearchUtils'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; import SearchTypeMenu from './SearchTypeMenu'; -function SearchPageBottomTab() { +type SearchPageBottomTabProps = { + queryJSON?: SearchQueryJSON; + policyID?: string; +}; + +function SearchPageBottomTab({queryJSON, policyID}: SearchPageBottomTabProps) { const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const activeCentralPaneRoute = useActiveCentralPaneRoute(); + const styles = useThemeStyles(); const {clearSelectedTransactions} = useSearchContext(); const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); - const {queryJSON, policyID} = useMemo(() => { - if (activeCentralPaneRoute?.name !== SCREENS.SEARCH.CENTRAL_PANE) { - return {queryJSON: undefined, policyID: undefined}; - } - - const searchParams = activeCentralPaneRoute?.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; - const parsedQuery = SearchUtils.buildSearchQueryJSON(searchParams?.q); - - return { - queryJSON: parsedQuery, - policyID: parsedQuery && SearchUtils.getPolicyIDFromSearchQuery(parsedQuery), - }; - }, [activeCentralPaneRoute]); - const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()})); return ( diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index e84788bea7a5..a546774b950a 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,4 +1,4 @@ -import {useRoute} from '@react-navigation/native'; +import {useNavigationState, useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; @@ -8,6 +8,7 @@ import {useOnyx, withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; import AccountSwitcherSkeletonView from '@components/AccountSwitcherSkeletonView'; +// eslint-disable-next-line no-restricted-imports import ConfirmModal from '@components/ConfirmModal'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -21,7 +22,6 @@ import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useActiveCentralPaneRoute from '@hooks/useActiveCentralPaneRoute'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useSingleExecution from '@hooks/useSingleExecution'; @@ -31,6 +31,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; +import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; import Navigation from '@libs/Navigation/Navigation'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -104,7 +105,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms const waitForNavigate = useWaitForNavigation(); const popoverAnchor = useRef(null); const {translate} = useLocalize(); - const activeCentralPaneRoute = useActiveCentralPaneRoute(); + const activeRoute = useNavigationState(getTopmostRouteName); const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? ''; const [allConnectionSyncProgresses] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}`); const {setInitialURL} = useContext(InitialURLContext); @@ -338,11 +339,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms hoverAndPressStyle={styles.hoveredComponentBG} shouldBlockSelection={!!item.link} onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} - focused={ - !!activeCentralPaneRoute && - !!item.routeName && - !!(activeCentralPaneRoute.name.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')) - } + focused={!!activeRoute && !!item.routeName && !!(activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} isPaneMenu iconRight={item.iconRight} shouldShowRightIcon={item.shouldShowRightIcon} @@ -362,7 +359,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms userWallet?.currentBalance, isExecuting, singleExecution, - activeCentralPaneRoute, + activeRoute, waitForNavigate, ], ); From 7418af5dfb1e3daa3a8b441b7e045c696958af62 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 5 Sep 2024 12:28:47 +0200 Subject: [PATCH 16/34] Fix displaying search page --- src/components/Search/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 766d4c2445ee..771c6bb17fd8 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -201,7 +201,7 @@ function Search({queryJSON}: SearchProps) { if (shouldShowLoadingState) { return ( - <> + )} - + ); } @@ -236,7 +236,7 @@ function Search({queryJSON}: SearchProps) { if (shouldShowEmptyState) { return ( - <> + - + ); } From 86cc6c40cd1cc66e7de149ef74efe3e143e963a9 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Sat, 7 Sep 2024 11:29:20 +0200 Subject: [PATCH 17/34] Refactor useActiveWorkspace --- src/App.tsx | 4 -- .../ActiveWorkspaceContext.tsx | 8 +--- .../ActiveWorkspaceProvider/index.tsx | 25 ++++++----- .../ActiveWorkspaceProvider/index.website.tsx | 29 ------------ src/components/Search/SearchPageHeader.tsx | 2 +- src/hooks/useActiveWorkspace.ts | 3 +- .../useActiveWorkspaceFromNavigationState.ts | 31 ------------- src/hooks/useReportIDs.tsx | 2 +- .../Navigation/AppNavigator/AuthScreens.tsx | 14 ++---- .../Navigators/ReportsSplitNavigator.tsx | 2 +- .../BottomTabBar.tsx | 4 +- .../CustomRouter.ts | 29 +++++++++--- src/libs/Navigation/NavigationRoot.tsx | 6 --- src/libs/Navigation/getPolicyIDFromState.ts | 20 ++++----- .../linkingConfig/createSplitNavigator.ts | 44 +++++++++++++++++++ .../linkingConfig/customGetPathFromState.ts | 17 ++++--- .../linkingConfig/getAdaptedStateFromPath.ts | 5 ++- src/libs/Navigation/linkingConfig/index.ts | 2 +- src/libs/Navigation/types.ts | 2 +- src/pages/Search/SearchPageBottomTab.tsx | 3 ++ src/pages/WorkspaceSwitcherPage/index.tsx | 5 +-- src/pages/home/ReportScreen.tsx | 3 +- src/pages/home/sidebar/SidebarLinksData.tsx | 6 +-- .../SidebarScreen/BaseSidebarScreen.tsx | 4 +- src/pages/workspace/WorkspaceNewRoomPage.tsx | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 5 +-- src/pages/workspace/WorkspacesListPage.tsx | 3 +- 27 files changed, 129 insertions(+), 151 deletions(-) delete mode 100644 src/components/ActiveWorkspaceProvider/index.website.tsx delete mode 100644 src/hooks/useActiveWorkspaceFromNavigationState.ts create mode 100644 src/libs/Navigation/linkingConfig/createSplitNavigator.ts diff --git a/src/App.tsx b/src/App.tsx index cf0fd5528eec..76cf2f2385e0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,6 @@ import {PickerStateProvider} from 'react-native-picker-select'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import '../wdyr'; import ActiveElementRoleProvider from './components/ActiveElementRoleProvider'; -import ActiveWorkspaceContextProvider from './components/ActiveWorkspaceProvider'; import ColorSchemeWrapper from './components/ColorSchemeWrapper'; import ComposeProviders from './components/ComposeProviders'; import CustomStatusBarAndBackground from './components/CustomStatusBarAndBackground'; @@ -33,7 +32,6 @@ import {KeyboardStateProvider} from './components/withKeyboardState'; import CONFIG from './CONFIG'; import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; -import {ReportIDsContextProvider} from './hooks/useReportIDs'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; @@ -87,8 +85,6 @@ function App({url}: AppProps) { EnvironmentProvider, CustomStatusBarAndBackgroundContextProvider, ActiveElementRoleProvider, - ActiveWorkspaceContextProvider, - ReportIDsContextProvider, PlaybackContextProvider, FullScreenContextProvider, VolumeContextProvider, diff --git a/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx b/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx index 466f0f492c8e..7908020d16d2 100644 --- a/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx +++ b/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx @@ -1,11 +1,5 @@ import {createContext} from 'react'; -type ActiveWorkspaceContextType = { - activeWorkspaceID?: string; - setActiveWorkspaceID: (activeWorkspaceID?: string) => void; -}; - -const ActiveWorkspaceContext = createContext({activeWorkspaceID: undefined, setActiveWorkspaceID: () => undefined}); +const ActiveWorkspaceContext = createContext(undefined); export default ActiveWorkspaceContext; -export {type ActiveWorkspaceContextType}; diff --git a/src/components/ActiveWorkspaceProvider/index.tsx b/src/components/ActiveWorkspaceProvider/index.tsx index bc7260cdf10b..19cadaca6318 100644 --- a/src/components/ActiveWorkspaceProvider/index.tsx +++ b/src/components/ActiveWorkspaceProvider/index.tsx @@ -1,19 +1,24 @@ -import React, {useMemo, useState} from 'react'; +import {useNavigationState} from '@react-navigation/native'; +import React from 'react'; import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; +import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; function ActiveWorkspaceContextProvider({children}: ChildrenProps) { - const [activeWorkspaceID, setActiveWorkspaceID] = useState(undefined); + const activeWorkspaceID = useNavigationState(getPolicyIDFromState); - const value = useMemo( - () => ({ - activeWorkspaceID, - setActiveWorkspaceID, - }), - [activeWorkspaceID, setActiveWorkspaceID], - ); + // @TODO Remember to handle saving activeWorkspaceID in the session storage - return {children}; + // const setActiveWorkspaceID = useCallback((workspaceID: string | undefined) => { + // updateActiveWorkspaceID(workspaceID); + // if (workspaceID && sessionStorage) { + // sessionStorage?.setItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID, workspaceID); + // } else { + // sessionStorage?.removeItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); + // } + // }, []); + + return {children}; } export default ActiveWorkspaceContextProvider; diff --git a/src/components/ActiveWorkspaceProvider/index.website.tsx b/src/components/ActiveWorkspaceProvider/index.website.tsx deleted file mode 100644 index 82e46d70f896..000000000000 --- a/src/components/ActiveWorkspaceProvider/index.website.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, {useCallback, useMemo, useState} from 'react'; -import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; -import CONST from '@src/CONST'; -import type ChildrenProps from '@src/types/utils/ChildrenProps'; - -function ActiveWorkspaceContextProvider({children}: ChildrenProps) { - const [activeWorkspaceID, updateActiveWorkspaceID] = useState(undefined); - - const setActiveWorkspaceID = useCallback((workspaceID: string | undefined) => { - updateActiveWorkspaceID(workspaceID); - if (workspaceID && sessionStorage) { - sessionStorage?.setItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID, workspaceID); - } else { - sessionStorage?.removeItem(CONST.SESSION_STORAGE_KEYS.ACTIVE_WORKSPACE_ID); - } - }, []); - - const value = useMemo( - () => ({ - activeWorkspaceID, - setActiveWorkspaceID, - }), - [activeWorkspaceID, setActiveWorkspaceID], - ); - - return {children}; -} - -export default ActiveWorkspaceContextProvider; diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 73829989409c..e884daf4e788 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -125,7 +125,7 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); - const {activeWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = useActiveWorkspace(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {selectedTransactions} = useSearchContext(); const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); diff --git a/src/hooks/useActiveWorkspace.ts b/src/hooks/useActiveWorkspace.ts index cce3c2a4b31f..11a986cdb053 100644 --- a/src/hooks/useActiveWorkspace.ts +++ b/src/hooks/useActiveWorkspace.ts @@ -1,8 +1,7 @@ import {useContext} from 'react'; import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; -import type {ActiveWorkspaceContextType} from '@components/ActiveWorkspace/ActiveWorkspaceContext'; -function useActiveWorkspace(): ActiveWorkspaceContextType { +function useActiveWorkspace(): string { return useContext(ActiveWorkspaceContext); } diff --git a/src/hooks/useActiveWorkspaceFromNavigationState.ts b/src/hooks/useActiveWorkspaceFromNavigationState.ts deleted file mode 100644 index d2851e83ab6c..000000000000 --- a/src/hooks/useActiveWorkspaceFromNavigationState.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {useNavigationState} from '@react-navigation/native'; -import Log from '@libs/Log'; -import type {BottomTabNavigatorParamList} from '@libs/Navigation/types'; -import SCREENS from '@src/SCREENS'; - -/** - * Get the currently selected policy ID stored in the navigation state. This hook should only be called only from screens in BottomTab. - * Differences between this hook and useActiveWorkspace: - * - useActiveWorkspaceFromNavigationState reads the active workspace id directly from the navigation state, it's a bit slower than useActiveWorkspace and it can be called only from BottomTabScreens. - * It allows to read a value of policyID immediately after the update. - * - useActiveWorkspace allows to read the current policyID anywhere, it's faster because it doesn't require searching in the navigation state. - */ -function useActiveWorkspaceFromNavigationState() { - // The last policyID value is always stored in the last route in BottomTabNavigator. - const activeWorkspaceID = useNavigationState((state) => { - // SCREENS.HOME is a screen located in the BottomTabNavigator, if it's not in state.routeNames it means that this hook was called from a screen in another navigator. - if (!state.routeNames.includes(SCREENS.HOME)) { - Log.warn('useActiveWorkspaceFromNavigationState should be called only from BottomTab screens'); - } - - const params = state.routes.at(-1)?.params ?? {}; - - if ('policyID' in params) { - return params?.policyID; - } - }); - - return activeWorkspaceID; -} - -export default useActiveWorkspaceFromNavigationState; diff --git a/src/hooks/useReportIDs.tsx b/src/hooks/useReportIDs.tsx index b7d84cb25196..d51d9e53c48c 100644 --- a/src/hooks/useReportIDs.tsx +++ b/src/hooks/useReportIDs.tsx @@ -91,7 +91,7 @@ function ReportIDsContextProvider({ const {accountID} = useCurrentUserPersonalDetails(); const currentReportIDValue = useCurrentReportID(); const derivedCurrentReportID = currentReportIDForTests ?? currentReportIDValue?.currentReportID; - const {activeWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = useActiveWorkspace(); const policyMemberAccountIDs = useMemo(() => getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID), [policies, activeWorkspaceID, accountID]); diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index ba323f9fd31f..c33142236265 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -3,11 +3,13 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Onyx, {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import ActiveWorkspaceContextProvider from '@components/ActiveWorkspaceProvider'; import ComposeProviders from '@components/ComposeProviders'; import OptionsListContextProvider from '@components/OptionListContextProvider'; import {SearchContextProvider} from '@components/Search/SearchContext'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import usePermissions from '@hooks/usePermissions'; +import {ReportIDsContextProvider} from '@hooks/useReportIDs'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -218,7 +220,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie const {shouldUseNarrowLayout, onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils); const {canUseDefaultRooms} = usePermissions(); - const {activeWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = useActiveWorkspace(); const onboardingModalScreenOptions = useMemo(() => screenOptions.onboardingModalNavigator(onboardingIsMediumOrLargerScreenWidth), [screenOptions, onboardingIsMediumOrLargerScreenWidth]); const onboardingScreenOptions = useMemo( () => getOnboardingModalScreenOptions(shouldUseNarrowLayout, styles, StyleUtils, onboardingIsMediumOrLargerScreenWidth), @@ -384,16 +386,8 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); - const CentralPaneScreenOptions = { - headerShown: false, - title: 'New Expensify', - - // Prevent unnecessary scrolling - cardStyle: styles.cardStyleNavigator, - }; - return ( - + (getChatTabBrickRoad(activeWorkspaceID)); @@ -168,7 +168,7 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) { diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 62517376798b..9d8e046f2fea 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -2,9 +2,11 @@ import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigatio import {findFocusedRoute, getPathFromState, StackRouter} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/routers'; import * as Localize from '@libs/Localize'; +import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import isSideModalNavigator from '@libs/Navigation/isSideModalNavigator'; import linkingConfig from '@libs/Navigation/linkingConfig'; -import {RootStackParamList} from '@libs/Navigation/types'; +import createSplitNavigator from '@libs/Navigation/linkingConfig/createSplitNavigator'; +import type {RootStackParamList} from '@libs/Navigation/types'; import {isOnboardingFlowName} from '@libs/NavigationUtils'; import * as SearchUtils from '@libs/SearchUtils'; import * as Welcome from '@userActions/Welcome'; @@ -100,18 +102,19 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { if (action.payload.policyID) { queryJSON.policyID = action.payload.policyID; - newParams = {...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON)}; } else { delete queryJSON.policyID; - newParams = {...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON)}; } - const newRoutes = [...state.routes, {...lastRoute, params: newParams}]; + newParams = {...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON)}; + const newRoutes = [...state.routes, {...lastRoute, key: lastRoute.key + 'key', params: newParams}]; return {...state, routes: newRoutes, index: newRoutes.length - 1}; } if (lastRoute?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { // TODO: handle change policy id of reports navigator. - return; + const newRoute = createSplitNavigator('Home', {name: SCREENS.REPORT, params: {reportID: ''}}, action.payload.policyID, lastRoute.key + 'key'); + const newRoutes = [...state.routes, newRoute]; + return {...state, routes: newRoutes, index: newRoutes.length - 1}; } // In other cases, do nothing. return null; @@ -122,6 +125,22 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { return state; } + if (action.type === 'PUSH' && action.payload.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { + const policyID = getPolicyIDFromState(state); + const currentParams = {...action.payload.params}; + action.payload.params = {...currentParams, policyID}; + } + + if (action.type === 'PUSH' && action.payload.name === SCREENS.SEARCH.CENTRAL_PANE) { + const policyID = getPolicyIDFromState(state); + const params = policyID ? {policyID} : {}; + action.payload.params = {q: SearchUtils.buildSearchQueryString(params), isCustomQuery: false}; + } + + if (action.type === 'RESET') { + const policyID = getPolicyIDFromState(action.payload); + } + // TODO: I don't remember if the code below makes sense Wojtek :D but it's possible. // One part seems redundant to the lines 102-108 above // diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index a1aa53bc0b7e..7dd62c327f38 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -5,7 +5,6 @@ import {NativeModules} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import HybridAppMiddleware from '@components/HybridAppMiddleware'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; -import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useCurrentReportID from '@hooks/useCurrentReportID'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; @@ -22,13 +21,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import AppNavigator from './AppNavigator'; -import getPolicyIDFromState from './getPolicyIDFromState'; import linkingConfig from './linkingConfig'; import customGetPathFromState from './linkingConfig/customGetPathFromState'; import getAdaptedStateFromPath from './linkingConfig/getAdaptedStateFromPath'; import Navigation, {navigationRef} from './Navigation'; import setupCustomAndroidBackHandler from './setupCustomAndroidBackHandler'; -import type {RootStackParamList} from './types'; type NavigationRootProps = { /** Whether the current user is logged in with an authToken */ @@ -89,7 +86,6 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh const currentReportIDValue = useCurrentReportID(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const {setActiveWorkspaceID} = useActiveWorkspace(); const [user] = useOnyx(ONYXKEYS.USER); const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { @@ -162,11 +158,9 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh const currentRoute = navigationRef.getCurrentRoute(); Firebase.log(`[NAVIGATION] screen: ${currentRoute?.name}, params: ${JSON.stringify(currentRoute?.params ?? {})}`); - const activeWorkspaceID = getPolicyIDFromState(state as NavigationState); // Performance optimization to avoid context consumers to delay first render setTimeout(() => { currentReportIDValue?.updateCurrentReportID(state); - setActiveWorkspaceID(activeWorkspaceID); }, 0); parseAndLogRoute(state); diff --git a/src/libs/Navigation/getPolicyIDFromState.ts b/src/libs/Navigation/getPolicyIDFromState.ts index 702fb654780d..8077c5eea998 100644 --- a/src/libs/Navigation/getPolicyIDFromState.ts +++ b/src/libs/Navigation/getPolicyIDFromState.ts @@ -1,8 +1,7 @@ +import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import extractPolicyIDFromQuery from './extractPolicyIDFromQuery'; -import getTopmostBottomTabRoute from './getTopmostBottomTabRoute'; -import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; -import type {RootStackParamList, State} from './types'; +import type {NavigationPartialRoute, RootStackParamList, State} from './types'; /** * returns policyID value if one exists in navigation state @@ -12,19 +11,16 @@ import type {RootStackParamList, State} from './types'; * - on Search related screens as policyID filter inside `q` (SearchQuery) param (only for SEARCH_CENTRAL_PANE) */ const getPolicyIDFromState = (state: State): string | undefined => { - const topmostBottomTabRoute = getTopmostBottomTabRoute(state); - - if (!topmostBottomTabRoute) { - return; + const lastPolicyRoute = state.routes.findLast((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR || route.name === SCREENS.SEARCH.CENTRAL_PANE); + if (lastPolicyRoute?.params && 'policyID' in lastPolicyRoute.params) { + return lastPolicyRoute?.params?.policyID; } - if (topmostBottomTabRoute.name === SCREENS.SEARCH.BOTTOM_TAB) { - const topmostCentralPaneRoute = getTopmostCentralPaneRoute(state); - return extractPolicyIDFromQuery(topmostCentralPaneRoute); + if (lastPolicyRoute) { + return extractPolicyIDFromQuery(lastPolicyRoute as NavigationPartialRoute); } - const policyID = topmostBottomTabRoute && topmostBottomTabRoute.params && 'policyID' in topmostBottomTabRoute.params && topmostBottomTabRoute.params?.policyID; - return policyID ? (topmostBottomTabRoute.params?.policyID as string) : undefined; + return undefined; }; export default getPolicyIDFromState; diff --git a/src/libs/Navigation/linkingConfig/createSplitNavigator.ts b/src/libs/Navigation/linkingConfig/createSplitNavigator.ts new file mode 100644 index 000000000000..7ee473560955 --- /dev/null +++ b/src/libs/Navigation/linkingConfig/createSplitNavigator.ts @@ -0,0 +1,44 @@ +import type {NavigationState, PartialState} from '@react-navigation/native'; +import type {NavigationPartialRoute, SplitNavigatorScreenName} from '@libs/Navigation/types'; +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; + +const mapLhnToSplitNavigatorName = { + [SCREENS.SETTINGS.ROOT]: NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, + [SCREENS.HOME]: NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, + [SCREENS.WORKSPACE.INITIAL]: NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, +}; + +type SplitNavigatorLHNScreen = keyof typeof mapLhnToSplitNavigatorName; +type SplitNavigator = (typeof mapLhnToSplitNavigatorName)[SplitNavigatorLHNScreen]; + +// The function getPathFromState that we are using in some places isn't working correctly without defined index. +const getRoutesWithIndex = (routes: NavigationPartialRoute[]): PartialState => ({routes, index: routes.length - 1}); + +function createSplitNavigator( + splitNavigatorLHNScreen: SplitNavigatorLHNScreen, + route?: NavigationPartialRoute, + policyID?: string, + key?: string, +): NavigationPartialRoute { + const routes = []; + + const params = policyID ? {policyID} : {}; + + // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator + routes.push({ + name: splitNavigatorLHNScreen, + }); + + if (route) { + routes.push(route); + } + return { + key, + name: mapLhnToSplitNavigatorName[splitNavigatorLHNScreen], + state: getRoutesWithIndex(routes), + params, + }; +} + +export default createSplitNavigator; diff --git a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts index a9c9b6f23b19..199e6656dabd 100644 --- a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts +++ b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts @@ -1,20 +1,19 @@ import {getPathFromState} from '@react-navigation/native'; -import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; -import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; -import type {BottomTabName, RootStackParamList, State} from '@libs/Navigation/types'; +import type {RootStackParamList, State} from '@libs/Navigation/types'; import {removePolicyIDParamFromState} from '@libs/NavigationUtils'; -import SCREENS from '@src/SCREENS'; +import NAVIGATORS from '@src/NAVIGATORS'; -// The policy ID parameter should be included in the URL when any of these pages is opened in the bottom tab. -const SCREENS_WITH_POLICY_ID_IN_URL: BottomTabName[] = [SCREENS.HOME] as const; +function getTopmostSplitNavigator(state: State | undefined) { + return state?.routes.findLast((route) => route.name.endsWith('SplitNavigator')); +} const customGetPathFromState: typeof getPathFromState = (state, options) => { // For the Home and Settings pages we should remove policyID from the params, because on small screens it's displayed twice in the URL const stateWithoutPolicyID = removePolicyIDParamFromState(state as State); const path = getPathFromState(stateWithoutPolicyID, options); - const policyIDFromState = getPolicyIDFromState(state as State); - const topmostBottomTabRouteName = getTopmostBottomTabRoute(state as State)?.name; - const shouldAddPolicyID = !!topmostBottomTabRouteName && SCREENS_WITH_POLICY_ID_IN_URL.includes(topmostBottomTabRouteName); + const topmostSplitNavigator = getTopmostSplitNavigator(state as State); + const policyIDFromState = topmostSplitNavigator?.params?.policyID; + const shouldAddPolicyID = !!topmostSplitNavigator && topmostSplitNavigator?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR; return `${policyIDFromState && shouldAddPolicyID ? `/w/${policyIDFromState}` : ''}${path}`; }; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index a6dfa775f8a8..9fdda9360cd3 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -139,7 +139,7 @@ function createSplitNavigator(splitNavigatorLHNScreen: SplitNavigatorLHNScreen, } // This function will return CentralPaneNavigator route or WorkspaceNavigator route. -function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | undefined { +function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | undefined { // Check for backTo param. One screen with different backTo value may need diferent screens visible under the overlay. if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { const stateForBackTo = getStateFromPath(route.params.backTo, config); @@ -159,7 +159,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat // If we know that backTo targets the root route (central pane or full screen) we want to use it. if (centralPaneOrWorkspaceNavigator && centralPaneOrWorkspaceNavigator.state) { - return centralPaneOrWorkspaceNavigator as NavigationPartialRoute; + return centralPaneOrWorkspaceNavigator as NavigationPartialRoute; } } } @@ -367,6 +367,7 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options, shouldR if (shouldReplacePathInNestedState) { replacePathInNestedState(state, path); } + if (state === undefined) { throw new Error('Unable to parse path'); } diff --git a/src/libs/Navigation/linkingConfig/index.ts b/src/libs/Navigation/linkingConfig/index.ts index 7bb5e8ae5b5a..1f556aa67809 100644 --- a/src/libs/Navigation/linkingConfig/index.ts +++ b/src/libs/Navigation/linkingConfig/index.ts @@ -15,7 +15,7 @@ const linkingConfig: LinkingOptions = { return adaptedState; }, subscribe, - // getPathFromState: customGetPathFromState, + getPathFromState: customGetPathFromState, prefixes, config, }; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index d3834112262b..5318a3df5230 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1385,7 +1385,7 @@ type AuthScreensParamList = CentralPaneScreensParamList & }; [SCREENS.NOT_FOUND]: undefined; [NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: NavigatorScreenParams & {policyID?: string}; - [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: NavigatorScreenParams & {policyID?: string}; [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index c35382ec5188..08329afc36ca 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -10,11 +10,13 @@ import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; +import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; import Navigation from '@libs/Navigation/Navigation'; import * as SearchUtils from '@libs/SearchUtils'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import SearchTypeMenu from './SearchTypeMenu'; type SearchPageBottomTabProps = { @@ -62,6 +64,7 @@ function SearchPageBottomTab({queryJSON, policyID}: SearchPageBottomTabProps) { /> )} {shouldUseNarrowLayout && queryJSON && } + ); diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index 51f76c811557..c2c9c93b71ff 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -41,7 +41,7 @@ function WorkspaceSwitcherPage() { const {isOffline} = useNetwork(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const {translate} = useLocalize(); - const {activeWorkspaceID, setActiveWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = useActiveWorkspace(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); @@ -90,14 +90,13 @@ function WorkspaceSwitcherPage() { const {policyID} = option; - // setActiveWorkspaceID(policyID); Navigation.goBack(); if (policyID !== activeWorkspaceID) { // Navigation.navigateWithSwitchPolicyID({policyID}); navigationRef.dispatch({type: 'SWITCH_POLICY_ID', payload: {policyID}}); } }, - [activeWorkspaceID, setActiveWorkspaceID], + [activeWorkspaceID], ); const usersWorkspaces = useMemo(() => { diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4491a12e6bbd..18c167774679 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -109,8 +109,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const reactionListRef = useRef(null); const {isOffline} = useNetwork(); const {shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout(); - const {activeWorkspaceID} = useActiveWorkspace(); - + const activeWorkspaceID = useActiveWorkspace(); const [modal] = useOnyx(ONYXKEYS.MODAL); const [isComposerFullSize] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportIDFromRoute}`, {initialValue: false}); const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, {initialValue: ''}); diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index e5a74db796d8..d83b4758ce52 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -6,7 +6,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; import type {ValueOf} from 'type-fest'; -import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import {useReportIDs} from '@hooks/useReportIDs'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -34,7 +34,7 @@ type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { function SidebarLinksData({insets, isLoadingApp = true, onLinkClick, priorityMode = CONST.PRIORITY_MODE.DEFAULT}: SidebarLinksDataProps) { const isFocused = useIsFocused(); const styles = useThemeStyles(); - const activeWorkspaceID = useActiveWorkspaceFromNavigationState(); + const activeWorkspaceID = useActiveWorkspace(); const {translate} = useLocalize(); const {orderedReportIDs, currentReportID, policyMemberAccountIDs} = useReportIDs(); @@ -52,7 +52,6 @@ function SidebarLinksData({insets, isLoadingApp = true, onLinkClick, priorityMod const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; const isActiveReport = useCallback((reportID: string): boolean => currentReportIDRef.current === reportID, []); - return ( diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index b82865dd13c7..71a8587fb889 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -2,7 +2,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import ScreenWrapper from '@components/ScreenWrapper'; -import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {updateLastAccessedWorkspace} from '@libs/actions/Policy/Policy'; @@ -27,7 +27,7 @@ const startTimer = () => { function BaseSidebarScreen() { const styles = useThemeStyles(); - const activeWorkspaceID = useActiveWorkspaceFromNavigationState(); + const activeWorkspaceID = useActiveWorkspace(); const {translate} = useLocalize(); const [activeWorkspace] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID ?? -1}`); diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index c0e953a6a350..254afded739d 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -71,7 +71,7 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli const wasLoading = usePrevious(!!formState?.isLoading); const visibilityDescription = useMemo(() => translate(`newRoomPage.${visibility}Description`), [translate, visibility]); const {isLoading = false, errorFields = {}} = formState ?? {}; - const {activeWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = useActiveWorkspace(); const activeWorkspaceOrDefaultID = activeWorkspaceID ?? activePolicyID; diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index e99afe7ba408..9fce5cd9dff7 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -52,7 +52,7 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, currencyList = { const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const illustrations = useThemeIllustrations(); - const {activeWorkspaceID, setActiveWorkspaceID} = useActiveWorkspace(); + const activeWorkspaceID = route.params.policyID; const {canUseSpotnanaTravel} = usePermissions(); const [currentUserAccountID = -1] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID}); @@ -136,10 +136,9 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, currencyList = { // If the workspace being deleted is the active workspace, switch to the "All Workspaces" view if (activeWorkspaceID === policy?.id) { - setActiveWorkspaceID(undefined); Navigation.navigateWithSwitchPolicyID({policyID: undefined}); } - }, [policy?.id, policyName, activeWorkspaceID, setActiveWorkspaceID]); + }, [policy?.id, policyName, activeWorkspaceID]); return ( (); @@ -144,7 +144,6 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: // If the workspace being deleted is the active workspace, switch to the "All Workspaces" view if (activeWorkspaceID === policyIDToDelete) { - setActiveWorkspaceID(undefined); Navigation.navigateWithSwitchPolicyID({policyID: undefined}); } }; From e2832eed6e2c9b9beb862cba4351b7bb73505281 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 11 Sep 2024 11:53:37 +0200 Subject: [PATCH 18/34] Add createSplitNavigator --- .../ActiveWorkspaceProvider/index.tsx | 31 +++- .../Navigation/AppNavigator/AuthScreens.tsx | 2 + .../CustomRouter.ts | 40 ++-- .../AppNavigator/getActionsFromPartialDiff.ts | 2 +- .../AppNavigator/getPartialStateDiff.ts | 6 +- src/libs/Navigation/Navigation.ts | 28 ++- src/libs/Navigation/dismissModal.ts | 2 +- src/libs/Navigation/dismissModalWithReport.ts | 2 +- src/libs/Navigation/getPolicyIDFromState.ts | 2 +- .../Navigation/getTopmostWorkspaceRoute.ts | 2 +- src/libs/Navigation/linkTo/index.ts | 6 +- .../CENTRAL_PANE_TO_RHP_MAPPING.ts | 20 -- .../LHN_TO_SPLIT_NAVIGATOR_MAPPING.ts | 10 + .../linkingConfig/SEARCH_RHP_SCREENS.ts | 24 +++ .../TAB_TO_CENTRAL_PANE_MAPPING.ts | 4 +- .../linkingConfig/createSplitNavigator.ts | 38 ++-- .../linkingConfig/getAdaptedStateFromPath.ts | 171 +++++------------- .../getMatchingBottomTabRouteForState.ts | 2 +- src/libs/Navigation/newLinkTo/index.ts | 6 +- src/libs/Navigation/types.ts | 16 +- 20 files changed, 206 insertions(+), 208 deletions(-) create mode 100644 src/libs/Navigation/linkingConfig/LHN_TO_SPLIT_NAVIGATOR_MAPPING.ts create mode 100644 src/libs/Navigation/linkingConfig/SEARCH_RHP_SCREENS.ts diff --git a/src/components/ActiveWorkspaceProvider/index.tsx b/src/components/ActiveWorkspaceProvider/index.tsx index 19cadaca6318..011b58720d35 100644 --- a/src/components/ActiveWorkspaceProvider/index.tsx +++ b/src/components/ActiveWorkspaceProvider/index.tsx @@ -1,14 +1,37 @@ import {useNavigationState} from '@react-navigation/native'; -import React from 'react'; +import React, {useMemo} from 'react'; import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; -import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; +import * as SearchUtils from '@libs/SearchUtils'; +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; function ActiveWorkspaceContextProvider({children}: ChildrenProps) { - const activeWorkspaceID = useNavigationState(getPolicyIDFromState); + const lastPolicyRoute = useNavigationState((state) => + state?.routes?.findLast((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR || route.name === SCREENS.SEARCH.CENTRAL_PANE), + ); - // @TODO Remember to handle saving activeWorkspaceID in the session storage + const policyIDFromRouteParam = lastPolicyRoute?.params && 'policyID' in lastPolicyRoute.params ? (lastPolicyRoute?.params?.policyID as string) : ''; + const queryFromRouteParam = lastPolicyRoute?.params && 'q' in lastPolicyRoute.params ? (lastPolicyRoute.params.q as string) : ''; + + const activeWorkspaceID = useMemo(() => { + if (policyIDFromRouteParam) { + return policyIDFromRouteParam; + } + + if (queryFromRouteParam) { + const queryJSON = SearchUtils.buildSearchQueryJSON(queryFromRouteParam); + if (!queryJSON) { + return undefined; + } + return SearchUtils.getPolicyIDFromSearchQuery(queryJSON); + } + + return undefined; + }, [policyIDFromRouteParam, queryFromRouteParam]); + + // @TODO Remember to handle saving activeWorkspaceID in the session storage // const setActiveWorkspaceID = useCallback((workspaceID: string | undefined) => { // updateActiveWorkspaceID(workspaceID); // if (workspaceID && sessionStorage) { diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index c33142236265..3fc647aec865 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -92,6 +92,7 @@ function shouldOpenOnAdminRoom() { return url ? new URL(url).searchParams.get('openOnAdminRoom') === 'true' : false; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars function getCentralPaneScreenInitialParams(screenName: CentralPaneName, initialReportID?: string): Partial> { if (screenName === SCREENS.SEARCH.CENTRAL_PANE) { // Generate default query string with buildSearchQueryString without argument. @@ -408,6 +409,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie name={SCREENS.SEARCH.CENTRAL_PANE} options={screenOptions.fullScreen} component={SearchPage} + initialParams={{q: buildSearchQueryString()}} /> ); const currentParams = {...action.payload.params}; action.payload.params = {...currentParams, policyID}; } if (action.type === 'PUSH' && action.payload.name === SCREENS.SEARCH.CENTRAL_PANE) { - const policyID = getPolicyIDFromState(state); - const params = policyID ? {policyID} : {}; - action.payload.params = {q: SearchUtils.buildSearchQueryString(params), isCustomQuery: false}; - } + const policyID = getPolicyIDFromState(state as State); + const currentParams = action.payload.params as RootStackParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; + const queryJSON = SearchUtils.buildSearchQueryJSON(currentParams.q); + if (!queryJSON) { + return; + } + if (policyID) { + queryJSON.policyID = policyID; + } else { + delete queryJSON.policyID; + } - if (action.type === 'RESET') { - const policyID = getPolicyIDFromState(action.payload); + action.payload.params = {q: SearchUtils.buildSearchQueryString(queryJSON), isCustomQuery: false}; } // TODO: I don't remember if the code below makes sense Wojtek :D but it's possible. diff --git a/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts b/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts index d66eb313cbb1..1cbb5ec3d718 100644 --- a/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts +++ b/src/libs/Navigation/AppNavigator/getActionsFromPartialDiff.ts @@ -13,7 +13,7 @@ function getActionsFromPartialDiff(diff: GetPartialStateDiffReturnType): Navigat const bottomTabDiff = diff[NAVIGATORS.BOTTOM_TAB_NAVIGATOR]; const centralPaneDiff = diff[NAVIGATORS.CENTRAL_PANE_NAVIGATOR]; - const fullScreenDiff = diff[NAVIGATORS.WORKSPACE_NAVIGATOR]; + const fullScreenDiff = diff[NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]; // There is only one bottom tab navigator so we can just push this route. if (bottomTabDiff) { diff --git a/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts b/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts index 974f98dccccb..79def4ee9218 100644 --- a/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts +++ b/src/libs/Navigation/AppNavigator/getPartialStateDiff.ts @@ -9,7 +9,7 @@ import NAVIGATORS from '@src/NAVIGATORS'; type GetPartialStateDiffReturnType = { [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]?: NavigationPartialRoute; [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]?: NavigationPartialRoute; - [NAVIGATORS.WORKSPACE_NAVIGATOR]?: NavigationPartialRoute; + [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]?: NavigationPartialRoute; }; /** @@ -65,7 +65,7 @@ function getPartialStateDiff(state: State, templateState: St if (metainfo.isWorkspaceNavigatorMandatory) { const stateTopmostWorkspaceRoute = getTopmostWorkspaceRoute(state); const templateStateTopmostWorkspaceRoute = getTopmostWorkspaceRoute(templateState); - const workspaceNavDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1) as NavigationPartialRoute; + const workspaceNavDiff = templateState.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR).at(-1) as NavigationPartialRoute; if ( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -78,7 +78,7 @@ function getPartialStateDiff(state: State, templateState: St templateStateTopmostWorkspaceRoute.params as Record | undefined, ))) ) { - diff[NAVIGATORS.WORKSPACE_NAVIGATOR] = workspaceNavDiff; + diff[NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR] = workspaceNavDiff; } } diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 1ea5822a1333..d5761da9378b 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -89,7 +89,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number if ( 'name' in stateOrRoute && - (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.WORKSPACE_NAVIGATOR) + (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR) ) { return 0; } @@ -190,6 +190,28 @@ function navigate(route: Route = ROUTES.HOME, type?: string) { linkTo(navigationRef.current, route, type, isActiveRoute(route)); } +function newGoBack(fallbackRoute?: Route, shouldEnforceFallback = false, shouldPopToTop = false) { + if (!canNavigate('goBack')) { + return; + } + + if (!navigationRef.current?.canGoBack()) { + Log.hmmm('[Navigation] Unable to go back'); + return; + } + navigationRef.current.goBack(); + + if (fallbackRoute) { + /** + * Cases to handle: + * 1. RHP + * 2. fallbackRoute is in the current navigator + * 3. fallbackRoute is in the different navigator + * 4. fallbackRoute isn't present in the current state + */ + } +} + /** * @param fallbackRoute - Fallback route if pop/goBack action should, but is not possible within RHP * @param shouldEnforceFallback - Enforces navigation to fallback route @@ -225,7 +247,7 @@ function goBack(fallbackRoute?: Route, shouldEnforceFallback = false, shouldPopT } if (shouldEnforceFallback || (isFirstRouteInNavigator && fallbackRoute)) { - navigate(fallbackRoute, CONST.NAVIGATION.TYPE.UP); + navigate(fallbackRoute, 'REPLACE'); return; } @@ -235,7 +257,7 @@ function goBack(fallbackRoute?: Route, shouldEnforceFallback = false, shouldPopT if (isCentralPaneFocused && fallbackRoute) { // Allow CentralPane to use UP with fallback route if the path is not found in root navigator. if (distanceFromPathInRootNavigator === -1) { - navigate(fallbackRoute, CONST.NAVIGATION.TYPE.UP); + navigate(fallbackRoute, 'REPLACE'); return; } diff --git a/src/libs/Navigation/dismissModal.ts b/src/libs/Navigation/dismissModal.ts index ea00a8646c85..ff24f8f944b1 100644 --- a/src/libs/Navigation/dismissModal.ts +++ b/src/libs/Navigation/dismissModal.ts @@ -18,7 +18,7 @@ function dismissModal(navigationRef: NavigationContainerRef) const state = navigationRef.getState(); const lastRoute = state.routes.at(-1); switch (lastRoute?.name) { - case NAVIGATORS.WORKSPACE_NAVIGATOR: + case NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR: case NAVIGATORS.LEFT_MODAL_NAVIGATOR: case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR: diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index 99f23a25ad3a..eeebb7bd8a2a 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -34,7 +34,7 @@ function dismissModalWithReport(targetReport: OnyxEntry, navigationRef: const state = navigationRef.getState(); const lastRoute = state.routes.at(-1); switch (lastRoute?.name) { - case NAVIGATORS.WORKSPACE_NAVIGATOR: + case NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR: case NAVIGATORS.LEFT_MODAL_NAVIGATOR: case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case SCREENS.NOT_FOUND: diff --git a/src/libs/Navigation/getPolicyIDFromState.ts b/src/libs/Navigation/getPolicyIDFromState.ts index 8077c5eea998..f5604c8a3733 100644 --- a/src/libs/Navigation/getPolicyIDFromState.ts +++ b/src/libs/Navigation/getPolicyIDFromState.ts @@ -11,7 +11,7 @@ import type {NavigationPartialRoute, RootStackParamList, State} from './types'; * - on Search related screens as policyID filter inside `q` (SearchQuery) param (only for SEARCH_CENTRAL_PANE) */ const getPolicyIDFromState = (state: State): string | undefined => { - const lastPolicyRoute = state.routes.findLast((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR || route.name === SCREENS.SEARCH.CENTRAL_PANE); + const lastPolicyRoute = state?.routes?.findLast((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR || route.name === SCREENS.SEARCH.CENTRAL_PANE); if (lastPolicyRoute?.params && 'policyID' in lastPolicyRoute.params) { return lastPolicyRoute?.params?.policyID; } diff --git a/src/libs/Navigation/getTopmostWorkspaceRoute.ts b/src/libs/Navigation/getTopmostWorkspaceRoute.ts index 4e9c5c607c5e..04e1237643dd 100644 --- a/src/libs/Navigation/getTopmostWorkspaceRoute.ts +++ b/src/libs/Navigation/getTopmostWorkspaceRoute.ts @@ -7,7 +7,7 @@ function getTopmostWorkspaceRoute(state: State): NavigationP return; } - const topmostWorkspaceRoute = state.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR).at(-1); + const topmostWorkspaceRoute = state.routes.filter((route) => route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR).at(-1); if (!topmostWorkspaceRoute) { return; diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 0a393100e375..321055ab3452 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -57,7 +57,7 @@ export default function linkTo(navigation: NavigationContainerRef = { +const TAB_TO_CENTRAL_PANE_MAPPING: Record = { [SCREENS.HOME]: [SCREENS.REPORT], [SCREENS.SETTINGS.ROOT]: [ SCREENS.SETTINGS.PROFILE.ROOT, diff --git a/src/libs/Navigation/linkingConfig/createSplitNavigator.ts b/src/libs/Navigation/linkingConfig/createSplitNavigator.ts index 7ee473560955..b016e535052e 100644 --- a/src/libs/Navigation/linkingConfig/createSplitNavigator.ts +++ b/src/libs/Navigation/linkingConfig/createSplitNavigator.ts @@ -1,44 +1,28 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import type {NavigationPartialRoute, SplitNavigatorScreenName} from '@libs/Navigation/types'; -import NAVIGATORS from '@src/NAVIGATORS'; -import SCREENS from '@src/SCREENS'; +import type {NavigationPartialRoute, SplitNavigatorByLHN, SplitNavigatorLHNScreen, SplitNavigatorParamListType} from '@libs/Navigation/types'; +import LHN_TO_SPLIT_NAVIGATOR_NAME from './LHN_TO_SPLIT_NAVIGATOR_MAPPING'; -const mapLhnToSplitNavigatorName = { - [SCREENS.SETTINGS.ROOT]: NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR, - [SCREENS.HOME]: NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, - [SCREENS.WORKSPACE.INITIAL]: NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, -}; - -type SplitNavigatorLHNScreen = keyof typeof mapLhnToSplitNavigatorName; -type SplitNavigator = (typeof mapLhnToSplitNavigatorName)[SplitNavigatorLHNScreen]; +type ExtractRouteType = Extract; // The function getPathFromState that we are using in some places isn't working correctly without defined index. const getRoutesWithIndex = (routes: NavigationPartialRoute[]): PartialState => ({routes, index: routes.length - 1}); -function createSplitNavigator( - splitNavigatorLHNScreen: SplitNavigatorLHNScreen, - route?: NavigationPartialRoute, - policyID?: string, - key?: string, -): NavigationPartialRoute { +function createSplitNavigator( + splitNavigatorLHN: NavigationPartialRoute, + route?: NavigationPartialRoute>, + splitNavigatorParams?: SplitNavigatorParamListType[SplitNavigatorByLHN], +): NavigationPartialRoute> { const routes = []; - const params = policyID ? {policyID} : {}; - - // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator - routes.push({ - name: splitNavigatorLHNScreen, - }); + routes.push(splitNavigatorLHN); if (route) { routes.push(route); } return { - key, - name: mapLhnToSplitNavigatorName[splitNavigatorLHNScreen], + name: LHN_TO_SPLIT_NAVIGATOR_NAME[splitNavigatorLHN.name], state: getRoutesWithIndex(routes), - params, + params: splitNavigatorParams, }; } - export default createSplitNavigator; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 9fdda9360cd3..000759306c07 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -4,8 +4,7 @@ import pick from 'lodash/pick'; import type {TupleToUnion} from 'type-fest'; import {isAnonymousUser} from '@libs/actions/Session'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import type {BottomTabName, CentralPaneName, NavigationPartialRoute, RootStackParamList, SplitNavigatorScreenName} from '@libs/Navigation/types'; -import {isCentralPaneName} from '@libs/NavigationUtils'; +import type {NavigationPartialRoute, RootStackParamList, SplitNavigatorScreenName} from '@libs/Navigation/types'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import * as ReportConnection from '@libs/ReportConnection'; import extractPolicyIDFromQuery from '@navigation/extractPolicyIDFromQuery'; @@ -16,8 +15,9 @@ import type {Screen} from '@src/SCREENS'; import SCREENS from '@src/SCREENS'; import CENTRAL_PANE_TO_RHP_MAPPING from './CENTRAL_PANE_TO_RHP_MAPPING'; import config, {normalizedConfigs} from './config'; -import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState'; +import createSplitNavigator from './createSplitNavigator'; import replacePathInNestedState from './replacePathInNestedState'; +import SEARCH_RHP_SCREENS from './SEARCH_RHP_SCREENS'; import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING'; const RHP_SCREENS_OPENED_FROM_LHN = [ @@ -60,54 +60,6 @@ type SplitNavigator = (typeof mapLhnToSplitNavigatorName)[SplitNavigatorLHNScree // The function getPathFromState that we are using in some places isn't working correctly without defined index. const getRoutesWithIndex = (routes: NavigationPartialRoute[]): PartialState => ({routes, index: routes.length - 1}); -const addPolicyIDToRoute = (route: NavigationPartialRoute, policyID?: string) => { - const routeWithPolicyID = {...route}; - if (!routeWithPolicyID.params) { - routeWithPolicyID.params = {policyID}; - return routeWithPolicyID; - } - - if ('policyID' in routeWithPolicyID.params && !!routeWithPolicyID.params.policyID) { - return routeWithPolicyID; - } - - routeWithPolicyID.params = {...routeWithPolicyID.params, policyID}; - - return routeWithPolicyID; -}; - -function createBottomTabNavigator(route: NavigationPartialRoute, policyID?: string): NavigationPartialRoute { - const routesForBottomTabNavigator: Array> = []; - routesForBottomTabNavigator.push(addPolicyIDToRoute(route, policyID) as NavigationPartialRoute); - - return { - name: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, - state: getRoutesWithIndex(routesForBottomTabNavigator), - }; -} - -// function createWorkspaceNavigator(route?: NavigationPartialRoute): NavigationPartialRoute { -// const routes = []; - -// const policyID = route?.params && 'policyID' in route.params ? route.params.policyID : undefined; - -// // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator -// routes.push({ -// name: SCREENS.WORKSPACE.INITIAL, -// params: { -// policyID, -// }, -// }); - -// if (route) { -// routes.push(route); -// } -// return { -// name: NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, -// state: getRoutesWithIndex(routes), -// }; -// } - function getParamsFromRoute(screenName: string): string[] { const routeConfig = normalizedConfigs[screenName as Screen]; @@ -116,30 +68,8 @@ function getParamsFromRoute(screenName: string): string[] { return route.match(/(?<=[:?&])(\w+)(?=[/=?&]|$)/g) ?? []; } -function createSplitNavigator(splitNavigatorLHNScreen: SplitNavigatorLHNScreen, route?: NavigationPartialRoute): NavigationPartialRoute { - const routes = []; - - const policyID = route?.params && 'policyID' in route.params ? route.params.policyID : undefined; - - // Both routes in WorkspaceNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in WorkspaceNavigator - routes.push({ - name: splitNavigatorLHNScreen, - params: { - policyID, - }, - }); - - if (route) { - routes.push(route); - } - return { - name: mapLhnToSplitNavigatorName[splitNavigatorLHNScreen], - state: getRoutesWithIndex(routes), - }; -} - // This function will return CentralPaneNavigator route or WorkspaceNavigator route. -function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | undefined { +function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): NavigationPartialRoute | NavigationPartialRoute<'Search_Central_Pane'> | undefined { // Check for backTo param. One screen with different backTo value may need diferent screens visible under the overlay. if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { const stateForBackTo = getStateFromPath(route.params.backTo, config); @@ -147,19 +77,19 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat // eslint-disable-next-line @typescript-eslint/no-shadow const rhpNavigator = stateForBackTo.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); - const centralPaneOrWorkspaceNavigator = stateForBackTo.routes.find( - // eslint-disable-next-line @typescript-eslint/no-shadow - (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, - ); - // If there is rhpNavigator in the state generated for backTo url, we want to get root route matching to this rhp screen. if (rhpNavigator && rhpNavigator.state) { return getMatchingRootRouteForRHPRoute(findFocusedRoute(stateForBackTo) as NavigationPartialRoute); } + const splitNavigator = stateForBackTo.routes.find( + // eslint-disable-next-line @typescript-eslint/no-shadow + (route) => route.name.endsWith('SplitNavigator'), + ); + // If we know that backTo targets the root route (central pane or full screen) we want to use it. - if (centralPaneOrWorkspaceNavigator && centralPaneOrWorkspaceNavigator.state) { - return centralPaneOrWorkspaceNavigator as NavigationPartialRoute; + if (splitNavigator && splitNavigator.state) { + return splitNavigator as NavigationPartialRoute; } } } @@ -168,27 +98,30 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat for (const [centralPaneName, RHPNames] of Object.entries(CENTRAL_PANE_TO_RHP_MAPPING)) { if (RHPNames.includes(route.name)) { const paramsFromRoute = getParamsFromRoute(centralPaneName); - return createSplitNavigator(CENTRAL_PANE_TO_TAB_MAPPING[centralPaneName] as SplitNavigatorLHNScreen, { - name: centralPaneName as SplitNavigatorScreenName, - params: pick(route.params, paramsFromRoute), - }); + return createSplitNavigator( + {name: CENTRAL_PANE_TO_TAB_MAPPING[centralPaneName as SplitNavigatorScreenName] as SplitNavigatorLHNScreen, params: pick(route.params, paramsFromRoute)}, + { + name: centralPaneName, + params: pick(route.params, paramsFromRoute), + }, + ); } } - // Check for WorkspaceNavigator - // for (const [workspaceScreenName, RHPNames] of Object.entries(WORKSPACE_SCREEN_TO_RHP_MAPPING)) { - // if (RHPNames.includes(route.name)) { - // const paramsFromRoute = getParamsFromRoute(workspaceScreenName); + if (SEARCH_RHP_SCREENS.includes(route.name)) { + const paramsFromRoute = getParamsFromRoute(SCREENS.SEARCH.CENTRAL_PANE); - // return createWorkspaceNavigator({name: workspaceScreenName as WorkspaceScreenName, params: pick(route.params, paramsFromRoute)}); - // } - // } + return { + name: SCREENS.SEARCH.CENTRAL_PANE, + params: pick(route.params, paramsFromRoute), + }; + } // check for valid reportID in the route params // if the reportID is valid, we should navigate back to screen report in CPN const reportID = (route.params as Record)?.reportID; if (ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportID) { - return {name: SCREENS.REPORT, params: {reportID}}; + return createSplitNavigator({name: SCREENS.HOME}, {name: SCREENS.REPORT, params: {reportID}}); } } @@ -226,13 +159,13 @@ function getAdaptedState(state: PartialState // metainfo.isCentralPaneAndBottomTabMandatory = false; // metainfo.isWorkspaceNavigatorMandatory = false; // If matchingRootRoute is undefined and it's a narrow layout, don't add a report screen under the RHP. - matchingRootRoute = matchingRootRoute ?? (!isNarrowLayout ? {name: SCREENS.REPORT} : undefined); + matchingRootRoute = matchingRootRoute ?? (!isNarrowLayout ? createSplitNavigator({name: SCREENS.HOME}, {name: SCREENS.REPORT}) : createSplitNavigator({name: SCREENS.HOME})); } // When we open a screen in RHP from WorkspaceNavigator, we need to add the appropriate screen in CentralPane. // Then, when we close WorkspaceNavigator, we will be redirected to the correct page in CentralPane. if (matchingRootRoute?.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR) { - routes.push(createSplitNavigator(SCREENS.SETTINGS.ROOT, {name: SCREENS.SETTINGS.WORKSPACES})); + routes.push(createSplitNavigator({name: SCREENS.SETTINGS.ROOT}, {name: SCREENS.SETTINGS.WORKSPACES})); } if (matchingRootRoute && (!isNarrowLayout || !isRHPScreenOpenedFromLHN)) { @@ -263,7 +196,7 @@ function getAdaptedState(state: PartialState } : undefined; - routes.push(createSplitNavigator(SCREENS.HOME, splitNavigatorMainScreen)); + routes.push(createSplitNavigator({name: SCREENS.HOME}, splitNavigatorMainScreen)); // Separate ifs are necessary for typescript to see that we are not pushing undefined to the array. if (lhpNavigator) { @@ -284,7 +217,6 @@ function getAdaptedState(state: PartialState return { adaptedState: getRoutesWithIndex(routes), - // metainfo, }; } if (WorkspaceNavigator) { @@ -295,35 +227,24 @@ function getAdaptedState(state: PartialState const routes = []; routes.push( - createSplitNavigator(SCREENS.SETTINGS.ROOT, { - name: SCREENS.SETTINGS.WORKSPACES, - params: { - policyID, + createSplitNavigator( + {name: SCREENS.SETTINGS.ROOT}, + { + name: SCREENS.SETTINGS.WORKSPACES, + params: { + policyID, + }, }, - }), + ), ); routes.push(WorkspaceNavigator); return { adaptedState: getRoutesWithIndex(routes), - // metainfo, }; } - // if (centralPaneNavigator) { - // // Routes - // // - matching bottom tab - // // - found central pane - // const routes = []; - // const matchingBottomTabRoute = getMatchingBottomTabRouteForState(state); - // routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); - // routes.push(centralPaneNavigator); - - // return { - // adaptedState: getRoutesWithIndex(routes), - // // metainfo, - // }; - // } + if (attachmentsScreen) { // Routes // - matching bottom tab @@ -333,25 +254,25 @@ function getAdaptedState(state: PartialState const reportAttachments = attachmentsScreen as Route<'Attachments', RootStackParamList['Attachments']>; if (reportAttachments.params?.type === CONST.ATTACHMENT_TYPE.REPORT) { - const matchingBottomTabRoute = getMatchingBottomTabRouteForState(state); - routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID)); - if (!isNarrowLayout) { - routes.push({name: SCREENS.REPORT, params: {reportID: reportAttachments.params?.reportID ?? '-1'}}); - } + const splitNavigatorMainScreen = !isNarrowLayout + ? { + name: SCREENS.REPORT, + params: {reportID: reportAttachments.params?.reportID ?? '-1'}, + } + : undefined; + + routes.push(createSplitNavigator({name: SCREENS.HOME}, splitNavigatorMainScreen)); routes.push(reportAttachments); return { adaptedState: getRoutesWithIndex(routes), - // metainfo, }; } } // We need to make sure that this if only handles states where we deeplink to the bottom tab directly - return { adaptedState: state, - // metainfo, }; } diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts index 1db77f9917ff..0645a45e0924 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts @@ -8,7 +8,7 @@ import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING'; function getMatchingBottomTabRouteForState(state: State, policyID?: string): NavigationPartialRoute { const paramsWithPolicyID = policyID ? {policyID} : undefined; const defaultRoute = {name: SCREENS.HOME, params: paramsWithPolicyID}; - const isWorkspaceNavigatorOpened = state.routes.some((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR); + const isWorkspaceNavigatorOpened = state.routes.some((route) => route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR); if (isWorkspaceNavigatorOpened) { return {name: SCREENS.SETTINGS.ROOT, params: paramsWithPolicyID}; diff --git a/src/libs/Navigation/newLinkTo/index.ts b/src/libs/Navigation/newLinkTo/index.ts index 0f278a9dfc24..100491b04bd3 100644 --- a/src/libs/Navigation/newLinkTo/index.ts +++ b/src/libs/Navigation/newLinkTo/index.ts @@ -74,7 +74,7 @@ export default function linkTo(navigation: NavigationContainerRef; @@ -50,6 +51,16 @@ type NavigationPartialRoute = PartialRoute = NavigationState | PartialState>; +type SplitNavigatorLHNScreen = keyof typeof LHN_TO_SPLIT_NAVIGATOR_NAME; + +type SplitNavigatorParamListType = { + [NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR]: SettingsSplitNavigatorParamList; + [NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]: ReportsSplitNavigatorParamList; + [NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]: WorkspaceSplitNavigatorParamList; +}; + +type SplitNavigatorByLHN = (typeof LHN_TO_SPLIT_NAVIGATOR_NAME)[T]; + type CentralPaneScreensParamList = { [SCREENS.REPORT]: { reportActionID: string; @@ -1435,7 +1446,7 @@ type CentralPaneName = keyof CentralPaneScreensParamList; type OnboardingFlowName = keyof OnboardingModalNavigatorParamList; -type SplitNavigatorScreenName = keyof WorkspaceSplitNavigatorParamList & keyof SettingsSplitNavigatorParamList & keyof ReportsSplitNavigatorParamList; +type SplitNavigatorScreenName = keyof (WorkspaceSplitNavigatorParamList & SettingsSplitNavigatorParamList & ReportsSplitNavigatorParamList); type SwitchPolicyIDParams = { policyID?: string; @@ -1510,4 +1521,7 @@ export type { RestrictedActionParamList, MissingPersonalDetailsParamList, SplitNavigatorScreenName, + SplitNavigatorLHNScreen, + SplitNavigatorParamListType, + SplitNavigatorByLHN, }; From 7e21462bd3791f51c4dde4ae21a55a97d0e4133e Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 13 Sep 2024 07:48:00 +0200 Subject: [PATCH 19/34] Refactor switching policyID --- src/CONST.ts | 1 + .../CustomRouter.ts | 24 +++++++++++++++-- src/libs/Navigation/Navigation.ts | 24 +++++++++-------- src/libs/Navigation/dismissModalWithReport.ts | 12 +++++---- src/libs/Navigation/getTopmostReportId.ts | 26 ++++++++++++------- .../subscribePushNotification/index.ts | 5 ++-- src/libs/actions/Report.ts | 6 ++--- src/pages/WorkspaceSwitcherPage/index.tsx | 3 +-- .../SidebarScreen/BaseSidebarScreen.tsx | 14 +++++----- src/pages/workspace/WorkspaceProfilePage.tsx | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 11 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index cd4d2b24e97a..259e9edd7a68 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4155,6 +4155,7 @@ const CONST = { REPLACE: 'REPLACE', PUSH: 'PUSH', NAVIGATE: 'NAVIGATE', + SWITCH_POLICY_ID: 'SWITCH_POLICY_ID', }, }, TIME_PERIOD: { diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index d3ac1f0cb685..6fe95cd93c6b 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -89,8 +89,7 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { return { ...stackRouter, getStateForAction(state: StackNavigationState, action: CommonActions.Action | StackActionType | CustomRootStackActionType, configOptions: RouterConfigOptions) { - // TODO: Put this into const; - if (action.type === 'SWITCH_POLICY_ID') { + if (action.type === CONST.NAVIGATION.ACTION_TYPE.SWITCH_POLICY_ID) { const lastRoute = state.routes.at(-1); if (lastRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { const currentParams = lastRoute.params as RootStackParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; @@ -114,6 +113,27 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { if (lastRoute?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { // TODO: handle change policy id of reports navigator. + if (action.payload.reportID) { + const newRoute = createSplitNavigator( + { + name: SCREENS.HOME, + }, + { + name: SCREENS.REPORT, + params: { + reportID: action.payload.reportID, + reportActionID: action.payload.reportActionID, + referrer: action.payload.referrer, + }, + }, + { + policyID: action.payload.policyID, + }, + ); + const newRoutes = [...state.routes, newRoute]; + return {...state, stale: true, routes: newRoutes, index: newRoutes.length - 1}; + } + const splitNavigatorMainScreen = getIsNarrowLayout() ? undefined : {name: SCREENS.REPORT, params: {reportID: ''}}; const newRoute = createSplitNavigator( diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index d5761da9378b..0aedb95fa315 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -25,8 +25,7 @@ import getMatchingBottomTabRouteForState from './linkingConfig/getMatchingBottom import navigationRef from './navigationRef'; import linkTo from './newLinkTo'; import setNavigationActionToMicrotaskQueue from './setNavigationActionToMicrotaskQueue'; -import switchPolicyID from './switchPolicyID'; -import type {NavigationStateRoute, RootStackParamList, State, StateOrRoute, SwitchPolicyIDParams} from './types'; +import type {NavigationStateRoute, RootStackParamList, State, StateOrRoute} from './types'; let resolveNavigationIsReadyPromise: () => void; const navigationIsReadyPromise = new Promise((resolve) => { @@ -421,18 +420,21 @@ function waitForProtectedRoutes() { }); } -function navigateWithSwitchPolicyID(params: SwitchPolicyIDParams) { - if (!canNavigate('navigateWithSwitchPolicyID')) { - return; - } - - return switchPolicyID(navigationRef.current, params); -} - function getTopMostCentralPaneRouteFromRootState() { return getTopmostCentralPaneRoute(navigationRef.getRootState() as State); } +type SwitchPolicyIDPayload = { + policyID?: string; + reportID?: number; + reportActionID?: string; + referrer?: string; +}; + +function switchPolicyID(payload: SwitchPolicyIDPayload) { + navigationRef.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.SWITCH_POLICY_ID, payload}); +} + export default { setShouldPopAllStateOnUP, navigate, @@ -451,7 +453,7 @@ export default { getTopmostReportActionId, waitForProtectedRoutes, parseHybridAppUrl, - navigateWithSwitchPolicyID, + switchPolicyID, resetToHome, closeRHPFlow, setNavigationActionToMicrotaskQueue, diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index eeebb7bd8a2a..98595ead6132 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -51,11 +51,13 @@ function dismissModalWithReport(targetReport: OnyxEntry, navigationRef: const policyMemberAccountIDs = getPolicyEmployeeAccountIDs(policyID); const shouldOpenAllWorkspace = isEmptyObject(targetReport) ? true : !doesReportBelongToWorkspace(targetReport, policyMemberAccountIDs, policyID); - if (shouldOpenAllWorkspace) { - switchPolicyID(navigationRef, {route: ROUTES.HOME}); - } else { - switchPolicyID(navigationRef, {policyID, route: ROUTES.HOME}); - } + // @TODO Handle dismissing modal with switching a policyID + + // if (shouldOpenAllWorkspace) { + // switchPolicyID(navigationRef, {route: ROUTES.HOME}); + // } else { + // switchPolicyID(navigationRef, {policyID, route: ROUTES.HOME}); + // } const action: StackNavigationAction = getActionFromState(reportState, linkingConfig.config); if (action) { diff --git a/src/libs/Navigation/getTopmostReportId.ts b/src/libs/Navigation/getTopmostReportId.ts index dc53d040f087..d1d8bf2eacfc 100644 --- a/src/libs/Navigation/getTopmostReportId.ts +++ b/src/libs/Navigation/getTopmostReportId.ts @@ -1,5 +1,5 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import {isCentralPaneName} from '@libs/NavigationUtils'; +import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {RootStackParamList} from './types'; @@ -16,23 +16,29 @@ function getTopmostReportId(state: NavigationState | NavigationState isCentralPaneName(route.name)).at(-1); - if (!topmostCentralPane) { + const topmostReportsSplitNavigator = state.routes?.filter((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR).at(-1); + + if (!topmostReportsSplitNavigator) { return; } - const directReportParams = topmostCentralPane.params; - const directReportIdParam = directReportParams && 'reportID' in directReportParams && directReportParams?.reportID; + const topmostReport = topmostReportsSplitNavigator.state?.routes.filter((route) => route.name === SCREENS.REPORT).at(-1); - if (!topmostCentralPane.state && !directReportIdParam) { + if (!topmostReport) { return; } - if (directReportIdParam) { - return directReportIdParam; - } + // const directReportParams = topmostCentralPane.params; + // const directReportIdParam = directReportParams && 'reportID' in directReportParams && directReportParams?.reportID; + + // if (!topmostCentralPane.state && !directReportIdParam) { + // return; + // } + + // if (directReportIdParam) { + // return directReportIdParam; + // } - const topmostReport = topmostCentralPane.state?.routes.filter((route) => route.name === SCREENS.REPORT).at(-1); if (!topmostReport) { return; } diff --git a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts index c1a1442b1e53..b4563e12ac8d 100644 --- a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts +++ b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts @@ -97,9 +97,10 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati Log.info('[PushNotification] onSelected() - Navigation is ready. Navigating...', false, {reportID, reportActionID}); if (!reportBelongsToWorkspace) { - Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); + Navigation.switchPolicyID({policyID, reportID}); + } else { + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); } - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); } catch (error) { let errorMessage = String(error); if (error instanceof Error) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 40a7059b54b9..029ccfb4d376 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1034,8 +1034,7 @@ function navigateToAndOpenReport( if (shouldDismissModal) { Navigation.dismissModalWithReport(report); } else { - Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report?.reportID ?? '-1'), actionType); + Navigation.switchPolicyID({policyID: undefined, reportID: report?.reportID ?? '-1'}); } } @@ -2483,7 +2482,8 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; const reportBelongsToWorkspace = policyID ? doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID) : false; if (!reportBelongsToWorkspace) { - Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); + Navigation.switchPolicyID({policyID: undefined, reportID, referrer: CONST.REFERRER.NOTIFICATION}); + return; } navigateFromNotification(reportID); }); diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index c2c9c93b71ff..ce8ddfd1f881 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -92,8 +92,7 @@ function WorkspaceSwitcherPage() { Navigation.goBack(); if (policyID !== activeWorkspaceID) { - // Navigation.navigateWithSwitchPolicyID({policyID}); - navigationRef.dispatch({type: 'SWITCH_POLICY_ID', payload: {policyID}}); + Navigation.switchPolicyID({policyID}); } }, [activeWorkspaceID], diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index 71a8587fb889..08ce4a7df94d 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -36,14 +36,14 @@ function BaseSidebarScreen() { Timing.start(CONST.TIMING.SIDEBAR_LOADED); }, []); - useEffect(() => { - if (!!activeWorkspace || activeWorkspaceID === undefined) { - return; - } + // useEffect(() => { + // if (!!activeWorkspace || activeWorkspaceID === undefined) { + // return; + // } - Navigation.navigateWithSwitchPolicyID({policyID: undefined}); - updateLastAccessedWorkspace(undefined); - }, [activeWorkspace, activeWorkspaceID]); + // Navigation.switchPolicyID({policyID: undefined}); + // updateLastAccessedWorkspace(undefined); + // }, [activeWorkspace, activeWorkspaceID]); return ( Date: Wed, 18 Sep 2024 16:09:37 +0200 Subject: [PATCH 20/34] improve routers --- .../createCustomBottomTabNavigator/TopBar.tsx | 2 + .../CustomRouter.ts | 179 ++++++----------- .../SplitStackRouter.ts | 44 ++++- .../createSplitStackNavigator/index.tsx | 7 +- .../createSplitStackNavigator/types.ts | 4 +- src/libs/Navigation/newLinkTo/index.ts | 182 +----------------- 6 files changed, 103 insertions(+), 315 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx index dc3ab4c48c08..2b3d7600119f 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx @@ -26,6 +26,8 @@ function TopBar({breadcrumbLabel, activeWorkspaceID, shouldDisplaySearch = true} const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); + + // @TODO use policyID from state instead of activeWorkspaceID. It will help with glitching. const policy = usePolicy(activeWorkspaceID); const [session] = useOnyx(ONYXKEYS.SESSION, {selector: (sessionValue) => sessionValue && {authTokenType: sessionValue.authTokenType}}); const isAnonymousUser = Session.isAnonymousUser(session); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 6fe95cd93c6b..b790713df3d1 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -1,12 +1,10 @@ import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigationState} from '@react-navigation/native'; -import {findFocusedRoute, getPathFromState, StackRouter} from '@react-navigation/native'; +import {findFocusedRoute, getPathFromState, StackActions, StackRouter} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/routers'; -import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import * as Localize from '@libs/Localize'; import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import isSideModalNavigator from '@libs/Navigation/isSideModalNavigator'; import linkingConfig from '@libs/Navigation/linkingConfig'; -import createSplitNavigator from '@libs/Navigation/linkingConfig/createSplitNavigator'; import type {RootStackParamList, State} from '@libs/Navigation/types'; import {isOnboardingFlowName} from '@libs/NavigationUtils'; import * as SearchUtils from '@libs/SearchUtils'; @@ -27,6 +25,7 @@ function shouldPreventReset(state: StackNavigationState, action: if (isOnboardingFlowName(currentFocusedRoute?.name) && !isOnboardingFlowName(targetFocusedRoute?.name)) { Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); // We reset the URL as the browser sets it in a way that doesn't match the navigation state + // @TODO is it working? Maybe we should split it for platforms. // eslint-disable-next-line no-restricted-globals history.replaceState({}, '', getPathFromState(state, linkingConfig.config)); return true; @@ -49,39 +48,17 @@ function shouldDismissSideModalNavigator(state: StackNavigationState) { -// for (let i = state.routes.length - 1; i >= 0; i--) { -// const route = state.routes[i]; -// if (route.name === SCREENS.SEARCH.CENTRAL_PANE) { -// return extractPolicyIDFromQuery(route as NavigationPartialRoute); -// } -// if (route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { -// const params = route.params ? (route.params as RootStackParamList[typeof NAVIGATORS.REPORTS_SPLIT_NAVIGATOR]) : undefined; -// return params?.policyID; -// } -// } -// } + // @TODO not sure about these properties. + reportID?: string; + reportActionID?: string; + referrer?: string; + }; +}; function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); @@ -94,61 +71,38 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { if (lastRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { const currentParams = lastRoute.params as RootStackParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; const queryJSON = SearchUtils.buildSearchQueryJSON(currentParams.q); - let newParams = null; - if (!queryJSON) { - return; + return null; } - if (action.payload.policyID) { queryJSON.policyID = action.payload.policyID; } else { delete queryJSON.policyID; } - - newParams = {...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON)}; - const newRoutes = [...state.routes, {...lastRoute, params: newParams}]; - return {...state, routes: newRoutes, index: newRoutes.length - 1}; + const newAction = StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, { + ...currentParams, + q: SearchUtils.buildSearchQueryString(queryJSON), + }); + return stackRouter.getStateForAction(state, newAction, configOptions); } if (lastRoute?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { - // TODO: handle change policy id of reports navigator. - + // @TODO: It would be great to avoid coupling switchPolicy with specific actions for report screen if (action.payload.reportID) { - const newRoute = createSplitNavigator( - { - name: SCREENS.HOME, - }, - { - name: SCREENS.REPORT, - params: { - reportID: action.payload.reportID, - reportActionID: action.payload.reportActionID, - referrer: action.payload.referrer, - }, - }, - { - policyID: action.payload.policyID, + const newAction = StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, { + policyID: action.payload.policyID, + screen: SCREENS.REPORT, + params: { + reportID: action.payload.reportID, + reportActionID: action.payload.reportActionID, + referrer: action.payload.referrer, }, - ); - const newRoutes = [...state.routes, newRoute]; - return {...state, stale: true, routes: newRoutes, index: newRoutes.length - 1}; + }); + return stackRouter.getStateForAction(state, newAction, configOptions); } - - const splitNavigatorMainScreen = getIsNarrowLayout() ? undefined : {name: SCREENS.REPORT, params: {reportID: ''}}; - - const newRoute = createSplitNavigator( - { - name: SCREENS.HOME, - }, - splitNavigatorMainScreen, - { - policyID: action.payload.policyID, - }, - ); - const newRoutes = [...state.routes, newRoute]; - return {...state, stale: true, routes: newRoutes, index: newRoutes.length - 1}; + const newAction = StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, {policyID: action.payload.policyID}); + return stackRouter.getStateForAction(state, newAction, configOptions); } - // In other cases, do nothing. + // We don't have other navigators that should handle switch policy action. return null; } @@ -159,68 +113,49 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { if (action.type === 'PUSH' && action.payload.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { const policyID = getPolicyIDFromState(state as State); - const currentParams = {...action.payload.params}; - action.payload.params = {...currentParams, policyID}; + + const modifiedAction = { + ...action, + payload: { + ...action.payload, + params: { + ...action.payload.params, + policyID, + }, + }, + }; + + return stackRouter.getStateForAction(state, modifiedAction, configOptions); } if (action.type === 'PUSH' && action.payload.name === SCREENS.SEARCH.CENTRAL_PANE) { const policyID = getPolicyIDFromState(state as State); const currentParams = action.payload.params as RootStackParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; const queryJSON = SearchUtils.buildSearchQueryJSON(currentParams.q); + if (!queryJSON) { - return; + return null; } + if (policyID) { queryJSON.policyID = policyID; } else { delete queryJSON.policyID; } - action.payload.params = {q: SearchUtils.buildSearchQueryString(queryJSON), isCustomQuery: false}; - } + const modifiedAction = { + ...action, + payload: { + ...action.payload, + params: { + ...action.payload.params, + q: SearchUtils.buildSearchQueryString(queryJSON), + }, + }, + }; - // TODO: I don't remember if the code below makes sense Wojtek :D but it's possible. - // One part seems redundant to the lines 102-108 above - // - // Copy existing policyID to the new screen. - // TODO: Can't figure out the types for - // if (action.type === 'PUSH' && isPushingScreenWithWorkspaceSwitcher(action)) { - // const policyID = extractPolicyIDFromState(state); - - // // If there is no policyID, continue with the default behavior. - // if (!policyID) { - // return stackRouter.getStateForAction(state, action, configOptions); - // } - - // // If there is, we need to add it to the new screen. - // // - // const modifiedAction = {...action}; - - // if (action.payload.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { - // modifiedAction.payload.params = {...action.payload.params, policyID}; - // } - - // if (action.payload.name === SCREENS.SEARCH.CENTRAL_PANE) { - // if (policyID) { - // const queryJSON = SearchUtils.buildSearchQueryJSON(params.q); - // if (queryJSON) { - // queryJSON.policyID = policyID; - // params.q = SearchUtils.buildSearchQueryString(queryJSON); - // } - // } else { - // const queryJSON = SearchUtils.buildSearchQueryJSON(params.q); - // if (queryJSON) { - // delete queryJSON.policyID; - // params.q = SearchUtils.buildSearchQueryString(queryJSON); - // } - // } - // action.payload.params = {...action.payload.params, policyID: extractPolicyIDFromState(state)}; - // } - - // // This shouldn't happen, but if it does, we don't want to break the app. - // console.error('Unhandled screen with workspace switcher:', action.payload.name); - // return; - // } + return stackRouter.getStateForAction(state, modifiedAction, configOptions); + } if (shouldDismissSideModalNavigator(state, action)) { const modifiedState = {...state, routes: state.routes.slice(0, -1), index: state.index !== 0 ? state.index - 1 : 0}; diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts index dbb9980acede..4811b8451c80 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/SplitStackRouter.ts @@ -1,26 +1,36 @@ import type {CommonActions, ParamListBase, PartialState, RouterConfigOptions, StackActionType, StackNavigationState} from '@react-navigation/native'; -import {StackRouter} from '@react-navigation/native'; +import {StackActions, StackRouter} from '@react-navigation/native'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; +import navigationRef from '@libs/Navigation/navigationRef'; import type {SplitStackNavigatorRouterOptions} from './types'; type StackState = StackNavigationState | PartialState>; const isAtLeastOneInState = (state: StackState, screenName: string): boolean => state.routes.some((route) => route.name === screenName); -function adaptStateIfNecessary(state: StackState, sidebarScreen: string, defaultCentralScreen: string) { +type AdaptStateIfNecessaryArgs = { + state: StackState; + options: SplitStackNavigatorRouterOptions; +}; + +function adaptStateIfNecessary({state, options: {sidebarScreen, defaultCentralScreen, parentRoute}}: AdaptStateIfNecessaryArgs) { const isNarrowLayout = getIsNarrowLayout(); + + // @TODO leftover from times when there was fullscreen navigator only for the workspaces. const workspaceCentralPane = state.routes.at(-1); + // There should always be sidebarScreen screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings. if (!isAtLeastOneInState(state, sidebarScreen)) { // @ts-expect-error Updating read only property // noinspection JSConstantReassignment state.stale = true; // eslint-disable-line - // This is necessary for ts to narrow type down to PartialState. + // This is necessary for typescript to narrow type down to PartialState. if (state.stale === true) { // Unshift the root screen to fill left pane. state.routes.unshift({ name: sidebarScreen, + // @TODO why we need to pass params here? params: workspaceCentralPane?.params, }); } @@ -31,13 +41,21 @@ function adaptStateIfNecessary(state: StackState, sidebarScreen: string, default // - defaultCentralScreen to cover central pane. if (!isNarrowLayout) { if (state.routes.length === 1 && state.routes[0].name === sidebarScreen) { + const rootState = navigationRef.getRootState(); + + // @TODO: If we have optimization for not rendering all split navigators, then last selected option won't be in the state. + // @TODO: Do we want to use previous selected screen if the policyID is different. + const previousSameNavigator = rootState?.routes.findLast((route) => route.name === parentRoute.name && route.state !== undefined); + const previousSelectedCentralScreen = + previousSameNavigator?.state?.routes && previousSameNavigator.state.routes.length > 1 ? previousSameNavigator.state.routes.at(-1)?.name : undefined; + // @ts-expect-error Updating read only property // noinspection JSConstantReassignment state.stale = true; // eslint-disable-line // Push the default settings central pane screen. if (state.stale === true) { state.routes.push({ - name: defaultCentralScreen, + name: previousSelectedCentralScreen ?? defaultCentralScreen, params: state.routes[0]?.params, }); } @@ -61,9 +79,9 @@ function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { getStateForAction(state: StackNavigationState, action: CommonActions.Action | StackActionType, configOptions: RouterConfigOptions) { if (isPushingSidebarOnCentralPane(state, action, options)) { if (getIsNarrowLayout()) { - // TODO: It's possible that it's better to push whole new SplitNavigator in such case. Not sure yet. - // Pop to top on narrow layout. - return {...state, routes: [state.routes.at(0)], index: 0}; + // @TODO: It's possible that it's better to push whole new SplitNavigator in such case. Not sure yet. + const newAction = StackActions.popToTop(); + return stackRouter.getStateForAction(state, newAction, configOptions); } // On wide screen do nothing as we want to keep the central pane screen and the sidebar is visible. return state; @@ -72,7 +90,11 @@ function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { }, getInitialState({routeNames, routeParamList, routeGetIdList}: RouterConfigOptions) { const initialState = stackRouter.getInitialState({routeNames, routeParamList, routeGetIdList}); - adaptStateIfNecessary(initialState, options.sidebarScreen, options.defaultCentralScreen); + + adaptStateIfNecessary({ + state: initialState, + options, + }); // If we needed to modify the state we need to rehydrate it to get keys for new routes. if (initialState.stale) { @@ -82,7 +104,11 @@ function SplitStackRouter(options: SplitStackNavigatorRouterOptions) { return initialState; }, getRehydratedState(partialState: StackState, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions): StackNavigationState { - adaptStateIfNecessary(partialState, options.sidebarScreen, options.defaultCentralScreen); + adaptStateIfNecessary({ + state: partialState, + options, + }); + const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); return state; }, diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx index ea3c8ecad21b..d90b60d2ac4d 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/index.tsx @@ -1,5 +1,5 @@ import type {ParamListBase, StackActionHelpers, StackNavigationState} from '@react-navigation/native'; -import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/native'; +import {createNavigatorFactory, useNavigationBuilder, useRoute} from '@react-navigation/native'; import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; import {StackView} from '@react-navigation/stack'; import React from 'react'; @@ -22,6 +22,8 @@ function SplitStackNavigator(props: SplitStackN const children = usePrepareSplitStackNavigatorChildren(props.children, props.sidebarScreen, screenOptions.homeScreen); + const route = useRoute(); + const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< StackNavigationState, SplitStackNavigatorRouterOptions, @@ -34,6 +36,9 @@ function SplitStackNavigator(props: SplitStackN initialRouteName: props.initialRouteName, sidebarScreen: props.sidebarScreen, defaultCentralScreen: props.defaultCentralScreen, + + // @TODO figure out if we can end in a situation where the state and route are not in sync. If so, we may need to figure out a getter. + parentRoute: route, }); useHandleScreenResize(navigation); diff --git a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts index 5e62b4e710a6..cc9db03c75ae 100644 --- a/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts +++ b/src/libs/Navigation/AppNavigator/createSplitStackNavigator/types.ts @@ -1,7 +1,7 @@ -import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; +import type {DefaultNavigatorOptions, ParamListBase, RouteProp, StackNavigationState, StackRouterOptions} from '@react-navigation/native'; import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; -type SplitStackNavigatorRouterOptions = StackRouterOptions & {defaultCentralScreen: string; sidebarScreen: string}; +type SplitStackNavigatorRouterOptions = StackRouterOptions & {defaultCentralScreen: string; sidebarScreen: string; parentRoute: RouteProp}; type SplitStackNavigatorProps = DefaultNavigatorOptions< ParamListBase, diff --git a/src/libs/Navigation/newLinkTo/index.ts b/src/libs/Navigation/newLinkTo/index.ts index 100491b04bd3..e19f02687f1c 100644 --- a/src/libs/Navigation/newLinkTo/index.ts +++ b/src/libs/Navigation/newLinkTo/index.ts @@ -24,7 +24,7 @@ function shouldDispatchAction(currentState: NavigationState, return true; } -export default function linkTo(navigation: NavigationContainerRef | null, path: Route, type?: string, isActiveRoute?: boolean) { +export default function linkTo(navigation: NavigationContainerRef | null, path: Route) { if (!navigation) { throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); } @@ -54,186 +54,6 @@ export default function linkTo(navigation: NavigationContainerRef; - // const stateFromPath = getStateFromPath(pathWithoutPolicyID) as PartialState>; - // // Creating path with /w/ included if necessary. - // const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState); - // const policyIDs = !!topmostCentralPaneRoute?.params && 'policyIDs' in topmostCentralPaneRoute.params ? (topmostCentralPaneRoute?.params?.policyIDs as string) : ''; - // const extractedPolicyID = extractPolicyIDFromPath(`/${path}`); - // const policyIDFromState = getPolicyIDFromState(rootState); - // const policyID = extractedPolicyID ?? policyIDFromState ?? policyIDs; - // const lastRoute = rootState?.routes?.at(-1); - - // const isNarrowLayout = getIsNarrowLayout(); - - // const isWorkspaceScreenOnTop = lastRoute?.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR; - - // // policyIDs is present only on SCREENS.SEARCH.CENTRAL_PANE and it's displayed in the url as a query param, on the other pages this parameter is called policyID and it's shown in the url in the format: /w/:policyID - // if (policyID && !isWorkspaceScreenOnTop && !policyIDs) { - // // The stateFromPath doesn't include proper path if there is a policy passed with /w/id. - // // We need to replace the path in the state with the proper one. - // // To avoid this hacky solution we may want to create custom getActionFromState function in the future. - // replacePathInNestedState(stateFromPath, `/w/${policyID}${pathWithoutPolicyID}`); - // } - - // const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config); - - // const isReportInRhpOpened = isReportOpenInRHP(rootState); - - // // If action type is different than NAVIGATE we can't change it to the PUSH safely - // if (action?.type === CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { - // const actionPayloadParams = action.payload.params as ActionPayloadParams; - - // const topRouteName = lastRoute?.name; - - // // CentralPane screens aren't nested in any navigator, if actionPayloadParams?.screen is undefined, it means the screen name and parameters have to be read directly from action.payload - // const targetName = actionPayloadParams?.screen ?? action.payload.name; - // const targetParams = actionPayloadParams?.params ?? actionPayloadParams; - // const isTargetNavigatorOnTop = topRouteName === action.payload.name; - - // const isTargetScreenDifferentThanCurrent = !!(!topmostCentralPaneRoute || topmostCentralPaneRoute.name !== targetName); - // const areParamsDifferent = - // targetName === SCREENS.REPORT - // ? getTopmostReportId(rootState) !== getTopmostReportId(stateFromPath) - // : !shallowCompare( - // omitBy(topmostCentralPaneRoute?.params as Record | undefined, (value) => value === undefined), - // omitBy(targetParams as Record | undefined, (value) => value === undefined), - // ); - - // // If this action is navigating to the report screen and the top most navigator is different from the one we want to navigate - PUSH the new screen to the top of the stack by default - // if (isCentralPaneName(action.payload.name) && (isTargetScreenDifferentThanCurrent || areParamsDifferent)) { - // // We need to push a tab if the tab doesn't match the central pane route that we are going to push. - // const topmostBottomTabRoute = getTopmostBottomTabRoute(rootState); - // const policyIDsFromState = extractPolicyIDsFromState(stateFromPath); - // const matchingBottomTabRoute = getMatchingBottomTabRouteForState(stateFromPath, policyID || policyIDsFromState); - // const isOpeningSearch = matchingBottomTabRoute.name === SCREENS.SEARCH.BOTTOM_TAB; - // const isNewPolicyID = - // ((topmostBottomTabRoute?.params as Record)?.policyID ?? '') !== - // ((matchingBottomTabRoute?.params as Record)?.policyID ?? ''); - - // if (topmostBottomTabRoute && (topmostBottomTabRoute.name !== matchingBottomTabRoute.name || isNewPolicyID || isOpeningSearch)) { - // root.dispatch({ - // type: CONST.NAVIGATION.ACTION_TYPE.PUSH, - // payload: matchingBottomTabRoute, - // }); - // } - - // if (type === CONST.NAVIGATION.TYPE.UP) { - // action.type = CONST.NAVIGATION.ACTION_TYPE.REPLACE; - // } else { - // action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; - // } - - // // If we navigate to SCREENS.SEARCH.CENTRAL_PANE, it's necessary to pass the current policyID, but we have to remember that this param is called policyIDs on this page - // if (targetName === SCREENS.SEARCH.CENTRAL_PANE && targetParams && policyID) { - // (targetParams as Record).policyIDs = policyID; - // } - - // // If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow - // // and at the same time we want the back button to go to the page we were before the deeplink - // } else if (type === CONST.NAVIGATION.TYPE.UP) { - // action.type = CONST.NAVIGATION.ACTION_TYPE.REPLACE; - - // // If this action is navigating to ModalNavigator or WorkspaceNavigator and the last route on the root navigator is not already opened Navigator then push - // } else if ((action.payload.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR || isSideModalNavigator(action.payload.name)) && !isTargetNavigatorOnTop) { - // if (isSideModalNavigator(topRouteName)) { - // dismissModal(navigation); - // } - - // // If this RHP has mandatory central pane and bottom tab screens defined we need to push them. - // const {adaptedState, metainfo} = getAdaptedStateFromPath(path, linkingConfig.config); - // if (adaptedState && (metainfo.isCentralPaneAndBottomTabMandatory || metainfo.isWorkspaceNavigatorMandatory)) { - // const diff = getPartialStateDiff(rootState, adaptedState as State, metainfo); - // const diffActions = getActionsFromPartialDiff(diff); - // for (const diffAction of diffActions) { - // root.dispatch(diffAction); - // } - // } - // // All actions related to FullScreenNavigator on wide screen are pushed when comparing differences between rootState and adaptedState. - // if (action.payload.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR) { - // return; - // } - // action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; - - // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - // } else if (action.payload.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR) { - // // If path contains a policyID, we should invoke the navigate function - // const shouldNavigate = !!extractedPolicyID; - // const actionForBottomTabNavigator = getActionForBottomTabNavigator(action, rootState, policyID, shouldNavigate); - - // if (!actionForBottomTabNavigator) { - // return; - // } - - // root.dispatch(actionForBottomTabNavigator); - - // // If the layout is wide we need to push matching central pane route to the stack. - // if (!isNarrowLayout) { - // // stateFromPath should always include bottom tab navigator state, so getMatchingCentralPaneRouteForState will be always defined. - // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - // const matchingCentralPaneRoute = getMatchingCentralPaneRouteForState(stateFromPath, rootState)!; - // if (matchingCentralPaneRoute && 'name' in matchingCentralPaneRoute) { - // root.dispatch({ - // type: CONST.NAVIGATION.ACTION_TYPE.PUSH, - // payload: { - // name: matchingCentralPaneRoute.name, - // params: matchingCentralPaneRoute.params, - // }, - // }); - // } - // } else { - // // If the layout is small we need to pop everything from the central pane so the bottom tab navigator is visible. - // root.dispatch({ - // type: 'POP_TO_TOP', - // target: rootState.key, - // }); - // } - // return; - // } - // } - - // if (action && 'payload' in action && action.payload && 'name' in action.payload && isSideModalNavigator(action.payload.name)) { - // // Information about the state may be in the params. - // const currentFocusedRoute = findFocusedRoute(extrapolateStateFromParams(rootState)); - // const targetFocusedRoute = findFocusedRoute(stateFromPath); - - // // If the current focused route is the same as the target focused route, we don't want to navigate. - // if ( - // currentFocusedRoute?.name === targetFocusedRoute?.name && - // shallowCompare(currentFocusedRoute?.params as Record, targetFocusedRoute?.params as Record) - // ) { - // return; - // } - - // const minimalAction = getMinimalAction(action, navigation.getRootState()); - // if (minimalAction) { - // // There are situations where a route already exists on the current navigation stack - // // But we want to push the same route instead of going back in the stack - // // Which would break the user navigation history - // if (!isActiveRoute && type === CONST.NAVIGATION.ACTION_TYPE.PUSH) { - // minimalAction.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; - // } - // root.dispatch(minimalAction); - // return; - // } - // } - - // // When we navigate from the ReportScreen opened in RHP, this page shouldn't be removed from the navigation state to allow users to go back to it. - // if (isReportInRhpOpened && action) { - // action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; - // } - - // if (action !== undefined) { - // root.dispatch(action); - // } else { - // root.reset(stateFromPath); - // } } From af1452e43313a0c647e5a3a8455322b29faca83d Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 18 Sep 2024 17:46:43 +0200 Subject: [PATCH 21/34] add setter to activeWorkspaceID context --- .../ActiveWorkspaceContext.tsx | 5 ++- .../ActiveWorkspaceProvider/index.tsx | 41 ++++++++++++------- src/components/Search/SearchPageHeader.tsx | 2 +- src/hooks/useActiveWorkspace.ts | 2 +- src/hooks/useReportIDs.tsx | 2 +- .../Navigation/AppNavigator/AuthScreens.tsx | 2 +- .../Navigators/ReportsSplitNavigator.tsx | 2 +- .../BottomTabBar.tsx | 2 +- .../CustomRouter.ts | 8 ++++ src/pages/WorkspaceSwitcherPage/index.tsx | 4 +- src/pages/home/ReportScreen.tsx | 2 +- src/pages/home/sidebar/SidebarLinksData.tsx | 2 +- .../SidebarScreen/BaseSidebarScreen.tsx | 2 +- src/pages/workspace/WorkspaceNewRoomPage.tsx | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 15 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx b/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx index 7908020d16d2..8e336a421b92 100644 --- a/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx +++ b/src/components/ActiveWorkspace/ActiveWorkspaceContext.tsx @@ -1,5 +1,8 @@ import {createContext} from 'react'; -const ActiveWorkspaceContext = createContext(undefined); +const ActiveWorkspaceContext = createContext<{activeWorkspaceID: string | undefined; setActiveWorkspaceID: (workspaceID: string | undefined) => void}>({ + activeWorkspaceID: undefined, + setActiveWorkspaceID: () => {}, +}); export default ActiveWorkspaceContext; diff --git a/src/components/ActiveWorkspaceProvider/index.tsx b/src/components/ActiveWorkspaceProvider/index.tsx index 011b58720d35..2f734909ec2d 100644 --- a/src/components/ActiveWorkspaceProvider/index.tsx +++ b/src/components/ActiveWorkspaceProvider/index.tsx @@ -1,5 +1,5 @@ import {useNavigationState} from '@react-navigation/native'; -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; import * as SearchUtils from '@libs/SearchUtils'; import NAVIGATORS from '@src/NAVIGATORS'; @@ -7,6 +7,8 @@ import SCREENS from '@src/SCREENS'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; function ActiveWorkspaceContextProvider({children}: ChildrenProps) { + const [activeWorkspaceID, setActiveWorkspaceID] = useState(undefined); + const lastPolicyRoute = useNavigationState((state) => state?.routes?.findLast((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR || route.name === SCREENS.SEARCH.CENTRAL_PANE), ); @@ -14,22 +16,32 @@ function ActiveWorkspaceContextProvider({children}: ChildrenProps) { const policyIDFromRouteParam = lastPolicyRoute?.params && 'policyID' in lastPolicyRoute.params ? (lastPolicyRoute?.params?.policyID as string) : ''; const queryFromRouteParam = lastPolicyRoute?.params && 'q' in lastPolicyRoute.params ? (lastPolicyRoute.params.q as string) : ''; - const activeWorkspaceID = useMemo(() => { - if (policyIDFromRouteParam) { - return policyIDFromRouteParam; + useEffect(() => { + if (!policyIDFromRouteParam) { + return; } + setActiveWorkspaceID(policyIDFromRouteParam); + }, [policyIDFromRouteParam, setActiveWorkspaceID]); - if (queryFromRouteParam) { - const queryJSON = SearchUtils.buildSearchQueryJSON(queryFromRouteParam); - if (!queryJSON) { - return undefined; - } - - return SearchUtils.getPolicyIDFromSearchQuery(queryJSON); + useEffect(() => { + if (!queryFromRouteParam) { + return; } - return undefined; - }, [policyIDFromRouteParam, queryFromRouteParam]); + const queryJSON = SearchUtils.buildSearchQueryJSON(queryFromRouteParam); + if (!queryJSON) { + return undefined; + } + setActiveWorkspaceID(SearchUtils.getPolicyIDFromSearchQuery(queryJSON)); + }, [queryFromRouteParam]); + + const value = useMemo( + () => ({ + activeWorkspaceID, + setActiveWorkspaceID, + }), + [activeWorkspaceID, setActiveWorkspaceID], + ); // @TODO Remember to handle saving activeWorkspaceID in the session storage // const setActiveWorkspaceID = useCallback((workspaceID: string | undefined) => { @@ -41,7 +53,8 @@ function ActiveWorkspaceContextProvider({children}: ChildrenProps) { // } // }, []); - return {children}; + return {children}; } export default ActiveWorkspaceContextProvider; +export {}; diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index e884daf4e788..73829989409c 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -125,7 +125,7 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {selectedTransactions} = useSearchContext(); const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); diff --git a/src/hooks/useActiveWorkspace.ts b/src/hooks/useActiveWorkspace.ts index 11a986cdb053..568f1d73727c 100644 --- a/src/hooks/useActiveWorkspace.ts +++ b/src/hooks/useActiveWorkspace.ts @@ -1,7 +1,7 @@ import {useContext} from 'react'; import ActiveWorkspaceContext from '@components/ActiveWorkspace/ActiveWorkspaceContext'; -function useActiveWorkspace(): string { +function useActiveWorkspace(): {activeWorkspaceID: string | undefined; setActiveWorkspaceID: (workspaceID: string | undefined) => void} { return useContext(ActiveWorkspaceContext); } diff --git a/src/hooks/useReportIDs.tsx b/src/hooks/useReportIDs.tsx index d51d9e53c48c..b7d84cb25196 100644 --- a/src/hooks/useReportIDs.tsx +++ b/src/hooks/useReportIDs.tsx @@ -91,7 +91,7 @@ function ReportIDsContextProvider({ const {accountID} = useCurrentUserPersonalDetails(); const currentReportIDValue = useCurrentReportID(); const derivedCurrentReportID = currentReportIDForTests ?? currentReportIDValue?.currentReportID; - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const policyMemberAccountIDs = useMemo(() => getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID), [policies, activeWorkspaceID, accountID]); diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 3fc647aec865..70e2e0507bd7 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -221,7 +221,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie const {shouldUseNarrowLayout, onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils); const {canUseDefaultRooms} = usePermissions(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const onboardingModalScreenOptions = useMemo(() => screenOptions.onboardingModalNavigator(onboardingIsMediumOrLargerScreenWidth), [screenOptions, onboardingIsMediumOrLargerScreenWidth]); const onboardingScreenOptions = useMemo( () => getOnboardingModalScreenOptions(shouldUseNarrowLayout, styles, StyleUtils, onboardingIsMediumOrLargerScreenWidth), diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index c52c97fef17a..2ad1d7a480c9 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -23,7 +23,7 @@ function shouldOpenOnAdminRoom() { function ReportsSplitNavigator() { const {canUseDefaultRooms} = usePermissions(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); let initialReportID: string | undefined; const isInitialRender = useRef(true); diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 1a3c281b6fa6..791032088207 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -69,7 +69,7 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const navigation = useNavigation(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const transactionViolations = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const [chatTabBrickRoad, setChatTabBrickRoad] = useState(getChatTabBrickRoad(activeWorkspaceID)); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index b790713df3d1..d75a1b04a856 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -1,6 +1,7 @@ import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigationState} from '@react-navigation/native'; import {findFocusedRoute, getPathFromState, StackActions, StackRouter} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/routers'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import * as Localize from '@libs/Localize'; import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import isSideModalNavigator from '@libs/Navigation/isSideModalNavigator'; @@ -62,6 +63,7 @@ type CustomRootStackActionType = { function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { const stackRouter = StackRouter(options); + const {setActiveWorkspaceID} = useActiveWorkspace(); return { ...stackRouter, @@ -74,15 +76,19 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { if (!queryJSON) { return null; } + if (action.payload.policyID) { queryJSON.policyID = action.payload.policyID; } else { delete queryJSON.policyID; } + const newAction = StackActions.push(SCREENS.SEARCH.CENTRAL_PANE, { ...currentParams, q: SearchUtils.buildSearchQueryString(queryJSON), }); + + setActiveWorkspaceID(action.payload.policyID); return stackRouter.getStateForAction(state, newAction, configOptions); } if (lastRoute?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { @@ -100,6 +106,8 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { return stackRouter.getStateForAction(state, newAction, configOptions); } const newAction = StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, {policyID: action.payload.policyID}); + + setActiveWorkspaceID(action.payload.policyID); return stackRouter.getStateForAction(state, newAction, configOptions); } // We don't have other navigators that should handle switch policy action. diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index ce8ddfd1f881..241485204019 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import {sortWorkspacesBySelected} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -41,7 +41,7 @@ function WorkspaceSwitcherPage() { const {isOffline} = useNetwork(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const {translate} = useLocalize(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 18c167774679..b119bd89bf61 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -109,7 +109,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const reactionListRef = useRef(null); const {isOffline} = useNetwork(); const {shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const [modal] = useOnyx(ONYXKEYS.MODAL); const [isComposerFullSize] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportIDFromRoute}`, {initialValue: false}); const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, {initialValue: ''}); diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index d83b4758ce52..5b7faa9a20b2 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -34,7 +34,7 @@ type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { function SidebarLinksData({insets, isLoadingApp = true, onLinkClick, priorityMode = CONST.PRIORITY_MODE.DEFAULT}: SidebarLinksDataProps) { const isFocused = useIsFocused(); const styles = useThemeStyles(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const {translate} = useLocalize(); const {orderedReportIDs, currentReportID, policyMemberAccountIDs} = useReportIDs(); diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index 08ce4a7df94d..e48b27ad67c7 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -27,7 +27,7 @@ const startTimer = () => { function BaseSidebarScreen() { const styles = useThemeStyles(); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const {translate} = useLocalize(); const [activeWorkspace] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID ?? -1}`); diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index 254afded739d..c0e953a6a350 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -71,7 +71,7 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli const wasLoading = usePrevious(!!formState?.isLoading); const visibilityDescription = useMemo(() => translate(`newRoomPage.${visibility}Description`), [translate, visibility]); const {isLoading = false, errorFields = {}} = formState ?? {}; - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const activeWorkspaceOrDefaultID = activeWorkspaceID ?? activePolicyID; diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index f70b34c8ee50..05b1116cd226 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -127,7 +127,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); const [allConnectionSyncProgresses] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS); - const activeWorkspaceID = useActiveWorkspace(); + const {activeWorkspaceID} = useActiveWorkspace(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [policyIDToDelete, setPolicyIDToDelete] = useState(); From 1f7825e7b8fd18e6ed281b975d6a2ecf76c111c2 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 19 Sep 2024 12:53:06 +0200 Subject: [PATCH 22/34] Fix LHN paddings --- src/pages/Search/SearchPageBottomTab.tsx | 1 - src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx | 4 +--- src/pages/settings/InitialSettingsPage.tsx | 7 +++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 08329afc36ca..f3e1a70ad853 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -37,7 +37,6 @@ function SearchPageBottomTab({queryJSON, policyID}: SearchPageBottomTabProps) { return ( {({insets}) => ( <> diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index a546774b950a..0a307c4a6cd0 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -427,9 +427,8 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms return ( {headerContent} @@ -439,7 +438,7 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms scrollEventThrottle={16} contentContainerStyle={[styles.w100]} showsVerticalScrollIndicator={false} - > + > {accountMenuItems} {workspaceMenuItems} {generalMenuItems} From 17d627cbb5c034a46b42f61e71509d61c8aad45b Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Thu, 19 Sep 2024 14:22:34 +0200 Subject: [PATCH 23/34] Refactor ActiveWorkspaceProvider useEffect --- .../ActiveWorkspaceProvider/index.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/ActiveWorkspaceProvider/index.tsx b/src/components/ActiveWorkspaceProvider/index.tsx index 2f734909ec2d..d306226a85c2 100644 --- a/src/components/ActiveWorkspaceProvider/index.tsx +++ b/src/components/ActiveWorkspaceProvider/index.tsx @@ -17,23 +17,23 @@ function ActiveWorkspaceContextProvider({children}: ChildrenProps) { const queryFromRouteParam = lastPolicyRoute?.params && 'q' in lastPolicyRoute.params ? (lastPolicyRoute.params.q as string) : ''; useEffect(() => { - if (!policyIDFromRouteParam) { + if (policyIDFromRouteParam) { + setActiveWorkspaceID(policyIDFromRouteParam); return; } - setActiveWorkspaceID(policyIDFromRouteParam); - }, [policyIDFromRouteParam, setActiveWorkspaceID]); - useEffect(() => { - if (!queryFromRouteParam) { + if (queryFromRouteParam) { + const queryJSON = SearchUtils.buildSearchQueryJSON(queryFromRouteParam); + if (!queryJSON) { + setActiveWorkspaceID(undefined); + return; + } + setActiveWorkspaceID(SearchUtils.getPolicyIDFromSearchQuery(queryJSON)); return; } - const queryJSON = SearchUtils.buildSearchQueryJSON(queryFromRouteParam); - if (!queryJSON) { - return undefined; - } - setActiveWorkspaceID(SearchUtils.getPolicyIDFromSearchQuery(queryJSON)); - }, [queryFromRouteParam]); + setActiveWorkspaceID(undefined); + }, [policyIDFromRouteParam, queryFromRouteParam, setActiveWorkspaceID]); const value = useMemo( () => ({ From cb329e66b1f6dba0e4524f2a4a31daaad078fb03 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 20 Sep 2024 12:05:31 +0200 Subject: [PATCH 24/34] Add navigateToReportWithPolicyCheck --- .../CustomRouter.ts | 67 ++++++++++------ src/libs/Navigation/Navigation.ts | 77 ++++++++++++++----- .../subscribePushNotification/index.ts | 9 +-- src/libs/actions/Report.ts | 13 +--- src/pages/ChatFinderPage/index.tsx | 2 +- src/pages/WorkspaceSwitcherPage/index.tsx | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 8 files changed, 106 insertions(+), 68 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index d75a1b04a856..87a652d56d1d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -3,6 +3,7 @@ import {findFocusedRoute, getPathFromState, StackActions, StackRouter} from '@re import type {ParamListBase} from '@react-navigation/routers'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import * as Localize from '@libs/Localize'; +import Log from '@libs/Log'; import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import isSideModalNavigator from '@libs/Navigation/isSideModalNavigator'; import linkingConfig from '@libs/Navigation/linkingConfig'; @@ -49,17 +50,14 @@ function shouldDismissSideModalNavigator(state: StackNavigationState); + const haveParamsPolicyID = action.payload.params && 'policyID' in action.payload.params; + let policyID; + + if (haveParamsPolicyID) { + policyID = (action.payload.params as Record)?.policyID; + setActiveWorkspaceID(policyID); + } else { + policyID = getPolicyIDFromState(state as State); + } const modifiedAction = { ...action, diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 0aedb95fa315..531d1f1c6bde 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -4,6 +4,7 @@ import {CommonActions, getPathFromState, StackActions} from '@react-navigation/n import type {OnyxEntry} from 'react-native-onyx'; import Log from '@libs/Log'; import {isCentralPaneName, removePolicyIDParamFromState} from '@libs/NavigationUtils'; +import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; @@ -11,11 +12,11 @@ import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {HybridAppRoute, Route} from '@src/ROUTES'; import ROUTES, {HYBRID_APP_ROUTES} from '@src/ROUTES'; -import {PROTECTED_SCREENS} from '@src/SCREENS'; +import SCREENS, {PROTECTED_SCREENS} from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import originalCloseRHPFlow from './closeRHPFlow'; -import originalDismissModal from './dismissModal'; -import originalDismissModalWithReport from './dismissModalWithReport'; +import getPolicyIDFromState from './getPolicyIDFromState'; import getTopmostBottomTabRoute from './getTopmostBottomTabRoute'; import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; import originalGetTopmostReportActionId from './getTopmostReportActionID'; @@ -57,22 +58,12 @@ const getTopmostReportId = (state = navigationRef.getState()) => originalGetTopm // Re-exporting the getTopmostReportActionID here to fill in default value for state. The getTopmostReportActionID isn't defined in this file to avoid cyclic dependencies. const getTopmostReportActionId = (state = navigationRef.getState()) => originalGetTopmostReportActionId(state); -// Re-exporting the dismissModal here to fill in default value for navigationRef. The dismissModal isn't defined in this file to avoid cyclic dependencies. -const dismissModal = (reportID?: string, ref = navigationRef) => { - if (!reportID) { - originalDismissModal(ref); - return; - } - const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - originalDismissModalWithReport({reportID, ...report}, ref); -}; // Re-exporting the closeRHPFlow here to fill in default value for navigationRef. The closeRHPFlow isn't defined in this file to avoid cyclic dependencies. const closeRHPFlow = (ref = navigationRef) => originalCloseRHPFlow(ref); // Re-exporting the dismissModalWithReport here to fill in default value for navigationRef. The dismissModalWithReport isn't defined in this file to avoid cyclic dependencies. // This method is needed because it allows to dismiss the modal and then open the report. Within this method is checked whether the report belongs to a specific workspace. Sometimes the report we want to check, hasn't been added to the Onyx yet. // Then we can pass the report as a param without getting it from the Onyx. -const dismissModalWithReport = (report: OnyxEntry, ref = navigationRef) => originalDismissModalWithReport(report, ref); /** Method for finding on which index in stack we are. */ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number | undefined { @@ -424,17 +415,60 @@ function getTopMostCentralPaneRouteFromRootState() { return getTopmostCentralPaneRoute(navigationRef.getRootState() as State); } -type SwitchPolicyIDPayload = { - policyID?: string; - reportID?: number; - reportActionID?: string; - referrer?: string; -}; +function switchPolicyID(policyID?: string) { + navigationRef.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.SWITCH_POLICY_ID, payload: {policyID}}); +} + +type NavigateToReportWithPolicyCheckPayload = {report?: OnyxEntry; reportID?: string; reportActionID?: string; referrer?: string; policyIDToCheck?: string}; + +function navigateToReportWithPolicyCheck({report, reportID, reportActionID, referrer, policyIDToCheck}: NavigateToReportWithPolicyCheckPayload, ref = navigationRef) { + const targetReport = reportID ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] : report; + if (!targetReport) { + return; + } + + const policyID = policyIDToCheck ?? getPolicyIDFromState(navigationRef.getRootState() as State); + const policyMemberAccountIDs = getPolicyEmployeeAccountIDs(policyID); + const shouldOpenAllWorkspace = isEmptyObject(targetReport) ? true : !ReportUtils.doesReportBelongToWorkspace(targetReport, policyMemberAccountIDs, policyID); + + if ((shouldOpenAllWorkspace && !policyID) || !shouldOpenAllWorkspace) { + linkTo(ref.current, ROUTES.REPORT_WITH_ID.getRoute(targetReport.reportID, reportActionID, referrer)); + return; + } + + const params: Record = { + reportID: targetReport.reportID, + }; + + if (reportActionID) { + params.reportActionID = reportActionID; + } -function switchPolicyID(payload: SwitchPolicyIDPayload) { - navigationRef.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.SWITCH_POLICY_ID, payload}); + if (referrer) { + params.referrer = referrer; + } + + ref.dispatch({ + ...StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, { + policyID: null, + screen: SCREENS.REPORT, + params, + }), + }); } +const dismissModal = (reportID?: string, ref = navigationRef) => { + ref.dispatch({type: 'DISMISS_MODAL'}); + if (!reportID) { + return; + } + navigateToReportWithPolicyCheck({reportID}); +}; +const dismissModalWithReport = (report: OnyxEntry) => { + dismissModal(); + navigateToReportWithPolicyCheck({report}); +}; + export default { setShouldPopAllStateOnUP, navigate, @@ -458,6 +492,7 @@ export default { closeRHPFlow, setNavigationActionToMicrotaskQueue, getTopMostCentralPaneRouteFromRootState, + navigateToReportWithPolicyCheck, }; export {navigationRef}; diff --git a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts index b4563e12ac8d..ad162523b2c6 100644 --- a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts +++ b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts @@ -76,9 +76,6 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati Log.info('[PushNotification] Navigating to report', false, {reportID, reportActionID}); const policyID = lastVisitedPath && extractPolicyIDFromPath(lastVisitedPath); - const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; - const reportBelongsToWorkspace = policyID && !isEmptyObject(report) && doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID); Navigation.isNavigationReady() .then(Navigation.waitForProtectedRoutes) @@ -96,11 +93,7 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati } Log.info('[PushNotification] onSelected() - Navigation is ready. Navigating...', false, {reportID, reportActionID}); - if (!reportBelongsToWorkspace) { - Navigation.switchPolicyID({policyID, reportID}); - } else { - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(String(reportID))); - } + Navigation.navigateToReportWithPolicyCheck({reportID, policyIDToCheck: policyID}); } catch (error) { let errorMessage = String(error); if (error instanceof Error) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 029ccfb4d376..bb8d65fc3e11 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1032,10 +1032,9 @@ function navigateToAndOpenReport( // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server openReport(report?.reportID ?? '', '', userLogins, newChat, undefined, undefined, undefined, avatarFile); if (shouldDismissModal) { - Navigation.dismissModalWithReport(report); - } else { - Navigation.switchPolicyID({policyID: undefined, reportID: report?.reportID ?? '-1'}); + Navigation.dismissModal(); } + Navigation.navigateToReportWithPolicyCheck({report}); } /** @@ -2479,13 +2478,7 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi const onClick = () => Modal.close(() => { const policyID = lastVisitedPath && extractPolicyIDFromPath(lastVisitedPath); - const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; - const reportBelongsToWorkspace = policyID ? doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID) : false; - if (!reportBelongsToWorkspace) { - Navigation.switchPolicyID({policyID: undefined, reportID, referrer: CONST.REFERRER.NOTIFICATION}); - return; - } - navigateFromNotification(reportID); + Navigation.navigateToReportWithPolicyCheck({reportID, referrer: CONST.REFERRER.NOTIFICATION, policyIDToCheck: policyID}); }); if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE) { diff --git a/src/pages/ChatFinderPage/index.tsx b/src/pages/ChatFinderPage/index.tsx index aabf881a8bed..b18917e20b40 100644 --- a/src/pages/ChatFinderPage/index.tsx +++ b/src/pages/ChatFinderPage/index.tsx @@ -152,7 +152,7 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa } if (option.reportID) { - Navigation.closeAndNavigate(ROUTES.REPORT_WITH_ID.getRoute(option.reportID)); + Navigation.dismissModal(option.reportID); } else { Report.navigateToAndOpenReport(option.login ? [option.login] : []); } diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index 241485204019..611def7717c5 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -92,7 +92,7 @@ function WorkspaceSwitcherPage() { Navigation.goBack(); if (policyID !== activeWorkspaceID) { - Navigation.switchPolicyID({policyID}); + Navigation.switchPolicyID(policyID); } }, [activeWorkspaceID], diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index a5d295f379a2..4c26e82c08c5 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -136,7 +136,7 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, currencyList = { // If the workspace being deleted is the active workspace, switch to the "All Workspaces" view if (activeWorkspaceID === policy?.id) { - Navigation.switchPolicyID({policyID: undefined}); + Navigation.switchPolicyID(undefined); } }, [policy?.id, policyName, activeWorkspaceID]); diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 05b1116cd226..344bb58deef7 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -144,7 +144,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: // If the workspace being deleted is the active workspace, switch to the "All Workspaces" view if (activeWorkspaceID === policyIDToDelete) { - Navigation.switchPolicyID({policyID: undefined}); + Navigation.switchPolicyID(undefined); } }; From 94ebeef21e2e12e99f2a478c9e3477d7eec99e0c Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 20 Sep 2024 13:38:10 +0200 Subject: [PATCH 25/34] Add MODAL_ROUTES_TO_DISMISS --- .../CustomRouter.ts | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 87a652d56d1d..0ff878f48bc7 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -16,6 +16,21 @@ import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; +const MODAL_ROUTES_TO_DISMISS: string[] = [ + NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, + NAVIGATORS.LEFT_MODAL_NAVIGATOR, + NAVIGATORS.RIGHT_MODAL_NAVIGATOR, + NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR, + NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR, + SCREENS.NOT_FOUND, + SCREENS.ATTACHMENTS, + SCREENS.TRANSACTION_RECEIPT, + SCREENS.PROFILE_AVATAR, + SCREENS.WORKSPACE_AVATAR, + SCREENS.REPORT_AVATAR, + SCREENS.CONCIERGE, +]; + function shouldPreventReset(state: StackNavigationState, action: CommonActions.Action | StackActionType) { if (action.type !== CONST.NAVIGATION_ACTIONS.RESET || !action?.payload) { return false; @@ -102,25 +117,13 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { if (action.type === 'DISMISS_MODAL') { const lastRoute = state.routes.at(-1); const newAction = StackActions.pop(); - switch (lastRoute?.name) { - case NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR: - case NAVIGATORS.LEFT_MODAL_NAVIGATOR: - case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: - case NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR: - case NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR: - case SCREENS.NOT_FOUND: - case SCREENS.ATTACHMENTS: - case SCREENS.TRANSACTION_RECEIPT: - case SCREENS.PROFILE_AVATAR: - case SCREENS.WORKSPACE_AVATAR: - case SCREENS.REPORT_AVATAR: - case SCREENS.CONCIERGE: - return stackRouter.getStateForAction(state, newAction, configOptions); - default: { - Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); - } + + if (!lastRoute?.name || !MODAL_ROUTES_TO_DISMISS.includes(lastRoute?.name)) { + Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); + return; } - return; + + return stackRouter.getStateForAction(state, newAction, configOptions); } // Don't let the user navigate back to a non-onboarding screen if they are currently on an onboarding screen and it's not finished. From 0a138066799f8bc7c7df04774e93b7f6e42abfe1 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 20 Sep 2024 13:39:21 +0200 Subject: [PATCH 26/34] Refactor navigateToReportWithPolicyCheck --- src/libs/Navigation/Navigation.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 531d1f1c6bde..39d3ff335728 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -448,13 +448,13 @@ function navigateToReportWithPolicyCheck({report, reportID, reportActionID, refe params.referrer = referrer; } - ref.dispatch({ - ...StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, { + ref.dispatch( + StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, { policyID: null, screen: SCREENS.REPORT, params, }), - }); + ); } const dismissModal = (reportID?: string, ref = navigationRef) => { From 70534a683265079c795cda85b6de1c302a8ef0f9 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Fri, 20 Sep 2024 15:20:52 +0200 Subject: [PATCH 27/34] remember state between tabs --- src/libs/Navigation/newLinkTo/index.ts | 6 ++-- src/pages/home/sidebar/BottomTabAvatar.tsx | 36 ++++++++++++++++++-- src/pages/workspace/WorkspaceInitialPage.tsx | 18 +++++++--- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/libs/Navigation/newLinkTo/index.ts b/src/libs/Navigation/newLinkTo/index.ts index e19f02687f1c..f8ac74d6e61f 100644 --- a/src/libs/Navigation/newLinkTo/index.ts +++ b/src/libs/Navigation/newLinkTo/index.ts @@ -24,7 +24,7 @@ function shouldDispatchAction(currentState: NavigationState, return true; } -export default function linkTo(navigation: NavigationContainerRef | null, path: Route) { +export default function linkTo(navigation: NavigationContainerRef | null, path: Route, type?: typeof CONST.NAVIGATION.ACTION_TYPE.REPLACE) { if (!navigation) { throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); } @@ -49,7 +49,9 @@ export default function linkTo(navigation: NavigationContainerRef Navigation.navigate(ROUTES.SETTINGS)); + interceptAnonymousUser(() => { + const rootState = navigationRef.getRootState(); + const lastSettingsOrWorkspaceNavigatorRoute = rootState.routes.findLast( + (rootRoute) => rootRoute.name === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR || rootRoute.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, + ); + + // If there is a workspace navigator route, then we should open the workspace initial screen as it should be "remembered". + if (lastSettingsOrWorkspaceNavigatorRoute?.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR) { + const params = lastSettingsOrWorkspaceNavigatorRoute.params as AuthScreensParamList[typeof NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR]; + + // Screens of this navigator should always have policyID + if ('params' in params && params.params?.policyID) { + Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(params.params.policyID)); + } + return; + } + + // If there is settings workspace screen in the settings navigator, then we should open the settings workspaces as it should be "remembered". + if ( + lastSettingsOrWorkspaceNavigatorRoute && + lastSettingsOrWorkspaceNavigatorRoute.state && + lastSettingsOrWorkspaceNavigatorRoute.state.routes.at(-1)?.name === SCREENS.SETTINGS.WORKSPACES + ) { + Navigation.navigate(ROUTES.SETTINGS_WORKSPACES); + return; + } + + // Otherwise we should simply open the settings navigator. + // This case also covers if there is no route to remember. + Navigation.navigate(ROUTES.SETTINGS); + }); }, [isCreateMenuOpen, shouldUseNarrowLayout, route.name]); let children; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index ece2311b2d58..14c31e61bb5b 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -24,14 +24,15 @@ import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {isConnectionInProgress} from '@libs/actions/connections'; import BottomTabBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'; import getTopmostRouteName from '@libs/Navigation/getTopmostRouteName'; -import Navigation from '@libs/Navigation/Navigation'; +import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; -import type {WorkspaceNavigatorParamList} from '@navigation/types'; import * as Policy from '@userActions/Policy/Policy'; import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; +import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; @@ -76,7 +77,7 @@ type WorkspaceInitialPageOnyxProps = { policyCategories: OnyxEntry; }; -type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps; +type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps; type PolicyFeatureStates = Record; @@ -370,7 +371,16 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyCategories Navigation.resetToHome(); Navigation.isNavigationReady().then(() => Navigation.navigate(route.params?.backTo as Route)); } else { - Navigation.dismissModal(); + // @TODO This part could be done with the new goBack method when it will be implemented. + const previousRoute = navigationRef.getRootState().routes.at(-2); + + // If there is the settings split navigator we can dismiss safely + if (previousRoute?.name === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR) { + Navigation.dismissModal(); + } else { + // If not, we are going to replace this route with the settings route + Navigation.navigate(ROUTES.SETTINGS_WORKSPACES, CONST.NAVIGATION.ACTION_TYPE.REPLACE); + } } }} policyAvatar={policyAvatar} From a254890ddfbc1e5104d267b2d294966be7b8bfc9 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 20 Sep 2024 15:53:02 +0200 Subject: [PATCH 28/34] Disable animation for settings workspaces screen --- .../Navigators/SettingsSplitNavigator.tsx | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx index 62a1af45f10b..f912c3f5a225 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsSplitNavigator.tsx @@ -1,10 +1,14 @@ import React from 'react'; import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; import createSplitStackNavigator from '@libs/Navigation/AppNavigator/createSplitStackNavigator'; import type {SettingsSplitNavigatorParamList} from '@libs/Navigation/types'; import withPrepareCentralPaneScreen from '@src/components/withPrepareCentralPaneScreen'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; +import getRootNavigatorScreenOptions from '../getRootNavigatorScreenOptions'; const loadInitialSettingsPage = () => require('../../../../pages/settings/InitialSettingsPage').default; @@ -25,6 +29,11 @@ const CENTRAL_PANE_SETTINGS_SCREENS = { const Stack = createSplitStackNavigator(); function SettingsSplitNavigator() { + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); + const screenOptions = getRootNavigatorScreenOptions(shouldUseNarrowLayout, styles, StyleUtils); + return ( - {Object.entries(CENTRAL_PANE_SETTINGS_SCREENS).map(([screenName, componentGetter]) => ( - - ))} + {Object.entries(CENTRAL_PANE_SETTINGS_SCREENS).map(([screenName, componentGetter]) => { + const options = {...screenOptions.centralPaneNavigator}; + + if (screenName === SCREENS.SETTINGS.WORKSPACES) { + options.animationEnabled = false; + } + + return ( + + ); + })} ); From eba231e5b6d01f1b23cc133b272b046dcb5174b4 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 23 Sep 2024 07:34:05 +0200 Subject: [PATCH 29/34] Handle deeplinking to report with policyID in URL --- .../linkingConfig/getAdaptedStateFromPath.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 000759306c07..3d1727f71fa9 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -133,7 +133,8 @@ function getAdaptedState(state: PartialState // }; // We need to check what is defined to know what we need to add. - const WorkspaceNavigator = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR); + const workspaceNavigator = state.routes.find((route) => route.name === NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR); + const reportNavigator = state.routes.find((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR); const rhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); const lhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR); const onboardingModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR); @@ -219,7 +220,7 @@ function getAdaptedState(state: PartialState adaptedState: getRoutesWithIndex(routes), }; } - if (WorkspaceNavigator) { + if (workspaceNavigator) { // Routes // - default bottom tab // - default central pane on desktop layout @@ -238,7 +239,7 @@ function getAdaptedState(state: PartialState ), ); - routes.push(WorkspaceNavigator); + routes.push(workspaceNavigator); return { adaptedState: getRoutesWithIndex(routes), @@ -271,6 +272,19 @@ function getAdaptedState(state: PartialState } // We need to make sure that this if only handles states where we deeplink to the bottom tab directly + + // If policyID is defined, it should be passed to the reportNavigator params. + if (reportNavigator && policyID) { + const routes = []; + const reportNavigatorWithPolicyID = {...reportNavigator}; + reportNavigatorWithPolicyID.params = {...reportNavigatorWithPolicyID.params, policyID}; + routes.push(reportNavigatorWithPolicyID); + + return { + adaptedState: getRoutesWithIndex(routes), + }; + } + return { adaptedState: state, }; From ff16c1b914332e3867fe99a2ad6151f854366bce Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 24 Sep 2024 07:12:01 +0200 Subject: [PATCH 30/34] Test freezing split navigators --- .../withPrepareCentralPaneScreen/index.tsx | 3 ++- .../Navigation/AppNavigator/AuthScreens.tsx | 7 ++++--- .../Navigators/SettingsSplitNavigator.tsx | 19 +++++++++--------- .../createCustomStackNavigator/index.tsx | 20 +++++++++---------- src/libs/Navigation/FreezeWrapper.tsx | 5 ++++- src/libs/Navigation/NavigationRoot.tsx | 2 ++ 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/components/withPrepareCentralPaneScreen/index.tsx b/src/components/withPrepareCentralPaneScreen/index.tsx index f53368188b3d..84ba31cd63fd 100644 --- a/src/components/withPrepareCentralPaneScreen/index.tsx +++ b/src/components/withPrepareCentralPaneScreen/index.tsx @@ -1,9 +1,10 @@ import type React from 'react'; +import freezeScreenWithLazyLoading from '@libs/freezeScreenWithLazyLoading'; /** * This higher-order function 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. * 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. */ export default function withPrepareCentralPaneScreen(lazyComponent: () => React.ComponentType) { - return lazyComponent; + return freezeScreenWithLazyLoading(lazyComponent); } diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 70e2e0507bd7..ddf0c2093300 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -7,6 +7,7 @@ import ActiveWorkspaceContextProvider from '@components/ActiveWorkspaceProvider' import ComposeProviders from '@components/ComposeProviders'; import OptionsListContextProvider from '@components/OptionListContextProvider'; import {SearchContextProvider} from '@components/Search/SearchContext'; +import withPrepareCentralPaneScreen from '@components/withPrepareCentralPaneScreen'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import usePermissions from '@hooks/usePermissions'; import {ReportIDsContextProvider} from '@hooks/useReportIDs'; @@ -398,17 +399,17 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie ReportsSplitNavigator)} /> SettingsSplitNavigator)} /> SearchPage)} initialParams={{q: buildSearchQueryString()}} /> require('../../../.. type Screens = Partial React.ComponentType>>; const CENTRAL_PANE_SETTINGS_SCREENS = { - [SCREENS.SETTINGS.WORKSPACES]: withPrepareCentralPaneScreen(() => require('../../../../pages/workspace/WorkspacesListPage').default), - [SCREENS.SETTINGS.PREFERENCES.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Preferences/PreferencesPage').default), - [SCREENS.SETTINGS.SECURITY]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Security/SecuritySettingsPage').default), - [SCREENS.SETTINGS.PROFILE.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Profile/ProfilePage').default), - [SCREENS.SETTINGS.WALLET.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Wallet/WalletPage').default), - [SCREENS.SETTINGS.ABOUT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/AboutPage/AboutPage').default), - [SCREENS.SETTINGS.TROUBLESHOOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Troubleshoot/TroubleshootPage').default), - [SCREENS.SETTINGS.SAVE_THE_WORLD]: withPrepareCentralPaneScreen(() => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default), - [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: withPrepareCentralPaneScreen(() => require('../../../../pages/settings/Subscription/SubscriptionSettingsPage').default), + [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../../pages/workspace/WorkspacesListPage').default, + [SCREENS.SETTINGS.PREFERENCES.ROOT]: () => require('../../../../pages/settings/Preferences/PreferencesPage').default, + [SCREENS.SETTINGS.SECURITY]: () => require('../../../../pages/settings/Security/SecuritySettingsPage').default, + [SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../../pages/settings/Profile/ProfilePage').default, + [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../../pages/settings/Wallet/WalletPage').default, + [SCREENS.SETTINGS.ABOUT]: () => require('../../../../pages/settings/AboutPage/AboutPage').default, + [SCREENS.SETTINGS.TROUBLESHOOT]: () => require('../../../../pages/settings/Troubleshoot/TroubleshootPage').default, + [SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default, + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: () => require('../../../../pages/settings/Subscription/SubscriptionSettingsPage').default, } satisfies Screens; const Stack = createSplitStackNavigator(); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index af4407af6dc2..a5aa8552179f 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -2,7 +2,7 @@ import type {ParamListBase, StackActionHelpers, StackNavigationState} from '@rea import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/native'; import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack'; import {StackView} from '@react-navigation/stack'; -import React, {useMemo} from 'react'; +import React from 'react'; import CustomRouter from './CustomRouter'; import type {ResponsiveStackNavigatorProps, ResponsiveStackNavigatorRouterOptions} from './types'; @@ -20,21 +20,21 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { initialRouteName: props.initialRouteName, }); - const stateToRender = useMemo(() => { - const routes = state.routes.slice(-3); - return { - ...state, - routes, - index: routes.length - 1, - }; - }, [state]); + // const stateToRender = useMemo(() => { + // const routes = state.routes.slice(-3); + // return { + // ...state, + // routes, + // index: routes.length - 1, + // }; + // }, [state]); return ( diff --git a/src/libs/Navigation/FreezeWrapper.tsx b/src/libs/Navigation/FreezeWrapper.tsx index fb5f769b19c1..09904485b4bf 100644 --- a/src/libs/Navigation/FreezeWrapper.tsx +++ b/src/libs/Navigation/FreezeWrapper.tsx @@ -16,7 +16,7 @@ function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) { const isFocused = useIsFocused(); const navigation = useNavigation(); const currentRoute = useRoute(); - + console.log('currentRoute', currentRoute); useEffect(() => { const index = navigation.getState()?.routes.findIndex((route) => route.key === currentRoute.key) ?? 0; screenIndexRef.current = index; @@ -26,6 +26,9 @@ function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) { useEffect(() => { const unsubscribe = navigation.addListener('state', () => { const navigationIndex = (navigation.getState()?.index ?? 0) - (screenIndexRef.current ?? 0); + if (shouldSetScreenBlurred(navigationIndex)) { + console.log('diff', navigation.getState()?.index ?? 0, screenIndexRef.current ?? 0); + } setIsScreenBlurred(shouldSetScreenBlurred(navigationIndex)); }); return () => unsubscribe(); diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 7dd62c327f38..c1bafa6ffa94 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -166,6 +166,8 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh // We want to clean saved scroll offsets for screens that aren't anymore in the state. cleanStaleScrollOffsets(state); + + console.log('state', state); }; return ( From 00810b243c2f462374ec41c4620a439af1a35605 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 24 Sep 2024 13:13:46 +0200 Subject: [PATCH 31/34] Remove state listener from FreezeWrapper --- .../index.native.tsx | 10 ---------- src/libs/Navigation/FreezeWrapper.tsx | 16 ++-------------- .../shouldSetScreenBlurred/index.native.tsx | 12 ------------ .../Navigation/shouldSetScreenBlurred/index.tsx | 2 +- 4 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 src/components/withPrepareCentralPaneScreen/index.native.tsx delete mode 100644 src/libs/Navigation/shouldSetScreenBlurred/index.native.tsx diff --git a/src/components/withPrepareCentralPaneScreen/index.native.tsx b/src/components/withPrepareCentralPaneScreen/index.native.tsx deleted file mode 100644 index 84ba31cd63fd..000000000000 --- a/src/components/withPrepareCentralPaneScreen/index.native.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type React from 'react'; -import freezeScreenWithLazyLoading from '@libs/freezeScreenWithLazyLoading'; - -/** - * This higher-order function 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. - * 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. - */ -export default function withPrepareCentralPaneScreen(lazyComponent: () => React.ComponentType) { - return freezeScreenWithLazyLoading(lazyComponent); -} diff --git a/src/libs/Navigation/FreezeWrapper.tsx b/src/libs/Navigation/FreezeWrapper.tsx index 09904485b4bf..3bdff941d4fc 100644 --- a/src/libs/Navigation/FreezeWrapper.tsx +++ b/src/libs/Navigation/FreezeWrapper.tsx @@ -10,31 +10,19 @@ type FreezeWrapperProps = ChildrenProps & { }; function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) { - const [isScreenBlurred, setIsScreenBlurred] = useState(false); // we need to know the screen index to determine if the screen can be frozen const screenIndexRef = useRef(null); const isFocused = useIsFocused(); const navigation = useNavigation(); const currentRoute = useRoute(); - console.log('currentRoute', currentRoute); + const isBlurred = shouldSetScreenBlurred((navigation.getState()?.index ?? 0) - (screenIndexRef.current ?? 0)); useEffect(() => { const index = navigation.getState()?.routes.findIndex((route) => route.key === currentRoute.key) ?? 0; screenIndexRef.current = index; // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); - useEffect(() => { - const unsubscribe = navigation.addListener('state', () => { - const navigationIndex = (navigation.getState()?.index ?? 0) - (screenIndexRef.current ?? 0); - if (shouldSetScreenBlurred(navigationIndex)) { - console.log('diff', navigation.getState()?.index ?? 0, screenIndexRef.current ?? 0); - } - setIsScreenBlurred(shouldSetScreenBlurred(navigationIndex)); - }); - return () => unsubscribe(); - }, [isFocused, isScreenBlurred, navigation]); - - return {children}; + return {children}; } FreezeWrapper.displayName = 'FreezeWrapper'; diff --git a/src/libs/Navigation/shouldSetScreenBlurred/index.native.tsx b/src/libs/Navigation/shouldSetScreenBlurred/index.native.tsx deleted file mode 100644 index 4043fddb7372..000000000000 --- a/src/libs/Navigation/shouldSetScreenBlurred/index.native.tsx +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @param navigationIndex - * - * Decides whether to set screen to blurred state. - * - * If the screen is more than 1 screen away from the current screen, freeze it, - * we don't want to freeze the screen if it's the previous screen because the freeze placeholder - * would be visible at the beginning of the back animation then - */ -const shouldSetScreenBlurred = (navigationIndex: number) => navigationIndex > 1; - -export default shouldSetScreenBlurred; diff --git a/src/libs/Navigation/shouldSetScreenBlurred/index.tsx b/src/libs/Navigation/shouldSetScreenBlurred/index.tsx index 14b45921bdb2..2461187046d8 100644 --- a/src/libs/Navigation/shouldSetScreenBlurred/index.tsx +++ b/src/libs/Navigation/shouldSetScreenBlurred/index.tsx @@ -8,6 +8,6 @@ * LHN, we have FlashList rendering in the back while we are on * Settings screen. */ -const shouldSetScreenBlurred = (navigationIndex: number) => navigationIndex >= 1; +const shouldSetScreenBlurred = (navigationIndex: number) => navigationIndex > 1; export default shouldSetScreenBlurred; From bfc35ab3b5d000229e1bb94c436b65b77f2bbc5b Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 24 Sep 2024 13:18:45 +0200 Subject: [PATCH 32/34] Freeze WorkspaceSplitNavigator --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index ddf0c2093300..c25f10f4814f 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -412,6 +412,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie getComponent={withPrepareCentralPaneScreen(() => SearchPage)} initialParams={{q: buildSearchQueryString()}} /> + WorkspaceSplitNavigator)} + /> - Date: Fri, 27 Sep 2024 17:19:40 +0200 Subject: [PATCH 33/34] Fix flickering in frozen split navigators --- .../Navigation/AppNavigator/AuthScreens.tsx | 25 +++++----- .../index.native.tsx => FreezeWrapper.tsx} | 4 +- src/libs/Navigation/FreezeWrapper/index.tsx | 46 ------------------- src/libs/Navigation/NavigationRoot.tsx | 2 - 4 files changed, 15 insertions(+), 62 deletions(-) rename src/libs/Navigation/{FreezeWrapper/index.native.tsx => FreezeWrapper.tsx} (89%) delete mode 100644 src/libs/Navigation/FreezeWrapper/index.tsx diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index a792a9599344..ea08eefff015 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -9,6 +9,7 @@ import ComposeProviders from '@components/ComposeProviders'; import OptionsListContextProvider from '@components/OptionListContextProvider'; import {SearchContextProvider} from '@components/Search/SearchContext'; import SearchRouter from '@components/Search/SearchRouter/SearchRouter'; +import withPrepareCentralPaneScreen from '@components/withPrepareCentralPaneScreen'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import usePermissions from '@hooks/usePermissions'; import {ReportIDsContextProvider} from '@hooks/useReportIDs'; @@ -32,7 +33,6 @@ import {buildSearchQueryString} from '@libs/SearchUtils'; import * as SessionUtils from '@libs/SessionUtils'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import SearchPage from '@pages/Search/SearchPage'; import DesktopSignInRedirectPage from '@pages/signin/DesktopSignInRedirectPage'; import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; @@ -53,7 +53,6 @@ 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 createCustomStackNavigator from './createCustomStackNavigator'; import defaultScreenOptions from './defaultScreenOptions'; @@ -62,11 +61,8 @@ import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator'; import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator'; import LeftModalNavigator from './Navigators/LeftModalNavigator'; import OnboardingModalNavigator from './Navigators/OnboardingModalNavigator'; -import ReportsSplitNavigator from './Navigators/ReportsSplitNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; -import SettingsSplitNavigator from './Navigators/SettingsSplitNavigator'; import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator'; -import WorkspaceSplitNavigator from './Navigators/WorkspaceSplitNavigator'; const loadReportAttachments = () => require('../../../pages/home/report/ReportAttachments').default; const loadValidateLoginPage = () => require('../../../pages/ValidateLoginPage').default; @@ -80,6 +76,11 @@ const loadReportAvatar = () => require('../../../pages/Rep const loadReceiptView = () => require('../../../pages/TransactionReceiptPage').default; const loadWorkspaceJoinUser = () => require('@pages/workspace/WorkspaceJoinUserPage').default; +const loadReportSplitNavigator = withPrepareCentralPaneScreen(() => require('./Navigators/ReportsSplitNavigator').default); +const loadSettingsSplitNavigator = withPrepareCentralPaneScreen(() => require('./Navigators/SettingsSplitNavigator').default); +const loadWorkspaceSplitNavigator = withPrepareCentralPaneScreen(() => require('./Navigators/WorkspaceSplitNavigator').default); +const loadSearchPage = withPrepareCentralPaneScreen(() => require('@pages/Search/SearchPage').default); + function shouldOpenOnAdminRoom() { const url = getCurrentUrl(); return url ? new URL(url).searchParams.get('openOnAdminRoom') === 'true' : false; @@ -209,9 +210,9 @@ const modalScreenListenersWithCancelSearch = { }; function AuthScreens() { - const [session, sessionStatus] = 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 [session] = useOnyx(ONYXKEYS.SESSION); + const [lastOpenedPublicRoomID] = useOnyx(ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID); + const [initialLastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {shouldUseNarrowLayout, onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth} = useResponsiveLayout(); @@ -397,23 +398,23 @@ function AuthScreens() { ReportsSplitNavigator)} + getComponent={loadReportSplitNavigator} /> SettingsSplitNavigator)} + getComponent={loadSettingsSplitNavigator} /> SearchPage)} + getComponent={loadSearchPage} initialParams={{q: buildSearchQueryString()}} /> WorkspaceSplitNavigator)} + getComponent={loadWorkspaceSplitNavigator} /> (null); - const isFocused = useIsFocused(); - const navigation = useNavigation(); - const currentRoute = useRoute(); - - useEffect(() => { - const index = navigation.getState()?.routes.findIndex((route) => route.key === currentRoute.key) ?? 0; - screenIndexRef.current = index; - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - const unsubscribe = navigation.addListener('state', () => { - const navigationIndex = (navigation.getState()?.index ?? 0) - (screenIndexRef.current ?? 0); - setIsScreenBlurred(shouldSetScreenBlurred(navigationIndex)); - }); - return () => unsubscribe(); - }, [isFocused, isScreenBlurred, navigation]); - - // Decouple the Suspense render task so it won't be interuptted by React's concurrent mode - // and stuck in an infinite loop - useLayoutEffect(() => { - setFreezed(!isFocused && isScreenBlurred && !keepVisible); - }, [isFocused, isScreenBlurred, keepVisible]); - - return {children}; -} - -FreezeWrapper.displayName = 'FreezeWrapper'; - -export default FreezeWrapper; diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index c1bafa6ffa94..7dd62c327f38 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -166,8 +166,6 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh // We want to clean saved scroll offsets for screens that aren't anymore in the state. cleanStaleScrollOffsets(state); - - console.log('state', state); }; return ( From 93e92332f101b5f2ecfda6b3e778c10bce38e026 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 30 Sep 2024 07:32:13 +0200 Subject: [PATCH 34/34] Fix lint in createCustomStackNavigator/index.tsx --- .../Navigation/AppNavigator/createCustomStackNavigator/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index 5c9eeb16ed7b..8c98002e5c7b 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -20,7 +20,6 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { initialRouteName: props.initialRouteName, }); - return (