Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/libs/Navigation/AppNavigator/AuthScreens.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import {createStackNavigator} from '@react-navigation/stack';

import {getNavigationModalCardStyle} from '../../../styles/styles';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import CONST from '../../../CONST';
Expand All @@ -25,6 +23,8 @@ import KeyboardShortcut from '../../KeyboardShortcut';
import Navigation from '../Navigation';
import * as User from '../../actions/User';
import NameValuePair from '../../actions/NameValuePair';
import modalCardStyleInterpolator from './modalCardStyleInterpolator';
import createCustomModalStackNavigator from './createCustomModalStackNavigator';

// Main drawer navigator
import MainDrawerNavigator from './MainDrawerNavigator';
Expand All @@ -40,7 +40,7 @@ import {
SettingsModalStackNavigator,
} from './ModalStackNavigators';

const RootStack = createStackNavigator();
const RootStack = createCustomModalStackNavigator();

const propTypes = {
network: PropTypes.shape({isOffline: PropTypes.bool}),
Expand Down Expand Up @@ -116,8 +116,14 @@ class AuthScreens extends React.Component {
const modalScreenOptions = {
headerShown: false,
cardStyle: getNavigationModalCardStyle(this.props.isSmallScreenWidth),
};
cardStyleInterpolator: modalCardStyleInterpolator,
animationEnabled: true,
gestureDirection: 'horizontal',

// This is a custom prop we are passing to custom navigator so that we will know to add a Pressable overlay
// when displaying a modal. This allows us to dismiss by clicking outside on web / large screens.
isModal: true,
};
return (
<RootStack.Navigator
mode="modal"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Pressable} from 'react-native';
import Navigation from '../../Navigation';
import styles from '../../../../styles/styles';

const propTypes = {
isDisplayingModal: PropTypes.bool.isRequired,
};

const ClickAwayHandler = (props) => {
if (!props.isDisplayingModal) {
return null;
}

return (
<Pressable
style={styles.navigationModalOverlay}
onPress={Navigation.dismissModal}
/>
);
};

ClickAwayHandler.propTypes = propTypes;
ClickAwayHandler.displayName = 'ClickAwayHandler';
export default ClickAwayHandler;
3 changes: 3 additions & 0 deletions src/libs/Navigation/AppNavigator/ClickAwayHandler/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ClickAwayHandler from './ClickAwayHandler';

export default ClickAwayHandler;
20 changes: 20 additions & 0 deletions src/libs/Navigation/AppNavigator/ClickAwayHandler/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions';
import ClickAwayHandler from './ClickAwayHandler';

const propTypes = {
...windowDimensionsPropTypes,
};

const ClickAwayHandlerWithWindowDimensions = (props) => {
if (props.isSmallScreenWidth) {
return null;
}

// eslint-disable-next-line react/jsx-props-no-spreading
return <ClickAwayHandler {...props} />;
};

ClickAwayHandlerWithWindowDimensions.propTypes = propTypes;
ClickAwayHandlerWithWindowDimensions.displayName = 'ClickAwayHandlerWithWindowDimensions';
export default withWindowDimensions(ClickAwayHandlerWithWindowDimensions);
20 changes: 10 additions & 10 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import React from 'react';

import {createStackNavigator} from '@react-navigation/stack';
import styles from '../../../styles/styles';
import ROUTES from '../../../ROUTES';
import {
SettingsModalStack,
NewChatModalStack,
NewGroupModalStack,
SearchModalStack,
DetailsModalStack,
IOURequestModalStack,
IOUBillModalStack,
} from './ModalStacks';
import NewChatPage from '../../../pages/NewChatPage';
import NewGroupPage from '../../../pages/NewGroupPage';
import SearchPage from '../../../pages/SearchPage';
Expand All @@ -23,6 +14,15 @@ import SettingsPreferencesPage from '../../../pages/settings/PreferencesPage';
import SettingsPasswordPage from '../../../pages/settings/PasswordPage';
import SettingsPaymentsPage from '../../../pages/settings/PaymentsPage';

// Setup the modal stack navigators so we only have to create them once
const SettingsModalStack = createStackNavigator();
const NewChatModalStack = createStackNavigator();
const NewGroupModalStack = createStackNavigator();
const SearchModalStack = createStackNavigator();
const DetailsModalStack = createStackNavigator();
const IOURequestModalStack = createStackNavigator();
const IOUBillModalStack = createStackNavigator();

const defaultSubRouteOptions = {
cardStyle: styles.navigationScreenCardStyle,
headerShown: false,
Expand Down
20 changes: 0 additions & 20 deletions src/libs/Navigation/AppNavigator/ModalStacks/index.js

This file was deleted.

26 changes: 0 additions & 26 deletions src/libs/Navigation/AppNavigator/ModalStacks/index.native.js

This file was deleted.

118 changes: 22 additions & 96 deletions src/libs/Navigation/AppNavigator/createCustomModalStackNavigator.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,40 @@
import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/core';
import {StackRouter} from '@react-navigation/routers';
import withWindowDimensions from '../../../components/withWindowDimensions';
import Modal from '../../../components/Modal';
import themeColors from '../../../styles/themes/default';
import ONYXKEYS from '../../../ONYXKEYS';
import Navigation from '../Navigation';
import compose from '../../compose';
import CONST from '../../../CONST';
import {StackView} from '@react-navigation/stack';
import ClickAwayHandler from './ClickAwayHandler';

const propTypes = {
// Navigation state for this navigator
// See: https://reactnavigation.org/docs/navigation-state/
state: PropTypes.shape({
// Index of the focused route object in the routes array
index: PropTypes.number,

// List of route objects (screens) which are rendered in the navigator. It also represents the history in a
// stack navigator. There should be at least one item present in this array.
routes: PropTypes.arrayOf(PropTypes.shape({

// A unique key name for a screen. Created automatically by react-nav.
key: PropTypes.string,
})),
}).isRequired,

// Object containing descriptors for each route with the route keys as its properties
// See: https://reactnavigation.org/docs/custom-navigators/#usenavigationbuilder
// eslint-disable-next-line react/no-unused-prop-types
descriptors: PropTypes.objectOf(PropTypes.shape({

// A function which can be used to render the actual screen. Calling descriptors[route.key].render() will return
// a React element containing the screen content.
render: PropTypes.func,
})).isRequired,

// Current url we are navigated to
currentURL: PropTypes.string,

// Path for the modal parent to match on
path: PropTypes.string.isRequired,
};

const defaultProps = {
currentURL: '',
};

/**
* Returns the current descriptor for the focused screen in this navigators state. The descriptor has a function
* called render() that we must call each time this navigator updates. It's important to use this method to render
* a screen, otherwise any child navigators won't be connected to the navigation tree properly.
*
* @param {Object} props
* @returns {Object}
*/
function getCurrentViewDescriptor(props) {
const currentRoute = props.state.routes[props.state.index];
const currentRouteKey = currentRoute.key;
const currentDescriptor = props.descriptors[currentRouteKey];
return currentDescriptor;
}

const ResponsiveView = props => (
<Modal
isVisible={props.currentURL
&& props.currentURL.includes(props.path)}
backgroundColor={themeColors.componentBG}
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED}
onClose={Navigation.dismissModal}
>
{getCurrentViewDescriptor(props).render()}
</Modal>
);

ResponsiveView.propTypes = propTypes;
ResponsiveView.defaultProps = defaultProps;
ResponsiveView.displayName = 'ResponsiveView';

const ResponsiveViewWithHOCs = compose(
withWindowDimensions,
withOnyx({
currentURL: {
key: ONYXKEYS.CURRENT_URL,
},
}),
)(ResponsiveView);

const ResponsiveNavigator = ({
const CustomRootStackNavigator = ({
children,
...rest
}) => {
const {state, navigation, descriptors} = useNavigationBuilder(StackRouter, {
children,
});

const isDisplayingModal = Boolean(_.find(descriptors, descriptor => descriptor.options.isModal));
return (
<ResponsiveViewWithHOCs
state={state}
navigation={navigation}
descriptors={descriptors}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
<>
<StackView
state={state}
navigation={navigation}
descriptors={descriptors}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>

{/* We need to superimpose a clickaway handler when showing modals so that they can be dismissed. Capturing
press events on the cardOverlay element in react-navigation is not yet supported on web */}
<ClickAwayHandler
isDisplayingModal={isDisplayingModal}
/>
</>
);
};

ResponsiveNavigator.propTypes = {
CustomRootStackNavigator.propTypes = {
children: PropTypes.node.isRequired,
};

export default createNavigatorFactory(ResponsiveNavigator);
export default createNavigatorFactory(CustomRootStackNavigator);
31 changes: 31 additions & 0 deletions src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {Animated} from 'react-native';

export default ({
current: {progress},
inverted,
layouts: {
screen,
},
}) => {
const translateX = Animated.multiply(progress.interpolate({
inputRange: [0, 1],
outputRange: [screen.width, 0],
extrapolate: 'clamp',
}), inverted);

return ({
containerStyle: {
overflow: 'hidden',
},
cardStyle: {
transform: [{translateX}],
},
overlayStyle: {
opacity: progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.3],
extrapolate: 'clamp',
}),
},
});
};
9 changes: 9 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,15 @@ const styles = {
zIndex: 2,
},

navigationModalOverlay: {
position: 'absolute',
width: '100%',
height: '100%',
transform: [{
translateX: -variables.sideBarWidth,
}],
},

sidebarVisible: {
borderRightWidth: 1,
},
Expand Down