add loading state to view model and spinner to room list vieqw

This commit is contained in:
David Langley
2025-04-11 17:57:17 +01:00
parent c929eedd81
commit c6b1a09b55
7 changed files with 24 additions and 2 deletions

View File

@@ -21,6 +21,11 @@ import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
import { useStickyRoomList } from "./useStickyRoomList"; import { useStickyRoomList } from "./useStickyRoomList";
export interface RoomListViewState { export interface RoomListViewState {
/**
* Whether the list of rooms is being loaded.
*/
isLoadingRooms: boolean;
/** /**
* A list of rooms to be displayed in the left panel. * A list of rooms to be displayed in the left panel.
*/ */
@@ -98,6 +103,7 @@ export interface RoomListViewState {
export function useRoomListViewModel(): RoomListViewState { export function useRoomListViewModel(): RoomListViewState {
const matrixClient = useMatrixClientContext(); const matrixClient = useMatrixClientContext();
const { const {
isLoadingRooms,
primaryFilters, primaryFilters,
activePrimaryFilter, activePrimaryFilter,
rooms: filteredRooms, rooms: filteredRooms,
@@ -120,6 +126,7 @@ export function useRoomListViewModel(): RoomListViewState {
const createRoom = useCallback(() => createRoomFunc(currentSpace), [currentSpace]); const createRoom = useCallback(() => createRoomFunc(currentSpace), [currentSpace]);
return { return {
isLoadingRooms,
rooms, rooms,
canCreateRoom, canCreateRoom,
createRoom, createRoom,

View File

@@ -35,6 +35,7 @@ export interface PrimaryFilter {
interface FilteredRooms { interface FilteredRooms {
primaryFilters: PrimaryFilter[]; primaryFilters: PrimaryFilter[];
isLoadingRooms: boolean;
rooms: Room[]; rooms: Room[];
activateSecondaryFilter: (filter: SecondaryFilters) => void; activateSecondaryFilter: (filter: SecondaryFilters) => void;
activeSecondaryFilter: SecondaryFilters; activeSecondaryFilter: SecondaryFilters;
@@ -115,6 +116,7 @@ export function useFilteredRooms(): FilteredRooms {
); );
const [rooms, setRooms] = useState(() => RoomListStoreV3.instance.getSortedRoomsInActiveSpace()); const [rooms, setRooms] = useState(() => RoomListStoreV3.instance.getSortedRoomsInActiveSpace());
const [isLoadingRooms, setIsLoadingRooms] = useState(true);
const updateRoomsFromStore = useCallback((filters: FilterKey[] = []): void => { const updateRoomsFromStore = useCallback((filters: FilterKey[] = []): void => {
const newRooms = RoomListStoreV3.instance.getSortedRoomsInActiveSpace(filters); const newRooms = RoomListStoreV3.instance.getSortedRoomsInActiveSpace(filters);
@@ -135,6 +137,7 @@ export function useFilteredRooms(): FilteredRooms {
}; };
useEventEmitter(RoomListStoreV3.instance, LISTS_UPDATE_EVENT, () => { useEventEmitter(RoomListStoreV3.instance, LISTS_UPDATE_EVENT, () => {
setIsLoadingRooms(false);
const filters = getAppliedFilters(); const filters = getAppliedFilters();
updateRoomsFromStore(filters); updateRoomsFromStore(filters);
}); });
@@ -194,5 +197,12 @@ export function useFilteredRooms(): FilteredRooms {
const activePrimaryFilter = useMemo(() => primaryFilters.find((filter) => filter.active), [primaryFilters]); const activePrimaryFilter = useMemo(() => primaryFilters.find((filter) => filter.active), [primaryFilters]);
return { primaryFilters, activePrimaryFilter, rooms, activateSecondaryFilter, activeSecondaryFilter }; return {
isLoadingRooms,
primaryFilters,
activePrimaryFilter,
rooms,
activateSecondaryFilter,
activeSecondaryFilter,
};
} }

View File

@@ -11,6 +11,7 @@ import { useRoomListViewModel } from "../../../viewmodels/roomlist/RoomListViewM
import { RoomList } from "./RoomList"; import { RoomList } from "./RoomList";
import { EmptyRoomList } from "./EmptyRoomList"; import { EmptyRoomList } from "./EmptyRoomList";
import { RoomListPrimaryFilters } from "./RoomListPrimaryFilters"; import { RoomListPrimaryFilters } from "./RoomListPrimaryFilters";
import Spinner from "../../elements/Spinner";
/** /**
* Host the room list and the (future) room filters * Host the room list and the (future) room filters
@@ -22,7 +23,7 @@ export function RoomListView(): JSX.Element {
return ( return (
<> <>
<RoomListPrimaryFilters vm={vm} /> <RoomListPrimaryFilters vm={vm} />
{isRoomListEmpty ? <EmptyRoomList vm={vm} /> : <RoomList vm={vm} />} {vm.isLoadingRooms ? <Spinner /> : isRoomListEmpty ? <EmptyRoomList vm={vm} /> : <RoomList vm={vm} />}
</> </>
); );
} }

View File

@@ -20,6 +20,7 @@ describe("<EmptyRoomList />", () => {
beforeEach(() => { beforeEach(() => {
vm = { vm = {
isLoadingRooms: false,
rooms: [], rooms: [],
primaryFilters: [], primaryFilters: [],
activateSecondaryFilter: jest.fn().mockReturnValue({}), activateSecondaryFilter: jest.fn().mockReturnValue({}),

View File

@@ -29,6 +29,7 @@ describe("<RoomList />", () => {
matrixClient = stubClient(); matrixClient = stubClient();
const rooms = Array.from({ length: 10 }, (_, i) => mkRoom(matrixClient, `room${i}`)); const rooms = Array.from({ length: 10 }, (_, i) => mkRoom(matrixClient, `room${i}`));
vm = { vm = {
isLoadingRooms: false,
rooms, rooms,
primaryFilters: [], primaryFilters: [],
activateSecondaryFilter: () => {}, activateSecondaryFilter: () => {},

View File

@@ -20,6 +20,7 @@ describe("<RoomListPrimaryFilters />", () => {
beforeEach(() => { beforeEach(() => {
vm = { vm = {
isLoadingRooms: false,
rooms: [], rooms: [],
canCreateRoom: true, canCreateRoom: true,
createRoom: jest.fn(), createRoom: jest.fn(),

View File

@@ -24,6 +24,7 @@ jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListViewMode
describe("<RoomListView />", () => { describe("<RoomListView />", () => {
const defaultValue: RoomListViewState = { const defaultValue: RoomListViewState = {
isLoadingRooms: false,
rooms: [], rooms: [],
primaryFilters: [], primaryFilters: [],
activateSecondaryFilter: jest.fn().mockReturnValue({}), activateSecondaryFilter: jest.fn().mockReturnValue({}),