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

Commit 82f2551

Browse files
authored
Merge pull request #4737 from matrix-org/travis/room-list/filter-priority
Support prioritized room list filters
2 parents 7a3ed0e + 1f82396 commit 82f2551

File tree

6 files changed

+132
-9
lines changed

6 files changed

+132
-9
lines changed

src/stores/room-list/algorithms/list-ordering/Algorithm.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
2020
import { EffectiveMembership, splitRoomsByMembership } from "../../membership";
2121
import { ITagMap, ITagSortingMap } from "../models";
2222
import DMRoomMap from "../../../../utils/DMRoomMap";
23-
import { FILTER_CHANGED, IFilterCondition } from "../../filters/IFilterCondition";
23+
import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "../../filters/IFilterCondition";
2424
import { EventEmitter } from "events";
2525
import { UPDATE_EVENT } from "../../../AsyncStore";
26+
import { ArrayUtil } from "../../../../utils/arrays";
27+
import { getEnumValues } from "../../../../utils/enums";
2628

2729
// TODO: Add locking support to avoid concurrent writes?
2830

@@ -184,30 +186,42 @@ export abstract class Algorithm extends EventEmitter {
184186
}
185187

186188
console.warn("Recalculating filtered room list");
187-
const allowedByFilters = new Set<Room>();
188189
const filters = Array.from(this.allowedByFilter.keys());
190+
const orderedFilters = new ArrayUtil(filters)
191+
.groupBy(f => f.relativePriority)
192+
.orderBy(getEnumValues(FilterPriority))
193+
.value;
189194
const newMap: ITagMap = {};
190195
for (const tagId of Object.keys(this.cachedRooms)) {
191196
// Cheaply clone the rooms so we can more easily do operations on the list.
192197
// We optimize our lookups by trying to reduce sample size as much as possible
193198
// to the rooms we know will be deduped by the Set.
194199
const rooms = this.cachedRooms[tagId];
195-
const remainingRooms = rooms.map(r => r).filter(r => !allowedByFilters.has(r));
196-
const allowedRoomsInThisTag = [];
197-
for (const filter of filters) {
200+
let remainingRooms = rooms.map(r => r);
201+
let allowedRoomsInThisTag = [];
202+
let lastFilterPriority = orderedFilters[0].relativePriority;
203+
for (const filter of orderedFilters) {
204+
if (filter.relativePriority !== lastFilterPriority) {
205+
// Every time the filter changes priority, we want more specific filtering.
206+
// To accomplish that, reset the variables to make it look like the process
207+
// has started over, but using the filtered rooms as the seed.
208+
remainingRooms = allowedRoomsInThisTag;
209+
allowedRoomsInThisTag = [];
210+
lastFilterPriority = filter.relativePriority;
211+
}
198212
const filteredRooms = remainingRooms.filter(r => filter.isVisible(r));
199213
for (const room of filteredRooms) {
200214
const idx = remainingRooms.indexOf(room);
201215
if (idx >= 0) remainingRooms.splice(idx, 1);
202-
allowedByFilters.add(room);
203216
allowedRoomsInThisTag.push(room);
204217
}
205218
}
206219
newMap[tagId] = allowedRoomsInThisTag;
207220
console.log(`[DEBUG] ${newMap[tagId].length}/${rooms.length} rooms filtered into ${tagId}`);
208221
}
209222

210-
this.allowedRoomsByFilters = allowedByFilters;
223+
const allowedRooms = Object.values(newMap).reduce((rv, v) => { rv.push(...v); return rv; }, <Room[]>[]);
224+
this.allowedRoomsByFilters = new Set(allowedRooms);
211225
this.filteredRooms = newMap;
212226
this.emit(LIST_UPDATED_EVENT);
213227
}

src/stores/room-list/filters/CommunityFilterCondition.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import { Room } from "matrix-js-sdk/src/models/room";
18-
import { FILTER_CHANGED, IFilterCondition } from "./IFilterCondition";
18+
import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "./IFilterCondition";
1919
import { Group } from "matrix-js-sdk/src/models/group";
2020
import { EventEmitter } from "events";
2121
import GroupStore from "../../GroupStore";
@@ -37,6 +37,11 @@ export class CommunityFilterCondition extends EventEmitter implements IFilterCon
3737
this.onStoreUpdate(); // trigger a false update to seed the store
3838
}
3939

