New room list: add notification options menu (#29639)

* feat: add `utils.hasAccessToNotificationMenu`

* feat(room list item view model): use `hasAccessToNotificationMenu` to compute `showHoverMenu`

* feat(room list item menu view model): add notification options menu attributes

* feat(room list item menu view): add notification options

* test: add tests for `utils.hasAccessToNotificationMenu`

* test(room list item view model): add test for `showHoverMenu`

* test(room list item menu view model): add tests for new attributes

* test(room list item menu view): add tests for notification options menu

* chore: update i18n

* test(e2e): update screenshots

* test(e2e): add tests for notification options menu
This commit is contained in:
Florian Duros
2025-04-02 12:30:27 +00:00
committed by GitHub
parent 31a59a5fa3
commit 817d7b78b8
16 changed files with 448 additions and 23 deletions
@@ -11,7 +11,10 @@ import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
import { mkStubRoom, stubClient, withClientContextRenderOptions } from "../../../../test-utils";
import { useRoomListItemMenuViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListItemMenuViewModel";
import { hasAccessToOptionsMenu } from "../../../../../src/components/viewmodels/roomlist/utils";
import {
hasAccessToNotificationMenu,
hasAccessToOptionsMenu,
} from "../../../../../src/components/viewmodels/roomlist/utils";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
import { useUnreadNotifications } from "../../../../../src/hooks/useUnreadNotifications";
@@ -19,15 +22,22 @@ import { NotificationLevel } from "../../../../../src/stores/notifications/Notif
import { clearRoomNotification, setMarkedUnreadState } from "../../../../../src/utils/notifications";
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
import dispatcher from "../../../../../src/dispatcher/dispatcher";
import { useNotificationState } from "../../../../../src/hooks/useRoomNotificationState";
import { RoomNotifState } from "../../../../../src/RoomNotifs";
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
hasAccessToOptionsMenu: jest.fn().mockReturnValue(false),
hasAccessToNotificationMenu: jest.fn().mockReturnValue(false),
}));
jest.mock("../../../../../src/hooks/useUnreadNotifications", () => ({
useUnreadNotifications: jest.fn(),
}));
jest.mock("../../../../../src/hooks/useRoomNotificationState", () => ({
useNotificationState: jest.fn(),
}));
jest.mock("../../../../../src/utils/notifications", () => ({
clearRoomNotification: jest.fn(),
setMarkedUnreadState: jest.fn(),
@@ -49,6 +59,7 @@ describe("RoomListItemMenuViewModel", () => {
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
mocked(useUnreadNotifications).mockReturnValue({ symbol: null, count: 0, level: NotificationLevel.None });
mocked(useNotificationState).mockReturnValue([RoomNotifState.AllMessages, jest.fn()]);
jest.spyOn(dispatcher, "dispatch");
});
@@ -76,6 +87,12 @@ describe("RoomListItemMenuViewModel", () => {
expect(result.current.showMoreOptionsMenu).toBe(true);
});
it("should has showNotificationMenu to be true", () => {
mocked(hasAccessToNotificationMenu).mockReturnValue(true);
const { result } = render();
expect(result.current.showNotificationMenu).toBe(true);
});
it("should be able to invite", () => {
jest.spyOn(room, "canInvite").mockReturnValue(true);
const { result } = render();
@@ -106,6 +123,29 @@ describe("RoomListItemMenuViewModel", () => {
expect(result.current.canMarkAsUnread).toBe(false);
});
it("should has isNotificationAllMessage to be true", () => {
const { result } = render();
expect(result.current.isNotificationAllMessage).toBe(true);
});
it("should has isNotificationAllMessageLoud to be true", () => {
mocked(useNotificationState).mockReturnValue([RoomNotifState.AllMessagesLoud, jest.fn()]);
const { result } = render();
expect(result.current.isNotificationAllMessageLoud).toBe(true);
});
it("should has isNotificationMentionOnly to be true", () => {
mocked(useNotificationState).mockReturnValue([RoomNotifState.MentionsOnly, jest.fn()]);
const { result } = render();
expect(result.current.isNotificationMentionOnly).toBe(true);
});
it("should has isNotificationMute to be true", () => {
mocked(useNotificationState).mockReturnValue([RoomNotifState.Mute, jest.fn()]);
const { result } = render();
expect(result.current.isNotificationMute).toBe(true);
});
// Actions
it("should mark as read", () => {
@@ -170,4 +210,12 @@ describe("RoomListItemMenuViewModel", () => {
room_id: room.roomId,
});
});
it("should call setRoomNotifState", () => {
const setRoomNotifState = jest.fn();
mocked(useNotificationState).mockReturnValue([RoomNotifState.AllMessages, setRoomNotifState]);
const { result } = render();
result.current.setRoomNotifState(RoomNotifState.Mute);
expect(setRoomNotifState).toHaveBeenCalledWith(RoomNotifState.Mute);
});
});