diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 644adfdd66fc..eec6cdf81a7e 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -5,7 +5,7 @@ import {InteractionManager, View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, Text as RNText, View as ViewType} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; @@ -23,25 +23,14 @@ import shouldEnableContextMenuEnterShortcut from '@libs/shouldEnableContextMenuE import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Beta, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; +import type {ReportAction} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {ContextMenuAction, ContextMenuActionPayload} from './ContextMenuActions'; import ContextMenuActions from './ContextMenuActions'; import type {ContextMenuAnchor, ContextMenuType} from './ReportActionContextMenu'; import {hideContextMenu, showContextMenu} from './ReportActionContextMenu'; -type BaseReportActionContextMenuOnyxProps = { - /** Beta features list */ - betas: OnyxEntry; - - /** All of the actions of the report */ - reportActions: OnyxEntry; - - /** The transaction linked to the report action this context menu is attached to. */ - transaction: OnyxEntry; -}; - -type BaseReportActionContextMenuProps = BaseReportActionContextMenuOnyxProps & { +type BaseReportActionContextMenuProps = { /** The ID of the report this report action is attached to. */ reportID: string; @@ -114,10 +103,7 @@ function BaseReportActionContextMenu({ selection = '', draftMessage = '', reportActionID, - transaction, reportID, - betas, - reportActions, checkIfContextMenuActive, disabledActions = [], setIsEmojiPickerActive, @@ -131,6 +117,13 @@ function BaseReportActionContextMenu({ const {isOffline} = useNetwork(); const {isProduction} = useEnvironment(); const threedotRef = useRef(null); + const [betas] = useOnyx(ONYXKEYS.BETAS); + const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + canEvict: false, + }); + const transactionID = ReportActionsUtils.getLinkedTransactionID(reportActionID, reportID); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`); + const [user] = useOnyx(ONYXKEYS.USER); const reportAction: OnyxEntry = useMemo(() => { if (isEmptyObject(reportActions) || reportActionID === '0' || reportActionID === '-1') { @@ -185,22 +178,23 @@ function BaseReportActionContextMenu({ let filteredContextMenuActions = ContextMenuActions.filter( (contextAction) => !disabledActions.includes(contextAction) && - contextAction.shouldShow( + contextAction.shouldShow({ type, reportAction, isArchivedRoom, betas, - anchor, + menuTarget: anchor, isChronosReport, reportID, isPinnedChat, isUnreadChat, - !!isOffline, + isOffline: !!isOffline, isMini, isProduction, moneyRequestAction, areHoldRequirementsMet, - ), + user, + }), ); if (isMini) { @@ -358,35 +352,6 @@ function BaseReportActionContextMenu({ ); } -export default withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, - reportActions: { - key: ({originalReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, - canEvict: false, - }, - transaction: { - key: ({reportActions, reportActionID}) => { - const reportAction = reportActions?.[reportActionID]; - return `${ONYXKEYS.COLLECTION.TRANSACTION}${(reportAction && ReportActionsUtils.getLinkedTransactionID(reportAction)) ?? -1}`; - }, - }, -})( - memo(BaseReportActionContextMenu, (prevProps, nextProps) => { - const {reportActions: prevReportActions, ...prevPropsWithoutReportActions} = prevProps; - const {reportActions: nextReportActions, ...nextPropsWithoutReportActions} = nextProps; - - const prevReportAction = prevReportActions?.[prevProps.reportActionID] ?? ''; - const nextReportAction = nextReportActions?.[nextProps.reportActionID] ?? ''; - - // We only want to re-render when the report action that is attached to is changed - if (prevReportAction !== nextReportAction) { - return false; - } - - return lodashIsEqual(prevPropsWithoutReportActions, nextPropsWithoutReportActions); - }), -); +export default memo(BaseReportActionContextMenu, lodashIsEqual); export type {BaseReportActionContextMenuProps}; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 80f0b1de8a46..c31a32b81427 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -31,7 +31,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Beta, Download as DownloadOnyx, OnyxInputOrEntry, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; +import type {Beta, Download as DownloadOnyx, OnyxInputOrEntry, ReportAction, ReportActionReactions, Transaction, User} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; @@ -54,22 +54,23 @@ function setClipboardMessage(content: string) { } } -type ShouldShow = ( - type: string, - reportAction: OnyxEntry, - isArchivedRoom: boolean, - betas: OnyxEntry, - menuTarget: MutableRefObject | undefined, - isChronosReport: boolean, - reportID: string, - isPinnedChat: boolean, - isUnreadChat: boolean, - isOffline: boolean, - isMini: boolean, - isProduction: boolean, - moneyRequestAction: ReportAction | undefined, - areHoldRequirementsMet: boolean, -) => boolean; +type ShouldShow = (args: { + type: string; + reportAction: OnyxEntry; + isArchivedRoom: boolean; + betas: OnyxEntry; + menuTarget: MutableRefObject | undefined; + isChronosReport: boolean; + reportID: string; + isPinnedChat: boolean; + isUnreadChat: boolean; + isOffline: boolean; + isMini: boolean; + isProduction: boolean; + moneyRequestAction: ReportAction | undefined; + areHoldRequirementsMet: boolean; + user: OnyxEntry; +}) => boolean; type ContextMenuActionPayload = { reportAction: ReportAction; @@ -119,7 +120,7 @@ type ContextMenuAction = (ContextMenuActionWithContent | ContextMenuActionWithIc const ContextMenuActions: ContextMenuAction[] = [ { isAnonymousAction: false, - shouldShow: (type, reportAction): reportAction is ReportAction => + shouldShow: ({type, reportAction}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !!reportAction && 'message' in reportAction && !ReportActionsUtils.isMessageDeleted(reportAction), renderContent: (closePopover, {reportID, reportAction, close: closeManually, openContextMenu, setIsEmojiPickerActive}) => { const isMini = !closePopover; @@ -176,7 +177,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.replyInThread', icon: Expensicons.ChatBubbleReply, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID) => { + shouldShow: ({type, reportAction, reportID}) => { if (type !== CONST.CONTEXT_MENU_TYPES.REPORT_ACTION) { return false; } @@ -204,8 +205,7 @@ const ContextMenuActions: ContextMenuAction[] = [ textTranslateKey: 'reportActionContextMenu.markAsUnread', icon: Expensicons.ChatBubbleUnread, successIcon: Expensicons.Checkmark, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID, isPinnedChat, isUnreadChat) => - type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONST.CONTEXT_MENU_TYPES.REPORT && !isUnreadChat), + shouldShow: ({type, isUnreadChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONST.CONTEXT_MENU_TYPES.REPORT && !isUnreadChat), onPress: (closePopover, {reportAction, reportID}) => { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction) ?? '-1'; Report.markCommentAsUnread(originalReportID, reportAction?.created); @@ -220,8 +220,7 @@ const ContextMenuActions: ContextMenuAction[] = [ textTranslateKey: 'reportActionContextMenu.markAsRead', icon: Expensicons.Mail, successIcon: Expensicons.Checkmark, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID, isPinnedChat, isUnreadChat) => - type === CONST.CONTEXT_MENU_TYPES.REPORT && isUnreadChat, + shouldShow: ({type, isUnreadChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT && isUnreadChat, onPress: (closePopover, {reportID}) => { Report.readNewestAction(reportID, true); if (closePopover) { @@ -234,7 +233,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.editAction', icon: Expensicons.Pencil, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport) => + shouldShow: ({type, reportAction, isArchivedRoom, isChronosReport}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && !isArchivedRoom && !isChronosReport, onPress: (closePopover, {reportID, reportAction, draftMessage}) => { if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { @@ -267,22 +266,8 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'iou.unhold', icon: Expensicons.Stopwatch, - shouldShow: ( - type, - reportAction, - isArchivedRoom, - betas, - anchor, - isChronosReport, - reportID, - isPinnedChat, - isUnreadChat, - isOffline, - isMini, - isProduction, - moneyRequestAction, - areHoldRequirementsMet, - ) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canUnholdRequest, + shouldShow: ({type, moneyRequestAction, areHoldRequirementsMet}) => + type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canUnholdRequest, onPress: (closePopover, {moneyRequestAction}) => { if (closePopover) { hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction)); @@ -298,22 +283,8 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'iou.hold', icon: Expensicons.Stopwatch, - shouldShow: ( - type, - reportAction, - isArchivedRoom, - betas, - anchor, - isChronosReport, - reportID, - isPinnedChat, - isUnreadChat, - isOffline, - isMini, - isProduction, - moneyRequestAction, - areHoldRequirementsMet, - ) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canHoldRequest, + shouldShow: ({type, moneyRequestAction, areHoldRequirementsMet}) => + type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canHoldRequest, onPress: (closePopover, {moneyRequestAction}) => { if (closePopover) { hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction)); @@ -329,7 +300,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.joinThread', icon: Expensicons.Bell, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID) => { + shouldShow: ({reportAction, isArchivedRoom, reportID}) => { const childReportNotificationPreference = ReportUtils.getChildReportNotificationPreference(reportAction); const isDeletedAction = ReportActionsUtils.isDeletedAction(reportAction); const shouldDisplayThreadReplies = ReportUtils.shouldDisplayThreadReplies(reportAction, reportID); @@ -361,7 +332,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.Copy, successTextTranslateKey: 'reportActionContextMenu.copied', successIcon: Expensicons.Checkmark, - shouldShow: (type) => type === CONST.CONTEXT_MENU_TYPES.LINK, + shouldShow: ({type}) => type === CONST.CONTEXT_MENU_TYPES.LINK, onPress: (closePopover, {selection}) => { Clipboard.setString(selection); hideContextMenu(true, ReportActionComposeFocusManager.focus); @@ -374,7 +345,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.Copy, successTextTranslateKey: 'reportActionContextMenu.copied', successIcon: Expensicons.Checkmark, - shouldShow: (type) => type === CONST.CONTEXT_MENU_TYPES.EMAIL, + shouldShow: ({type}) => type === CONST.CONTEXT_MENU_TYPES.EMAIL, onPress: (closePopover, {selection}) => { Clipboard.setString(EmailUtils.trimMailTo(selection)); hideContextMenu(true, ReportActionComposeFocusManager.focus); @@ -387,7 +358,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.Copy, successTextTranslateKey: 'reportActionContextMenu.copied', successIcon: Expensicons.Checkmark, - shouldShow: (type, reportAction) => + shouldShow: ({type, reportAction}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !ReportActionsUtils.isReportActionAttachment(reportAction) && !ReportActionsUtils.isMessageDeleted(reportAction) && @@ -526,7 +497,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.LinkCopy, successIcon: Expensicons.Checkmark, successTextTranslateKey: 'reportActionContextMenu.copied', - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget) => { + shouldShow: ({type, reportAction, menuTarget}) => { const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction); // Only hide the copylink menu item when context menu is opened over img element. @@ -547,7 +518,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'common.pin', icon: Expensicons.Pin, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID, isPinnedChat) => type === CONST.CONTEXT_MENU_TYPES.REPORT && !isPinnedChat, + shouldShow: ({type, isPinnedChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT && !isPinnedChat, onPress: (closePopover, {reportID}) => { Report.togglePinnedState(reportID, false); if (closePopover) { @@ -560,7 +531,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'common.unPin', icon: Expensicons.Pin, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID, isPinnedChat) => type === CONST.CONTEXT_MENU_TYPES.REPORT && isPinnedChat, + shouldShow: ({type, isPinnedChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT && isPinnedChat, onPress: (closePopover, {reportID}) => { Report.togglePinnedState(reportID, true); if (closePopover) { @@ -573,7 +544,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.flagAsOffensive', icon: Expensicons.Flag, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID) => + shouldShow: ({type, reportAction, isArchivedRoom, isChronosReport, reportID}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canFlagReportAction(reportAction, reportID) && !isArchivedRoom && @@ -596,7 +567,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.Download, successTextTranslateKey: 'common.download', successIcon: Expensicons.Download, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID, isPinnedChat, isUnreadChat, isOffline): reportAction is ReportAction => { + shouldShow: ({reportAction, isOffline}) => { const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction); const html = getActionHtml(reportAction); const isUploading = html.includes(CONST.ATTACHMENT_OPTIMISTIC_SOURCE_ATTRIBUTE); @@ -624,7 +595,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.Copy, successTextTranslateKey: 'reportActionContextMenu.copied', successIcon: Expensicons.Checkmark, - shouldShow: (type, isProduction) => type === CONST.CONTEXT_MENU_TYPES.REPORT && !isProduction, + shouldShow: ({type, isProduction}) => type === CONST.CONTEXT_MENU_TYPES.REPORT && !isProduction, onPress: (closePopover, {reportID}) => { const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; Clipboard.setString(JSON.stringify(report, null, 4)); @@ -632,11 +603,22 @@ const ContextMenuActions: ContextMenuAction[] = [ }, getDescription: () => {}, }, + { + isAnonymousAction: true, + textTranslateKey: 'debug.debug', + icon: Expensicons.Bug, + shouldShow: ({type, user}) => type === CONST.CONTEXT_MENU_TYPES.REPORT && !!user?.isDebugModeEnabled, + onPress: (closePopover, {reportID}) => { + Navigation.navigate(ROUTES.DEBUG_REPORT.getRoute(reportID)); + hideContextMenu(false, ReportActionComposeFocusManager.focus); + }, + getDescription: () => {}, + }, { isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.deleteAction', icon: Expensicons.Trashcan, - shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport, reportID) => + shouldShow: ({type, reportAction, isArchivedRoom, isChronosReport, reportID}) => // Until deleting parent threads is supported in FE, we will prevent the user from deleting a thread parent type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canDeleteReportAction(reportAction, reportID) && @@ -659,7 +641,7 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: true, textTranslateKey: 'reportActionContextMenu.menu', icon: Expensicons.ThreeDots, - shouldShow: (type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat, isUnreadChat, isOffline, isMini) => isMini, + shouldShow: ({isMini}) => isMini, onPress: (closePopover, {openOverflowMenu, event, openContextMenu, anchorRef}) => { openOverflowMenu(event as GestureResponderEvent | MouseEvent, anchorRef ?? {current: null}); openContextMenu();