diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index cc6767318274..3e95818c1137 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState, useEffect, useCallback, useImperativeHandle, forwardRef} from 'react'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; @@ -25,6 +25,7 @@ import withNavigationFocus from '../../../../components/withNavigationFocus'; import * as Task from '../../../../libs/actions/Task'; import * as Session from '../../../../libs/actions/Session'; import * as IOU from '../../../../libs/actions/IOU'; +import usePrevious from '../../../../hooks/usePrevious'; /** * @param {Object} [policy] @@ -56,6 +57,9 @@ const propTypes = { /** Indicated whether the report data is loading */ isLoading: PropTypes.bool, + + /** Forwarded ref to FloatingActionButtonAndPopover */ + innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), }; const defaultProps = { onHideCreateMenu: () => {}, @@ -63,44 +67,20 @@ const defaultProps = { allPolicies: {}, betas: [], isLoading: false, + innerRef: null, }; /** * Responsible for rendering the {@link PopoverMenu}, and the accompanying * FAB that can open or close the menu. + * @param {Object} props + * @returns {JSX.Element} */ -class FloatingActionButtonAndPopover extends React.Component { - constructor(props) { - super(props); - - this.showCreateMenu = this.showCreateMenu.bind(this); - this.hideCreateMenu = this.hideCreateMenu.bind(this); - this.interceptAnonymousUser = this.interceptAnonymousUser.bind(this); - - this.state = { - isCreateMenuActive: false, - isAnonymousUser: Session.isAnonymousUser(), - }; - } - - componentDidMount() { - const navigationState = this.props.navigation.getState(); - const routes = lodashGet(navigationState, 'routes', []); - const currentRoute = routes[navigationState.index]; - if (currentRoute && ![NAVIGATORS.CENTRAL_PANE_NAVIGATOR, SCREENS.HOME].includes(currentRoute.name)) { - return; - } - Welcome.show({routes, showCreateMenu: this.showCreateMenu}); - } +function FloatingActionButtonAndPopover(props) { + const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); + const isAnonymousUser = Session.isAnonymousUser(); - componentDidUpdate(prevProps) { - if (!this.didScreenBecomeInactive(prevProps)) { - return; - } - - // Hide menu manually when other pages are opened using shortcut key - this.hideCreateMenu(); - } + const prevIsFocused = usePrevious(props.isFocused); /** * Check if LHN status changed from active to inactive. @@ -109,42 +89,44 @@ class FloatingActionButtonAndPopover extends React.Component { * @param {Object} prevProps * @return {Boolean} */ - didScreenBecomeInactive(prevProps) { - // When any other page is opened over LHN - if (!this.props.isFocused && prevProps.isFocused) { - return true; - } - - return false; - } + const didScreenBecomeInactive = useCallback( + () => + // When any other page is opened over LHN + !props.isFocused && prevIsFocused, + [props.isFocused, prevIsFocused], + ); /** * Method called when we click the floating action button */ - showCreateMenu() { - if (!this.props.isFocused && this.props.isSmallScreenWidth) { - return; - } - this.setState({ - isCreateMenuActive: true, - }); - this.props.onShowCreateMenu(); - } + const showCreateMenu = useCallback( + () => { + if (!props.isFocused && props.isSmallScreenWidth) { + return; + } + setIsCreateMenuActive(true); + props.onShowCreateMenu(); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [props.isFocused, props.isSmallScreenWidth], + ); /** * 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() { - if (!this.state.isCreateMenuActive) { - return; - } - this.props.onHideCreateMenu(); - this.setState({ - isCreateMenuActive: false, - }); - } + const hideCreateMenu = useCallback( + () => { + if (!isCreateMenuActive) { + return; + } + props.onHideCreateMenu(); + setIsCreateMenuActive(false); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [isCreateMenuActive], + ); /** * Checks if user is anonymous. If true, shows the sign in modal, else, @@ -152,108 +134,135 @@ class FloatingActionButtonAndPopover extends React.Component { * * @param {Function} callback */ - interceptAnonymousUser(callback) { - if (this.state.isAnonymousUser) { + const interceptAnonymousUser = (callback) => { + if (isAnonymousUser) { Session.signOutAndRedirectToSignIn(); } else { callback(); } - } + }; + + useEffect( + () => { + const navigationState = props.navigation.getState(); + const routes = lodashGet(navigationState, 'routes', []); + const currentRoute = routes[navigationState.index]; + if (currentRoute && ![NAVIGATORS.CENTRAL_PANE_NAVIGATOR, SCREENS.HOME].includes(currentRoute.name)) { + return; + } + Welcome.show({routes, showCreateMenu}); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + useEffect(() => { + if (!didScreenBecomeInactive()) { + return; + } - render() { - // Workspaces are policies with type === 'free' - const workspaces = _.filter(this.props.allPolicies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE); + // Hide menu manually when other pages are opened using shortcut key + hideCreateMenu(); + }, [didScreenBecomeInactive, hideCreateMenu]); - return ( - - this.interceptAnonymousUser(() => Navigation.navigate(ROUTES.NEW_CHAT)), - }, - { - icon: Expensicons.Users, - text: this.props.translate('sidebarScreen.newGroup'), - onSelected: () => this.interceptAnonymousUser(() => Navigation.navigate(ROUTES.NEW_GROUP)), - }, - ...(Permissions.canUsePolicyRooms(this.props.betas) && workspaces.length - ? [ - { - icon: Expensicons.Hashtag, - text: this.props.translate('sidebarScreen.newRoom'), - onSelected: () => this.interceptAnonymousUser(() => Navigation.navigate(ROUTES.WORKSPACE_NEW_ROOM)), - }, - ] - : []), - ...(Permissions.canUseIOUSend(this.props.betas) - ? [ - { - icon: Expensicons.Send, - text: this.props.translate('iou.sendMoney'), - onSelected: () => this.interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.SEND)), - }, - ] - : []), - { - icon: Expensicons.MoneyCircle, - text: this.props.translate('iou.requestMoney'), - onSelected: () => this.interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)), - }, - { - icon: Expensicons.Receipt, - text: this.props.translate('iou.splitBill'), - onSelected: () => this.interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)), - }, - ...(Permissions.canUseTasks(this.props.betas) - ? [ - { - icon: Expensicons.Task, - text: this.props.translate('newTaskPage.assignTask'), - onSelected: () => this.interceptAnonymousUser(() => Task.clearOutTaskInfoAndNavigate()), - }, - ] - : []), - ...(!this.props.isLoading && !Policy.hasActiveFreePolicy(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: () => this.interceptAnonymousUser(() => Policy.createWorkspace()), - }, - ] - : []), - ]} - /> - - - ); - } + useImperativeHandle(props.innerRef, () => ({ + hideCreateMenu() { + hideCreateMenu(); + }, + })); + + // Workspaces are policies with type === 'free' + const workspaces = _.filter(props.allPolicies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE); + + return ( + + interceptAnonymousUser(() => Navigation.navigate(ROUTES.NEW_CHAT)), + }, + { + icon: Expensicons.Users, + text: props.translate('sidebarScreen.newGroup'), + onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.NEW_GROUP)), + }, + ...(Permissions.canUsePolicyRooms(props.betas) && workspaces.length + ? [ + { + icon: Expensicons.Hashtag, + text: props.translate('sidebarScreen.newRoom'), + onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.WORKSPACE_NEW_ROOM)), + }, + ] + : []), + ...(Permissions.canUseIOUSend(props.betas) + ? [ + { + icon: Expensicons.Send, + text: props.translate('iou.sendMoney'), + onSelected: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.SEND)), + }, + ] + : []), + { + icon: Expensicons.MoneyCircle, + text: props.translate('iou.requestMoney'), + onSelected: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)), + }, + { + icon: Expensicons.Receipt, + text: props.translate('iou.splitBill'), + onSelected: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)), + }, + ...(Permissions.canUseTasks(props.betas) + ? [ + { + icon: Expensicons.Task, + text: props.translate('newTaskPage.assignTask'), + onSelected: () => interceptAnonymousUser(() => Task.clearOutTaskInfoAndNavigate()), + }, + ] + : []), + ...(!props.isLoading && !Policy.hasActiveFreePolicy(props.allPolicies) + ? [ + { + icon: Expensicons.NewWorkspace, + iconWidth: 46, + iconHeight: 40, + text: props.translate('workspace.new.newWorkspace'), + description: props.translate('workspace.new.getTheExpensifyCardAndMore'), + onSelected: () => interceptAnonymousUser(() => Policy.createWorkspace()), + }, + ] + : []), + ]} + /> + + + ); } FloatingActionButtonAndPopover.propTypes = propTypes; FloatingActionButtonAndPopover.defaultProps = defaultProps; +FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; export default compose( withLocalize, withNavigation, withNavigationFocus, withWindowDimensions, - withWindowDimensions, withOnyx({ allPolicies: { key: ONYXKEYS.COLLECTION.POLICY, @@ -266,4 +275,12 @@ export default compose( key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, }), -)(FloatingActionButtonAndPopover); +)( + forwardRef((props, ref) => ( + + )), +); diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 27716995fc10..705d9d1e2d08 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -27,14 +27,14 @@ function SidebarScreen(props) { * Method create event listener */ const createDragoverListener = () => { - document.addEventListener('dragover', popoverModal.current.hideCreateMenu); + document.addEventListener('dragover', () => popoverModal.current.hideCreateMenu()); }; /** * Method remove event listener. */ const removeDragoverListener = () => { - document.removeEventListener('dragover', popoverModal.current.hideCreateMenu); + document.removeEventListener('dragover', () => popoverModal.current.hideCreateMenu()); }; return (