diff --git a/src/components/ScreenWrapper/BaseScreenWrapper.js b/src/components/ScreenWrapper/BaseScreenWrapper.js index a80a013a1c66..56a0c30b2ecf 100644 --- a/src/components/ScreenWrapper/BaseScreenWrapper.js +++ b/src/components/ScreenWrapper/BaseScreenWrapper.js @@ -44,6 +44,23 @@ class BaseScreenWrapper extends React.Component { }); } + /** + * We explicitly want to ignore if props.modal changes, and only want to rerender if + * any of the other props **used for the rendering output** is changed. + * @param {Object} nextProps + * @param {Object} nextState + * @returns {boolean} + */ + shouldComponentUpdate(nextProps, nextState) { + return this.state !== nextState + || this.props.children !== nextProps.children + || this.props.network.isOffline !== nextProps.network.isOffline + || this.props.includePaddingBottom !== nextProps.includePaddingBottom + || this.props.includePaddingTop !== nextProps.includePaddingTop + || this.props.isSmallScreenWidth !== nextProps.isSmallScreenWidth + || this.props.keyboardAvoidingViewBehavior !== nextProps.keyboardAvoidingViewBehavior; + } + componentWillUnmount() { if (this.unsubscribeEscapeKey) { this.unsubscribeEscapeKey(); diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js index 6dfb496cb28f..2f9eb5c4c2bc 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js @@ -1,56 +1,31 @@ import lodashGet from 'lodash/get'; -import _ from 'underscore'; import React, {Component} from 'react'; import {View} from 'react-native'; -import PropTypes from 'prop-types'; import styles from '../../../../styles/styles'; import SidebarLinks from '../SidebarLinks'; -import PopoverMenu from '../../../../components/PopoverMenu'; -import FloatingActionButton from '../../../../components/FloatingActionButton'; import ScreenWrapper from '../../../../components/ScreenWrapper'; import Navigation from '../../../../libs/Navigation/Navigation'; import ROUTES from '../../../../ROUTES'; import Timing from '../../../../libs/actions/Timing'; import CONST from '../../../../CONST'; -import * as Expensicons from '../../../../components/Icon/Expensicons'; -import Permissions from '../../../../libs/Permissions'; -import * as Policy from '../../../../libs/actions/Policy'; import Performance from '../../../../libs/Performance'; import * as Welcome from '../../../../libs/actions/Welcome'; -import {sidebarPropTypes, sidebarDefaultProps} from './sidebarPropTypes'; import withDrawerState from '../../../../components/withDrawerState'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import compose from '../../../../libs/compose'; +import sidebarPropTypes from './sidebarPropTypes'; const propTypes = { - - /** Callback function when the menu is shown */ - onShowCreateMenu: PropTypes.func, - - /** Callback function before the menu is hidden */ - onHideCreateMenu: PropTypes.func, - - /** reportID in the current navigation state */ - reportIDFromRoute: PropTypes.string, - ...sidebarPropTypes, -}; -const defaultProps = { - onHideCreateMenu: () => {}, - onShowCreateMenu: () => {}, - ...sidebarDefaultProps, + ...windowDimensionsPropTypes, }; class BaseSidebarScreen extends Component { constructor(props) { super(props); - this.hideCreateMenu = this.hideCreateMenu.bind(this); this.startTimer = this.startTimer.bind(this); this.navigateToSettings = this.navigateToSettings.bind(this); - this.showCreateMenu = this.showCreateMenu.bind(this); - - this.state = { - isCreateMenuActive: false, - }; } componentDidMount() { @@ -61,16 +36,6 @@ class BaseSidebarScreen extends Component { Welcome.show({routes, showCreateMenu: this.showCreateMenu}); } - /** - * Method called when we click the floating action button - */ - showCreateMenu() { - this.setState({ - isCreateMenuActive: true, - }); - this.props.onShowCreateMenu(); - } - /** * Method called when avatar is clicked */ @@ -78,18 +43,6 @@ class BaseSidebarScreen extends Component { Navigation.navigate(ROUTES.SETTINGS); } - /** - * Method called either when: - * Pressing the floating action button to open the CreateMenu modal - * Selecting an item on CreateMenu or closing it by clicking outside of the modal component - */ - hideCreateMenu() { - this.props.onHideCreateMenu(); - this.setState({ - isCreateMenuActive: false, - }); - } - /** * Method called when a pinned chat is selected. */ @@ -99,89 +52,22 @@ class BaseSidebarScreen extends Component { } render() { - // Workspaces are policies with type === 'free' - const workspaces = _.filter(this.props.allPolicies, policy => policy && policy.type === CONST.POLICY.TYPE.FREE); return ( {({insets}) => ( - <> - - - - - Navigation.navigate(ROUTES.NEW_CHAT), - }, - { - icon: Expensicons.Users, - text: this.props.translate('sidebarScreen.newGroup'), - onSelected: () => Navigation.navigate(ROUTES.NEW_GROUP), - }, - ...(Permissions.canUsePolicyRooms(this.props.betas) && workspaces.length ? [ - { - icon: Expensicons.Hashtag, - text: this.props.translate('sidebarScreen.newRoom'), - onSelected: () => Navigation.navigate(ROUTES.WORKSPACE_NEW_ROOM), - }, - ] : []), - ...(Permissions.canUseIOUSend(this.props.betas) ? [ - { - icon: Expensicons.Send, - text: this.props.translate('iou.sendMoney'), - onSelected: () => Navigation.navigate(ROUTES.IOU_SEND), - }, - ] : []), - ...(Permissions.canUseIOU(this.props.betas) ? [ - { - icon: Expensicons.MoneyCircle, - text: this.props.translate('iou.requestMoney'), - onSelected: () => Navigation.navigate(ROUTES.IOU_REQUEST), - }, - ] : []), - ...(Permissions.canUseIOU(this.props.betas) ? [ - { - icon: Expensicons.Receipt, - text: this.props.translate('iou.splitBill'), - onSelected: () => Navigation.navigate(ROUTES.IOU_BILL), - }, - ] : []), - ...(!Policy.isAdminOfFreePolicy(this.props.allPolicies) ? [ - { - icon: Expensicons.NewWorkspace, - iconWidth: 46, - iconHeight: 40, - text: this.props.translate('workspace.new.newWorkspace'), - description: this.props.translate('workspace.new.getTheExpensifyCardAndMore'), - onSelected: () => Policy.createWorkspace(), - }, - ] : []), - ]} + + - + )} ); @@ -189,6 +75,8 @@ class BaseSidebarScreen extends Component { } BaseSidebarScreen.propTypes = propTypes; -BaseSidebarScreen.defaultProps = defaultProps; -export default withDrawerState(BaseSidebarScreen); +export default compose( + withWindowDimensions, + withDrawerState, +)(BaseSidebarScreen); diff --git a/src/pages/home/sidebar/SidebarScreen/PopoverModal.js b/src/pages/home/sidebar/SidebarScreen/PopoverModal.js new file mode 100644 index 000000000000..6b62f5875dd2 --- /dev/null +++ b/src/pages/home/sidebar/SidebarScreen/PopoverModal.js @@ -0,0 +1,170 @@ +import React from 'react'; +import _ from 'underscore'; +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; +import styles from '../../../../styles/styles'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import ROUTES from '../../../../ROUTES'; +import Permissions from '../../../../libs/Permissions'; +import * as Policy from '../../../../libs/actions/Policy'; +import PopoverMenu from '../../../../components/PopoverMenu'; +import CONST from '../../../../CONST'; +import FloatingActionButton from '../../../../components/FloatingActionButton'; +import compose from '../../../../libs/compose'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import withWindowDimensions from '../../../../components/withWindowDimensions'; +import ONYXKEYS from '../../../../ONYXKEYS'; + +const propTypes = { + /* Callback function when the menu is shown */ + onShowCreateMenu: PropTypes.func, + + /* Callback function before the menu is hidden */ + onHideCreateMenu: PropTypes.func, + + /** The list of policies the user has access to. */ + allPolicies: PropTypes.shape({ + /** The policy name */ + name: PropTypes.string, + }), + + /* Beta features list */ + betas: PropTypes.arrayOf(PropTypes.string), + + ...withLocalizePropTypes, +}; +const defaultProps = { + onHideCreateMenu: () => {}, + onShowCreateMenu: () => {}, + allPolicies: {}, + betas: [], +}; + +/** + * Responsible for rendering the {@link PopoverMenu}, and the accompanying + * FAB that can open or close the menu. + */ +class PopoverModal extends React.Component { + constructor(props) { + super(props); + + this.showCreateMenu = this.showCreateMenu.bind(this); + this.hideCreateMenu = this.hideCreateMenu.bind(this); + + this.state = { + isCreateMenuActive: false, + }; + } + + /** + * Method called when we click the floating action button + */ + showCreateMenu() { + this.setState({ + isCreateMenuActive: true, + }); + this.props.onShowCreateMenu(); + } + + /** + * Method called either when: + * - Pressing the floating action button to open the CreateMenu modal + * - Selecting an item on CreateMenu or closing it by clicking outside of the modal component + */ + hideCreateMenu() { + this.props.onHideCreateMenu(); + this.setState({ + isCreateMenuActive: false, + }); + } + + render() { + // Workspaces are policies with type === 'free' + const workspaces = _.filter(this.props.allPolicies, policy => policy && policy.type === CONST.POLICY.TYPE.FREE); + + return ( + <> + Navigation.navigate(ROUTES.NEW_CHAT), + }, + { + icon: Expensicons.Users, + text: this.props.translate('sidebarScreen.newGroup'), + onSelected: () => Navigation.navigate(ROUTES.NEW_GROUP), + }, + ...(Permissions.canUsePolicyRooms(this.props.betas) && workspaces.length ? [ + { + icon: Expensicons.Hashtag, + text: this.props.translate('sidebarScreen.newRoom'), + onSelected: () => Navigation.navigate(ROUTES.WORKSPACE_NEW_ROOM), + }, + ] : []), + ...(Permissions.canUseIOUSend(this.props.betas) ? [ + { + icon: Expensicons.Send, + text: this.props.translate('iou.sendMoney'), + onSelected: () => Navigation.navigate(ROUTES.IOU_SEND), + }, + ] : []), + ...(Permissions.canUseIOU(this.props.betas) ? [ + { + icon: Expensicons.MoneyCircle, + text: this.props.translate('iou.requestMoney'), + onSelected: () => Navigation.navigate(ROUTES.IOU_REQUEST), + }, + ] : []), + ...(Permissions.canUseIOU(this.props.betas) ? [ + { + icon: Expensicons.Receipt, + text: this.props.translate('iou.splitBill'), + onSelected: () => Navigation.navigate(ROUTES.IOU_BILL), + }, + ] : []), + ...(!Policy.isAdminOfFreePolicy(this.props.allPolicies) ? [ + { + icon: Expensicons.NewWorkspace, + iconWidth: 46, + iconHeight: 40, + text: this.props.translate('workspace.new.newWorkspace'), + description: this.props.translate('workspace.new.getTheExpensifyCardAndMore'), + onSelected: () => Policy.createWorkspace(), + }, + ] : []), + ]} + /> + + + ); + } +} + +PopoverModal.propTypes = propTypes; +PopoverModal.defaultProps = defaultProps; + +export default compose( + withLocalize, + withWindowDimensions, + withOnyx({ + allPolicies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + }), +)(PopoverModal); diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 584bcc49f8e4..75b5e07e7150 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -1,52 +1,41 @@ import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import compose from '../../../../libs/compose'; -import withWindowDimensions from '../../../../components/withWindowDimensions'; -import withLocalize from '../../../../components/withLocalize'; -import ONYXKEYS from '../../../../ONYXKEYS'; -import {sidebarPropTypes, sidebarDefaultProps} from './sidebarPropTypes'; +import sidebarPropTypes from './sidebarPropTypes'; import BaseSidebarScreen from './BaseSidebarScreen'; +import PopoverModal from './PopoverModal'; const SidebarScreen = (props) => { - let baseSidebarScreen = null; + let popoverModal = null; /** * Method create event listener */ const createDragoverListener = () => { - document.addEventListener('dragover', baseSidebarScreen.hideCreateMenu); + document.addEventListener('dragover', popoverModal.hideCreateMenu); }; /** * Method remove event listener. */ const removeDragoverListener = () => { - document.removeEventListener('dragover', baseSidebarScreen.hideCreateMenu); + document.removeEventListener('dragover', popoverModal.hideCreateMenu); }; + return ( - baseSidebarScreen = el} - onShowCreateMenu={createDragoverListener} - onHideCreateMenu={removeDragoverListener} - // eslint-disable-next-line react/jsx-props-no-spreading - {...props} - /> + <> + + popoverModal = el} + onShowCreateMenu={createDragoverListener} + onHideCreateMenu={removeDragoverListener} + /> + ); }; SidebarScreen.propTypes = sidebarPropTypes; -SidebarScreen.defaultProps = sidebarDefaultProps; SidebarScreen.displayName = 'SidebarScreen'; -export default compose( - withLocalize, - withWindowDimensions, - withOnyx({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - }), -)(SidebarScreen); +export default SidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/index.native.js b/src/pages/home/sidebar/SidebarScreen/index.native.js index e2cb2838efe8..cbf0d5c085a0 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.native.js +++ b/src/pages/home/sidebar/SidebarScreen/index.native.js @@ -1,28 +1,19 @@ import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import compose from '../../../../libs/compose'; -import withWindowDimensions from '../../../../components/withWindowDimensions'; -import withLocalize from '../../../../components/withLocalize'; -import ONYXKEYS from '../../../../ONYXKEYS'; -import {sidebarPropTypes, sidebarDefaultProps} from './sidebarPropTypes'; +import sidebarPropTypes from './sidebarPropTypes'; import BaseSidebarScreen from './BaseSidebarScreen'; +import PopoverModal from './PopoverModal'; -// eslint-disable-next-line react/jsx-props-no-spreading -const SidebarScreen = props => ; +const SidebarScreen = props => ( + <> + + + +); SidebarScreen.propTypes = sidebarPropTypes; -SidebarScreen.defaultProps = sidebarDefaultProps; SidebarScreen.displayName = 'SidebarScreen'; -export default compose( - withLocalize, - withWindowDimensions, - withOnyx({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - }), -)(SidebarScreen); +export default SidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js b/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js index 996bba9d676b..3affaa2d00be 100644 --- a/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js +++ b/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js @@ -1,26 +1,8 @@ import PropTypes from 'prop-types'; -import {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; -import {withLocalizePropTypes} from '../../../../components/withLocalize'; const sidebarPropTypes = { - /** The list of policies the user has access to. */ - allPolicies: PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, - }), - - /* Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), - - ...windowDimensionsPropTypes, - - ...withLocalizePropTypes, + /** reportID in the current navigation state */ + reportIDFromRoute: PropTypes.string, }; - -const sidebarDefaultProps = { - allPolicies: {}, - betas: [], -}; - -export {sidebarPropTypes, sidebarDefaultProps}; +export default sidebarPropTypes;