diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 0154bef51f2c..906a2963baa8 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -20,7 +20,7 @@ import type { OriginalMessageReimbursementDequeued, } from '@src/types/onyx/OriginalMessage'; import type Report from '@src/types/onyx/Report'; -import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; +import type {Message, ReportActionBase, ReportActionMessageJSON, ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -143,8 +143,27 @@ function isModifiedExpenseAction(reportAction: OnyxEntry | ReportA return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE; } +/** + * We are in the process of deprecating reportAction.originalMessage and will be setting the db version of "message" to reportAction.message in the future see: https://github.com/Expensify/App/issues/39797 + * In the interim, we must check to see if we have an object or array for the reportAction.message, if we have an array we will use the originalMessage as this means we have not yet migrated. + */ +function getWhisperedTo(reportAction: OnyxEntry | EmptyObject): number[] { + const originalMessage = reportAction?.originalMessage; + const message = reportAction?.message; + + if (!Array.isArray(message) && typeof message === 'object') { + return (message as ReportActionMessageJSON)?.whisperedTo ?? []; + } + + if (originalMessage) { + return (originalMessage as ReportActionMessageJSON)?.whisperedTo ?? []; + } + + return []; +} + function isWhisperAction(reportAction: OnyxEntry | EmptyObject): boolean { - return (reportAction?.whisperedToAccountIDs ?? []).length > 0; + return getWhisperedTo(reportAction).length > 0; } /** @@ -154,7 +173,7 @@ function isWhisperActionTargetedToOthers(reportAction: OnyxEntry): if (!isWhisperAction(reportAction)) { return false; } - return !reportAction?.whisperedToAccountIDs?.includes(currentUserAccountID ?? 0); + return !getWhisperedTo(reportAction).includes(currentUserAccountID ?? 0); } function isReimbursementQueuedAction(reportAction: OnyxEntry) { @@ -278,8 +297,8 @@ function shouldIgnoreGap(currentReportAction: ReportAction | undefined, nextRepo return ( isOptimisticAction(currentReportAction) || isOptimisticAction(nextReportAction) || - !!currentReportAction.whisperedToAccountIDs?.length || - !!nextReportAction.whisperedToAccountIDs?.length || + !!getWhisperedTo(currentReportAction).length || + !!getWhisperedTo(nextReportAction).length || currentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.INVITE_TO_ROOM || nextReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED || nextReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED @@ -1196,6 +1215,7 @@ export { getParentReportAction, getReportAction, getReportActionMessageText, + getWhisperedTo, isApprovedOrSubmittedReportAction, getReportPreviewAction, getSortedReportActions, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 64b77cae6313..5749e241b6fb 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -51,7 +51,7 @@ import type { } from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference, Participants, PendingChatMember, Participant as ReportParticipant} from '@src/types/onyx/Report'; -import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; +import type {Message, ReportActionBase, ReportActions, ReportPreviewAction} from '@src/types/onyx/ReportAction'; import type {Comment, Receipt, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -148,7 +148,6 @@ type OptimisticAddCommentReportAction = Pick< | 'childCommenterCount' | 'childLastVisibleActionCreated' | 'childOldestFourAccountIDs' - | 'whisperedToAccountIDs' > & {isOptimisticAction: boolean}; type OptimisticReportAction = { @@ -197,7 +196,6 @@ type OptimisticIOUReportAction = Pick< | 'created' | 'pendingAction' | 'receipt' - | 'whisperedToAccountIDs' | 'childReportID' | 'childVisibleActionCount' | 'childCommenterCount' @@ -3898,6 +3896,7 @@ function buildOptimisticIOUReportAction( IOUTransactionID: transactionID, IOUReportID, type, + whisperedTo: [CONST.IOU.RECEIPT_STATE.SCANREADY, CONST.IOU.RECEIPT_STATE.SCANNING].some((value) => value === receipt?.state) ? [currentUserAccountID ?? -1] : [], }; if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { @@ -3951,7 +3950,6 @@ function buildOptimisticIOUReportAction( shouldShow: true, created, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - whisperedToAccountIDs: [CONST.IOU.RECEIPT_STATE.SCANREADY, CONST.IOU.RECEIPT_STATE.SCANNING].some((value) => value === receipt?.state) ? [currentUserAccountID ?? -1] : [], }; } @@ -4083,6 +4081,7 @@ function buildOptimisticReportPreview(chatReport: OnyxEntry, iouReport: pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, originalMessage: { linkedReportID: iouReport?.reportID, + whisperedTo: isReceiptBeingScanned ? [currentUserAccountID ?? -1] : [], }, message: [ { @@ -4100,7 +4099,6 @@ function buildOptimisticReportPreview(chatReport: OnyxEntry, iouReport: childMoneyRequestCount: 1, childLastMoneyRequestComment: comment, childRecentReceiptTransactionIDs: hasReceipt && !isEmptyObject(transaction) ? {[transaction?.transactionID ?? '']: created} : undefined, - whisperedToAccountIDs: isReceiptBeingScanned ? [currentUserAccountID ?? -1] : [], }; } @@ -4190,7 +4188,13 @@ function buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThrea * @param [transaction] - optimistic newest transaction of a report preview * */ -function updateReportPreview(iouReport: OnyxEntry, reportPreviewAction: ReportAction, isPayRequest = false, comment = '', transaction: OnyxEntry = null): ReportAction { +function updateReportPreview( + iouReport: OnyxEntry, + reportPreviewAction: ReportPreviewAction, + isPayRequest = false, + comment = '', + transaction: OnyxEntry = null, +): ReportPreviewAction { const hasReceipt = TransactionUtils.hasReceipt(transaction); const recentReceiptTransactions = reportPreviewAction?.childRecentReceiptTransactionIDs ?? {}; const transactionsToKeep = TransactionUtils.getRecentTransactions(recentReceiptTransactions); @@ -4226,7 +4230,10 @@ function updateReportPreview(iouReport: OnyxEntry, reportPreviewAction: : recentReceiptTransactions, // As soon as we add a transaction without a receipt to the report, it will have ready expenses, // so we remove the whisper - whisperedToAccountIDs: hasReceipt ? reportPreviewAction?.whisperedToAccountIDs : [], + originalMessage: { + ...(reportPreviewAction.originalMessage ?? {}), + whisperedTo: hasReceipt ? reportPreviewAction?.originalMessage?.whisperedTo : [], + }, }; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7171a7d6732a..eb7b82800358 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -55,6 +55,7 @@ import type {Participant, Split} from '@src/types/onyx/IOU'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type {IOUMessage, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type ReportAction from '@src/types/onyx/ReportAction'; +import type {ReportPreviewAction} from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, ReceiptSource, SplitShares, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -1875,7 +1876,7 @@ function getMoneyRequestInformation( let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : getReportPreviewAction(chatReport.reportID, iouReport.reportID); if (reportPreviewAction) { - reportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction, false, comment, optimisticTransaction); + reportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction as ReportPreviewAction, false, comment, optimisticTransaction); } else { reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, iouReport, comment, optimisticTransaction); @@ -2099,7 +2100,7 @@ function getTrackExpenseInformation( reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : getReportPreviewAction(chatReport.reportID, iouReport.reportID); if (reportPreviewAction) { - reportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction, false, comment, optimisticTransaction); + reportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction as ReportPreviewAction, false, comment, optimisticTransaction); } else { reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, iouReport, comment, optimisticTransaction); // Generated ReportPreview action is a parent report action of the iou report. @@ -2444,7 +2445,9 @@ function getUpdateMoneyRequestParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: { [transactionThread?.parentReportActionID ?? '']: { - whisperedToAccountIDs: [], + originalMessage: { + whisperedTo: [], + }, }, }, }, @@ -2453,7 +2456,9 @@ function getUpdateMoneyRequestParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.parentReportID}`, value: { [iouReport?.parentReportActionID ?? '']: { - whisperedToAccountIDs: [], + originalMessage: { + whisperedTo: [], + }, }, }, }, @@ -2687,7 +2692,9 @@ function getUpdateTrackExpenseParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { [transactionThread?.parentReportActionID ?? '']: { - whisperedToAccountIDs: [], + originalMessage: { + whisperedTo: [], + }, }, }, }); @@ -3949,7 +3956,7 @@ function createSplitsAndOnyxData( let oneOnOneReportPreviewAction = getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport.reportID); if (oneOnOneReportPreviewAction) { - oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); + oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction as ReportPreviewAction); } else { oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport); } @@ -4509,7 +4516,9 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA value: { [reportAction.reportActionID]: { lastModified: DateUtils.getDBTime(), - whisperedToAccountIDs: [], + originalMessage: { + whisperedTo: [], + }, }, }, }, @@ -4636,7 +4645,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA let oneOnOneReportPreviewAction = getReportPreviewAction(oneOnOneChatReport?.reportID ?? '', oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { - oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); + oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction as ReportPreviewAction); } else { oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport, '', oneOnOneTransaction); } @@ -4825,7 +4834,9 @@ function editRegularMoneyRequest( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: { [transactionThread?.parentReportActionID ?? '']: { - whisperedToAccountIDs: [], + originalMessage: { + whisperedTo: [], + }, }, }, }, @@ -4834,7 +4845,9 @@ function editRegularMoneyRequest( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.parentReportID}`, value: { [iouReport?.parentReportActionID ?? '']: { - whisperedToAccountIDs: [], + originalMessage: { + whisperedTo: [], + }, }, }, }, @@ -5673,7 +5686,7 @@ function getPayMoneyRequestParams( let optimisticReportPreviewAction = null; const reportPreviewAction = getReportPreviewAction(chatReport.reportID, iouReport.reportID); if (reportPreviewAction) { - optimisticReportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction, true); + optimisticReportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction as ReportPreviewAction, true); } const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`] ?? null; diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index fddbbadde33c..d8b1bc774de0 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -918,12 +918,12 @@ function ReportActionItem({ } const hasErrors = !isEmptyObject(action.errors); - const whisperedToAccountIDs = action.whisperedToAccountIDs ?? []; - const isWhisper = whisperedToAccountIDs.length > 0; - const isMultipleParticipant = whisperedToAccountIDs.length > 1; - const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedToAccountIDs); + const whisperedTo = ReportActionsUtils.getWhisperedTo(action); + const isWhisper = whisperedTo.length > 0; + const isMultipleParticipant = whisperedTo.length > 1; + const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedTo); const whisperedToPersonalDetails = isWhisper - ? (Object.values(personalDetails ?? {}).filter((details) => whisperedToAccountIDs.includes(details?.accountID ?? -1)) as OnyxTypes.PersonalDetails[]) + ? (Object.values(personalDetails ?? {}).filter((details) => whisperedTo.includes(details?.accountID ?? -1)) as OnyxTypes.PersonalDetails[]) : []; const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; @@ -993,7 +993,7 @@ function ReportActionItem({   ; type ReportAction = ReportActionBase & OriginalMessage; +type ReportPreviewAction = ReportActionBase & OriginalMessageReportPreview; +type ModifiedExpenseAction = ReportActionBase & OriginalMessageModifiedExpense; type ReportActions = Record; type ReportActionsCollectionDataSet = CollectionDataSet; export default ReportAction; -export type {ReportActions, ReportActionBase, Message, LinkMetadata, OriginalMessage, ReportActionsCollectionDataSet}; +export type {ReportActions, ReportActionBase, Message, LinkMetadata, OriginalMessage, ReportActionsCollectionDataSet, ReportPreviewAction, ModifiedExpenseAction, ReportActionMessageJSON}; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index d0ea948bdb6c..1cab8f563b24 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -5,6 +5,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, Report, ReportAction} from '@src/types/onyx'; +import type {ModifiedExpenseAction} from '@src/types/onyx/ReportAction'; import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet'; import * as NumberUtils from '../../src/libs/NumberUtils'; import * as LHNTestUtils from '../utils/LHNTestUtils'; @@ -769,8 +770,10 @@ describe('ReportUtils', () => { 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, - whisperedToAccountIDs: [123456], - } as ReportAction; + originalMessage: { + whisperedTo: [123456], + }, + } as ModifiedExpenseAction; expect(ReportUtils.shouldDisableThread(reportAction, reportID)).toBeTruthy(); }); diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx index abfaf9bd8b00..2a9f9074125b 100644 --- a/tests/utils/LHNTestUtils.tsx +++ b/tests/utils/LHNTestUtils.tsx @@ -158,7 +158,6 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = text: 'Email One', }, ], - whisperedToAccountIDs: [], automatic: false, message: [ { @@ -182,6 +181,7 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = }, ], originalMessage: { + whisperedTo: [], childReportID: `${reportActionID}`, emojiReactions: { heart: { diff --git a/tests/utils/ReportTestUtils.ts b/tests/utils/ReportTestUtils.ts index 3948baca3113..b9dc50aecec0 100644 --- a/tests/utils/ReportTestUtils.ts +++ b/tests/utils/ReportTestUtils.ts @@ -30,6 +30,7 @@ const getFakeReportAction = (index: number, actionName?: ActionName): ReportActi lastModified: '2021-07-14T15:00:00Z', // IOUReportID: index, linkedReportID: index.toString(), + whisperedTo: [], }, pendingAction: null, person: [ @@ -45,7 +46,6 @@ const getFakeReportAction = (index: number, actionName?: ActionName): ReportActi sequenceNumber: 0, shouldShow: true, timestamp: 1696243169, - whisperedToAccountIDs: [], } as ReportAction); const getMockedSortedReportActions = (length = 100): ReportAction[] => diff --git a/tests/utils/collections/reportActions.ts b/tests/utils/collections/reportActions.ts index f19e939083d2..65cbb3ba966e 100644 --- a/tests/utils/collections/reportActions.ts +++ b/tests/utils/collections/reportActions.ts @@ -68,8 +68,8 @@ export default function createRandomReportAction(index: number): ReportAction { originalMessage: { html: randWord(), lastModified: getRandomDate(), + whisperedTo: randAggregation(), }, - whisperedToAccountIDs: randAggregation(), avatar: randWord(), automatic: randBoolean(), shouldShow: randBoolean(),