Make it possible to swap sorting algorithm
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user