Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Ideal nav LHP #32473

Merged
merged 27 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
379ac08
Add LeftModalNavigator
WojtekBoman Nov 22, 2023
3af4938
Add LHP Animation, dismissing modals, borderRadius
WojtekBoman Nov 23, 2023
b2130bb
Modify linkTo function
WojtekBoman Nov 28, 2023
bdb17ef
Add types for LHP screens, move dismissModal to separated file
WojtekBoman Nov 29, 2023
e0cc1c7
Remove dismissModal from Navigation.ts
WojtekBoman Nov 29, 2023
a0806cb
Refactor linkTo method
WojtekBoman Nov 29, 2023
33108e3
Refactor dismissModal function
WojtekBoman Nov 30, 2023
456eff6
fix: 30062
dukenv0307 Oct 23, 2023
ca645b6
add dependency
dukenv0307 Nov 29, 2023
6a315f6
add name value
DylanDylann Nov 27, 2023
f32a907
fix lint
DylanDylann Nov 27, 2023
e180b99
update name to null when offline
DylanDylann Dec 1, 2023
9677a22
spread the array
bernhardoj Dec 4, 2023
152c136
Add TS to LHP
WojtekBoman Dec 4, 2023
0c7d7b8
Refactor LHP linking config
WojtekBoman Dec 5, 2023
4b875bb
Rename RHPScreenOptions to ModalNavigatorScreenOptions
WojtekBoman Dec 5, 2023
7694af7
Fix import order in LHP and RHP
WojtekBoman Dec 5, 2023
062f969
Merge branch 'main' into ideal-nav-lhp
WojtekBoman Dec 6, 2023
4ccba51
Merge branch 'main' into ideal-nav-lhp
WojtekBoman Dec 14, 2023
6f5fab2
Refactor LHPNavigatorContainer borderRadius
WojtekBoman Dec 14, 2023
7753b91
Remove referral from SearchPage
WojtekBoman Dec 14, 2023
3bbad0e
Remove borderRadius from the LHP on small screens
WojtekBoman Dec 14, 2023
c959ad8
LHP code cleanup
WojtekBoman Dec 15, 2023
fb9e622
Merge branch 'main' into ideal-nav-lhp
WojtekBoman Dec 15, 2023
4d45639
Fix overlay animationfor LHP
WojtekBoman Dec 15, 2023
02953bf
Refactor goBack function to handle LHP
WojtekBoman Dec 15, 2023
b54996f
Update leftModalNavigator comment
WojtekBoman Dec 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/NAVIGATORS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* */
export default {
CENTRAL_PANE_NAVIGATOR: 'CentralPaneNavigator',
LEFT_MODAL_NAVIGATOR: 'LeftModalNavigator',
RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator',
FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator',
} as const;
4 changes: 3 additions & 1 deletion src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ 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',
Expand Down
7 changes: 7 additions & 0 deletions src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ 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 = {
Expand Down Expand Up @@ -318,6 +319,12 @@ function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoom
component={RightModalNavigator}
listeners={modalScreenListeners}
/>
<RootStack.Screen
name={NAVIGATORS.LEFT_MODAL_NAVIGATOR}
options={screenOptions.leftModalNavigator}
component={LeftModalNavigator}
listeners={modalScreenListeners}
/>
<RootStack.Screen
name={SCREENS.DESKTOP_SIGN_IN_REDIRECT}
options={screenOptions.fullScreen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import {CardStyleInterpolators, StackNavigationOptions} from '@react-navigation/
import {ThemeStyles} from '@styles/index';

/**
* RHP stack navigator screen options generator function
* Modal stack navigator screen options generator function
* @param themeStyles - The styles object
* @returns The screen options object
*/
const RHPScreenOptions = (themeStyles: ThemeStyles): StackNavigationOptions => ({
const ModalNavigatorScreenOptions = (themeStyles: ThemeStyles): StackNavigationOptions => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments above this function are outdated. Please update

headerShown: false,
animationEnabled: true,
gestureDirection: 'horizontal',
cardStyle: themeStyles.navigationScreenCardStyle,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
});

export default RHPScreenOptions;
export default ModalNavigatorScreenOptions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {createStackNavigator, StackScreenProps} from '@react-navigation/stack';
import React, {useMemo} from 'react';
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 {AuthScreensParamList, LeftModalNavigatorParamList} from '@libs/Navigation/types';
import NAVIGATORS from '@src/NAVIGATORS';
import SCREENS from '@src/SCREENS';
import Overlay from './Overlay';

type LeftModalNavigatorProps = StackScreenProps<AuthScreensParamList, typeof NAVIGATORS.LEFT_MODAL_NAVIGATOR>;

const Stack = createStackNavigator<LeftModalNavigatorParamList>();

function LeftModalNavigator({navigation}: LeftModalNavigatorProps) {
const styles = useThemeStyles();
const {isSmallScreenWidth} = useWindowDimensions();
const screenOptions = useMemo(() => ModalNavigatorScreenOptions(styles), [styles]);

return (
<NoDropZone>
{!isSmallScreenWidth && (
<Overlay
isModalOnTheLeft
onPress={navigation.goBack}
/>
)}
<View style={styles.LHPNavigatorContainer(isSmallScreenWidth)}>
<Stack.Navigator screenOptions={screenOptions}>
<Stack.Screen
name={SCREENS.LEFT_MODAL.SEARCH}
component={ModalStackNavigators.SearchModalStackNavigator}
/>
</Stack.Navigator>
</View>
</NoDropZone>
);
}

LeftModalNavigator.displayName = 'LeftModalNavigator';

export default LeftModalNavigator;
7 changes: 5 additions & 2 deletions src/libs/Navigation/AppNavigator/Navigators/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ 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}: OverlayProps) {
function Overlay({onPress, isModalOnTheLeft = false}: OverlayProps) {
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
const styles = useThemeStyles();
const {current} = useCardAnimation();
const {translate} = useLocalize();

return (
<Animated.View style={styles.overlayStyles(current)}>
<Animated.View style={styles.overlayStyles(current, isModalOnTheLeft)}>
<View style={[styles.flex1, styles.flexColumn]}>
{/* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,7 +18,7 @@ const Stack = createStackNavigator<RightModalNavigatorParamList>();
function RightModalNavigator({navigation}: RightModalNavigatorProps) {
const styles = useThemeStyles();
const {isSmallScreenWidth} = useWindowDimensions();
const screenOptions = useMemo(() => RHPScreenOptions(styles), [styles]);
const screenOptions = useMemo(() => ModalNavigatorScreenOptions(styles), [styles]);

return (
<NoDropZone>
Expand All @@ -33,10 +33,6 @@ function RightModalNavigator({navigation}: RightModalNavigatorProps) {
name={SCREENS.RIGHT_MODAL.NEW_CHAT}
component={ModalStackNavigators.NewChatModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.SEARCH}
component={ModalStackNavigators.SearchModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.DETAILS}
component={ModalStackNavigators.DetailsModalStackNavigator}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const commonScreenOptions: StackNavigationOptions = {
animationTypeForReplace: 'push',
};

const SLIDE_LEFT_OUTPUT_RANGE_MULTIPLIER = -1;
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved

export default (isSmallScreenWidth: boolean, themeStyles: ThemeStyles): ScreenOptions => ({
rightModalNavigator: {
...commonScreenOptions,
Expand All @@ -32,7 +34,22 @@ 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%',

transform: [{translateX: isSmallScreenWidth ? 0 : -variables.sideBarWidth}],
},
},
homeScreen: {
title: CONFIG.SITE_TITLE,
...commonScreenOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ 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): StackCardInterpolatedStyle => {
export default (
isSmallScreenWidth: boolean,
isFullScreenModal: boolean,
{current: {progress}, inverted, layouts: {screen}}: StackCardInterpolationProps,
outputRangeMultiplier = 1,
): StackCardInterpolatedStyle => {
const translateX = Animated.multiply(
progress.interpolate({
inputRange: [0, 1],
outputRange: [isSmallScreenWidth ? screen.width : variables.sideBarWidth, 0],
outputRange: [outputRangeMultiplier * (isSmallScreenWidth ? screen.width : variables.sideBarWidth), 0],
extrapolate: 'clamp',
}),
inverted,
Expand Down
57 changes: 10 additions & 47 deletions src/libs/Navigation/Navigation.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

goBack function contains logic for the RHP (isFirstRouteInNavigator). We should account for the LHP too

Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import {findFocusedRoute, getActionFromState} from '@react-navigation/core';
import {findFocusedRoute} 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 SCREENS, {PROTECTED_SCREENS} from '@src/SCREENS';
import getStateFromPath from './getStateFromPath';
import {PROTECTED_SCREENS} from '@src/SCREENS';
import originalDismissModal from './dismissModal';
import originalGetTopmostReportActionId from './getTopmostReportActionID';
import originalGetTopmostReportId from './getTopmostReportId';
import linkingConfig from './linkingConfig';
import linkTo from './linkTo';
import navigationRef from './navigationRef';
import {StackNavigationAction, StateOrRoute} from './types';
import {StateOrRoute} from './types';

let resolveNavigationIsReadyPromise: () => void;
const navigationIsReadyPromise = new Promise<void>((resolve) => {
Expand Down Expand Up @@ -44,6 +43,9 @@ 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) {
Expand All @@ -56,7 +58,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) {
if ('name' in stateOrRoute && (stateOrRoute.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || stateOrRoute.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR)) {
return 0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

goBack function contains logic for the RHP (isFirstRouteInNavigator). We should account for the LHP too

@s77rt I've modified this condition to check it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that does not seem enough. In goBack there is another check for the root name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the additional check

}

Expand Down Expand Up @@ -160,8 +162,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 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) {
// 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) {
navigationRef.current.goBack();
return;
}
Expand Down Expand Up @@ -200,45 +202,6 @@ function setParams(params: Record<string, unknown>, 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
*/
Expand Down
56 changes: 56 additions & 0 deletions src/libs/Navigation/dismissModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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<RootStackParamList>) {
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;
Loading
Loading