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 (