40+
public get relativePriority(): FilterPriority {
41+
// Lowest priority so we can coarsely find rooms.
42+
return FilterPriority.Lowest;
43+
}
44+
4045
public isVisible(room: Room): boolean {
4146
return this.roomIds.includes(room.roomId);
4247
}

src/stores/room-list/filters/IFilterCondition.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ import { EventEmitter } from "events";
1919

2020
export const FILTER_CHANGED = "filter_changed";
2121

22+
export enum FilterPriority {
23+
Lowest,
24+
// in the middle would be Low, Normal, and High if we had a need
25+
Highest,
26+
}
27+
2228
/**
2329
* A filter condition for the room list, determining if a room
2430
* should be shown or not.
@@ -32,6 +38,12 @@ export const FILTER_CHANGED = "filter_changed";
3238
* as a change in the user's input), this emits FILTER_CHANGED.
3339
*/
3440
export interface IFilterCondition extends EventEmitter {
41+
/**
42+
* The relative priority that this filter should be applied with.
43+
* Lower priorities get applied first.
44+
*/
45+
relativePriority: FilterPriority;
46+
3547
/**
3648
* Determines if a given room should be visible under this
3749
* condition.

src/stores/room-list/filters/NameFilterCondition.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import { Room } from "matrix-js-sdk/src/models/room";
18-
import { FILTER_CHANGED, IFilterCondition } from "./IFilterCondition";
18+
import { FILTER_CHANGED, FilterPriority, IFilterCondition } from "./IFilterCondition";
1919
import { EventEmitter } from "events";
2020

2121
/**
@@ -29,6 +29,11 @@ export class NameFilterCondition extends EventEmitter implements IFilterConditio
2929
super();
3030
}
3131

32+
public get relativePriority(): FilterPriority {
33+
// We want this one to be at the highest priority so it can search within other filters.
34+
return FilterPriority.Highest;
35+
}
36+
3237
public get search(): string {
3338
return this._search;
3439
}

src/utils/arrays.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,63 @@ export function arrayDiff<T>(a: T[], b: T[]): { added: T[], removed: T[] } {
4545
removed: a.filter(i => !b.includes(i)),
4646
};
4747
}
48+
49+
/**
50+
* Helper functions to perform LINQ-like queries on arrays.
51+
*/
52+
export class ArrayUtil<T> {
53+
/**
54+
* Create a new array helper.
55+
* @param a The array to help. Can be modified in-place.
56+
*/
57+
constructor(private a: T[]) {
58+
}
59+
60+
/**
61+
* The value of this array, after all appropriate alterations.
62+
*/
63+
public get value(): T[] {
64+
return this.a;
65+
}
66+
67+
/**
68+
* Groups an array by keys.
69+
* @param fn The key-finding function.
70+
* @returns This.
71+
*/
72+
public groupBy<K>(fn: (a: T) => K): GroupedArray<K, T> {
73+
const obj = this.a.reduce((rv: Map<K, T[]>, val: T) => {
74+
const k = fn(val);
75+
if (!rv.has(k)) rv.set(k, []);
76+
rv.get(k).push(val);
77+
return rv;
78+
}, new Map<K, T[]>());
79+
return new GroupedArray(obj);
80+
}
81+
}
82+
83+
/**
84+
* Helper functions to perform LINQ-like queries on groups (maps).
85+
*/
86+
export class GroupedArray<K, T> {
87+
/**
88+
* Creates a new group helper.
89+
* @param val The group to help. Can be modified in-place.
90+
*/
91+
constructor(private val: Map<K, T[]>) {
92+
}
93+
94+
/**
95+
* Orders the grouping into an array using the provided key order.
96+
* @param keyOrder The key order.
97+
* @returns An array helper of the result.
98+
*/
99+
public orderBy(keyOrder: K[]): ArrayUtil<T> {
100+
const a: T[] = [];
101+
for (const k of keyOrder) {
102+
if (!this.val.has(k)) continue;
103+
a.push(...this.val.get(k));
104+
}
105+
return new ArrayUtil(a);
106+
}
107+
}

src/utils/enums.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
/**
18+
* Get the values for an enum.
19+
* @param e The enum.
20+
* @returns The enum values.
21+
*/
22+
export function getEnumValues<T>(e: any): T[] {
23+
const keys = Object.keys(e);
24+
return keys
25+
.filter(k => ['string', 'number'].includes(typeof(e[k])))
26+
.map(k => e[k]);
27+
}

0 commit comments

Comments
 (0)