Make it possible to swap sorting algorithm

This commit is contained in:
R Midhun Suresh
2025-02-24 21:44:05 +05:30
parent 791895504b
commit 0ca6449f3c
4 changed files with 62 additions and 16 deletions

View File

@@ -15,6 +15,7 @@ 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";
export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
private roomSkipList?: RoomSkipList;
@@ -25,17 +26,36 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
this.msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors");
}
public getRooms(): Room[] {
let rooms = this.matrixClient?.getVisibleRooms(this.msc3946ProcessDynamicPredecessor) ?? [];
rooms = rooms.filter((r) => VisibilityProvider.instance.isRoomVisible(r));
return rooms;
}
public getSortedRooms(): Room[] {
if (this.roomSkipList?.initialized) return Array.from(this.roomSkipList);
else return [];
}
public useAlphabeticSorting(): void {
if (this.roomSkipList) {
const sorter = new AlphabeticSorter();
this.roomSkipList.useNewSorter(sorter, this.getRooms());
}
}
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() ?? "");
const sorter = new RecencySorter(this.matrixClient.getSafeUserId());
this.roomSkipList = new RoomSkipList(sorter);
const rooms = this.fetchRoomsFromSdk();
if (!rooms) return;
const rooms = this.getRooms();
this.roomSkipList.seed(rooms);
this.emit(LISTS_UPDATE_EVENT);
}
@@ -43,13 +63,6 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
protected async onAction(payload: ActionPayload): Promise<void> {
return;
}
private fetchRoomsFromSdk(): Room[] | null {
if (!this.matrixClient) return null;
let rooms = this.matrixClient.getVisibleRooms(this.msc3946ProcessDynamicPredecessor);
rooms = rooms.filter((r) => VisibilityProvider.instance.isRoomVisible(r));
return rooms;
}
}
export default class RoomListStoreV3 {

View File

@@ -16,11 +16,16 @@ import { Level } from "./Level";
* See See https://en.wikipedia.org/wiki/Skip_list
*/
export class RoomSkipList implements Iterable<Room> {
private readonly levels: Level[] = [new Level(0)];
private readonly roomNodeMap: Map<string, RoomNode> = new Map();
private levels: Level[] = [new Level(0)];
private roomNodeMap: Map<string, RoomNode> = new Map();
public initialized: boolean = false;
public constructor(private readonly sorter: Sorter) {}
public constructor(private sorter: Sorter) {}
private reset(): void {
this.levels = [new Level(0)];
this.roomNodeMap = new Map();
}
/**
* Seed the list with an initial list of rooms.
@@ -42,6 +47,16 @@ export class RoomSkipList implements Iterable<Room> {
this.initialized = true;
}
/**
* Change the sorting algorithm used by the skip list.
* This will reset the list and will rebuild from scratch.
*/
public useNewSorter(sorter: Sorter, rooms: Room[]): void {
this.reset();
this.sorter = sorter;
this.seed(rooms);
}
/**
* Removes a given room from the skip list.
*/

View File

@@ -12,7 +12,7 @@ export class AlphabeticSorter implements Sorter {
private readonly collator = new Intl.Collator();
public sort(rooms: Room[]): Room[] {
return rooms.sort((a, b) => {
return [...rooms].sort((a, b) => {
return this.comparator(a, b);
});
}

View File

@@ -8,9 +8,11 @@ Please see LICENSE files in the repository root for full details.
import { shuffle } from "lodash";
import type { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
import type { Sorter } from "../../../../../src/stores/room-list-v3/skip-list/sorters";
import { mkMessage, mkStubRoom, stubClient } from "../../../../test-utils";
import { RoomSkipList } from "../../../../../src/stores/room-list-v3/skip-list/RoomSkipList";
import { RecencySorter } from "../../../../../src/stores/room-list-v3/skip-list/sorters/RecencySorter";
import { AlphabeticSorter } from "../../../../../src/stores/room-list-v3/skip-list/sorters/AlphabeticSorter";
describe("RoomSkipList", () => {
function getMockedRooms(client: MatrixClient, roomCount: number = 100): Room[] {
@@ -25,13 +27,18 @@ describe("RoomSkipList", () => {
return rooms;
}
function generateSkipList(roomCount?: number): { skipList: RoomSkipList; rooms: Room[]; totalRooms: number } {
function generateSkipList(roomCount?: number): {
skipList: RoomSkipList;
rooms: Room[];
totalRooms: number;
sorter: Sorter;
} {
const client = stubClient();
const sorter = new RecencySorter(client.getSafeUserId());
const skipList = new RoomSkipList(sorter);
const rooms = getMockedRooms(client, roomCount);
skipList.seed(rooms);
return { skipList, rooms, totalRooms: rooms.length };
return { skipList, rooms, totalRooms: rooms.length, sorter };
}
it("Rooms are in sorted order after initial seed", () => {
@@ -81,6 +88,17 @@ describe("RoomSkipList", () => {
}
});
it("Re-sort works when sorter is swapped", () => {
const { skipList, rooms, sorter } = generateSkipList();
const sortedByRecency = [...rooms].sort((a, b) => sorter.comparator(a, b));
expect(sortedByRecency).toEqual([...skipList]);
// Now switch over to alphabetic sorter
const newSorter = new AlphabeticSorter();
skipList.useNewSorter(newSorter, rooms);
const sortedByAlphabet = [...rooms].sort((a, b) => newSorter.comparator(a, b));
expect(sortedByAlphabet).toEqual([...skipList]);
});
describe("Empty skip list functionality", () => {
it("Insertions into empty skip list works", () => {
// Create an empty skip list