diff --git a/src/components/ReportActionItem/TaskPreview.js b/src/components/ReportActionItem/TaskPreview.js index ca4103624440..d0181e3d736a 100644 --- a/src/components/ReportActionItem/TaskPreview.js +++ b/src/components/ReportActionItem/TaskPreview.js @@ -72,6 +72,11 @@ function TaskPreview(props) { const assigneeDisplayName = lodashGet(props.personalDetailsList, [taskAssigneeAccountID, 'displayName'], ''); const taskAssignee = assigneeLogin || assigneeDisplayName; const htmlForTaskPreview = taskAssignee ? `@${taskAssignee} ${taskTitle}` : `${taskTitle}`; + const isDeletedParentAction = ReportUtils.isCanceledTaskReport(props.taskReport, props.action); + + if (isDeletedParentAction) { + return ${props.translate('parentReportAction.deletedTask')}`} />; + } return ( diff --git a/src/languages/en.ts b/src/languages/en.ts index 03adaa1e66b1..bc5ac9d63b92 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1520,7 +1520,7 @@ export default { completed: 'Completed', messages: { completed: 'completed task', - canceled: 'canceled task', + canceled: 'deleted task', reopened: 'reopened task', error: 'You do not have the permission to do the requested action.', }, @@ -1692,6 +1692,7 @@ export default { parentReportAction: { deletedMessage: '[Deleted message]', deletedRequest: '[Deleted request]', + deletedTask: '[Deleted task]', hiddenMessage: '[Hidden message]', }, threads: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 7315c42cebab..42faf6e6c9a1 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1542,7 +1542,7 @@ export default { completed: 'Completada', messages: { completed: 'tarea completada', - canceled: 'tarea cancelada', + canceled: 'tarea eliminado', reopened: 'tarea reabrir', error: 'No tiene permiso para realizar la acción solicitada.', }, @@ -2174,6 +2174,7 @@ export default { parentReportAction: { deletedMessage: '[Mensaje eliminado]', deletedRequest: '[Pedido eliminado]', + deletedTask: '[Tarea eliminado]', hiddenMessage: '[Mensaje oculto]', }, threads: { diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f24959c4bac2..00a0a0dcbcba 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -187,23 +187,37 @@ function isTaskReport(report) { } /** - * Checks if a report is an open task report. + * Checks if a task has been cancelled + * When a task is deleted, the parentReportAction is updated to have a isDeletedParentAction deleted flag + * This is because when you delete a task, we still allow you to chat on the report itself + * There's another situation where you don't have access to the parentReportAction (because it was created in a chat you don't have access to) + * In this case, we have added the key to the report itself * * @param {Object} report + * @param {Object} parentReportAction * @returns {Boolean} */ -function isOpenTaskReport(report) { - return isTaskReport(report) && report.stateNum === CONST.REPORT.STATE_NUM.OPEN && report.statusNum === CONST.REPORT.STATUS.OPEN; +function isCanceledTaskReport(report = {}, parentReportAction = {}) { + if (!_.isEmpty(parentReportAction) && lodashGet(parentReportAction, ['message', 0, 'isDeletedParentAction'], false)) { + return true; + } + + if (!_.isEmpty(report) && report.isDeletedParentAction) { + return true; + } + + return false; } /** - * Checks if the current user is assigned to the task report + * Checks if a report is an open task report. * * @param {Object} report + * @param {Object} parentReportAction - The parent report action of the report (Used to check if the task has been canceled) * @returns {Boolean} */ -function isCanceledTaskReport(report) { - return isTaskReport(report) && report.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && report.statusNum === CONST.REPORT.STATUS.CLOSED; +function isOpenTaskReport(report, parentReportAction = {}) { + return isTaskReport(report) && !isCanceledTaskReport(report, parentReportAction) && report.stateNum === CONST.REPORT.STATE_NUM.OPEN && report.statusNum === CONST.REPORT.STATUS.OPEN; } /** @@ -1600,8 +1614,8 @@ function getParentReport(report) { */ function getReportName(report, policy = undefined) { let formattedName; + const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (isChatThread(report)) { - const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (ReportActionsUtils.isTransactionThread(parentReportAction)) { return getTransactionReportName(parentReportAction); } @@ -1619,6 +1633,11 @@ function getReportName(report, policy = undefined) { } return parentReportActionMessage || Localize.translateLocal('parentReportAction.deletedMessage'); } + + if (isTaskReport(report) && isCanceledTaskReport(report, parentReportAction)) { + return Localize.translateLocal('parentReportAction.deletedTask'); + } + if (isChatRoom(report) || isTaskReport(report)) { formattedName = report.reportName; } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 2d599053ea51..a5e8ac325217 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -755,26 +755,50 @@ function getShareDestination(reportID, reports, personalDetails) { * @param {number} originalStatusNum */ function cancelTask(taskReportID, taskTitle, originalStateNum, originalStatusNum) { - const message = `canceled task: ${taskTitle}`; + const message = `deleted task: ${taskTitle}`; const optimisticCancelReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED, message); const optimisticReportActionID = optimisticCancelReportAction.reportActionID; + const taskReport = ReportUtils.getReport(taskReportID); + const parentReportAction = ReportActionsUtils.getParentReportAction(taskReport); + const parentReport = ReportUtils.getParentReport(taskReport); + + const optimisticReportActions = { + [parentReportAction.reportActionID]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + previousMessage: parentReportAction.message, + message: [ + { + translationKey: '', + type: 'COMMENT', + html: '', + text: '', + isEdited: true, + isDeletedParentAction: true, + }, + ], + errors: null, + linkMetaData: [], + }, + }; const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`, value: { - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, - statusNum: CONST.REPORT.STATUS.CLOSED, + lastVisibleActionCreated: optimisticCancelReportAction.created, + lastMessageText: message, + lastActorAccountID: optimisticCancelReportAction.actorAccountID, + updateReportInLHN: true, + isDeletedParentAction: true, }, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, value: { - lastVisibleActionCreated: optimisticCancelReportAction.created, - lastMessageText: message, - lastActorAccountID: optimisticCancelReportAction.actorAccountID, + lastMessageText: ReportActionsUtils.getLastVisibleMessage(parentReport.reportID, {[parentReportAction.reportActionID]: null}).lastMessageText, + lastVisibleActionCreated: ReportActionsUtils.getLastVisibleAction(parentReport.reportID, {[parentReportAction.reportActionID]: null}).created, }, }, { @@ -784,6 +808,11 @@ function cancelTask(taskReportID, taskTitle, originalStateNum, originalStatusNum [optimisticReportActionID]: optimisticCancelReportAction, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReport.reportID}`, + value: optimisticReportActions, + }, ]; const successData = [ diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 8d389a8c8581..477d063c1747 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -29,6 +29,7 @@ import reportActionPropTypes from './report/reportActionPropTypes'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import PinButton from '../../components/PinButton'; import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; +import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; import ParentNavigationSubtitle from '../../components/ParentNavigationSubtitle'; const propTypes = { @@ -93,12 +94,14 @@ function HeaderView(props) { const isConcierge = ReportUtils.hasSingleParticipant(props.report) && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); const isAutomatedExpensifyAccount = ReportUtils.hasSingleParticipant(props.report) && ReportUtils.hasAutomatedExpensifyAccountIDs(participants); const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink'); + const parentReportAction = ReportActionsUtils.getParentReportAction(props.report); + const isCanceledTaskReport = ReportUtils.isCanceledTaskReport(props.report, parentReportAction); // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact // these users via alternative means. It is possible to request a call with Concierge so we leave the option for them. const shouldShowCallButton = (isConcierge && guideCalendarLink) || (!isAutomatedExpensifyAccount && !isTaskReport); const threeDotMenuItems = []; - if (isTaskReport) { + if (isTaskReport && !isCanceledTaskReport) { const canModifyTask = Task.canModifyTask(props.report, props.session.accountID); if (ReportUtils.isOpenTaskReport(props.report) && canModifyTask) { threeDotMenuItems.push({ diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 004087c22308..36063aa5c72d 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -316,7 +316,7 @@ function ReportScreen({ needsOffscreenAlphaCompositing > {headerView} - {ReportUtils.isTaskReport(report) && isSmallScreenWidth && ReportUtils.isOpenTaskReport(report) && ( + {ReportUtils.isTaskReport(report) && isSmallScreenWidth && ReportUtils.isOpenTaskReport(report, parentReportAction) && ( diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index a5c412e17a50..2f4545b6e1d5 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -67,6 +67,7 @@ import * as BankAccounts from '../../../libs/actions/BankAccounts'; import usePrevious from '../../../hooks/usePrevious'; import ReportScreenContext from '../ReportScreenContext'; import Permissions from '../../../libs/Permissions'; +import RenderHTML from '../../../components/RenderHTML'; import ReportAttachmentsContext from './ReportAttachmentsContext'; const propTypes = { @@ -514,6 +515,22 @@ function ReportActionItem(props) { ); } if (ReportUtils.isTaskReport(props.report)) { + if (ReportUtils.isCanceledTaskReport(props.report, parentReportAction)) { + return ( + <> + + ${props.translate('parentReportAction.deletedTask')}`} /> + + + + ); + } + return ( { const reports = {}; _.keys(props.reports).forEach((reportKey) => { - if (ReportUtils.shouldDisableWriteActions(props.reports[reportKey]) || ReportUtils.isExpensifyOnlyParticipantInReport(props.reports[reportKey])) { + if ( + ReportUtils.shouldDisableWriteActions(props.reports[reportKey]) || + ReportUtils.isExpensifyOnlyParticipantInReport(props.reports[reportKey]) || + ReportUtils.isCanceledTaskReport(props.reports[reportKey]) + ) { return; } reports[reportKey] = props.reports[reportKey];