mirror of
https://github.com/vector-im/element-web.git
synced 2026-04-20 21:12:18 +02:00
* fix: section being empty in flat list mode When switching space (or removing a section later), if the Chat section is collpased and the room list is in flat list mode in the other space, the room list is empty. The fix forces the section to be in expanded state if in flat list mode * fix: store section expanded state by space
191 lines
6.8 KiB
TypeScript
191 lines
6.8 KiB
TypeScript
/*
|
|
* Copyright 2026 Element Creations Ltd.
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
* Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
|
|
|
import { RoomListSectionHeaderViewModel } from "../../../src/viewmodels/room-list/RoomListSectionHeaderViewModel";
|
|
import { RoomNotificationState } from "../../../src/stores/notifications/RoomNotificationState";
|
|
import { RoomNotificationStateStore } from "../../../src/stores/notifications/RoomNotificationStateStore";
|
|
import { NotificationStateEvents } from "../../../src/stores/notifications/NotificationState";
|
|
import { createTestClient, mkRoom } from "../../test-utils";
|
|
|
|
describe("RoomListSectionHeaderViewModel", () => {
|
|
let onToggleExpanded: jest.Mock;
|
|
let matrixClient: MatrixClient;
|
|
|
|
beforeEach(() => {
|
|
onToggleExpanded = jest.fn();
|
|
matrixClient = createTestClient();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it("should initialize snapshot from props", () => {
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
|
|
const snapshot = vm.getSnapshot();
|
|
expect(snapshot.id).toBe("m.favourite");
|
|
expect(snapshot.title).toBe("Favourites");
|
|
expect(snapshot.isExpanded).toBe(true);
|
|
});
|
|
|
|
it("should toggle expanded state on click", () => {
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
expect(vm.isExpanded).toBe(true);
|
|
|
|
vm.onClick();
|
|
expect(vm.isExpanded).toBe(false);
|
|
expect(vm.getSnapshot().isExpanded).toBe(false);
|
|
expect(onToggleExpanded).toHaveBeenCalledWith(false);
|
|
|
|
vm.onClick();
|
|
expect(vm.isExpanded).toBe(true);
|
|
expect(vm.getSnapshot().isExpanded).toBe(true);
|
|
expect(onToggleExpanded).toHaveBeenCalledWith(true);
|
|
});
|
|
|
|
it("should track expanded state per space", () => {
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
|
|
// Default space: collapse
|
|
vm.onClick();
|
|
expect(vm.isExpanded).toBe(false);
|
|
|
|
// Switch to a different space: should default to expanded
|
|
vm.setSpace("!space2:server");
|
|
expect(vm.isExpanded).toBe(true);
|
|
|
|
// Collapse in the new space
|
|
vm.onClick();
|
|
expect(vm.isExpanded).toBe(false);
|
|
vm.onClick();
|
|
expect(vm.isExpanded).toBe(true);
|
|
|
|
// Switch to the other space: should still be collapsed
|
|
vm.setSpace("!space:server");
|
|
expect(vm.isExpanded).toBe(false);
|
|
});
|
|
|
|
describe("unread status", () => {
|
|
let room: Room;
|
|
let notificationState: RoomNotificationState;
|
|
|
|
beforeEach(() => {
|
|
room = mkRoom(matrixClient, "!room:server");
|
|
notificationState = new RoomNotificationState(room, false);
|
|
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockReturnValue(notificationState);
|
|
});
|
|
|
|
it("should set isUnread to false when no rooms have notifications", () => {
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
vm.setRooms([room]);
|
|
|
|
expect(vm.getSnapshot().isUnread).toBe(false);
|
|
});
|
|
|
|
it("should set isUnread to true when a room has notifications", () => {
|
|
jest.spyOn(notificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
|
|
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
vm.setRooms([room]);
|
|
|
|
expect(vm.getSnapshot().isUnread).toBe(true);
|
|
});
|
|
|
|
it("should subscribe to new rooms and unsubscribe from removed rooms", () => {
|
|
const room2 = mkRoom(matrixClient, "!room2:server");
|
|
const notificationState2 = new RoomNotificationState(room2, false);
|
|
|
|
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState")
|
|
.mockReturnValueOnce(notificationState)
|
|
.mockReturnValue(notificationState2);
|
|
|
|
jest.spyOn(notificationState, "on");
|
|
jest.spyOn(notificationState, "off");
|
|
jest.spyOn(notificationState2, "on");
|
|
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
vm.setRooms([room]);
|
|
|
|
expect(notificationState.on).toHaveBeenCalledWith(NotificationStateEvents.Update, expect.any(Function));
|
|
|
|
vm.setRooms([room2]);
|
|
|
|
expect(notificationState.off).toHaveBeenCalledWith(NotificationStateEvents.Update, expect.any(Function));
|
|
expect(notificationState2.on).toHaveBeenCalledWith(NotificationStateEvents.Update, expect.any(Function));
|
|
|
|
// Calling setRooms again with the same room should not re-subscribe
|
|
vm.setRooms([room2]);
|
|
expect(notificationState2.on).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("should update isUnread when a notification state update event fires", () => {
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
vm.setRooms([room]);
|
|
|
|
expect(vm.getSnapshot().isUnread).toBe(false);
|
|
|
|
jest.spyOn(notificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
|
|
notificationState.emit(NotificationStateEvents.Update);
|
|
|
|
expect(vm.getSnapshot().isUnread).toBe(true);
|
|
});
|
|
|
|
it("should unsubscribe from all notification states on dispose", () => {
|
|
jest.spyOn(notificationState, "off");
|
|
|
|
const vm = new RoomListSectionHeaderViewModel({
|
|
tag: "m.favourite",
|
|
title: "Favourites",
|
|
spaceId: "!space:server",
|
|
onToggleExpanded,
|
|
});
|
|
vm.setRooms([room]);
|
|
|
|
vm.dispose();
|
|
expect(notificationState.off).toHaveBeenCalledWith(NotificationStateEvents.Update, expect.any(Function));
|
|
});
|
|
});
|
|
});
|