Room List - Implement a minimal view model (#29357)
* Implement enough of the new store to get a list of rooms * Make it possible to swap sorting algorithm * Don't attach to window object We don't want the store to be created if the labs flag is off * Remove the store class Probably best to include this PR with the minimal vm implmentation * Create a new room list store that wraps around the skip list * Create a minimal view model * Fix CI * Add some basic tests for the store * Write more tests * Add some jsdoc comments * Add more documentation * Add more docs
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 type { Room } from "matrix-js-sdk/src/matrix";
|
||||
import RoomListStoreV3 from "../../../stores/room-list-v3/RoomListStoreV3";
|
||||
|
||||
export interface RoomListViewState {
|
||||
/**
|
||||
* A list of rooms to be displayed in the left panel.
|
||||
*/
|
||||
rooms: Room[];
|
||||
}
|
||||
|
||||
/**
|
||||
* View model for the new room list
|
||||
* @see {@link RoomListViewState} for more information about what this view model returns.
|
||||
*/
|
||||
export function useRoomListViewModel(): RoomListViewState {
|
||||
const rooms = RoomListStoreV3.instance.getSortedRooms();
|
||||
return { rooms };
|
||||
}
|
||||
@@ -6,11 +6,14 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { AutoSizer, List } from "react-virtualized";
|
||||
|
||||
import type { ListRowProps } from "react-virtualized";
|
||||
import { shouldShowComponent } from "../../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../settings/UIFeature";
|
||||
import { RoomListSearch } from "./RoomListSearch";
|
||||
import { RoomListHeaderView } from "./RoomListHeaderView";
|
||||
import { useRoomListViewModel } from "../../../viewmodels/roomlist/RoomListViewModel";
|
||||
|
||||
type RoomListPanelProps = {
|
||||
/**
|
||||
@@ -25,11 +28,31 @@ type RoomListPanelProps = {
|
||||
*/
|
||||
export const RoomListPanel: React.FC<RoomListPanelProps> = ({ activeSpace }) => {
|
||||
const displayRoomSearch = shouldShowComponent(UIComponent.FilterContainer);
|
||||
const { rooms } = useRoomListViewModel();
|
||||
|
||||
const rowRenderer = ({ key, index, style }: ListRowProps): React.JSX.Element => {
|
||||
return (
|
||||
<div key={key} style={style}>
|
||||
{rooms[index].name}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="mx_RoomListPanel" data-testid="room-list-panel">
|
||||
{displayRoomSearch && <RoomListSearch activeSpace={activeSpace} />}
|
||||
<RoomListHeaderView />
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
rowRenderer={rowRenderer}
|
||||
rowCount={rooms.length}
|
||||
rowHeight={20}
|
||||
height={height}
|
||||
width={width}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
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 type { EmptyObject, Room } from "matrix-js-sdk/src/matrix";
|
||||
import type { MatrixDispatcher } from "../../dispatcher/dispatcher";
|
||||
import type { ActionPayload } from "../../dispatcher/payloads";
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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".
|
||||
* This store is being actively developed so expect the methods to change in future.
|
||||
*/
|
||||
export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
|
||||
private roomSkipList?: RoomSkipList;
|
||||
private readonly msc3946ProcessDynamicPredecessor: boolean;
|
||||
|
||||
public constructor(dispatcher: MatrixDispatcher) {
|
||||
super(dispatcher);
|
||||
this.msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of unsorted, unfiltered rooms.
|
||||
*/
|
||||
public getRooms(): Room[] {
|
||||
let rooms = this.matrixClient?.getVisibleRooms(this.msc3946ProcessDynamicPredecessor) ?? [];
|
||||
rooms = rooms.filter((r) => VisibilityProvider.instance.isRoomVisible(r));
|
||||
return rooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of sorted rooms.
|
||||
*/
|
||||
public getSortedRooms(): Room[] {
|
||||
if (this.roomSkipList?.initialized) return Array.from(this.roomSkipList);
|
||||
else return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-sort the list of rooms by alphabetic order.
|
||||
*/
|
||||
public useAlphabeticSorting(): void {
|
||||
if (this.roomSkipList) {
|
||||
const sorter = new AlphabeticSorter();
|
||||
this.roomSkipList.useNewSorter(sorter, this.getRooms());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-sort the list of rooms by recency.
|
||||
*/
|
||||
public useRecencySorting(): void {
|
||||
if (this.roomSkipList && this.matrixClient) {
|
||||
const sorter = new RecencySorter(this.matrixClient?.getSafeUserId() ?? "");
|
||||
this.roomSkipList.useNewSorter(sorter, this.getRooms());
|
||||
}
|
||||
}
|
||||
|
||||
protected async onReady(): Promise<any> {
|
||||
if (this.roomSkipList?.initialized || !this.matrixClient) return;
|
||||
const sorter = new RecencySorter(this.matrixClient.getSafeUserId());
|
||||
this.roomSkipList = new RoomSkipList(sorter);
|
||||
const rooms = this.getRooms();
|
||||
this.roomSkipList.seed(rooms);
|
||||
this.emit(LISTS_UPDATE_EVENT);
|
||||
}
|
||||
|
||||
protected async onAction(payload: ActionPayload): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export default class RoomListStoreV3 {
|
||||
private static internalInstance: RoomListStoreV3Class;
|
||||
|
||||
public static get instance(): RoomListStoreV3Class {
|
||||
if (!RoomListStoreV3.internalInstance) {
|
||||
const instance = new RoomListStoreV3Class(defaultDispatcher);
|
||||
instance.start();
|
||||
RoomListStoreV3.internalInstance = instance;
|
||||
}
|
||||
|
||||
return this.internalInstance;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user