From c6b1a09b55ed04530cec7db7b899263662ffb65e Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 11 Apr 2025 17:57:17 +0100 Subject: [PATCH] add loading state to view model and spinner to room list vieqw --- .../viewmodels/roomlist/RoomListViewModel.tsx | 7 +++++++ .../viewmodels/roomlist/useFilteredRooms.tsx | 12 +++++++++++- .../views/rooms/RoomListPanel/RoomListView.tsx | 3 ++- .../views/rooms/RoomListPanel/EmptyRoomList-test.tsx | 1 + .../views/rooms/RoomListPanel/RoomList-test.tsx | 1 + .../RoomListPanel/RoomListPrimaryFilters-test.tsx | 1 + .../views/rooms/RoomListPanel/RoomListView-test.tsx | 1 + 7 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/viewmodels/roomlist/RoomListViewModel.tsx b/src/components/viewmodels/roomlist/RoomListViewModel.tsx index 217eaefbd9..4b02c26647 100644 --- a/src/components/viewmodels/roomlist/RoomListViewModel.tsx +++ b/src/components/viewmodels/roomlist/RoomListViewModel.tsx @@ -21,6 +21,11 @@ import { useMatrixClientContext } from "../../../contexts/MatrixClientContext"; import { useStickyRoomList } from "./useStickyRoomList"; export interface RoomListViewState { + /** + * Whether the list of rooms is being loaded. + */ + isLoadingRooms: boolean; + /** * A list of rooms to be displayed in the left panel. */ @@ -98,6 +103,7 @@ export interface RoomListViewState { export function useRoomListViewModel(): RoomListViewState { const matrixClient = useMatrixClientContext(); const { + isLoadingRooms, primaryFilters, activePrimaryFilter, rooms: filteredRooms, @@ -120,6 +126,7 @@ export function useRoomListViewModel(): RoomListViewState { const createRoom = useCallback(() => createRoomFunc(currentSpace), [currentSpace]); return { + isLoadingRooms, rooms, canCreateRoom, createRoom, diff --git a/src/components/viewmodels/roomlist/useFilteredRooms.tsx b/src/components/viewmodels/roomlist/useFilteredRooms.tsx index 4b4b9c0ec8..d8442a9d0b 100644 --- a/src/components/viewmodels/roomlist/useFilteredRooms.tsx +++ b/src/components/viewmodels/roomlist/useFilteredRooms.tsx @@ -35,6 +35,7 @@ export interface PrimaryFilter { interface FilteredRooms { primaryFilters: PrimaryFilter[]; + isLoadingRooms: boolean; rooms: Room[]; activateSecondaryFilter: (filter: SecondaryFilters) => void; activeSecondaryFilter: SecondaryFilters; @@ -115,6 +116,7 @@ export function useFilteredRooms(): FilteredRooms { ); const [rooms, setRooms] = useState(() => RoomListStoreV3.instance.getSortedRoomsInActiveSpace()); + const [isLoadingRooms, setIsLoadingRooms] = useState(true); const updateRoomsFromStore = useCallback((filters: FilterKey[] = []): void => { const newRooms = RoomListStoreV3.instance.getSortedRoomsInActiveSpace(filters); @@ -135,6 +137,7 @@ export function useFilteredRooms(): FilteredRooms { }; useEventEmitter(RoomListStoreV3.instance, LISTS_UPDATE_EVENT, () => { + setIsLoadingRooms(false); const filters = getAppliedFilters(); updateRoomsFromStore(filters); }); @@ -194,5 +197,12 @@ export function useFilteredRooms(): FilteredRooms { const activePrimaryFilter = useMemo(() => primaryFilters.find((filter) => filter.active), [primaryFilters]); - return { primaryFilters, activePrimaryFilter, rooms, activateSecondaryFilter, activeSecondaryFilter }; + return { + isLoadingRooms, + primaryFilters, + activePrimaryFilter, + rooms, + activateSecondaryFilter, + activeSecondaryFilter, + }; } diff --git a/src/components/views/rooms/RoomListPanel/RoomListView.tsx b/src/components/views/rooms/RoomListPanel/RoomListView.tsx index f4800f7009..069b49adb8 100644 --- a/src/components/views/rooms/RoomListPanel/RoomListView.tsx +++ b/src/components/views/rooms/RoomListPanel/RoomListView.tsx @@ -11,6 +11,7 @@ import { useRoomListViewModel } from "../../../viewmodels/roomlist/RoomListViewM import { RoomList } from "./RoomList"; import { EmptyRoomList } from "./EmptyRoomList"; import { RoomListPrimaryFilters } from "./RoomListPrimaryFilters"; +import Spinner from "../../elements/Spinner"; /** * Host the room list and the (future) room filters @@ -22,7 +23,7 @@ export function RoomListView(): JSX.Element { return ( <> - {isRoomListEmpty ? : } + {vm.isLoadingRooms ? : isRoomListEmpty ? : } ); } diff --git a/test/unit-tests/components/views/rooms/RoomListPanel/EmptyRoomList-test.tsx b/test/unit-tests/components/views/rooms/RoomListPanel/EmptyRoomList-test.tsx index b68929f74a..1483797778 100644 --- a/test/unit-tests/components/views/rooms/RoomListPanel/EmptyRoomList-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomListPanel/EmptyRoomList-test.tsx @@ -20,6 +20,7 @@ describe("", () => { beforeEach(() => { vm = { + isLoadingRooms: false, rooms: [], primaryFilters: [], activateSecondaryFilter: jest.fn().mockReturnValue({}), diff --git a/test/unit-tests/components/views/rooms/RoomListPanel/RoomList-test.tsx b/test/unit-tests/components/views/rooms/RoomListPanel/RoomList-test.tsx index 0d837c3a20..d72a78d36a 100644 --- a/test/unit-tests/components/views/rooms/RoomListPanel/RoomList-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomListPanel/RoomList-test.tsx @@ -29,6 +29,7 @@ describe("", () => { matrixClient = stubClient(); const rooms = Array.from({ length: 10 }, (_, i) => mkRoom(matrixClient, `room${i}`)); vm = { + isLoadingRooms: false, rooms, primaryFilters: [], activateSecondaryFilter: () => {}, diff --git a/test/unit-tests/components/views/rooms/RoomListPanel/RoomListPrimaryFilters-test.tsx b/test/unit-tests/components/views/rooms/RoomListPanel/RoomListPrimaryFilters-test.tsx index 79bfbb6dc0..4a80ea068e 100644 --- a/test/unit-tests/components/views/rooms/RoomListPanel/RoomListPrimaryFilters-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomListPanel/RoomListPrimaryFilters-test.tsx @@ -20,6 +20,7 @@ describe("", () => { beforeEach(() => { vm = { + isLoadingRooms: false, rooms: [], canCreateRoom: true, createRoom: jest.fn(), diff --git a/test/unit-tests/components/views/rooms/RoomListPanel/RoomListView-test.tsx b/test/unit-tests/components/views/rooms/RoomListPanel/RoomListView-test.tsx index f56d30976a..e66e430e18 100644 --- a/test/unit-tests/components/views/rooms/RoomListPanel/RoomListView-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomListPanel/RoomListView-test.tsx @@ -24,6 +24,7 @@ jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListViewMode describe("", () => { const defaultValue: RoomListViewState = { + isLoadingRooms: false, rooms: [], primaryFilters: [], activateSecondaryFilter: jest.fn().mockReturnValue({}),