Skip to content

Commit e574a07

Browse files
MidhunSureshRsnowping
authored andcommitted
RoomListStore: Sort low priority rooms to the bottom of the list (element-hq#30070)
* Sort low priority rooms to the bottom of the list * Write test
1 parent fb30357 commit e574a07

File tree

2 files changed

+75
-5
lines changed

2 files changed

+75
-5
lines changed

src/stores/room-list-v3/skip-list/sorters/RecencySorter.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { Room } from "matrix-js-sdk/src/matrix";
99
import { type Sorter, SortingAlgorithm } from ".";
1010
import { getLastTs } from "../../../room-list/algorithms/tag-sorting/RecentAlgorithm";
1111
import { RoomNotificationStateStore } from "../../../notifications/RoomNotificationStateStore";
12+
import { DefaultTagID } from "../../../room-list/models";
1213

1314
export class RecencySorter implements Sorter {
1415
public constructor(private myUserId: string) {}
@@ -19,11 +20,9 @@ export class RecencySorter implements Sorter {
1920
}
2021

2122
public comparator(roomA: Room, roomB: Room, cache?: any): number {
22-
// Check mute status first; muted rooms should be at the bottom
23-
const isRoomAMuted = RoomNotificationStateStore.instance.getRoomState(roomA).muted;
24-
const isRoomBMuted = RoomNotificationStateStore.instance.getRoomState(roomB).muted;
25-
if (isRoomAMuted && !isRoomBMuted) return 1;
26-
if (isRoomBMuted && !isRoomAMuted) return -1;
23+
// First check if the rooms are low priority or muted
24+
const exceptionalOrdering = this.getScore(roomA) - this.getScore(roomB);
25+
if (exceptionalOrdering !== 0) return exceptionalOrdering;
2726

2827
// Then check recency; recent rooms should be at the top
2928
const roomALastTs = this.getTs(roomA, cache);
@@ -35,6 +34,28 @@ export class RecencySorter implements Sorter {
3534
return SortingAlgorithm.Recency;
3635
}
3736

37+
/**
38+
* This sorter mostly sorts rooms by recency but there are two exceptions:
39+
* 1. Muted rooms are sorted to the bottom of the list.
40+
* 2. Low priority rooms are sorted to the bottom of the list but before muted rooms.
41+
*
42+
* The following method provides a numerical value that takes care of this
43+
* exceptional ordering. For two rooms A and B, it works as follows:
44+
* - If getScore(A) - getScore(B) > 0, A should come after B
45+
* - If getScore(A) - getScore(B) < 0, A should come before B
46+
* - If getScore(A) - getScore(B) = 0, no special ordering needed, just use recency
47+
*/
48+
private getScore(room: Room): number {
49+
const isLowPriority = !!room.tags[DefaultTagID.LowPriority];
50+
const isMuted = RoomNotificationStateStore.instance.getRoomState(room).muted;
51+
// These constants are chosen so that the following order is maintained:
52+
// Low priority rooms -> Low priority and muted rooms -> Muted rooms
53+
if (isMuted && isLowPriority) return 5;
54+
else if (isMuted) return 10;
55+
else if (isLowPriority) return 2;
56+
else return 0;
57+
}
58+
3859
private getTs(room: Room, cache?: { [roomId: string]: number }): number {
3960
const ts = cache?.[room.roomId] ?? getLastTs(room, this.myUserId);
4061
if (cache) {

test/unit-tests/stores/room-list-v3/RoomListStoreV3-test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,4 +798,53 @@ describe("RoomListStoreV3", () => {
798798
expect(store.getSortedRooms()[34]).toEqual(unmutedRoom);
799799
});
800800
});
801+
802+
describe("Low priority rooms", () => {
803+
async function getRoomListStoreWithRooms() {
804+
const client = stubClient();
805+
const rooms = getMockedRooms(client);
806+
807+
// Let's say that rooms 34, 84, 64, 14, 57 are low priority
808+
const lowPriorityIndices = [34, 84, 64, 14, 57];
809+
const lowPriorityRooms = lowPriorityIndices.map((i) => rooms[i]);
810+
for (const room of lowPriorityRooms) {
811+
room.tags[DefaultTagID.LowPriority] = {};
812+
}
813+
814+
// Let's say that rooms 14, 57, 65, 78, 82, 5, 36 are muted
815+
const mutedIndices = [14, 57, 65, 78, 82, 5, 36];
816+
const mutedRooms = mutedIndices.map((i) => rooms[i]);
817+
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
818+
const state = {
819+
muted: mutedRooms.includes(room),
820+
} as unknown as RoomNotificationState;
821+
return state;
822+
});
823+
824+
client.getVisibleRooms = jest.fn().mockReturnValue(rooms);
825+
jest.spyOn(AsyncStoreWithClient.prototype, "matrixClient", "get").mockReturnValue(client);
826+
const store = new RoomListStoreV3Class(dispatcher);
827+
await store.start();
828+
829+
// We expect the following order: Low Priority -> Low Priority & Muted -> Muted
830+
const expectedRoomIds = [84, 64, 34, 57, 14, 82, 78, 65, 36, 5].map((i) => rooms[i].roomId);
831+
832+
return {
833+
client,
834+
rooms,
835+
expectedRoomIds,
836+
store,
837+
dispatcher,
838+
};
839+
}
840+
841+
it("Low priority rooms are pushed to the bottom of the list just before muted rooms", async () => {
842+
const { store, expectedRoomIds } = await getRoomListStoreWithRooms();
843+
const result = store
844+
.getSortedRooms()
845+
.slice(90)
846+
.map((r) => r.roomId);
847+
expect(result).toEqual(expectedRoomIds);
848+
});
849+
});
801850
});

0 commit comments

Comments
 (0)