diff --git a/src/libs/API/parameters/MarkAsUnreadParams.ts b/src/libs/API/parameters/MarkAsUnreadParams.ts index 0a4a0d98c18c..56ab9bf563ea 100644 --- a/src/libs/API/parameters/MarkAsUnreadParams.ts +++ b/src/libs/API/parameters/MarkAsUnreadParams.ts @@ -1,6 +1,7 @@ type MarkAsUnreadParams = { reportID: string; lastReadTime: string; + reportActionID: string; }; export default MarkAsUnreadParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index cf638597ad04..9df72c376f6f 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1442,7 +1442,7 @@ function readNewestAction(reportID: string | undefined, shouldResetUnreadMarker /** * Sets the last read time on a report */ -function markCommentAsUnread(reportID: string | undefined, reportActionCreated: string) { +function markCommentAsUnread(reportID: string | undefined, reportAction: ReportAction) { if (!reportID) { Log.warn('7339cd6c-3263-4f89-98e5-730f0be15784 Invalid report passed to MarkCommentAsUnread. Not calling the API because it wil fail.'); return; @@ -1466,9 +1466,10 @@ function markCommentAsUnread(reportID: string | undefined, reportActionCreated: const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(reportID, reportActions ?? []); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`]; + // If no action created date is provided, use the last action's from other user const actionCreationTime = - reportActionCreated || (latestReportActionFromOtherUsers?.created ?? getReportLastVisibleActionCreated(report, transactionThreadReport) ?? DateUtils.getDBTime(0)); + reportAction?.created || (latestReportActionFromOtherUsers?.created ?? getReportLastVisibleActionCreated(report, transactionThreadReport) ?? DateUtils.getDBTime(0)); // We subtract 1 millisecond so that the lastReadTime is updated to just before a given reportAction's created date // For example, if we want to mark a report action with ID 100 and created date '2014-04-01 16:07:02.999' unread, we set the lastReadTime to '2014-04-01 16:07:02.998' @@ -1488,6 +1489,7 @@ function markCommentAsUnread(reportID: string | undefined, reportActionCreated: const parameters: MarkAsUnreadParams = { reportID, lastReadTime, + reportActionID: reportAction?.reportActionID, }; API.write(WRITE_COMMANDS.MARK_AS_UNREAD, parameters, {optimisticData}); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 4735921d1425..03e400649496 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -295,7 +295,7 @@ const ContextMenuActions: ContextMenuAction[] = [ successIcon: Expensicons.Checkmark, shouldShow: ({type, isUnreadChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONST.CONTEXT_MENU_TYPES.REPORT && !isUnreadChat), onPress: (closePopover, {reportAction, reportID}) => { - markCommentAsUnread(reportID, reportAction?.created); + markCommentAsUnread(reportID, reportAction); if (closePopover) { hideContextMenu(true, ReportActionComposeFocusManager.focus); } diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index d18213bd8f54..3b2ee21b31e3 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -317,7 +317,7 @@ describe('actions/Report', () => { // When the user manually marks a message as "unread" jest.advanceTimersByTime(10); - Report.markCommentAsUnread(REPORT_ID, reportActionCreatedDate); + Report.markCommentAsUnread(REPORT_ID, reportActions['1']); return waitForBatchedUpdates(); }) .then(() => { @@ -443,13 +443,13 @@ describe('actions/Report', () => { expect(ReportUtils.isUnread(report, undefined)).toBe(false); // When the user manually marks a message as "unread" - Report.markCommentAsUnread(REPORT_ID, reportActionCreatedDate); + Report.markCommentAsUnread(REPORT_ID, reportActions[400]); return waitForBatchedUpdates(); }) .then(() => { // Then we should expect the report to be to be unread expect(ReportUtils.isUnread(report, undefined)).toBe(true); - expect(report?.lastReadTime).toBe(DateUtils.subtractMillisecondsFromDateTime(reportActionCreatedDate, 1)); + expect(report?.lastReadTime).toBe(DateUtils.subtractMillisecondsFromDateTime(reportActions[400].created, 1)); // If the user deletes the last comment after the lastReadTime the lastMessageText will reflect the new last comment Report.deleteReportComment(REPORT_ID, {...reportActions[400]}); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index a508cf6b1261..0da3ee0d08ef 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -103,6 +103,26 @@ const USER_C_ACCOUNT_ID = 3; const USER_C_EMAIL = 'user_c@test.com'; let reportAction3CreatedDate: string; let reportAction9CreatedDate: string; +const TEN_MINUTES_AGO = subMinutes(new Date(), 10); +const createdReportActionID = rand64().toString(); +const createdReportAction = { + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + automatic: false, + created: format(TEN_MINUTES_AGO, CONST.DATE.FNS_DB_FORMAT_STRING), + reportActionID: createdReportActionID, + message: [ + { + style: 'strong', + text: '__FAKE__', + type: 'TEXT', + }, + { + style: 'normal', + text: 'created this report', + type: 'TEXT', + }, + ], +}; /** * Sets up a test with a logged in user that has one unread chat from another user. Returns the test instance. @@ -127,7 +147,6 @@ function signInAndGetAppWithUnreadChat(): Promise { return waitForBatchedUpdates(); }) .then(async () => { - const TEN_MINUTES_AGO = subMinutes(new Date(), 10); reportAction3CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 30), CONST.DATE.FNS_DB_FORMAT_STRING); reportAction9CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 90), CONST.DATE.FNS_DB_FORMAT_STRING); @@ -145,26 +164,8 @@ function signInAndGetAppWithUnreadChat(): Promise { lastActorAccountID: USER_B_ACCOUNT_ID, type: CONST.REPORT.TYPE.CHAT, }); - const createdReportActionID = rand64().toString(); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { - [createdReportActionID]: { - actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, - automatic: false, - created: format(TEN_MINUTES_AGO, CONST.DATE.FNS_DB_FORMAT_STRING), - reportActionID: createdReportActionID, - message: [ - { - style: 'strong', - text: '__FAKE__', - type: 'TEXT', - }, - { - style: 'normal', - text: 'created this report', - type: 'TEXT', - }, - ], - }, + [createdReportActionID]: createdReportAction, 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1'), 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2'), 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3'), @@ -292,7 +293,7 @@ describe('Unread Indicators', () => { const NEW_REPORT_ID = '2'; const NEW_REPORT_CREATED_DATE = subSeconds(new Date(), 5); const NEW_REPORT_FIST_MESSAGE_CREATED_DATE = addSeconds(NEW_REPORT_CREATED_DATE, 1); - const createdReportActionID = rand64(); + const createdReportActionIDLocal = rand64(); const commentReportActionID = rand64(); PusherHelper.emitOnyxUpdate([ { @@ -316,11 +317,11 @@ describe('Unread Indicators', () => { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${NEW_REPORT_ID}`, value: { - [createdReportActionID]: { + [createdReportActionIDLocal]: { actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, automatic: false, created: format(NEW_REPORT_CREATED_DATE, CONST.DATE.FNS_DB_FORMAT_STRING), - reportActionID: createdReportActionID, + reportActionID: createdReportActionIDLocal, }, [commentReportActionID]: { actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, @@ -386,7 +387,7 @@ describe('Unread Indicators', () => { .then(() => { // It's difficult to trigger marking a report comment as unread since we would have to mock the long press event and then // another press on the context menu item so we will do it via the action directly and then test if the UI has updated properly - markCommentAsUnread(REPORT_ID, reportAction3CreatedDate); + markCommentAsUnread(REPORT_ID, createdReportAction); return waitForBatchedUpdates(); }) .then(() => { @@ -484,7 +485,7 @@ describe('Unread Indicators', () => { expect(unreadIndicator).toHaveLength(0); // Mark a previous comment as unread and verify the unread action indicator returns - markCommentAsUnread(REPORT_ID, reportAction9CreatedDate); + markCommentAsUnread(REPORT_ID, createdReportAction); return waitForBatchedUpdates(); }) .then(() => { @@ -566,7 +567,7 @@ describe('Unread Indicators', () => { const firstNewReportAction = reportActions ? lastItem(reportActions) : undefined; if (firstNewReportAction) { - markCommentAsUnread(REPORT_ID, firstNewReportAction?.created); + markCommentAsUnread(REPORT_ID, firstNewReportAction); await waitForBatchedUpdates();