diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c07da3e53973..2cb7f025fbfb 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8380,71 +8380,6 @@ function canLeaveInvoiceRoom(report: OnyxEntry): boolean { return true; } -/** - * Allows a user to leave a policy room according to the following conditions of the visibility or chatType rNVP: - * `public` - Anyone can leave (because anybody can join) - * `public_announce` - Only non-policy members can leave (it's auto-shared with policy members) - * `policy_admins` - Nobody can leave (it's auto-shared with all policy admins) - * `policy_announce` - Nobody can leave (it's auto-shared with all policy members) - * `policyExpenseChat` - Nobody can leave (it's auto-shared with all policy members) - * `policy` - Anyone can leave (though only policy members can join) - * `domain` - Nobody can leave (it's auto-shared with domain members) - * `dm` - Nobody can leave (it's auto-shared with users) - * `private` - Anybody can leave (though you can only be invited to join) - * `invoice` - Invoice sender, invoice receiver and auto-invited admins cannot leave - */ -function canLeaveRoom(report: OnyxEntry, isPolicyEmployee: boolean): boolean { - if (isInvoiceRoom(report)) { - // This will get removed as part of https://github.com/Expensify/App/issues/59961 - // eslint-disable-next-line deprecation/deprecation - if (isArchivedNonExpenseReport(report, getReportNameValuePairs(report?.reportID))) { - return false; - } - - const invoiceReport = getReportOrDraftReport(report?.iouReportID); - - if (invoiceReport?.ownerAccountID === currentUserAccountID) { - return false; - } - - if (invoiceReport?.managerID === currentUserAccountID) { - return false; - } - - const isSenderPolicyAdmin = getPolicy(report?.policyID)?.role === CONST.POLICY.ROLE.ADMIN; - - if (isSenderPolicyAdmin) { - return false; - } - - const isReceiverPolicyAdmin = - report?.invoiceReceiver?.type === CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS ? getPolicy(report?.invoiceReceiver?.policyID)?.role === CONST.POLICY.ROLE.ADMIN : false; - - if (isReceiverPolicyAdmin) { - return false; - } - - return true; - } - - if (!report?.visibility) { - if ( - report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS || - report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE || - report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || - report?.chatType === CONST.REPORT.CHAT_TYPE.DOMAIN_ALL || - report?.chatType === CONST.REPORT.CHAT_TYPE.SELF_DM || - !report?.chatType - ) { - // DM chats don't have a chatType - return false; - } - } else if (isPublicAnnounceRoom(report) && isPolicyEmployee) { - return false; - } - return true; -} - function isCurrentUserTheOnlyParticipant(participantAccountIDs?: number[]): boolean { return !!(participantAccountIDs?.length === 1 && participantAccountIDs?.at(0) === currentUserAccountID); } @@ -9154,23 +9089,19 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, shouldExcludeNonR * - The action is a whisper action and it's neither a report preview nor IOU action * - The action is the thread's first chat */ -function shouldDisableThread(reportAction: OnyxInputOrEntry, reportID: string, isThreadReportParentAction: boolean): boolean { +function shouldDisableThread(reportAction: OnyxInputOrEntry, reportID: string, isThreadReportParentAction: boolean, isReportArchived = false): boolean { const isSplitBillAction = isSplitBillReportAction(reportAction); const isDeletedActionLocal = isDeletedAction(reportAction); const isReportPreviewActionLocal = isReportPreviewAction(reportAction); const isIOUAction = isMoneyRequestAction(reportAction); const isWhisperActionLocal = isWhisperAction(reportAction) || isActionableTrackExpense(reportAction); - - // This will get removed as part of https://github.com/Expensify/App/issues/59961 - // eslint-disable-next-line deprecation/deprecation - const isArchived = isArchivedNonExpenseReport(getReportOrDraftReport(reportID), getReportNameValuePairs(reportID)); const isActionDisabled = CONST.REPORT.ACTIONS.THREAD_DISABLED.some((action: string) => action === reportAction?.actionName); return ( isActionDisabled || isSplitBillAction || (isDeletedActionLocal && !reportAction?.childVisibleActionCount) || - (isArchived && !reportAction?.childVisibleActionCount) || + (isReportArchived && !reportAction?.childVisibleActionCount) || (isWhisperActionLocal && !isReportPreviewActionLocal && !isIOUAction) || isThreadReportParentAction ); @@ -10816,7 +10747,6 @@ export { canEditWriteCapability, canFlagReportAction, isNonAdminOrOwnerOfPolicyExpenseChat, - canLeaveRoom, canJoinChat, canLeaveChat, canReportBeMentionedWithinPolicy, diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index a8b464056d08..104e9f75fd03 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -269,11 +269,11 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.replyInThread', icon: Expensicons.ChatBubbleReply, - shouldShow: ({type, reportAction, reportID, isThreadReportParentAction}) => { + shouldShow: ({type, reportAction, reportID, isThreadReportParentAction, isArchivedRoom}) => { if (type !== CONST.CONTEXT_MENU_TYPES.REPORT_ACTION || !reportID) { return false; } - return !shouldDisableThread(reportAction, reportID, isThreadReportParentAction); + return !shouldDisableThread(reportAction, reportID, isThreadReportParentAction, isArchivedRoom); }, onPress: (closePopover, {reportAction, reportID}) => { const originalReportID = getOriginalReportID(reportID, reportAction); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 6d52363074fe..6aecb1d138d1 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -1234,42 +1234,6 @@ describe('ReportUtils', () => { expect(shouldDisableThread(reportAction, reportID, false)).toBeTruthy(); }); - it('should disable on deleted and not-thread actions', () => { - const reportAction = { - message: [ - { - translationKey: '', - type: 'COMMENT', - html: '', - text: '', - isEdited: true, - }, - ], - childVisibleActionCount: 1, - } as ReportAction; - expect(shouldDisableThread(reportAction, reportID, false)).toBeFalsy(); - - reportAction.childVisibleActionCount = 0; - expect(shouldDisableThread(reportAction, reportID, false)).toBeTruthy(); - }); - - it('should disable on archived reports and not-thread actions', () => { - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - }) - .then(() => waitForBatchedUpdates()) - .then(() => { - const reportAction = { - childVisibleActionCount: 1, - } as ReportAction; - expect(shouldDisableThread(reportAction, reportID, false)).toBeFalsy(); - - reportAction.childVisibleActionCount = 0; - expect(shouldDisableThread(reportAction, reportID, false)).toBeTruthy(); - }); - }); - it("should disable on a whisper action and it's neither a report preview nor IOU action", () => { const reportAction = { actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, @@ -1286,6 +1250,186 @@ describe('ReportUtils', () => { } as ReportAction; expect(shouldDisableThread(reportAction, reportID, true)).toBeTruthy(); }); + + describe('deleted threads', () => { + it('should be enabled if the report action is not-deleted and child visible action count is 1', () => { + // Given a normal report action with one child visible action count + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: 'test', + text: 'test', + }, + ], + childVisibleActionCount: 1, + } as ReportAction; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false); + + // Then the thread should be enabled + expect(isThreadDisabled).toBeFalsy(); + }); + + it('should be enabled if the report action is not-deleted and child visible action count is 0', () => { + // Given a normal report action with zero child visible action count + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: 'test', + text: 'test', + }, + ], + childVisibleActionCount: 0, + } as ReportAction; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false); + + // Then the thread should be enabled + expect(isThreadDisabled).toBeFalsy(); + }); + it('should be enabled if the report action is deleted and child visible action count is 1', () => { + // Given a normal report action with one child visible action count + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: '', + text: '', + }, + ], + childVisibleActionCount: 1, + } as ReportAction; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false); + + // Then the thread should be enabled + expect(isThreadDisabled).toBeFalsy(); + }); + + it('should be disabled if the report action is deleted and child visible action count is 0', () => { + // Given a normal report action with zero child visible action count + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: '', + text: '', + }, + ], + childVisibleActionCount: 0, + } as ReportAction; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false); + + // Then the thread should be disabled + expect(isThreadDisabled).toBeTruthy(); + }); + }); + + describe('archived report threads', () => { + it('should be enabled if the report is not-archived and child visible action count is 1', () => { + // Given a normal report action with one child visible action count + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: 'test', + text: 'test', + }, + ], + childVisibleActionCount: 1, + } as ReportAction; + + // And a report that is not archived + const isReportArchived = false; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false, isReportArchived); + + // Then the thread should be enabled + expect(isThreadDisabled).toBeFalsy(); + }); + it('should be enabled if the report is not-archived and child visible action count is 0', () => { + // Given a normal report action with zero child visible action counts + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: 'test', + text: 'test', + }, + ], + childVisibleActionCount: 1, + } as ReportAction; + + // And a report that is not archived + const isReportArchived = false; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false, isReportArchived); + + // Then the thread should be enabled + expect(isThreadDisabled).toBeFalsy(); + }); + it('should be enabled if the report is archived and child visible action count is 1', () => { + // Given a normal report action with one child visible action count + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: 'test', + text: 'test', + }, + ], + childVisibleActionCount: 1, + } as ReportAction; + + // And a report that is not archived + const isReportArchived = true; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false, isReportArchived); + + // Then the thread should be enabled + expect(isThreadDisabled).toBeFalsy(); + }); + it('should be disabled if the report is archived and child visible action count is 0', () => { + // Given a normal report action with zero child visible action counts + const reportAction = { + message: [ + { + translationKey: '', + type: 'COMMENT', + html: 'test', + text: 'test', + }, + ], + childVisibleActionCount: 0, + } as ReportAction; + + // And a report that is not archived + const isReportArchived = true; + + // When it's checked to see if the thread should be disabled + const isThreadDisabled = shouldDisableThread(reportAction, reportID, false, isReportArchived); + + // Then the thread should be disabled + expect(isThreadDisabled).toBeTruthy(); + }); + }); }); describe('getAllAncestorReportActions', () => {