From e9a3625bd6e9a64f216e3caeabca66f48b649332 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Wed, 5 Mar 2025 14:24:04 +0530 Subject: [PATCH] Add more functionality to the room list vm (#29402) * Add more vm functionality - Listen for updates from the store - Provide a method to open rooms * Write test --- .../viewmodels/roomlist/RoomListViewModel.tsx | 30 ++++++++- .../roomlist/RoomListViewModel-test.tsx | 62 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 test/unit-tests/components/viewmodels/roomlist/RoomListViewModel-test.tsx diff --git a/src/components/viewmodels/roomlist/RoomListViewModel.tsx b/src/components/viewmodels/roomlist/RoomListViewModel.tsx index 1dacd030e2..d1c1d994e0 100644 --- a/src/components/viewmodels/roomlist/RoomListViewModel.tsx +++ b/src/components/viewmodels/roomlist/RoomListViewModel.tsx @@ -5,14 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ +import { useCallback, useState } from "react"; + import type { Room } from "matrix-js-sdk/src/matrix"; +import type { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import RoomListStoreV3 from "../../../stores/room-list-v3/RoomListStoreV3"; +import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore"; +import dispatcher from "../../../dispatcher/dispatcher"; +import { Action } from "../../../dispatcher/actions"; export interface RoomListViewState { /** * A list of rooms to be displayed in the left panel. */ rooms: Room[]; + + /** + * Open the room having given roomId. + */ + openRoom: (roomId: string) => void; } /** @@ -20,6 +32,20 @@ export interface RoomListViewState { * @see {@link RoomListViewState} for more information about what this view model returns. */ export function useRoomListViewModel(): RoomListViewState { - const rooms = RoomListStoreV3.instance.getSortedRooms(); - return { rooms }; + const [rooms, setRooms] = useState(RoomListStoreV3.instance.getSortedRoomsInActiveSpace()); + + useEventEmitter(RoomListStoreV3.instance, LISTS_UPDATE_EVENT, () => { + const newRooms = RoomListStoreV3.instance.getSortedRoomsInActiveSpace(); + setRooms(newRooms); + }); + + const openRoom = useCallback((roomId: string): void => { + dispatcher.dispatch({ + action: Action.ViewRoom, + room_id: roomId, + metricsTrigger: "RoomList", + }); + }, []); + + return { rooms, openRoom }; } diff --git a/test/unit-tests/components/viewmodels/roomlist/RoomListViewModel-test.tsx b/test/unit-tests/components/viewmodels/roomlist/RoomListViewModel-test.tsx new file mode 100644 index 0000000000..b1da67ac18 --- /dev/null +++ b/test/unit-tests/components/viewmodels/roomlist/RoomListViewModel-test.tsx @@ -0,0 +1,62 @@ +/* +Copyright 2025 New Vector 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 { range } from "lodash"; +import { act, renderHook, waitFor } from "jest-matrix-react"; + +import RoomListStoreV3 from "../../../../../src/stores/room-list-v3/RoomListStoreV3"; +import { mkStubRoom } from "../../../../test-utils"; +import { LISTS_UPDATE_EVENT } from "../../../../../src/stores/room-list/SlidingRoomListStore"; +import { useRoomListViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListViewModel"; +import dispatcher from "../../../../../src/dispatcher/dispatcher"; +import { Action } from "../../../../../src/dispatcher/actions"; + +describe("RoomListViewModel", () => { + function mockAndCreateRooms() { + const rooms = range(10).map((i) => mkStubRoom(`foo${i}:matrix.org`, `Foo ${i}`, undefined)); + jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockImplementation(() => [...rooms]); + return rooms; + } + + it("should return a list of rooms", async () => { + const rooms = mockAndCreateRooms(); + const { result: vm } = renderHook(() => useRoomListViewModel()); + + expect(vm.current.rooms).toHaveLength(10); + for (const room of rooms) { + expect(vm.current.rooms).toContain(room); + } + }); + + it("should update list of rooms on event from room list store", async () => { + const rooms = mockAndCreateRooms(); + const { result: vm } = renderHook(() => useRoomListViewModel()); + + const newRoom = mkStubRoom("bar:matrix.org", "Bar", undefined); + rooms.push(newRoom); + act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT)); + + await waitFor(() => { + expect(vm.current.rooms).toContain(newRoom); + }); + }); + + it("should dispatch view room action on openRoom", async () => { + const rooms = mockAndCreateRooms(); + const { result: vm } = renderHook(() => useRoomListViewModel()); + + const fn = jest.spyOn(dispatcher, "dispatch"); + act(() => vm.current.openRoom(rooms[7].roomId)); + expect(fn).toHaveBeenCalledWith( + expect.objectContaining({ + action: Action.ViewRoom, + room_id: rooms[7].roomId, + metricsTrigger: "RoomList", + }), + ); + }); +});