diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 488dfc574ab2..d36c8c43d322 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -8,6 +8,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as HeaderUtils from '@libs/HeaderUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; @@ -15,6 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Button from './Button'; import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; @@ -32,6 +34,9 @@ type MoneyReportHeaderOnyxProps = { /** Session info for the currently logged in user. */ session: OnyxEntry; + + /** The transaction thread report associated with the current report, if any */ + transactionThreadReport: OnyxEntry; }; type MoneyReportHeaderProps = MoneyReportHeaderOnyxProps & { @@ -40,14 +45,35 @@ type MoneyReportHeaderProps = MoneyReportHeaderOnyxProps & { /** The policy tied to the money request report */ policy: OnyxEntry; + + /** Array of report actions for the report */ + reportActions: OnyxTypes.ReportAction[]; + + /** The reportID of the transaction thread report associated with this current report, if any */ + // eslint-disable-next-line react/no-unused-prop-types + transactionThreadReportID?: string | null; }; -function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport}: MoneyReportHeaderProps) { +function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport, transactionThreadReport, reportActions}: MoneyReportHeaderProps) { const styles = useThemeStyles(); + const [isDeleteRequestModalVisible, setIsDeleteRequestModalVisible] = useState(false); const {translate} = useLocalize(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport); const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); + const requestParentReportAction = useMemo(() => { + if (!reportActions || !transactionThreadReport?.parentReportActionID) { + return null; + } + return reportActions.find((action) => action.reportActionID === transactionThreadReport.parentReportActionID); + }, [reportActions, transactionThreadReport?.parentReportActionID]); + const isDeletedParentAction = ReportActionsUtils.isDeletedAction(requestParentReportAction as OnyxTypes.ReportAction); + + // Only the requestor can delete the request, admins can only edit it. + const isActionOwner = + typeof requestParentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && requestParentReportAction.actorAccountID === session?.accountID; + const canDeleteRequest = + isActionOwner && (ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) || ReportUtils.isTrackExpenseReport(transactionThreadReport)) && !isDeletedParentAction; const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [paymentType, setPaymentType] = useState(); const [requestType, setRequestType] = useState<'pay' | 'approve'>(); @@ -106,6 +132,19 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money } }; + const deleteTransaction = useCallback(() => { + if (requestParentReportAction) { + const iouTransactionID = requestParentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? requestParentReportAction.originalMessage?.IOUTransactionID ?? '' : ''; + if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { + IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, true); + return; + } + IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); + } + + setIsDeleteRequestModalVisible(false); + }, [moneyRequestReport?.reportID, requestParentReportAction, setIsDeleteRequestModalVisible]); + // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( () => chatReport?.isOwnPolicyExpenseChat && !policy?.harvesting?.enabled, @@ -121,6 +160,23 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money }); } + // If the report supports adding transactions to it, then it also supports deleting transactions from it. + if (canDeleteRequest && !isEmptyObject(transactionThreadReport)) { + threeDotsMenuItems.push({ + icon: Expensicons.Trashcan, + text: translate('reportActionContextMenu.deleteAction', {action: requestParentReportAction}), + onSelected: () => setIsDeleteRequestModalVisible(true), + }); + } + + useEffect(() => { + if (canDeleteRequest) { + return; + } + + setIsDeleteRequestModalVisible(false); + }, [canDeleteRequest]); + return ( + setIsDeleteRequestModalVisible(false)} + prompt={translate('iou.deleteConfirmation')} + confirmText={translate('common.delete')} + cancelText={translate('common.cancel')} + danger + /> ); } @@ -244,6 +310,9 @@ export default withOnyx({ nextStep: { key: ({report}) => `${ONYXKEYS.COLLECTION.NEXT_STEP}${report.reportID}`, }, + transactionThreadReport: { + key: ({transactionThreadReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, + }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 5d3231ca0a41..f451f5f15581 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -222,7 +222,7 @@ const MoneyRequestHeaderWithTransaction = withOnyx { const parentReportAction = (report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : {}) as ReportAction & OriginalMessageIOU; - return `${ONYXKEYS.COLLECTION.TRANSACTION}${parentReportAction.originalMessage.IOUTransactionID ?? 0}`; + return `${ONYXKEYS.COLLECTION.TRANSACTION}${parentReportAction?.originalMessage?.IOUTransactionID ?? 0}`; }, }, shownHoldUseExplanation: { diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 152b02366227..563aeb3c4e76 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -331,11 +331,14 @@ function ReportScreen({ ); } + const transactionThreadReportID = useMemo(() => ReportActionsUtils.getOneTransactionThreadReportID(reportActions ?? []), [reportActions]); if (ReportUtils.isMoneyRequestReport(report)) { headerView = ( ); } @@ -654,7 +657,7 @@ function ReportScreen({ isLoadingNewerReportActions={reportMetadata?.isLoadingNewerReportActions} isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions} isReadyForCommentLinking={!shouldShowSkeleton} - transactionThreadReportID={ReportActionsUtils.getOneTransactionThreadReportID(reportActions ?? [])} + transactionThreadReportID={transactionThreadReportID} /> )}