From 45dd97a8fd4f1c862cbd3dc977ff2585790280a1 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Mon, 14 Oct 2024 16:14:37 +0200 Subject: [PATCH] Migrate FloatingActionButton to useOnyx and add small cleanups --- src/libs/actions/Policy/Policy.ts | 3 +- src/pages/home/sidebar/BottomTabAvatar.tsx | 12 +- .../FloatingActionButtonAndPopover.tsx | 138 +++++------------- 3 files changed, 38 insertions(+), 115 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index ec37b2c4e1fa..5d86c30cc42a 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -73,7 +73,6 @@ import * as ReportActionsConnection from '@libs/ReportActionsConnection'; import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as PersistedRequests from '@userActions/PersistedRequests'; import CONST from '@src/CONST'; @@ -96,6 +95,8 @@ import type {OnyxData} from '@src/types/onyx/Request'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {buildOptimisticPolicyCategories} from './Category'; +type PolicySelector = Pick; + type ReportCreationData = Record< string, { diff --git a/src/pages/home/sidebar/BottomTabAvatar.tsx b/src/pages/home/sidebar/BottomTabAvatar.tsx index 137cd0ffe876..eeaad1fd32f3 100644 --- a/src/pages/home/sidebar/BottomTabAvatar.tsx +++ b/src/pages/home/sidebar/BottomTabAvatar.tsx @@ -21,14 +21,11 @@ import AvatarWithOptionalStatus from './AvatarWithOptionalStatus'; import ProfileAvatarWithIndicator from './ProfileAvatarWithIndicator'; type BottomTabAvatarProps = { - /** Whether the create menu is open or not */ - isCreateMenuOpen?: boolean; - /** Whether the avatar is selected */ isSelected?: boolean; }; -function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomTabAvatarProps) { +function BottomTabAvatar({isSelected = false}: BottomTabAvatarProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); @@ -39,11 +36,6 @@ function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomT const {shouldUseNarrowLayout} = useResponsiveLayout(); const showSettingsPage = useCallback(() => { - if (isCreateMenuOpen) { - // Prevent opening Settings page when click profile avatar quickly after clicking FAB icon - return; - } - if (route.name === SCREENS.SETTINGS.WORKSPACES && shouldUseNarrowLayout) { Navigation.goUp(ROUTES.SETTINGS); return; @@ -84,7 +76,7 @@ function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomT // This case also covers if there is no route to remember. Navigation.navigate(ROUTES.SETTINGS); }); - }, [isCreateMenuOpen, shouldUseNarrowLayout, route.name]); + }, [shouldUseNarrowLayout, route.name]); let children; diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index a49b474b185e..c51df3c46329 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -1,10 +1,9 @@ import {useIsFocused as useIsFocusedOriginal, useNavigationState} from '@react-navigation/native'; import type {ImageContentFit} from 'expo-image'; -import type {ForwardedRef, RefAttributes} from 'react'; +import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -36,7 +35,6 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; import type {QuickActionName} from '@src/types/onyx/QuickAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -49,35 +47,7 @@ const useIsFocused = () => { return isFocused || (topmostCentralPane?.name === SCREENS.SEARCH.CENTRAL_PANE && shouldUseNarrowLayout); }; -type PolicySelector = Pick; - -type FloatingActionButtonAndPopoverOnyxProps = { - /** The list of policies the user has access to. */ - allPolicies: OnyxCollection; - - /** Whether app is in loading state */ - isLoading: OnyxEntry; - - /** Information on the last taken action to display as Quick Action */ - quickAction: OnyxEntry; - - /** The report data of the quick action */ - quickActionReport: OnyxEntry; - - /** The policy data of the quick action */ - quickActionPolicy: OnyxEntry; - - /** The current session */ - session: OnyxEntry; - - /** Personal details of all the users */ - personalDetails: OnyxEntry; - - /** Has user seen track expense training interstitial */ - hasSeenTrackTraining: OnyxEntry; -}; - -type FloatingActionButtonAndPopoverProps = FloatingActionButtonAndPopoverOnyxProps & { +type FloatingActionButtonAndPopoverProps = { /* Callback function when the menu is shown */ onShowCreateMenu?: () => void; @@ -89,18 +59,6 @@ type FloatingActionButtonAndPopoverRef = { hideCreateMenu: () => void; }; -const policySelector = (policy: OnyxEntry): PolicySelector => - (policy && { - type: policy.type, - role: policy.role, - id: policy.id, - isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, - pendingAction: policy.pendingAction, - avatarURL: policy.avatarURL, - name: policy.name, - areInvoicesEnabled: policy.areInvoicesEnabled, - }) as PolicySelector; - const getQuickActionIcon = (action: QuickActionName): React.FC => { switch (action) { case CONST.QUICK_ACTIONS.REQUEST_MANUAL: @@ -161,24 +119,9 @@ const getQuickActionTitle = (action: QuickActionName): TranslationPaths => { * Responsible for rendering the {@link PopoverMenu}, and the accompanying * FAB that can open or close the menu. */ -function FloatingActionButtonAndPopover( - { - onHideCreateMenu, - onShowCreateMenu, - isLoading = false, - allPolicies, - quickAction, - quickActionReport, - quickActionPolicy, - session, - personalDetails, - hasSeenTrackTraining, - }: FloatingActionButtonAndPopoverProps, - ref: ForwardedRef, -) { +function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: FloatingActionButtonAndPopoverProps, ref: ForwardedRef) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID ?? -1}`); const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); const fabRef = useRef(null); const {windowHeight} = useWindowDimensions(); @@ -186,19 +129,33 @@ function FloatingActionButtonAndPopover( const isFocused = useIsFocused(); const prevIsFocused = usePrevious(isFocused); const {isOffline} = useNetwork(); - const {canUseSpotnanaTravel, canUseCombinedTrackSubmit} = usePermissions(); - const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); + + const [isLoading] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [policies = {}] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [hasSeenTrackTraining] = useOnyx(ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING); + const [quickAction = {}] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); + const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`); + const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID ?? -1}`); + const [quickActionPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`); + + const sessionEmail = session?.email; + const sessionAccountID = session?.accountID; + const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(policies, sessionEmail), [policies, sessionEmail]); + + const quickActionName = quickAction.action; const quickActionAvatars = useMemo(() => { if (quickActionReport) { const avatars = ReportUtils.getIcons(quickActionReport, personalDetails); - return avatars.length <= 1 || ReportUtils.isPolicyExpenseChat(quickActionReport) ? avatars : avatars.filter((avatar) => avatar.id !== session?.accountID); + return avatars.length <= 1 || ReportUtils.isPolicyExpenseChat(quickActionReport) ? avatars : avatars.filter((avatar) => avatar.id !== sessionAccountID); } return []; // Policy is needed as a dependency in order to update the shortcut details when the workspace changes // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [personalDetails, session?.accountID, quickActionReport, quickActionPolicy]); + }, [personalDetails, sessionAccountID, quickActionReport, quickActionPolicy]); const renderQuickActionTooltip = useCallback( () => ( @@ -214,13 +171,14 @@ function FloatingActionButtonAndPopover( if (isEmptyObject(quickActionReport)) { return ''; } - if (quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && quickActionAvatars.length > 0) { - const name: string = ReportUtils.getDisplayNameForParticipant(+(quickActionAvatars.at(0)?.id ?? -1), true) ?? ''; + + if (quickActionName === CONST.QUICK_ACTIONS.SEND_MONEY && quickActionAvatars.length > 0) { + const name = ReportUtils.getDisplayNameForParticipant(+(quickActionAvatars.at(0)?.id ?? -1), true) ?? ''; return translate('quickAction.paySomeone', {name}); } - const titleKey = getQuickActionTitle(quickAction?.action ?? ('' as QuickActionName)); + const titleKey = getQuickActionTitle(quickActionName ?? ('' as QuickActionName)); return titleKey ? translate(titleKey) : ''; - }, [quickAction, translate, quickActionAvatars, quickActionReport]); + }, [quickActionName, translate, quickActionAvatars, quickActionReport]); const hideQABSubtitle = useMemo(() => { if (isEmptyObject(quickActionReport)) { @@ -230,8 +188,8 @@ function FloatingActionButtonAndPopover( return false; } const displayName = personalDetails?.[quickActionAvatars.at(0)?.id ?? -1]?.firstName ?? ''; - return quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && displayName.length === 0; - }, [personalDetails, quickActionReport, quickAction?.action, quickActionAvatars]); + return quickActionName === CONST.QUICK_ACTIONS.SEND_MONEY && displayName.length === 0; + }, [personalDetails, quickActionReport, quickActionName, quickActionAvatars]); const navigateToQuickAction = () => { const selectOption = (onSelected: () => void, shouldRestrictAction: boolean) => { @@ -246,7 +204,7 @@ function FloatingActionButtonAndPopover( const isValidReport = !(isEmptyObject(quickActionReport) || ReportUtils.isArchivedRoom(quickActionReport, reportNameValuePairs)); const quickActionReportID = isValidReport ? quickActionReport?.reportID ?? '-1' : ReportUtils.generateReportID(); - switch (quickAction?.action) { + switch (quickActionName) { case CONST.QUICK_ACTIONS.REQUEST_MANUAL: selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SUBMIT, quickActionReportID, CONST.IOU.REQUEST_TYPE.MANUAL, true), true); return; @@ -454,7 +412,7 @@ function FloatingActionButtonAndPopover( }, ] : []), - ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) + ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(policies) ? [ { displayInDefaultIconColor: true, @@ -468,10 +426,10 @@ function FloatingActionButtonAndPopover( }, ] : []), - ...(quickAction?.action + ...(quickActionName ? [ { - icon: getQuickActionIcon(quickAction?.action), + icon: getQuickActionIcon(quickActionName), text: quickActionTitle, label: translate('quickAction.header'), isLabelHoverable: false, @@ -510,32 +468,4 @@ function FloatingActionButtonAndPopover( FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; -export default withOnyx, FloatingActionButtonAndPopoverOnyxProps>({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, - }, - isLoading: { - key: ONYXKEYS.IS_LOADING_APP, - }, - quickAction: { - key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, - }, - quickActionReport: { - key: ({quickAction}) => `${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`, - }, - quickActionPolicy: { - key: ({quickActionReport}) => `${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - hasSeenTrackTraining: { - key: ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING, - }, -})(forwardRef(FloatingActionButtonAndPopover)); - -export type {PolicySelector}; +export default forwardRef(FloatingActionButtonAndPopover);