Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit b8edbb4

Browse files
committed
Break up algorithms and use the new layering
Sorting and ordering has now been split apart. The ImportanceAlgorithm also finally makes use of the sorting. So far metrics look okay at 3ms for a simple account, though this could potentially get worse due to the multiple loops involved (one for tags, one for categories, one for ordering). We might be able to feed a whole list of rooms into the thing and have it regenerate the lists on demand.
1 parent 2fce69a commit b8edbb4

11 files changed

+283
-41
lines changed

src/stores/room-list/RoomListStore2.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
1919
import { ActionPayload, defaultDispatcher } from "../../dispatcher-types";
2020
import SettingsStore from "../../settings/SettingsStore";
2121
import { DefaultTagID, OrderedDefaultTagIDs, TagID } from "./models";
22-
import { Algorithm, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/Algorithm";
22+
import { Algorithm } from "./algorithms/list_ordering/Algorithm";
2323
import TagOrderStore from "../TagOrderStore";
24-
import { getAlgorithmInstance } from "./algorithms";
24+
import { getListAlgorithmInstance } from "./algorithms/list_ordering";
2525
import { AsyncStore } from "../AsyncStore";
26+
import { ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models";
2627

2728
interface IState {
2829
tagsEnabled?: boolean;
@@ -172,7 +173,7 @@ class _RoomListStore extends AsyncStore<ActionPayload> {
172173
}
173174

174175
private setAlgorithmClass() {
175-
this.algorithm = getAlgorithmInstance(this.state.preferredAlgorithm);
176+
this.algorithm = getListAlgorithmInstance(this.state.preferredAlgorithm);
176177
}
177178

178179
private async regenerateAllLists() {

src/stores/room-list/RoomListStoreTempProxy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
1919
import SettingsStore from "../../settings/SettingsStore";
2020
import RoomListStore from "./RoomListStore2";
2121
import OldRoomListStore from "../RoomListStore";
22-
import { ITagMap } from "./algorithms/Algorithm";
22+
import { ITagMap } from "./algorithms/models";
2323
import { UPDATE_EVENT } from "../AsyncStore";
2424

2525
/**

src/stores/room-list/algorithms/Algorithm.ts renamed to src/stores/room-list/algorithms/list_ordering/Algorithm.ts

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,20 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { DefaultTagID, TagID } from "../models";
17+
import { DefaultTagID, TagID } from "../../models";
1818
import { Room } from "matrix-js-sdk/src/models/room";
1919
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
20-
import { EffectiveMembership, splitRoomsByMembership } from "../membership";
21-
22-
export enum SortAlgorithm {
23-
Manual = "MANUAL",
24-
Alphabetic = "ALPHABETIC",
25-
Recent = "RECENT",
26-
}
27-
28-
export enum ListAlgorithm {
29-
// Orders Red > Grey > Bold > Idle
30-
Importance = "IMPORTANCE",
31-
32-
// Orders however the SortAlgorithm decides
33-
Natural = "NATURAL",
34-
}
35-
36-
export interface ITagSortingMap {
37-
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
38-
[tagId: TagID]: SortAlgorithm;
39-
}
40-
41-
export interface ITagMap {
42-
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
43-
[tagId: TagID]: Room[];
44-
}
20+
import { EffectiveMembership, splitRoomsByMembership } from "../../membership";
21+
import { ITagMap, ITagSortingMap } from "../models";
4522

4623
// TODO: Add locking support to avoid concurrent writes?
4724
// TODO: EventEmitter support? Might not be needed.
4825

26+
/**
27+
* Represents a list ordering algorithm. This class will take care of tag
28+
* management (which rooms go in which tags) and ask the implementation to
29+
* deal with ordering mechanics.
30+
*/
4931
export abstract class Algorithm {
5032
protected cached: ITagMap = {};
5133
protected sortAlgorithms: ITagSortingMap;
@@ -160,6 +142,7 @@ export abstract class Algorithm {
160142
* @param {Room[]} rooms The rooms within the tag, unordered.
161143
* @returns {Promise<Room[]>} Resolves to the ordered rooms in the tag.
162144
*/
145+
// TODO: Do we need this?
163146
protected abstract regenerateTag(tagId: TagID, rooms: Room[]): Promise<Room[]>;
164147

165148
/**
@@ -173,6 +156,6 @@ export abstract class Algorithm {
173156
* processing.
174157
*/
175158
// TODO: Take a ReasonForChange to better predict the behaviour?
176-
// TODO: Intercept here and handle tag changes automatically
159+
// TODO: Intercept here and handle tag changes automatically? May be best to let the impl do that.
177160
public abstract handleRoomUpdate(room: Room): Promise<boolean>;
178161
}

src/stores/room-list/algorithms/ChaoticAlgorithm.ts renamed to src/stores/room-list/algorithms/list_ordering/ChaoticAlgorithm.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { Algorithm, ITagMap } from "./Algorithm";
18-
import { DefaultTagID } from "../models";
17+
import { Algorithm } from "./Algorithm";
18+
import { DefaultTagID } from "../../models";
19+
import { ITagMap } from "../models";
1920

2021
/**
2122
* A demonstration/temporary algorithm to verify the API surface works.

src/stores/room-list/algorithms/ImportanceAlgorithm.ts renamed to src/stores/room-list/algorithms/list_ordering/ImportanceAlgorithm.ts

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
Copyright 2018, 2019 New Vector Ltd
23
Copyright 2020 The Matrix.org Foundation C.I.C.
34
45
Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,11 +15,12 @@ See the License for the specific language governing permissions and
1415
limitations under the License.
1516
*/
1617

17-
import { Algorithm, ITagMap, ITagSortingMap } from "./Algorithm";
18+
import { Algorithm } from "./Algorithm";
1819
import { Room } from "matrix-js-sdk/src/models/room";
19-
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
20-
import { DefaultTagID, TagID } from "../models";
21-
import { splitRoomsByMembership } from "../membership";
20+
import { DefaultTagID, TagID } from "../../models";
21+
import { ITagMap, SortAlgorithm } from "../models";
22+
import { getSortingAlgorithmInstance, sortRoomsWithAlgorithm } from "../tag_sorting";
23+
import * as Unread from '../../../../Unread';
2224

2325
/**
2426
* The determined category of a room.
@@ -44,6 +46,11 @@ export enum Category {
4446
Idle = "IDLE",
4547
}
4648

49+
interface ICategorizedRoomMap {
50+
// @ts-ignore - TS wants this to be a string, but we know better
51+
[category: Category]: Room[];
52+
}
53+
4754
/**
4855
* An implementation of the "importance" algorithm for room list sorting. Where
4956
* the tag sorting algorithm does not interfere, rooms will be ordered into
@@ -119,8 +126,72 @@ export class ImportanceAlgorithm extends Algorithm {
119126
console.log("Constructed an ImportanceAlgorithm");
120127
}
121128

129+
// noinspection JSMethodCanBeStatic
130+
private categorizeRooms(rooms: Room[]): ICategorizedRoomMap {
131+
const map: ICategorizedRoomMap = {
132+
[Category.Red]: [],
133+
[Category.Grey]: [],
134+
[Category.Bold]: [],
135+
[Category.Idle]: [],
136+
};
137+
for (const room of rooms) {
138+
const category = this.getRoomCategory(room);
139+
console.log(`[DEBUG] "${room.name}" (${room.roomId}) is a ${category} room`);
140+
map[category].push(room);
141+
}
142+
return map;
143+
}
144+
145+
// noinspection JSMethodCanBeStatic
146+
private getRoomCategory(room: Room): Category {
147+
// Function implementation borrowed from old RoomListStore
148+
149+
const mentions = room.getUnreadNotificationCount('highlight') > 0;
150+
if (mentions) {
151+
return Category.Red;
152+
}
153+
154+
let unread = room.getUnreadNotificationCount() > 0;
155+
if (unread) {
156+
return Category.Grey;
157+
}
158+
159+
unread = Unread.doesRoomHaveUnreadMessages(room);
160+
if (unread) {
161+
return Category.Bold;
162+
}
163+
164+
return Category.Idle;
165+
}
166+
122167
protected async generateFreshTags(updatedTagMap: ITagMap): Promise<any> {
123-
return Promise.resolve();
168+
for (const tagId of Object.keys(updatedTagMap)) {
169+
const unorderedRooms = updatedTagMap[tagId];
170+
171+
const sortBy = this.sortAlgorithms[tagId];
172+
if (!sortBy) throw new Error(`${tagId} does not have a sorting algorithm`);
173+
174+
if (sortBy === SortAlgorithm.Manual) {
175+
// Manual tags essentially ignore the importance algorithm, so don't do anything
176+
// special about them.
177+
updatedTagMap[tagId] = await sortRoomsWithAlgorithm(unorderedRooms, tagId, sortBy);
178+
} else {
179+
// Every other sorting type affects the categories, not the whole tag.
180+
const categorized = this.categorizeRooms(unorderedRooms);
181+
for (const category of Object.keys(categorized)) {
182+
const roomsToOrder = categorized[category];
183+
categorized[category] = await sortRoomsWithAlgorithm(roomsToOrder, tagId, sortBy);
184+
}
185+
186+
// TODO: Update positions of categories in cache
187+
updatedTagMap[tagId] = [
188+
...categorized[Category.Red],
189+
...categorized[Category.Grey],
190+
...categorized[Category.Bold],
191+
...categorized[Category.Idle],
192+
];
193+
}
194+
}
124195
}
125196

126197
protected async regenerateTag(tagId: string | DefaultTagID, rooms: []): Promise<[]> {

src/stores/room-list/algorithms/index.ts renamed to src/stores/room-list/algorithms/list_ordering/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { Algorithm, ListAlgorithm } from "./Algorithm";
17+
import { Algorithm } from "./Algorithm";
1818
import { ChaoticAlgorithm } from "./ChaoticAlgorithm";
1919
import { ImportanceAlgorithm } from "./ImportanceAlgorithm";
20+
import { ListAlgorithm } from "../models";
2021

2122
const ALGORITHM_FACTORIES: { [algorithm in ListAlgorithm]: () => Algorithm } = {
22-
[ListAlgorithm.Natural]: () => new ChaoticAlgorithm(ListAlgorithm.Natural),
23+
[ListAlgorithm.Natural]: () => new ChaoticAlgorithm(),
2324
[ListAlgorithm.Importance]: () => new ImportanceAlgorithm(),
2425
};
2526

@@ -28,7 +29,7 @@ const ALGORITHM_FACTORIES: { [algorithm in ListAlgorithm]: () => Algorithm } = {
2829
* @param {ListAlgorithm} algorithm The algorithm to get an instance of.
2930
* @returns {Algorithm} The algorithm instance.
3031
*/
31-
export function getAlgorithmInstance(algorithm: ListAlgorithm): Algorithm {
32+
export function getListAlgorithmInstance(algorithm: ListAlgorithm): Algorithm {
3233
if (!ALGORITHM_FACTORIES[algorithm]) {
3334
throw new Error(`${algorithm} is not a known algorithm`);
3435
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Copyright 2020 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { TagID } from "../models";
18+
import { Room } from "matrix-js-sdk/src/models/room";
19+
20+
export enum SortAlgorithm {
21+
Manual = "MANUAL",
22+
Alphabetic = "ALPHABETIC",
23+
Recent = "RECENT",
24+
}
25+
26+
export enum ListAlgorithm {
27+
// Orders Red > Grey > Bold > Idle
28+
Importance = "IMPORTANCE",
29+
30+
// Orders however the SortAlgorithm decides
31+
Natural = "NATURAL",
32+
}
33+
34+
export interface ITagSortingMap {
35+
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
36+
[tagId: TagID]: SortAlgorithm;
37+
}
38+
39+
export interface ITagMap {
40+
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
41+
[tagId: TagID]: Room[];
42+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2020 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { Room } from "matrix-js-sdk/src/models/room";
18+
import { TagID } from "../../models";
19+
import { IAlgorithm } from "./IAlgorithm";
20+
21+
/**
22+
* A demonstration to test the API surface.
23+
* TODO: Remove this before landing
24+
*/
25+
export class ChaoticAlgorithm implements IAlgorithm {
26+
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
27+
return rooms;
28+
}
29+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2020 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { Room } from "matrix-js-sdk/src/models/room";
18+
import { TagID } from "../../models";
19+
20+
/**
21+
* Represents a tag sorting algorithm.
22+
*/
23+
export interface IAlgorithm {
24+
/**
25+
* Sorts the given rooms according to the sorting rules of the algorithm.
26+
* @param {Room[]} rooms The rooms to sort.
27+
* @param {TagID} tagId The tag ID in which the rooms are being sorted.
28+
* @returns {Promise<Room[]>} Resolves to the sorted rooms.
29+
*/
30+
sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]>;
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2020 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { Room } from "matrix-js-sdk/src/models/room";
18+
import { TagID } from "../../models";
19+
import { IAlgorithm } from "./IAlgorithm";
20+
21+
/**
22+
* Sorts rooms according to the tag's `order` property on the room.
23+
*/
24+
export class ManualAlgorithm implements IAlgorithm {
25+
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
26+
const getOrderProp = (r: Room) => r.tags[tagId].order || 0;
27+
return rooms.sort((a, b) => {
28+
return getOrderProp(a) - getOrderProp(b);
29+
});
30+
}
31+
}

0 commit comments

Comments
 (0)