Add message preview support to the new room list (#29784)

* Add message preview support to the new room list

 * Support showing message previews in the room list items
 * Add the secondary filters bar with the '...' menu, containing
   just the option for message previews for now
 * Change message preview toggle hook to update when setting is updated

* Use new compund release

* Unused i18n keys

* Unused imports

* Fix test & update snapshot

* Fix more snapshots

* Fix test

Split into two tests that test setting & updating

* Type import

* Snapshots

* Remove unnecessary Flex container

and update screenshots as the room list has got shorter from the added bar

* More snapshots & screenshots

* More snapshots

* Add test and remove active filter that's not done yet

* Update snapshots & screenshots again

* Other screenshot

* Add more tests

* Fix syntax

* Fix tests

* Use setter directly

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix CSS

* Remopve filter button css for now

* Update to remove forwardRef

* Add comment on why lack of TypedEventEmitter

* snapshots again

* Screenshots again

* Use original screenshots, maybe they'll work now

* Add comment

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
David Baker
2025-04-24 16:03:39 +01:00
committed by GitHub
parent 22d5c00174
commit 714f8f40dd
30 changed files with 674 additions and 92 deletions

View File

@@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
import { renderHook } from "jest-matrix-react";
import { renderHook, waitFor } from "jest-matrix-react";
import { type Room } from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
@@ -20,22 +20,40 @@ import {
import { RoomNotificationState } from "../../../../../src/stores/notifications/RoomNotificationState";
import { RoomNotificationStateStore } from "../../../../../src/stores/notifications/RoomNotificationStateStore";
import * as UseCallModule from "../../../../../src/hooks/useCall";
import { type MessagePreview, MessagePreviewStore } from "../../../../../src/stores/room-list/MessagePreviewStore";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
import { useMessagePreviewToggle } from "../../../../../src/components/viewmodels/roomlist/useMessagePreviewToggle";
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
hasAccessToOptionsMenu: jest.fn().mockReturnValue(false),
hasAccessToNotificationMenu: jest.fn().mockReturnValue(false),
}));
jest.mock("../../../../../src/components/viewmodels/roomlist/useMessagePreviewToggle", () => ({
useMessagePreviewToggle: jest.fn().mockReturnValue({ shouldShowMessagePreview: true }),
}));
describe("RoomListItemViewModel", () => {
let room: Room;
beforeEach(() => {
const matrixClient = createTestClient();
room = mkStubRoom("roomId", "roomName", matrixClient);
const dmRoomMap = {
getUserIdForRoomId: jest.fn(),
getDMRoomsForUserId: jest.fn(),
} as unknown as DMRoomMap;
DMRoomMap.setShared(dmRoomMap);
mocked(useMessagePreviewToggle).mockReturnValue({
shouldShowMessagePreview: false,
toggleMessagePreview: jest.fn(),
});
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
it("should dispatch view room action on openRoom", async () => {
@@ -87,6 +105,39 @@ describe("RoomListItemViewModel", () => {
expect(vm.current.showHoverMenu).toBe(true);
});
it("should return a message preview if one is available and they are enabled", async () => {
jest.spyOn(MessagePreviewStore.instance, "getPreviewForRoom").mockResolvedValue({
text: "Message look like this",
} as MessagePreview);
mocked(useMessagePreviewToggle).mockReturnValue({
shouldShowMessagePreview: true,
toggleMessagePreview: jest.fn(),
});
const { result: vm } = renderHook(
() => useRoomListItemViewModel(room),
withClientContextRenderOptions(room.client),
);
await waitFor(() => expect(vm.current.messagePreview).toBe("Message look like this"));
});
it("should hide message previews when disabled", async () => {
jest.spyOn(MessagePreviewStore.instance, "getPreviewForRoom").mockResolvedValue({
text: "Message look like this",
} as MessagePreview);
const { result: vm, rerender } = renderHook(
() => useRoomListItemViewModel(room),
withClientContextRenderOptions(room.client),
);
// This doesn't seem to test that the hook actually triggers an update,
// but I can't see how to test that.
rerender();
expect(vm.current.messagePreview).toBe(undefined);
});
describe("notification", () => {
let notificationState: RoomNotificationState;
beforeEach(() => {

View File

@@ -17,13 +17,14 @@ import { FilterKey } from "../../../../../src/stores/room-list-v3/skip-list/filt
import { SecondaryFilters } from "../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
import { SortingAlgorithm } from "../../../../../src/stores/room-list-v3/skip-list/sorters";
import { SortOption } from "../../../../../src/components/viewmodels/roomlist/useSorter";
import SettingsStore from "../../../../../src/settings/SettingsStore";
import SettingsStore, { type CallbackFn } from "../../../../../src/settings/SettingsStore";
import { hasCreateRoomRights, createRoom } from "../../../../../src/components/viewmodels/roomlist/utils";
import dispatcher from "../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../src/dispatcher/actions";
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
import { UPDATE_SELECTED_SPACE } from "../../../../../src/stores/spaces";
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
hasCreateRoomRights: jest.fn().mockReturnValue(false),
@@ -308,6 +309,25 @@ describe("RoomListViewModel", () => {
expect(vm.current.shouldShowMessagePreview).toEqual(true);
});
it("should update when setting changes", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => true);
let watchFn: CallbackFn;
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((_settingname, _roomId, fn) => {
watchFn = fn;
return "";
});
mockAndCreateRooms();
const { result: vm } = renderHook(() => useRoomListViewModel());
expect(vm.current.shouldShowMessagePreview).toEqual(true);
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => false);
act(() => {
watchFn("RoomList.showMessagePreview", "", SettingLevel.DEVICE, false, false);
});
expect(vm.current.shouldShowMessagePreview).toEqual(false);
});
it("should change setting on toggle", () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => true);
const fn = jest.spyOn(SettingsStore, "setValue").mockImplementation(async () => {});
@@ -317,8 +337,7 @@ describe("RoomListViewModel", () => {
act(() => {
vm.current.toggleMessagePreview();
});
expect(vm.current.shouldShowMessagePreview).toEqual(false);
expect(fn).toHaveBeenCalled();
expect(fn).toHaveBeenCalledWith("RoomList.showMessagePreview", null, "device", false);
});
});