From d66fa2e6d3bedc916cfa4e98641da0a10d124094 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Fri, 9 Dec 2022 09:05:39 -0500 Subject: [PATCH] Mark a thread as unread in the threads list due to activity. --- src/Unread.ts | 2 +- src/hooks/useUnreadNotifications.ts | 13 +++-- .../UnreadNotificationBadge-test.tsx | 55 ++++++++++++++++--- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/Unread.ts b/src/Unread.ts index e492c7890fce..925a5e4bdbc3 100644 --- a/src/Unread.ts +++ b/src/Unread.ts @@ -70,7 +70,7 @@ export function doesRoomHaveUnreadMessages(room: Room): boolean { return false; } -function doesRoomOrThreadHaveUnreadMessages(room: Room | Thread): boolean { +export function doesRoomOrThreadHaveUnreadMessages(room: Room | Thread): boolean { const myUserId = MatrixClientPeg.get().getUserId(); // as we don't send RRs for our own messages, make sure we special case that diff --git a/src/hooks/useUnreadNotifications.ts b/src/hooks/useUnreadNotifications.ts index 5510eae46a38..f956f286284c 100644 --- a/src/hooks/useUnreadNotifications.ts +++ b/src/hooks/useUnreadNotifications.ts @@ -15,12 +15,13 @@ limitations under the License. */ import { NotificationCount, NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room"; +import { Thread } from "matrix-js-sdk/src/models/thread"; import { useCallback, useEffect, useState } from "react"; import { getUnsentMessages } from "../components/structures/RoomStatusBar"; import { getRoomNotifsState, getUnreadNotificationCount, RoomNotifState } from "../RoomNotifs"; import { NotificationColor } from "../stores/notifications/NotificationColor"; -import { doesRoomHaveUnreadMessages } from "../Unread"; +import { doesRoomOrThreadHaveUnreadMessages } from "../Unread"; import { EffectiveMembership, getEffectiveMembership } from "../utils/membership"; import { useEventEmitter } from "./useEventEmitter"; @@ -70,12 +71,14 @@ export const useUnreadNotifications = (room: Room, threadId?: string): { setColor(NotificationColor.Red); } else if (greyNotifs > 0) { setColor(NotificationColor.Grey); - } else if (!threadId) { - // TODO: No support for `Bold` on threads at the moment - + } else { // We don't have any notified messages, but we might have unread messages. Let's // find out. - const hasUnread = doesRoomHaveUnreadMessages(room); + let roomOrThread: Room | Thread = room; + if (threadId) { + roomOrThread = room.getThread(threadId)!; + } + const hasUnread = doesRoomOrThreadHaveUnreadMessages(roomOrThread); setColor(hasUnread ? NotificationColor.Bold : NotificationColor.None); } } diff --git a/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx index 20289dc6b910..c157be3365e5 100644 --- a/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx +++ b/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx @@ -17,15 +17,16 @@ limitations under the License. import React from "react"; import "jest-mock"; import { screen, act, render } from "@testing-library/react"; -import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client"; +import { MsgType } from "matrix-js-sdk/src/matrix"; +import { PendingEventOrdering } from "matrix-js-sdk/src/client"; import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room"; -import { mocked } from "jest-mock"; import { EventStatus } from "matrix-js-sdk/src/models/event-status"; import { UnreadNotificationBadge, } from "../../../../../src/components/views/rooms/NotificationBadge/UnreadNotificationBadge"; -import { mkMessage, stubClient } from "../../../../test-utils/test-utils"; +import { mkEvent, mkMessage, stubClient } from "../../../../test-utils/test-utils"; +import { mkThread } from "../../../../test-utils/threads"; import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; import * as RoomNotifs from "../../../../../src/RoomNotifs"; @@ -36,28 +37,35 @@ jest.mock('../../../../../src/RoomNotifs', () => ({ })); const ROOM_ID = "!roomId:example.org"; -let THREAD_ID; +let THREAD_ID: string; describe("UnreadNotificationBadge", () => { - let mockClient: MatrixClient; + stubClient(); + const client = MatrixClientPeg.get(); let room: Room; function getComponent(threadId?: string) { return ; } + beforeAll(() => { + client.supportsExperimentalThreads = () => true; + }); + beforeEach(() => { jest.clearAllMocks(); - stubClient(); - mockClient = mocked(MatrixClientPeg.get()); - - room = new Room(ROOM_ID, mockClient, mockClient.getUserId() ?? "", { + room = new Room(ROOM_ID, client, client.getUserId()!, { pendingEventOrdering: PendingEventOrdering.Detached, }); room.setUnreadNotificationCount(NotificationCountType.Total, 1); room.setUnreadNotificationCount(NotificationCountType.Highlight, 0); + const { rootEvent } = mkThread( + { room, client, authorId: client.getUserId()!, participantUserIds: [client.getUserId()!] }, + ); + THREAD_ID = rootEvent.getId()!; + room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 1); room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 0); @@ -129,4 +137,33 @@ describe("UnreadNotificationBadge", () => { const { container } = render(getComponent()); expect(container.querySelector(".mx_NotificationBadge")).toBeNull(); }); + + it.only("activity renders unread notification badge", () => { + act(() => { + room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 0); + room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 0); + + // Add another event on the thread which is not sent by us. + const event = mkEvent({ + event: true, + type: "m.room.message", + user: "@alice:server.org", + room: room.roomId, + content: { + "msgtype": MsgType.Text, + "body": 'Hello from Bob', + "m.relates_to": { + event_id: THREAD_ID, + rel_type: "m.thread", + }, + }, + }); + room.addLiveEvents([event]); + }); + + const { container } = render(getComponent(THREAD_ID)); + expect(container.querySelector(".mx_NotificationBadge_dot")).toBeTruthy(); + expect(container.querySelector(".mx_NotificationBadge_visible")).toBeTruthy(); + expect(container.querySelector(".mx_NotificationBadge_highlighted")).toBeFalsy(); + }); });