diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index c68a950d3501..a3a041e65684 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -4,7 +4,6 @@ * */ export default { CENTRAL_PANE_NAVIGATOR: 'CentralPaneNavigator', - LEFT_MODAL_NAVIGATOR: 'LeftModalNavigator', RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', } as const; diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 9e52ea0a38ca..2cd263237866 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -81,12 +81,10 @@ const SCREENS = { SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', }, - LEFT_MODAL: { - SEARCH: 'Search', - }, RIGHT_MODAL: { SETTINGS: 'Settings', NEW_CHAT: 'NewChat', + SEARCH: 'Search', DETAILS: 'Details', PROFILE: 'Profile', REPORT_DETAILS: 'Report_Details', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 369a37e09e60..f6572c84709d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -36,7 +36,6 @@ import createCustomStackNavigator from './createCustomStackNavigator'; import defaultScreenOptions from './defaultScreenOptions'; import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; import CentralPaneNavigator from './Navigators/CentralPaneNavigator'; -import LeftModalNavigator from './Navigators/LeftModalNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; type AuthScreensProps = { @@ -319,12 +318,6 @@ function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoom component={RightModalNavigator} listeners={modalScreenListeners} /> - ; - -const Stack = createStackNavigator(); - -function LeftModalNavigator({navigation}: LeftModalNavigatorProps) { - const styles = useThemeStyles(); - const {isSmallScreenWidth} = useWindowDimensions(); - const screenOptions = useMemo(() => ModalNavigatorScreenOptions(styles), [styles]); - - return ( - - {!isSmallScreenWidth && ( - - )} - - - - - - - ); -} - -LeftModalNavigator.displayName = 'LeftModalNavigator'; - -export default LeftModalNavigator; diff --git a/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx b/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx index a3fe1c657f34..065de8da578b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx @@ -9,18 +9,15 @@ import CONST from '@src/CONST'; type OverlayProps = { /* Callback to close the modal */ onPress: () => void; - - /* Returns whether a modal is displayed on the left side of the screen. By default, the modal is displayed on the right */ - isModalOnTheLeft?: boolean; }; -function Overlay({onPress, isModalOnTheLeft = false}: OverlayProps) { +function Overlay({onPress}: OverlayProps) { const styles = useThemeStyles(); const {current} = useCardAnimation(); const {translate} = useLocalize(); return ( - + {/* In the latest Electron version buttons can't be both clickable and draggable. That's why we added this workaround. Because of two Pressable components on the desktop app diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index d7c31bcae7d9..bd790589c8d1 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -4,8 +4,8 @@ import {View} from 'react-native'; import NoDropZone from '@components/DragAndDrop/NoDropZone'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import ModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/ModalNavigatorScreenOptions'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; +import RHPScreenOptions from '@libs/Navigation/AppNavigator/RHPScreenOptions'; import type {AuthScreensParamList, RightModalNavigatorParamList} from '@navigation/types'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; @@ -18,7 +18,7 @@ const Stack = createStackNavigator(); function RightModalNavigator({navigation}: RightModalNavigatorProps) { const styles = useThemeStyles(); const {isSmallScreenWidth} = useWindowDimensions(); - const screenOptions = useMemo(() => ModalNavigatorScreenOptions(styles), [styles]); + const screenOptions = useMemo(() => RHPScreenOptions(styles), [styles]); return ( @@ -33,6 +33,10 @@ function RightModalNavigator({navigation}: RightModalNavigatorProps) { name={SCREENS.RIGHT_MODAL.NEW_CHAT} component={ModalStackNavigators.NewChatModalStackNavigator} /> + ({ +const RHPScreenOptions = (themeStyles: ThemeStyles): StackNavigationOptions => ({ headerShown: false, animationEnabled: true, gestureDirection: 'horizontal', @@ -14,4 +14,4 @@ const ModalNavigatorScreenOptions = (themeStyles: ThemeStyles): StackNavigationO cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, }); -export default ModalNavigatorScreenOptions; +export default RHPScreenOptions; diff --git a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts index c80ae9914347..379c5281b78f 100644 --- a/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/getRootNavigatorScreenOptions.ts @@ -15,8 +15,6 @@ const commonScreenOptions: StackNavigationOptions = { animationTypeForReplace: 'push', }; -const SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER = -1; - export default (isSmallScreenWidth: boolean, themeStyles: ThemeStyles): ScreenOptions => ({ rightModalNavigator: { ...commonScreenOptions, @@ -34,23 +32,7 @@ export default (isSmallScreenWidth: boolean, themeStyles: ThemeStyles): ScreenOp right: 0, }, }, - leftModalNavigator: { - ...commonScreenOptions, - cardStyleInterpolator: (props) => modalCardStyleInterpolator(isSmallScreenWidth, false, props, SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER), - presentation: 'transparentModal', - - // We want pop in LHP since there are some flows that would work weird otherwise - animationTypeForReplace: 'pop', - cardStyle: { - ...getNavigationModalCardStyle(), - - // This is necessary to cover translated sidebar with overlay. - width: isSmallScreenWidth ? '100%' : '200%', - // LHP should be displayed in place of the sidebar - left: isSmallScreenWidth ? 0 : -variables.sideBarWidth, - }, - }, homeScreen: { title: CONFIG.SITE_TITLE, ...commonScreenOptions, diff --git a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts index fd59b02e724d..eff88422cc5c 100644 --- a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts +++ b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.ts @@ -3,16 +3,11 @@ import {Animated} from 'react-native'; import getCardStyles from '@styles/utils/cardStyles'; import variables from '@styles/variables'; -export default ( - isSmallScreenWidth: boolean, - isFullScreenModal: boolean, - {current: {progress}, inverted, layouts: {screen}}: StackCardInterpolationProps, - outputRangeMultiplier = 1, -): StackCardInterpolatedStyle => { +export default (isSmallScreenWidth: boolean, isFullScreenModal: boolean, {current: {progress}, inverted, layouts: {screen}}: StackCardInterpolationProps): StackCardInterpolatedStyle => { const translateX = Animated.multiply( progress.interpolate({ inputRange: [0, 1], - outputRange: [outputRangeMultiplier * (isSmallScreenWidth ? screen.width : variables.sideBarWidth), 0], + outputRange: [isSmallScreenWidth ? screen.width : variables.sideBarWidth, 0], extrapolate: 'clamp', }), inverted, diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index a3e89a983f98..3552ff9e7410 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -1,17 +1,18 @@ -import {findFocusedRoute} from '@react-navigation/core'; +import {findFocusedRoute, getActionFromState} from '@react-navigation/core'; import {CommonActions, EventArg, getPathFromState, NavigationContainerEventMap, NavigationState, PartialState, StackActions} from '@react-navigation/native'; +import findLastIndex from 'lodash/findLastIndex'; import Log from '@libs/Log'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES, {Route} from '@src/ROUTES'; -import {PROTECTED_SCREENS} from '@src/SCREENS'; -import originalDismissModal from './dismissModal'; +import SCREENS, {PROTECTED_SCREENS} from '@src/SCREENS'; +import getStateFromPath from './getStateFromPath'; import originalGetTopmostReportActionId from './getTopmostReportActionID'; import originalGetTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; import linkTo from './linkTo'; import navigationRef from './navigationRef'; -import {StateOrRoute} from './types'; +import {StackNavigationAction, StateOrRoute} from './types'; let resolveNavigationIsReadyPromise: () => void; const navigationIsReadyPromise = new Promise((resolve) => { @@ -43,9 +44,6 @@ 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 = (targetReportId = '', ref = navigationRef) => originalDismissModal(targetReportId, ref); - /** Method for finding on which index in stack we are. */ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number | undefined { if ('routes' in stateOrRoute && stateOrRoute.routes) { @@ -58,7 +56,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number return getActiveRouteIndex(childActiveRoute, stateOrRoute.state.index ?? 0); } - if ('name' in stateOrRoute && (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR)) { + if ('name' in stateOrRoute && stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR) { return 0; } @@ -162,8 +160,8 @@ function goBack(fallbackRoute: Route, shouldEnforceFallback = false, shouldPopTo if (isFirstRouteInNavigator) { const rootState = navigationRef.getRootState(); const lastRoute = rootState.routes.at(-1); - // If the user comes from a different flow (there is more than one route in ModalNavigator) we should go back to the previous flow on UP button press instead of using the fallbackRoute. - if ((lastRoute?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || lastRoute?.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR) && (lastRoute.state?.index ?? 0) > 0) { + // If the user comes from a different flow (there is more than one route in RHP) we should go back to the previous flow on UP button press instead of using the fallbackRoute. + if (lastRoute?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR && (lastRoute.state?.index ?? 0) > 0) { navigationRef.current.goBack(); return; } @@ -202,6 +200,45 @@ function setParams(params: Record, routeKey: string) { }); } +/** + * Dismisses the last modal stack if there is any + * + * @param targetReportID - The reportID to navigate to after dismissing the modal + */ +function dismissModal(targetReportID?: string) { + if (!canNavigate('dismissModal')) { + return; + } + const rootState = navigationRef.getRootState(); + const lastRoute = rootState.routes.at(-1); + switch (lastRoute?.name) { + case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: + case SCREENS.NOT_FOUND: + case SCREENS.REPORT_ATTACHMENTS: + // if we are not in the target report, we need to navigate to it after dismissing the modal + if (targetReportID && targetReportID !== getTopmostReportId(rootState)) { + const state = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReportID)); + + const action: StackNavigationAction = getActionFromState(state, linkingConfig.config); + if (action) { + action.type = 'REPLACE'; + navigationRef.current?.dispatch(action); + } + // If not-found page is in the route stack, we need to close it + } else if (targetReportID && rootState.routes.some((route) => route.name === SCREENS.NOT_FOUND)) { + const lastRouteIndex = rootState.routes.length - 1; + const centralRouteIndex = findLastIndex(rootState.routes, (route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); + navigationRef.current?.dispatch({...StackActions.pop(lastRouteIndex - centralRouteIndex), target: rootState.key}); + } else { + navigationRef.current?.dispatch({...StackActions.pop(), target: rootState.key}); + } + break; + default: { + Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); + } + } +} + /** * Returns the current active route without the URL params */ diff --git a/src/libs/Navigation/dismissModal.ts b/src/libs/Navigation/dismissModal.ts deleted file mode 100644 index 37b4c6d9b9e6..000000000000 --- a/src/libs/Navigation/dismissModal.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {getActionFromState} from '@react-navigation/core'; -import {NavigationContainerRef, StackActions} from '@react-navigation/native'; -import {findLastIndex} from 'lodash'; -import Log from '@libs/Log'; -import NAVIGATORS from '@src/NAVIGATORS'; -import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import getStateFromPath from './getStateFromPath'; -import getTopmostReportId from './getTopmostReportId'; -import linkingConfig from './linkingConfig'; -import {RootStackParamList, StackNavigationAction} from './types'; - -// This function is in a separate file than Navigation.js to avoid cyclic dependency. - -/** - * Dismisses the last modal stack if there is any - * - * @param targetReportID - The reportID to navigate to after dismissing the modal - */ -function dismissModal(targetReportID: string, navigationRef: NavigationContainerRef) { - if (!navigationRef.isReady()) { - return; - } - - const state = navigationRef.getState(); - const lastRoute = state.routes.at(-1); - switch (lastRoute?.name) { - case NAVIGATORS.LEFT_MODAL_NAVIGATOR: - case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: - case SCREENS.NOT_FOUND: - case SCREENS.REPORT_ATTACHMENTS: - // if we are not in the target report, we need to navigate to it after dismissing the modal - if (targetReportID && targetReportID !== getTopmostReportId(state)) { - const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReportID)); - - const action: StackNavigationAction = getActionFromState(reportState, linkingConfig.config); - if (action) { - action.type = 'REPLACE'; - navigationRef.dispatch(action); - } - // If not-found page is in the route stack, we need to close it - } else if (targetReportID && state.routes.some((route) => route.name === SCREENS.NOT_FOUND)) { - const lastRouteIndex = state.routes.length - 1; - const centralRouteIndex = findLastIndex(state.routes, (route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); - navigationRef.dispatch({...StackActions.pop(lastRouteIndex - centralRouteIndex), target: state.key}); - } else { - navigationRef.dispatch({...StackActions.pop(), target: state.key}); - } - break; - default: { - Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss'); - } - } -} - -export default dismissModal; diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts index 86558765a6e6..9694879f9aae 100644 --- a/src/libs/Navigation/linkTo.ts +++ b/src/libs/Navigation/linkTo.ts @@ -4,7 +4,6 @@ import {Writable} from 'type-fest'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import {Route} from '@src/ROUTES'; -import dismissModal from './dismissModal'; import getStateFromPath from './getStateFromPath'; import getTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; @@ -56,10 +55,6 @@ function getMinimalAction(action: NavigationAction, state: NavigationState): Wri return currentAction; } -function isModalNavigator(targetNavigator?: string) { - return targetNavigator === NAVIGATORS.LEFT_MODAL_NAVIGATOR || targetNavigator === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; -} - 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?"); @@ -80,9 +75,6 @@ export default function linkTo(navigation: NavigationContainerRef = { }, }, [SCREENS.NOT_FOUND]: '*', - [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: { - screens: { - [SCREENS.LEFT_MODAL.SEARCH]: { - screens: { - [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH, - }, - }, - }, - }, + [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: { screens: { [SCREENS.RIGHT_MODAL.SETTINGS]: { @@ -349,6 +341,11 @@ const linkingConfig: LinkingOptions = { [SCREENS.I_AM_A_TEACHER]: ROUTES.I_AM_A_TEACHER, }, }, + [SCREENS.RIGHT_MODAL.SEARCH]: { + screens: { + [SCREENS.SEARCH_ROOT]: ROUTES.SEARCH, + }, + }, [SCREENS.RIGHT_MODAL.DETAILS]: { screens: { [SCREENS.DETAILS_ROOT]: ROUTES.DETAILS.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 95d5c0f7ff15..7f13640f0e9b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -334,13 +334,10 @@ type PrivateNotesNavigatorParamList = { }; }; -type LeftModalNavigatorParamList = { - [SCREENS.LEFT_MODAL.SEARCH]: NavigatorScreenParams; -}; - type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.SETTINGS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.NEW_CHAT]: NavigatorScreenParams; + [SCREENS.RIGHT_MODAL.SEARCH]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.PROFILE]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: NavigatorScreenParams; @@ -410,7 +407,6 @@ type AuthScreensParamList = { name: string; }; [SCREENS.NOT_FOUND]: undefined; - [NAVIGATORS.LEFT_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: NavigatorScreenParams; [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined; [CONST.DEMO_PAGES.MONEY2020]: undefined; @@ -427,7 +423,6 @@ export type { NavigationStateRoute, NavigationRoot, AuthScreensParamList, - LeftModalNavigatorParamList, RightModalNavigatorParamList, PublicScreensParamList, MoneyRequestNavigatorParamList, diff --git a/src/styles/index.ts b/src/styles/index.ts index 905c25f4f7d8..67be7cce1642 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1415,12 +1415,6 @@ const styles = (theme: ThemeColors) => height: variables.lineHeightSizeh1, }, - LHPNavigatorContainer: (isSmallScreenWidth: boolean) => - ({ - ...modalNavigatorContainer(isSmallScreenWidth), - left: 0, - } satisfies ViewStyle), - RHPNavigatorContainer: (isSmallScreenWidth: boolean) => ({ ...modalNavigatorContainer(isSmallScreenWidth), @@ -1640,15 +1634,14 @@ const styles = (theme: ThemeColors) => marginBottom: 4, }, - overlayStyles: (current: OverlayStylesParams, isModalOnTheLeft: boolean) => + overlayStyles: (current: OverlayStylesParams) => ({ ...positioning.pFixed, // We need to stretch the overlay to cover the sidebar and the translate animation distance. - // The overlay must also cover borderRadius of the LHP component - left: isModalOnTheLeft ? 0 : -2 * variables.sideBarWidth, + left: -2 * variables.sideBarWidth, top: 0, bottom: 0, - right: isModalOnTheLeft ? -2 * variables.sideBarWidth : 0, + right: 0, backgroundColor: theme.overlay, opacity: current.progress.interpolate({ inputRange: [0, 1],