From 41e3e8d4687b9e2f1518e86e6cb81ee3b6f46df9 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 9 May 2025 13:48:39 +0800 Subject: [PATCH 01/20] add mark all reports as read shortcut --- src/CONST.ts | 9 +++++ src/languages/en.ts | 1 + src/languages/es.ts | 1 + .../KeyboardShortcut/getKeyEventModifiers.ts | 4 +++ src/libs/KeyboardShortcut/index.ts | 2 +- .../Navigation/AppNavigator/AuthScreens.tsx | 11 ++++++ src/libs/markAllReportsAsRead.ts | 34 +++++++++++++++++++ 7 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/libs/markAllReportsAsRead.ts diff --git a/src/CONST.ts b/src/CONST.ts index 30ba4d4e98b9..073b432e1bfa 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -37,6 +37,7 @@ const EXPENSIFY_POLICY_DOMAIN_EXTENSION = '.exfy'; const keyModifierControl = KeyCommand?.constants?.keyModifierControl ?? 'keyModifierControl'; const keyModifierCommand = KeyCommand?.constants?.keyModifierCommand ?? 'keyModifierCommand'; +const keyModifierShift = KeyCommand?.constants?.keyModifierShift ?? 'keyModifierShift'; const keyModifierShiftControl = KeyCommand?.constants?.keyModifierShiftControl ?? 'keyModifierShiftControl'; const keyModifierShiftCommand = KeyCommand?.constants?.keyModifierShiftCommand ?? 'keyModifierShiftCommand'; const keyInputEscape = KeyCommand?.constants?.keyInputEscape ?? 'keyInputEscape'; @@ -869,6 +870,14 @@ const CONST = { }, }, KEYBOARD_SHORTCUTS: { + MARK_ALL_REPORTS_AS_READ: { + descriptionKey: 'markAllReportsAsRead', + shortcutKey: 'Escape', + modifiers: ['SHIFT'], + trigger: { + DEFAULT: {input: keyInputEscape, modifierFlags: keyModifierShift}, + }, + }, SEARCH: { descriptionKey: 'search', shortcutKey: 'K', diff --git a/src/languages/en.ts b/src/languages/en.ts index b5adf3db2d2e..c7250d0c17de 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5192,6 +5192,7 @@ const translations = { subtitle: 'Save time with these handy keyboard shortcuts:', shortcuts: { openShortcutDialog: 'Opens the keyboard shortcuts dialog', + markAllReportsAsRead: 'Mark all reports as read', escape: 'Escape dialogs', search: 'Open search dialog', newChat: 'New chat screen', diff --git a/src/languages/es.ts b/src/languages/es.ts index dc4cdc9fa59a..ef7bc2263034 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5248,6 +5248,7 @@ const translations = { subtitle: 'Ahorra tiempo con estos atajos de teclado:', shortcuts: { openShortcutDialog: 'Abre el cuadro de diálogo de métodos abreviados de teclado', + markAllReportsAsRead: 'Marcar todos los informes como leídos', escape: 'Diálogos de escape', search: 'Abrir diálogo de búsqueda', newChat: 'Nueva pantalla de chat', diff --git a/src/libs/KeyboardShortcut/getKeyEventModifiers.ts b/src/libs/KeyboardShortcut/getKeyEventModifiers.ts index d6fbf80bc5bf..29d857293dd3 100644 --- a/src/libs/KeyboardShortcut/getKeyEventModifiers.ts +++ b/src/libs/KeyboardShortcut/getKeyEventModifiers.ts @@ -3,6 +3,7 @@ import type {KeyCommandEvent} from './bindHandlerToKeydownEvent/types'; const keyModifierControl = KeyCommand?.constants.keyModifierControl ?? 'keyModifierControl'; const keyModifierCommand = KeyCommand?.constants.keyModifierCommand ?? 'keyModifierCommand'; +const keyModifierShift = KeyCommand?.constants.keyModifierShift ?? 'keyModifierShift'; const keyModifierShiftControl = KeyCommand?.constants.keyModifierShiftControl ?? 'keyModifierShiftControl'; const keyModifierShiftCommand = KeyCommand?.constants.keyModifierShiftCommand ?? 'keyModifierShiftCommand'; @@ -22,6 +23,9 @@ function getKeyEventModifiers(event: KeyCommandEvent): string[] { if (event.modifierFlags === keyModifierShiftCommand) { return ['META', 'Shift']; } + if (event.modifierFlags === keyModifierShift) { + return ['Shift']; + } return []; } diff --git a/src/libs/KeyboardShortcut/index.ts b/src/libs/KeyboardShortcut/index.ts index a6f62967e9ee..f2d806fd0fda 100644 --- a/src/libs/KeyboardShortcut/index.ts +++ b/src/libs/KeyboardShortcut/index.ts @@ -20,7 +20,7 @@ type EventHandler = { // Handlers for the various keyboard listeners we set up const eventHandlers: Record = {}; -type ShortcutModifiers = readonly ['CTRL'] | readonly ['CTRL', 'SHIFT'] | readonly []; +type ShortcutModifiers = readonly ['CTRL'] | readonly ['SHIFT'] | readonly ['CTRL', 'SHIFT'] | readonly []; type Shortcut = { displayName: string; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index e889945648db..8644d05738e9 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -74,6 +74,7 @@ import TestToolsModalNavigator from './Navigators/TestToolsModalNavigator'; import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator'; import TestDriveDemoNavigator from './TestDriveDemoNavigator'; import useRootNavigatorScreenOptions from './useRootNavigatorScreenOptions'; +import markAllReportsAsRead from '@libs/markAllReportsAsRead'; type AuthScreensProps = { /** Session of currently logged in user */ @@ -277,6 +278,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie const shortcutsOverviewShortcutConfig = CONST.KEYBOARD_SHORTCUTS.SHORTCUTS; const searchShortcutConfig = CONST.KEYBOARD_SHORTCUTS.SEARCH; const chatShortcutConfig = CONST.KEYBOARD_SHORTCUTS.NEW_CHAT; + const markAllReportsAsReadShortcutConfig = CONST.KEYBOARD_SHORTCUTS.MARK_ALL_REPORTS_AS_READ; const isLoggingInAsNewUser = !!session?.email && SessionUtils.isLoggingInAsNewUser(currentUrl, session.email); // Sign out the current user if we're transitioning with a different user const isTransitioning = currentUrl.includes(ROUTES.TRANSITION_BETWEEN_APPS); @@ -413,12 +415,21 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie true, ); + const unsubscribeMarkAllReportsAsReadShortcut = KeyboardShortcut.subscribe( + markAllReportsAsReadShortcutConfig.shortcutKey, + markAllReportsAsRead, + markAllReportsAsReadShortcutConfig.descriptionKey, + markAllReportsAsReadShortcutConfig.modifiers, + true, + ); + return () => { unsubscribeEscapeKey(); unsubscribeOnyxModal(); unsubscribeShortcutsOverviewShortcut(); unsubscribeSearchShortcut(); unsubscribeChatShortcut(); + unsubscribeMarkAllReportsAsReadShortcut(); Session.cleanupSession(); }; diff --git a/src/libs/markAllReportsAsRead.ts b/src/libs/markAllReportsAsRead.ts new file mode 100644 index 000000000000..fdb9eabedcc7 --- /dev/null +++ b/src/libs/markAllReportsAsRead.ts @@ -0,0 +1,34 @@ +import type {OnyxCollection} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report, ReportActions} from '@src/types/onyx'; +import {readNewestAction} from './actions/Report'; +import {getOneTransactionThreadReportID} from './ReportActionsUtils'; +import {isUnread} from './ReportUtils'; + +let allReports: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + +let allReportActions: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (value) => (allReportActions = value), +}); + +export default function markAllReportsAsRead() { + Object.keys(allReports ?? {}).forEach((key: string) => { + const report = allReports?.[key]; + const oneTransactionThreadReportID = getOneTransactionThreadReportID(report?.reportID, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`]); + const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; + if (!isUnread(report, oneTransactionThreadReport)) { + return; + } + + readNewestAction(report?.reportID); + }); +} From 9e6bb58062277cd18d890931af680b50e17e3c84 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 9 May 2025 14:30:56 +0800 Subject: [PATCH 02/20] add test --- tests/unit/markAllReportsAsReadTest.ts | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/unit/markAllReportsAsReadTest.ts diff --git a/tests/unit/markAllReportsAsReadTest.ts b/tests/unit/markAllReportsAsReadTest.ts new file mode 100644 index 000000000000..b3c952a62dee --- /dev/null +++ b/tests/unit/markAllReportsAsReadTest.ts @@ -0,0 +1,43 @@ +import markAllReportsAsRead from "@libs/markAllReportsAsRead"; +import ONYXKEYS from "@src/ONYXKEYS"; +import type { Report } from "@src/types/onyx"; +import {readNewestAction} from "@libs/actions/Report"; +import Onyx from "react-native-onyx"; +import createCollection from "../utils/collections/createCollection"; +import createRandomReport from "../utils/collections/reports"; +import DateUtils from "@libs/DateUtils"; + +jest.mock('@src/libs/actions/Report', () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const originalModule = jest.requireActual('@src/libs/actions/Report'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return { + ...originalModule, + readNewestAction: jest.fn(), + }; +}); + +describe('mkarAllReportsAsRead', () => { + beforeAll(() => { + Onyx.init({ + keys: ONYXKEYS, + }); + }); + it('should mark all unread report', async () => { + // Given a collection of 10 unread and read reports, where even-index report is unread + const reportCollections: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report> = createCollection((item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, (index) => { + if (index % 2 === 0) { + const currentTime = DateUtils.getDBTime(); + return {...createRandomReport(index), lastMessageText: 'test', lastReadTime: currentTime, lastVisibleActionCreated: DateUtils.addMillisecondsFromDateTime(currentTime, 10)} + } + return createRandomReport(index) + }, 10); + await Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, reportCollections); + + // When mark all reports as read + markAllReportsAsRead(); + + // Then readNewestAction should be called exactly 5 times + expect(readNewestAction).toHaveBeenCalledTimes(5); + }); +}); \ No newline at end of file From 6e170c945b01044d370b569a0e6688fd7dd9feeb Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 9 May 2025 14:32:06 +0800 Subject: [PATCH 03/20] prettier --- .../Navigation/AppNavigator/AuthScreens.tsx | 2 +- tests/unit/markAllReportsAsReadTest.ts | 41 +++++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 8644d05738e9..ebde23c83fbd 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -24,6 +24,7 @@ import {READ_COMMANDS} from '@libs/API/types'; import HttpUtils from '@libs/HttpUtils'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; +import markAllReportsAsRead from '@libs/markAllReportsAsRead'; import NavBarManager from '@libs/NavBarManager'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import Navigation from '@libs/Navigation/Navigation'; @@ -74,7 +75,6 @@ import TestToolsModalNavigator from './Navigators/TestToolsModalNavigator'; import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator'; import TestDriveDemoNavigator from './TestDriveDemoNavigator'; import useRootNavigatorScreenOptions from './useRootNavigatorScreenOptions'; -import markAllReportsAsRead from '@libs/markAllReportsAsRead'; type AuthScreensProps = { /** Session of currently logged in user */ diff --git a/tests/unit/markAllReportsAsReadTest.ts b/tests/unit/markAllReportsAsReadTest.ts index b3c952a62dee..f4a1eee56d73 100644 --- a/tests/unit/markAllReportsAsReadTest.ts +++ b/tests/unit/markAllReportsAsReadTest.ts @@ -1,11 +1,11 @@ -import markAllReportsAsRead from "@libs/markAllReportsAsRead"; -import ONYXKEYS from "@src/ONYXKEYS"; -import type { Report } from "@src/types/onyx"; -import {readNewestAction} from "@libs/actions/Report"; -import Onyx from "react-native-onyx"; -import createCollection from "../utils/collections/createCollection"; -import createRandomReport from "../utils/collections/reports"; -import DateUtils from "@libs/DateUtils"; +import Onyx from 'react-native-onyx'; +import {readNewestAction} from '@libs/actions/Report'; +import DateUtils from '@libs/DateUtils'; +import markAllReportsAsRead from '@libs/markAllReportsAsRead'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; +import createCollection from '../utils/collections/createCollection'; +import createRandomReport from '../utils/collections/reports'; jest.mock('@src/libs/actions/Report', () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment @@ -25,13 +25,22 @@ describe('mkarAllReportsAsRead', () => { }); it('should mark all unread report', async () => { // Given a collection of 10 unread and read reports, where even-index report is unread - const reportCollections: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report> = createCollection((item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, (index) => { - if (index % 2 === 0) { - const currentTime = DateUtils.getDBTime(); - return {...createRandomReport(index), lastMessageText: 'test', lastReadTime: currentTime, lastVisibleActionCreated: DateUtils.addMillisecondsFromDateTime(currentTime, 10)} - } - return createRandomReport(index) - }, 10); + const reportCollections: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report> = createCollection( + (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, + (index) => { + if (index % 2 === 0) { + const currentTime = DateUtils.getDBTime(); + return { + ...createRandomReport(index), + lastMessageText: 'test', + lastReadTime: currentTime, + lastVisibleActionCreated: DateUtils.addMillisecondsFromDateTime(currentTime, 10), + }; + } + return createRandomReport(index); + }, + 10, + ); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, reportCollections); // When mark all reports as read @@ -40,4 +49,4 @@ describe('mkarAllReportsAsRead', () => { // Then readNewestAction should be called exactly 5 times expect(readNewestAction).toHaveBeenCalledTimes(5); }); -}); \ No newline at end of file +}); From 49bee3958a67bb3e93ece040b970e756205cc707 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 17:21:50 +0800 Subject: [PATCH 04/20] use the mark all messages as read api --- .../parameters/MarkAllMessagesAsReadParams.ts | 5 ++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + .../Navigation/AppNavigator/AuthScreens.tsx | 3 +- src/libs/actions/Report.ts | 36 +++++++++++++ src/libs/markAllReportsAsRead.ts | 34 ------------ tests/actions/ReportTest.ts | 45 ++++++++++++++++ tests/unit/markAllReportsAsReadTest.ts | 52 ------------------- 8 files changed, 90 insertions(+), 88 deletions(-) create mode 100644 src/libs/API/parameters/MarkAllMessagesAsReadParams.ts delete mode 100644 src/libs/markAllReportsAsRead.ts delete mode 100644 tests/unit/markAllReportsAsReadTest.ts diff --git a/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts b/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts new file mode 100644 index 000000000000..5347430778e4 --- /dev/null +++ b/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts @@ -0,0 +1,5 @@ +type MarkAllMessagesAsReadParams = { + reportIDs: string[]; +}; + +export default MarkAllMessagesAsReadParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 9d993d95f7a9..9cfdc1a552d8 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -99,6 +99,7 @@ export type {default as VerifyIdentityForBankAccountParams} from './VerifyIdenti export type {default as AnswerQuestionsForWalletParams} from './AnswerQuestionsForWalletParams'; export type {default as AddCommentOrAttachmentParams} from './AddCommentOrAttachmentParams'; export type {default as ReadNewestActionParams} from './ReadNewestActionParams'; +export type {default as MarkAllMessagesAsReadParams} from './MarkAllMessagesAsReadParams'; export type {default as MarkAsUnreadParams} from './MarkAsUnreadParams'; export type {default as TogglePinnedChatParams} from './TogglePinnedChatParams'; export type {default as DeleteCommentParams} from './DeleteCommentParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index d10e3b831712..31eb9fd45750 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -106,6 +106,7 @@ const WRITE_COMMANDS = { RESET_BANK_ACCOUNT_SETUP: 'ResetBankAccountSetup', RESEND_VALIDATE_CODE: 'ResendValidateCode', READ_NEWEST_ACTION: 'ReadNewestAction', + MARK_ALL_MESSAGES_AS_READ: 'MarkAllMessagesAsRead', MARK_AS_UNREAD: 'MarkAsUnread', TOGGLE_PINNED_CHAT: 'TogglePinnedChat', DELETE_COMMENT: 'DeleteComment', @@ -574,6 +575,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.RESET_BANK_ACCOUNT_SETUP]: Parameters.ResetBankAccountSetupParams; [WRITE_COMMANDS.RESEND_VALIDATE_CODE]: null; [WRITE_COMMANDS.READ_NEWEST_ACTION]: Parameters.ReadNewestActionParams; + [WRITE_COMMANDS.MARK_ALL_MESSAGES_AS_READ]: Parameters.MarkAllMessagesAsReadParams; [WRITE_COMMANDS.MARK_AS_UNREAD]: Parameters.MarkAsUnreadParams; [WRITE_COMMANDS.TOGGLE_PINNED_CHAT]: Parameters.TogglePinnedChatParams; [WRITE_COMMANDS.DELETE_COMMENT]: Parameters.DeleteCommentParams; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 28c2d275afb6..e2ade3badf75 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -24,7 +24,6 @@ import getPlatform from '@libs/getPlatform'; import HttpUtils from '@libs/HttpUtils'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import Log from '@libs/Log'; -import markAllReportsAsRead from '@libs/markAllReportsAsRead'; import NavBarManager from '@libs/NavBarManager'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import Navigation from '@libs/Navigation/Navigation'; @@ -435,7 +434,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie const unsubscribeMarkAllReportsAsReadShortcut = KeyboardShortcut.subscribe( markAllReportsAsReadShortcutConfig.shortcutKey, - markAllReportsAsRead, + Report.markAllMessagesAsRead, markAllReportsAsReadShortcutConfig.descriptionKey, markAllReportsAsReadShortcutConfig.modifiers, true, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 2de7dda4a5b0..49de60a4ffa8 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -27,6 +27,7 @@ import type { InviteToGroupChatParams, InviteToRoomParams, LeaveRoomParams, + MarkAllMessagesAsReadParams, MarkAsExportedParams, MarkAsUnreadParams, MoveIOUReportToExistingPolicyParams, @@ -144,6 +145,7 @@ import { isIOUReportUsingReport, isMoneyRequestReport, isSelfDM, + isUnread, isValidReportIDFromPath, prepareOnboardingOnyxData, } from '@libs/ReportUtils'; @@ -1607,6 +1609,39 @@ function readNewestAction(reportID: string | undefined, shouldResetUnreadMarker } } +function markAllMessagesAsRead() { + const lastReadTime = DateUtils.getDBTimeWithSkew(); + + const optimisticData: OnyxUpdate[] = []; + let reportIDs: string[] = []; + Object.keys(allReports ?? {}).forEach((key: string) => { + const report = allReports?.[key]; + const oneTransactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID( + report?.reportID, + allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`], + ); + const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; + if (!report || !isUnread(report, oneTransactionThreadReport)) { + return; + } + + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, + value: { + lastReadTime, + }, + }); + reportIDs.push(report.reportID); + }); + + const parameters: MarkAllMessagesAsReadParams = { + reportIDs, + }; + + API.write(WRITE_COMMANDS.MARK_ALL_MESSAGES_AS_READ, parameters, {optimisticData}); +} + /** * Sets the last read time on a report */ @@ -5574,6 +5609,7 @@ export { openReportFromDeepLink, openRoomMembersPage, readNewestAction, + markAllMessagesAsRead, removeFromGroupChat, removeFromRoom, resolveActionableMentionWhisper, diff --git a/src/libs/markAllReportsAsRead.ts b/src/libs/markAllReportsAsRead.ts deleted file mode 100644 index fdb9eabedcc7..000000000000 --- a/src/libs/markAllReportsAsRead.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type {OnyxCollection} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, ReportActions} from '@src/types/onyx'; -import {readNewestAction} from './actions/Report'; -import {getOneTransactionThreadReportID} from './ReportActionsUtils'; -import {isUnread} from './ReportUtils'; - -let allReports: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - -let allReportActions: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - waitForCollectionCallback: true, - callback: (value) => (allReportActions = value), -}); - -export default function markAllReportsAsRead() { - Object.keys(allReports ?? {}).forEach((key: string) => { - const report = allReports?.[key]; - const oneTransactionThreadReportID = getOneTransactionThreadReportID(report?.reportID, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`]); - const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - if (!isUnread(report, oneTransactionThreadReport)) { - return; - } - - readNewestAction(report?.reportID); - }); -} diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index b97e6693e246..903698bd82f4 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -19,6 +19,7 @@ import * as SequentialQueue from '@src/libs/Network/SequentialQueue'; import * as ReportUtils from '@src/libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; +import createCollection from '../utils/collections/createCollection'; import createRandomReportAction from '../utils/collections/reportActions'; import createRandomReport from '../utils/collections/reports'; import getIsUsingFakeTimers from '../utils/getIsUsingFakeTimers'; @@ -1617,4 +1618,48 @@ describe('actions/Report', () => { }); }); }); + + describe('markAllMessagesAsRead', () => { + it('should mark all unread report', async () => { + // Given a collection of 10 unread and read reports, where even-index report is unread + const currentTime = DateUtils.getDBTime(); + const reportCollections: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, OnyxTypes.Report> = createCollection( + (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, + (index) => { + if (index % 2 === 0) { + return { + ...createRandomReport(index), + lastMessageText: 'test', + lastReadTime: DateUtils.subtractMillisecondsFromDateTime(currentTime, 1), + lastVisibleActionCreated: currentTime, + }; + } + return createRandomReport(index); + }, + 10, + ); + await Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, reportCollections); + + // When mark all reports as read + Report.markAllMessagesAsRead(); + + await waitForBatchedUpdates(); + + // Then all report should be read + const isUnreadCollection = await Promise.all( + Object.values(reportCollections).map((report) => { + return new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, + callback: (report) => { + Onyx.disconnect(connection); + resolve(ReportUtils.isUnread(report, undefined)); + }, + }); + }); + }), + ); + expect(isUnreadCollection.some(Boolean)).toBe(false); + }); + }); }); diff --git a/tests/unit/markAllReportsAsReadTest.ts b/tests/unit/markAllReportsAsReadTest.ts deleted file mode 100644 index f4a1eee56d73..000000000000 --- a/tests/unit/markAllReportsAsReadTest.ts +++ /dev/null @@ -1,52 +0,0 @@ -import Onyx from 'react-native-onyx'; -import {readNewestAction} from '@libs/actions/Report'; -import DateUtils from '@libs/DateUtils'; -import markAllReportsAsRead from '@libs/markAllReportsAsRead'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; -import createCollection from '../utils/collections/createCollection'; -import createRandomReport from '../utils/collections/reports'; - -jest.mock('@src/libs/actions/Report', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const originalModule = jest.requireActual('@src/libs/actions/Report'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return { - ...originalModule, - readNewestAction: jest.fn(), - }; -}); - -describe('mkarAllReportsAsRead', () => { - beforeAll(() => { - Onyx.init({ - keys: ONYXKEYS, - }); - }); - it('should mark all unread report', async () => { - // Given a collection of 10 unread and read reports, where even-index report is unread - const reportCollections: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report> = createCollection( - (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, - (index) => { - if (index % 2 === 0) { - const currentTime = DateUtils.getDBTime(); - return { - ...createRandomReport(index), - lastMessageText: 'test', - lastReadTime: currentTime, - lastVisibleActionCreated: DateUtils.addMillisecondsFromDateTime(currentTime, 10), - }; - } - return createRandomReport(index); - }, - 10, - ); - await Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, reportCollections); - - // When mark all reports as read - markAllReportsAsRead(); - - // Then readNewestAction should be called exactly 5 times - expect(readNewestAction).toHaveBeenCalledTimes(5); - }); -}); From baf31a422fea3d4869b67ea26bdb847fc83fc9ad Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 17:24:52 +0800 Subject: [PATCH 05/20] rename --- src/CONST.ts | 4 ++-- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 7bf3ee7f3486..6577a53fb544 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -891,8 +891,8 @@ const CONST = { }, }, KEYBOARD_SHORTCUTS: { - MARK_ALL_REPORTS_AS_READ: { - descriptionKey: 'markAllReportsAsRead', + MARK_ALL_MESSAGES_AS_READ: { + descriptionKey: 'markAllMessagesAsRead', shortcutKey: 'Escape', modifiers: ['SHIFT'], trigger: { diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index e2ade3badf75..f1b87d0d3951 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -295,7 +295,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie const shortcutsOverviewShortcutConfig = CONST.KEYBOARD_SHORTCUTS.SHORTCUTS; const searchShortcutConfig = CONST.KEYBOARD_SHORTCUTS.SEARCH; const chatShortcutConfig = CONST.KEYBOARD_SHORTCUTS.NEW_CHAT; - const markAllReportsAsReadShortcutConfig = CONST.KEYBOARD_SHORTCUTS.MARK_ALL_REPORTS_AS_READ; + const markAllMessagesAsReadShortcutConfig = CONST.KEYBOARD_SHORTCUTS.MARK_ALL_MESSAGES_AS_READ; const isLoggingInAsNewUser = !!session?.email && SessionUtils.isLoggingInAsNewUser(currentUrl, session.email); // Sign out the current user if we're transitioning with a different user const isTransitioning = currentUrl.includes(ROUTES.TRANSITION_BETWEEN_APPS); @@ -432,11 +432,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie true, ); - const unsubscribeMarkAllReportsAsReadShortcut = KeyboardShortcut.subscribe( - markAllReportsAsReadShortcutConfig.shortcutKey, + const unsubscribeMarkAllMessagesAsReadShortcut = KeyboardShortcut.subscribe( + markAllMessagesAsReadShortcutConfig.shortcutKey, Report.markAllMessagesAsRead, - markAllReportsAsReadShortcutConfig.descriptionKey, - markAllReportsAsReadShortcutConfig.modifiers, + markAllMessagesAsReadShortcutConfig.descriptionKey, + markAllMessagesAsReadShortcutConfig.modifiers, true, ); @@ -446,7 +446,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie unsubscribeShortcutsOverviewShortcut(); unsubscribeSearchShortcut(); unsubscribeChatShortcut(); - unsubscribeMarkAllReportsAsReadShortcut(); + unsubscribeMarkAllMessagesAsReadShortcut(); Session.cleanupSession(); }; From d8549641f4acd391cefdeb2bf635093162d0a0dd Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 17:26:33 +0800 Subject: [PATCH 06/20] lint --- src/libs/actions/Report.ts | 2 +- tests/actions/ReportTest.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 49de60a4ffa8..f043dd15f5f9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1613,7 +1613,7 @@ function markAllMessagesAsRead() { const lastReadTime = DateUtils.getDBTimeWithSkew(); const optimisticData: OnyxUpdate[] = []; - let reportIDs: string[] = []; + const reportIDs: string[] = []; Object.keys(allReports ?? {}).forEach((key: string) => { const report = allReports?.[key]; const oneTransactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID( diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 903698bd82f4..cd2fa29ca44e 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -1648,12 +1648,12 @@ describe('actions/Report', () => { // Then all report should be read const isUnreadCollection = await Promise.all( Object.values(reportCollections).map((report) => { - return new Promise((resolve) => { + return new Promise((resolve) => { const connection = Onyx.connect({ key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, - callback: (report) => { + callback: (reportVal) => { Onyx.disconnect(connection); - resolve(ReportUtils.isUnread(report, undefined)); + resolve(ReportUtils.isUnread(reportVal, undefined)); }, }); }); From e71a101c297972a933281fd06ac172aa487c085d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 17:29:35 +0800 Subject: [PATCH 07/20] update copy --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index f3a46ba365e6..80189b418ea9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5236,7 +5236,7 @@ const translations = { subtitle: 'Save time with these handy keyboard shortcuts:', shortcuts: { openShortcutDialog: 'Opens the keyboard shortcuts dialog', - markAllReportsAsRead: 'Mark all reports as read', + markAllReportsAsRead: 'Mark all messages as read', escape: 'Escape dialogs', search: 'Open search dialog', newChat: 'New chat screen', diff --git a/src/languages/es.ts b/src/languages/es.ts index bc89d195e749..f29ce5d8cfec 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5289,7 +5289,7 @@ const translations = { subtitle: 'Ahorra tiempo con estos atajos de teclado:', shortcuts: { openShortcutDialog: 'Abre el cuadro de diálogo de métodos abreviados de teclado', - markAllReportsAsRead: 'Marcar todos los informes como leídos', + markAllReportsAsRead: 'Marcar todos los mensajes como leídos', escape: 'Diálogos de escape', search: 'Abrir diálogo de búsqueda', newChat: 'Nueva pantalla de chat', From 8ed969e3cd57c0ded12590a7fc822811bb76c977 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:41:16 +0800 Subject: [PATCH 08/20] rename key --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 80189b418ea9..9feaa0426a44 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5236,7 +5236,7 @@ const translations = { subtitle: 'Save time with these handy keyboard shortcuts:', shortcuts: { openShortcutDialog: 'Opens the keyboard shortcuts dialog', - markAllReportsAsRead: 'Mark all messages as read', + markAllMessagesAsRead: 'Mark all messages as read', escape: 'Escape dialogs', search: 'Open search dialog', newChat: 'New chat screen', diff --git a/src/languages/es.ts b/src/languages/es.ts index f29ce5d8cfec..b669d9f26736 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5289,7 +5289,7 @@ const translations = { subtitle: 'Ahorra tiempo con estos atajos de teclado:', shortcuts: { openShortcutDialog: 'Abre el cuadro de diálogo de métodos abreviados de teclado', - markAllReportsAsRead: 'Marcar todos los mensajes como leídos', + markAllMessagesAsRead: 'Marcar todos los mensajes como leídos', escape: 'Diálogos de escape', search: 'Abrir diálogo de búsqueda', newChat: 'Nueva pantalla de chat', From 488aaa8be44bfed77e703c36f10123cc5129bbe0 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:42:52 +0800 Subject: [PATCH 09/20] simplify --- src/libs/actions/Report.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f043dd15f5f9..b7a6756f3c42 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1614,8 +1614,7 @@ function markAllMessagesAsRead() { const optimisticData: OnyxUpdate[] = []; const reportIDs: string[] = []; - Object.keys(allReports ?? {}).forEach((key: string) => { - const report = allReports?.[key]; + Object.values(allReports ?? {}).forEach((report) => { const oneTransactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID( report?.reportID, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`], From 7b5c718eb943dd8ca903cadf97464536dbb9f16c Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:44:28 +0800 Subject: [PATCH 10/20] return early --- src/libs/actions/Report.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b7a6756f3c42..ba745ade3145 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1615,12 +1615,16 @@ function markAllMessagesAsRead() { const optimisticData: OnyxUpdate[] = []; const reportIDs: string[] = []; Object.values(allReports ?? {}).forEach((report) => { + if (!report) { + return; + } + const oneTransactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID( - report?.reportID, - allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`], + report.reportID, + allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`], ); const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - if (!report || !isUnread(report, oneTransactionThreadReport)) { + if (!isUnread(report, oneTransactionThreadReport)) { return; } From a214e0a596c7849344164cde09c5812fdc00d394 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:45:02 +0800 Subject: [PATCH 11/20] do nothing if no unread report --- src/libs/actions/Report.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ba745ade3145..d58ed386bfc3 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1638,6 +1638,10 @@ function markAllMessagesAsRead() { reportIDs.push(report.reportID); }); + if (reportIDs.length === 0) { + return; + } + const parameters: MarkAllMessagesAsReadParams = { reportIDs, }; From fb9206a0ec8d1a722b7c9f85c87da7167bab2467 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:50:49 +0800 Subject: [PATCH 12/20] use merge collection --- src/libs/actions/Report.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index d58ed386bfc3..6ff95e9bb084 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1612,7 +1612,7 @@ function readNewestAction(reportID: string | undefined, shouldResetUnreadMarker function markAllMessagesAsRead() { const lastReadTime = DateUtils.getDBTimeWithSkew(); - const optimisticData: OnyxUpdate[] = []; + const optimisticUnreadReports: Record> = {}; const reportIDs: string[] = []; Object.values(allReports ?? {}).forEach((report) => { if (!report) { @@ -1628,13 +1628,7 @@ function markAllMessagesAsRead() { return; } - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, - value: { - lastReadTime, - }, - }); + optimisticUnreadReports[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`] = {lastReadTime}; reportIDs.push(report.reportID); }); @@ -1642,6 +1636,12 @@ function markAllMessagesAsRead() { return; } + const optimisticData = [{ + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: ONYXKEYS.COLLECTION.REPORT, + value: optimisticUnreadReports, + }]; + const parameters: MarkAllMessagesAsReadParams = { reportIDs, }; From ff07fce3d948a1bfc63e4d486834bf3fab6b4c34 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:53:12 +0800 Subject: [PATCH 13/20] add failure data --- src/libs/actions/Report.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 6ff95e9bb084..9c7223157a1a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1613,6 +1613,7 @@ function markAllMessagesAsRead() { const lastReadTime = DateUtils.getDBTimeWithSkew(); const optimisticUnreadReports: Record> = {}; + const failureUnreadReports: Record> = {}; const reportIDs: string[] = []; Object.values(allReports ?? {}).forEach((report) => { if (!report) { @@ -1628,7 +1629,9 @@ function markAllMessagesAsRead() { return; } - optimisticUnreadReports[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`] = {lastReadTime}; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`; + optimisticUnreadReports[reportKey] = {lastReadTime}; + failureUnreadReports[reportKey] = {lastReadTime: report.lastReadTime}; reportIDs.push(report.reportID); }); @@ -1642,11 +1645,17 @@ function markAllMessagesAsRead() { value: optimisticUnreadReports, }]; + const failureData = [{ + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: ONYXKEYS.COLLECTION.REPORT, + value: failureUnreadReports, + }]; + const parameters: MarkAllMessagesAsReadParams = { reportIDs, }; - API.write(WRITE_COMMANDS.MARK_ALL_MESSAGES_AS_READ, parameters, {optimisticData}); + API.write(WRITE_COMMANDS.MARK_ALL_MESSAGES_AS_READ, parameters, {optimisticData, failureData}); } /** From aa33bda0f8d329d0f9f4ad092ab70dae9b8de77d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 May 2025 22:54:29 +0800 Subject: [PATCH 14/20] prettier --- src/libs/actions/Report.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9c7223157a1a..2446282b4cc1 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1639,17 +1639,21 @@ function markAllMessagesAsRead() { return; } - const optimisticData = [{ - onyxMethod: Onyx.METHOD.MERGE_COLLECTION, - key: ONYXKEYS.COLLECTION.REPORT, - value: optimisticUnreadReports, - }]; + const optimisticData = [ + { + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: ONYXKEYS.COLLECTION.REPORT, + value: optimisticUnreadReports, + }, + ]; - const failureData = [{ - onyxMethod: Onyx.METHOD.MERGE_COLLECTION, - key: ONYXKEYS.COLLECTION.REPORT, - value: failureUnreadReports, - }]; + const failureData = [ + { + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: ONYXKEYS.COLLECTION.REPORT, + value: failureUnreadReports, + }, + ]; const parameters: MarkAllMessagesAsReadParams = { reportIDs, From 049ac68df279882343a2edf256ca3ceea0d6f663 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 29 May 2025 15:29:33 +0800 Subject: [PATCH 15/20] update param --- src/libs/API/parameters/MarkAllMessagesAsReadParams.ts | 2 +- src/libs/actions/Report.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts b/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts index 5347430778e4..b1f21c8c056b 100644 --- a/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts +++ b/src/libs/API/parameters/MarkAllMessagesAsReadParams.ts @@ -1,5 +1,5 @@ type MarkAllMessagesAsReadParams = { - reportIDs: string[]; + reportIDList: string[]; }; export default MarkAllMessagesAsReadParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 83b267d12236..16d17d69c437 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1614,7 +1614,7 @@ function markAllMessagesAsRead() { const optimisticUnreadReports: Record> = {}; const failureUnreadReports: Record> = {}; - const reportIDs: string[] = []; + const reportIDList: string[] = []; Object.values(allReports ?? {}).forEach((report) => { if (!report) { return; @@ -1632,10 +1632,10 @@ function markAllMessagesAsRead() { const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`; optimisticUnreadReports[reportKey] = {lastReadTime}; failureUnreadReports[reportKey] = {lastReadTime: report.lastReadTime}; - reportIDs.push(report.reportID); + reportIDList.push(report.reportID); }); - if (reportIDs.length === 0) { + if (reportIDList.length === 0) { return; } @@ -1656,7 +1656,7 @@ function markAllMessagesAsRead() { ]; const parameters: MarkAllMessagesAsReadParams = { - reportIDs, + reportIDList, }; API.write(WRITE_COMMANDS.MARK_ALL_MESSAGES_AS_READ, parameters, {optimisticData, failureData}); From 34291e00587df3f61a9ac7d95ab28677feb9505d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 May 2025 14:20:16 +0800 Subject: [PATCH 16/20] prevent AU to use mark all as read --- src/libs/actions/Report.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 16d17d69c437..fac892b1b5ae 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1610,6 +1610,10 @@ function readNewestAction(reportID: string | undefined, shouldResetUnreadMarker } function markAllMessagesAsRead() { + if (isAnonymousUser()) { + return; + } + const lastReadTime = DateUtils.getDBTimeWithSkew(); const optimisticUnreadReports: Record> = {}; From 1208ed726bca0e3756421250f76af1f8942ed526 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 May 2025 20:07:36 +0800 Subject: [PATCH 17/20] make sure the last read time is cleared when previous report last read time is empty and the request fails --- src/libs/actions/Report.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index fac892b1b5ae..4e2dcc3e3b86 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1616,8 +1616,11 @@ function markAllMessagesAsRead() { const lastReadTime = DateUtils.getDBTimeWithSkew(); - const optimisticUnreadReports: Record> = {}; - const failureUnreadReports: Record> = {}; + type PartialReport = { + lastReadTime: Report['lastReadTime'] | null, + }; + const optimisticUnreadReports: Record = {}; + const failureUnreadReports: Record = {}; const reportIDList: string[] = []; Object.values(allReports ?? {}).forEach((report) => { if (!report) { @@ -1635,7 +1638,7 @@ function markAllMessagesAsRead() { const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`; optimisticUnreadReports[reportKey] = {lastReadTime}; - failureUnreadReports[reportKey] = {lastReadTime: report.lastReadTime}; + failureUnreadReports[reportKey] = {lastReadTime: report.lastReadTime ?? null}; reportIDList.push(report.reportID); }); From a9e6f5761bd3d899339a5becba47314566ec365a Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 30 May 2025 20:10:32 +0800 Subject: [PATCH 18/20] prettier --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4e2dcc3e3b86..116c4db7e488 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1617,7 +1617,7 @@ function markAllMessagesAsRead() { const lastReadTime = DateUtils.getDBTimeWithSkew(); type PartialReport = { - lastReadTime: Report['lastReadTime'] | null, + lastReadTime: Report['lastReadTime'] | null; }; const optimisticUnreadReports: Record = {}; const failureUnreadReports: Record = {}; From fa3d50a09e96386f93a9db93531c25393881eae0 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 31 May 2025 00:48:46 +0800 Subject: [PATCH 19/20] update test name --- tests/actions/ReportTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 98a0f8031496..38a4293310e8 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -1620,7 +1620,7 @@ describe('actions/Report', () => { }); describe('markAllMessagesAsRead', () => { - it('should mark all unread report', async () => { + it('should mark all unread reports as read', async () => { // Given a collection of 10 unread and read reports, where even-index report is unread const currentTime = DateUtils.getDBTime(); const reportCollections: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, OnyxTypes.Report> = createCollection( From 30080b76170cb031086cfd4d25ff9d79f3fa0a97 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 31 May 2025 00:52:31 +0800 Subject: [PATCH 20/20] rename --- src/libs/actions/Report.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 116c4db7e488..3fbf774e94e3 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1614,13 +1614,13 @@ function markAllMessagesAsRead() { return; } - const lastReadTime = DateUtils.getDBTimeWithSkew(); + const newLastReadTime = DateUtils.getDBTimeWithSkew(); type PartialReport = { lastReadTime: Report['lastReadTime'] | null; }; - const optimisticUnreadReports: Record = {}; - const failureUnreadReports: Record = {}; + const optimisticReports: Record = {}; + const failureReports: Record = {}; const reportIDList: string[] = []; Object.values(allReports ?? {}).forEach((report) => { if (!report) { @@ -1637,8 +1637,8 @@ function markAllMessagesAsRead() { } const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`; - optimisticUnreadReports[reportKey] = {lastReadTime}; - failureUnreadReports[reportKey] = {lastReadTime: report.lastReadTime ?? null}; + optimisticReports[reportKey] = {lastReadTime: newLastReadTime}; + failureReports[reportKey] = {lastReadTime: report.lastReadTime ?? null}; reportIDList.push(report.reportID); }); @@ -1650,7 +1650,7 @@ function markAllMessagesAsRead() { { onyxMethod: Onyx.METHOD.MERGE_COLLECTION, key: ONYXKEYS.COLLECTION.REPORT, - value: optimisticUnreadReports, + value: optimisticReports, }, ]; @@ -1658,7 +1658,7 @@ function markAllMessagesAsRead() { { onyxMethod: Onyx.METHOD.MERGE_COLLECTION, key: ONYXKEYS.COLLECTION.REPORT, - value: failureUnreadReports, + value: failureReports, }, ];