Add loading state to the new room list view (#29725)
* add loading state to view model and spinner to room list vieqw * Update snapshots and add loading test * avoid nested ternary operator * Add room list skeleton loading state * Fix loading logic - Create RoomListStoreV3Event as to not conflict with loading event definition in Create RoomListStoreEvent. - Add a loaded event - Use it to determine loaded state in useFilteredRooms rather than the update event which gets fired in other cases. * Fix isLoadingRooms logic * update snapshots and fix test * Forcing an empty commit to fix PR * Fix _components.pcss order * Fix test that wasn't doing anything * fix tests
This commit is contained in:
@@ -22,6 +22,11 @@ import { useStickyRoomList } from "./useStickyRoomList";
|
||||
import { useRoomListNavigation } from "./useRoomListNavigation";
|
||||
|
||||
export interface RoomListViewState {
|
||||
/**
|
||||
* Whether the list of rooms is being loaded.
|
||||
*/
|
||||
isLoadingRooms: boolean;
|
||||
|
||||
/**
|
||||
* A list of rooms to be displayed in the left panel.
|
||||
*/
|
||||
@@ -99,6 +104,7 @@ export interface RoomListViewState {
|
||||
export function useRoomListViewModel(): RoomListViewState {
|
||||
const matrixClient = useMatrixClientContext();
|
||||
const {
|
||||
isLoadingRooms,
|
||||
primaryFilters,
|
||||
activePrimaryFilter,
|
||||
rooms: filteredRooms,
|
||||
@@ -123,6 +129,7 @@ export function useRoomListViewModel(): RoomListViewState {
|
||||
const createRoom = useCallback(() => createRoomFunc(currentSpace), [currentSpace]);
|
||||
|
||||
return {
|
||||
isLoadingRooms,
|
||||
rooms,
|
||||
canCreateRoom,
|
||||
createRoom,
|
||||
|
||||
@@ -10,8 +10,7 @@ import { useCallback, useMemo, useState } from "react";
|
||||
import type { Room } from "matrix-js-sdk/src/matrix";
|
||||
import { FilterKey } from "../../../stores/room-list-v3/skip-list/filters";
|
||||
import { _t, _td, type TranslationKey } from "../../../languageHandler";
|
||||
import RoomListStoreV3 from "../../../stores/room-list-v3/RoomListStoreV3";
|
||||
import { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
|
||||
import RoomListStoreV3, { LISTS_LOADED_EVENT, LISTS_UPDATE_EVENT } from "../../../stores/room-list-v3/RoomListStoreV3";
|
||||
import { useEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { UPDATE_SELECTED_SPACE } from "../../../stores/spaces";
|
||||
@@ -35,6 +34,7 @@ export interface PrimaryFilter {
|
||||
|
||||
interface FilteredRooms {
|
||||
primaryFilters: PrimaryFilter[];
|
||||
isLoadingRooms: boolean;
|
||||
rooms: Room[];
|
||||
activateSecondaryFilter: (filter: SecondaryFilters) => void;
|
||||
activeSecondaryFilter: SecondaryFilters;
|
||||
@@ -115,6 +115,7 @@ export function useFilteredRooms(): FilteredRooms {
|
||||
);
|
||||
|
||||
const [rooms, setRooms] = useState(() => RoomListStoreV3.instance.getSortedRoomsInActiveSpace());
|
||||
const [isLoadingRooms, setIsLoadingRooms] = useState(() => RoomListStoreV3.instance.isLoadingRooms);
|
||||
|
||||
const updateRoomsFromStore = useCallback((filters: FilterKey[] = []): void => {
|
||||
const newRooms = RoomListStoreV3.instance.getSortedRoomsInActiveSpace(filters);
|
||||
@@ -139,6 +140,10 @@ export function useFilteredRooms(): FilteredRooms {
|
||||
updateRoomsFromStore(filters);
|
||||
});
|
||||
|
||||
useEventEmitter(RoomListStoreV3.instance, LISTS_LOADED_EVENT, () => {
|
||||
setIsLoadingRooms(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Secondary filters are activated using this function.
|
||||
* This is different to how primary filters work because the secondary
|
||||
@@ -194,5 +199,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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,12 +19,19 @@ import { RoomListSecondaryFilters } from "./RoomListSecondaryFilters";
|
||||
export function RoomListView(): JSX.Element {
|
||||
const vm = useRoomListViewModel();
|
||||
const isRoomListEmpty = vm.rooms.length === 0;
|
||||
|
||||
let listBody;
|
||||
if (vm.isLoadingRooms) {
|
||||
listBody = <div className="mx_RoomListSkeleton" />;
|
||||
} else if (isRoomListEmpty) {
|
||||
listBody = <EmptyRoomList vm={vm} />;
|
||||
} else {
|
||||
listBody = <RoomList vm={vm} />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<RoomListPrimaryFilters vm={vm} />
|
||||
<RoomListSecondaryFilters vm={vm} />
|
||||
{isRoomListEmpty ? <EmptyRoomList vm={vm} /> : <RoomList vm={vm} />}
|
||||
{listBody}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { VisibilityProvider } from "../room-list/filters/VisibilityProvider";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { LISTS_UPDATE_EVENT } from "../room-list/RoomListStore";
|
||||
import { RoomSkipList } from "./skip-list/RoomSkipList";
|
||||
import { RecencySorter } from "./skip-list/sorters/RecencySorter";
|
||||
import { AlphabeticSorter } from "./skip-list/sorters/AlphabeticSorter";
|
||||
@@ -49,6 +48,15 @@ const FILTERS = [
|
||||
new LowPriorityFilter(),
|
||||
];
|
||||
|
||||
export enum RoomListStoreV3Event {
|
||||
// The event/channel which is called when the room lists have been changed.
|
||||
ListsUpdate = "lists_update",
|
||||
// The event which is called when the room list is loaded.
|
||||
ListsLoaded = "lists_loaded",
|
||||
}
|
||||
|
||||
export const LISTS_UPDATE_EVENT = RoomListStoreV3Event.ListsUpdate;
|
||||
export const LISTS_LOADED_EVENT = RoomListStoreV3Event.ListsLoaded;
|
||||
/**
|
||||
* This store allows for fast retrieval of the room list in a sorted and filtered manner.
|
||||
* This is the third such implementation hence the "V3".
|
||||
@@ -76,6 +84,13 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
return rooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the initial list of rooms has loaded.
|
||||
*/
|
||||
public get isLoadingRooms(): boolean {
|
||||
return !this.roomSkipList?.initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of sorted rooms.
|
||||
*/
|
||||
@@ -127,6 +142,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
await SpaceStore.instance.storeReadyPromise;
|
||||
const rooms = this.getRooms();
|
||||
this.roomSkipList.seed(rooms);
|
||||
this.emit(LISTS_LOADED_EVENT);
|
||||
this.emit(LISTS_UPDATE_EVENT);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user