Remove the need for a tag manager
Instead putting the tag handling in the Algorithm class
This commit is contained in:
parent
9c0422691a
commit
e7fffee175
9 changed files with 212 additions and 269 deletions
|
@ -109,9 +109,10 @@ all kinds of filtering.
|
||||||
|
|
||||||
## Class breakdowns
|
## Class breakdowns
|
||||||
|
|
||||||
The `RoomListStore` is the major coordinator of various `IAlgorithm` implementations, which take care
|
The `RoomListStore` is the major coordinator of various `Algorithm` implementations, which take care
|
||||||
of the various `ListAlgorithm` and `SortingAlgorithm` options. A `TagManager` is responsible for figuring
|
of the various `ListAlgorithm` and `SortingAlgorithm` options. The `Algorithm` superclass is also
|
||||||
out which tags get which rooms, as Matrix specifies them as a reverse map: tags are defined on rooms and
|
responsible for figuring out which tags get which rooms, as Matrix specifies them as a reverse map:
|
||||||
are not defined as a collection of rooms (unlike how they are presented to the user). Various list-specific
|
tags are defined on rooms and are not defined as a collection of rooms (unlike how they are presented
|
||||||
utilities are also included, though they are expected to move somewhere more general when needed. For
|
to the user). Various list-specific utilities are also included, though they are expected to move
|
||||||
example, the `membership` utilities could easily be moved elsewhere as needed.
|
somewhere more general when needed. For example, the `membership` utilities could easily be moved
|
||||||
|
elsewhere as needed.
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { ActionPayload, defaultDispatcher } from "../../dispatcher-types";
|
import { ActionPayload, defaultDispatcher } from "../../dispatcher-types";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { DefaultTagID, OrderedDefaultTagIDs, TagID } from "./models";
|
import { DefaultTagID, OrderedDefaultTagIDs, TagID } from "./models";
|
||||||
import { IAlgorithm, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/IAlgorithm";
|
import { Algorithm, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/Algorithm";
|
||||||
import TagOrderStore from "../TagOrderStore";
|
import TagOrderStore from "../TagOrderStore";
|
||||||
import { getAlgorithmInstance } from "./algorithms";
|
import { getAlgorithmInstance } from "./algorithms";
|
||||||
import { AsyncStore } from "../AsyncStore";
|
import { AsyncStore } from "../AsyncStore";
|
||||||
|
@ -41,7 +41,7 @@ class _RoomListStore extends AsyncStore<ActionPayload> {
|
||||||
private matrixClient: MatrixClient;
|
private matrixClient: MatrixClient;
|
||||||
private initialListsGenerated = false;
|
private initialListsGenerated = false;
|
||||||
private enabled = false;
|
private enabled = false;
|
||||||
private algorithm: IAlgorithm;
|
private algorithm: Algorithm;
|
||||||
|
|
||||||
private readonly watchedSettings = [
|
private readonly watchedSettings = [
|
||||||
'RoomList.orderAlphabetically',
|
'RoomList.orderAlphabetically',
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import RoomListStore from "./RoomListStore2";
|
import RoomListStore from "./RoomListStore2";
|
||||||
import OldRoomListStore from "../RoomListStore";
|
import OldRoomListStore from "../RoomListStore";
|
||||||
import { ITagMap } from "./algorithms/IAlgorithm";
|
import { ITagMap } from "./algorithms/Algorithm";
|
||||||
import { UPDATE_EVENT } from "../AsyncStore";
|
import { UPDATE_EVENT } from "../AsyncStore";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {EventEmitter} from "events";
|
|
||||||
|
|
||||||
// TODO: Docs on what this is
|
|
||||||
export class TagManager extends EventEmitter {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implementation.
|
|
||||||
// This will need to track where rooms belong in tags, and which tags they
|
|
||||||
// should be tracked within. This is algorithm independent because all the
|
|
||||||
// algorithms need to know what each tag contains.
|
|
||||||
//
|
|
||||||
// This will likely make use of the effective membership to determine the
|
|
||||||
// invite+historical sections.
|
|
||||||
}
|
|
178
src/stores/room-list/algorithms/Algorithm.ts
Normal file
178
src/stores/room-list/algorithms/Algorithm.ts
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DefaultTagID, TagID } from "../models";
|
||||||
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||||
|
import { EffectiveMembership, splitRoomsByMembership } from "../membership";
|
||||||
|
|
||||||
|
export enum SortAlgorithm {
|
||||||
|
Manual = "MANUAL",
|
||||||
|
Alphabetic = "ALPHABETIC",
|
||||||
|
Recent = "RECENT",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ListAlgorithm {
|
||||||
|
// Orders Red > Grey > Bold > Idle
|
||||||
|
Importance = "IMPORTANCE",
|
||||||
|
|
||||||
|
// Orders however the SortAlgorithm decides
|
||||||
|
Natural = "NATURAL",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITagSortingMap {
|
||||||
|
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
|
||||||
|
[tagId: TagID]: SortAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITagMap {
|
||||||
|
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
|
||||||
|
[tagId: TagID]: Room[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add locking support to avoid concurrent writes?
|
||||||
|
// TODO: EventEmitter support? Might not be needed.
|
||||||
|
|
||||||
|
export abstract class Algorithm {
|
||||||
|
protected cached: ITagMap = {};
|
||||||
|
protected sortAlgorithms: ITagSortingMap;
|
||||||
|
protected rooms: Room[] = [];
|
||||||
|
protected roomsByTag: {
|
||||||
|
// @ts-ignore - TS wants this to be a string but we know better
|
||||||
|
[tagId: TagID]: Room[];
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
protected constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the Algorithm to regenerate all lists, using the tags given
|
||||||
|
* as reference for which lists to generate and which way to generate
|
||||||
|
* them.
|
||||||
|
* @param {ITagSortingMap} tagSortingMap The tags to generate.
|
||||||
|
* @returns {Promise<*>} A promise which resolves when complete.
|
||||||
|
*/
|
||||||
|
public async populateTags(tagSortingMap: ITagSortingMap): Promise<any> {
|
||||||
|
if (!tagSortingMap) throw new Error(`Map cannot be null or empty`);
|
||||||
|
this.sortAlgorithms = tagSortingMap;
|
||||||
|
return this.setKnownRooms(this.rooms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an ordered set of rooms for the all known tags.
|
||||||
|
* @returns {ITagMap} The cached list of rooms, ordered,
|
||||||
|
* for each tag. May be empty, but never null/undefined.
|
||||||
|
*/
|
||||||
|
public getOrderedRooms(): ITagMap {
|
||||||
|
return this.cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seeds the Algorithm with a set of rooms. The algorithm will discard all
|
||||||
|
* previously known information and instead use these rooms instead.
|
||||||
|
* @param {Room[]} rooms The rooms to force the algorithm to use.
|
||||||
|
* @returns {Promise<*>} A promise which resolves when complete.
|
||||||
|
*/
|
||||||
|
public async setKnownRooms(rooms: Room[]): Promise<any> {
|
||||||
|
if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`);
|
||||||
|
if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`);
|
||||||
|
|
||||||
|
this.rooms = rooms;
|
||||||
|
|
||||||
|
const newTags: ITagMap = {};
|
||||||
|
for (const tagId in this.sortAlgorithms) {
|
||||||
|
// noinspection JSUnfilteredForInLoop
|
||||||
|
newTags[tagId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can avoid doing work, do so.
|
||||||
|
if (!rooms.length) {
|
||||||
|
await this.generateFreshTags(newTags); // just in case it wants to do something
|
||||||
|
this.cached = newTags;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split out the easy rooms first (leave and invite)
|
||||||
|
const memberships = splitRoomsByMembership(rooms);
|
||||||
|
for (const room of memberships[EffectiveMembership.Invite]) {
|
||||||
|
console.log(`[DEBUG] "${room.name}" (${room.roomId}) is an Invite`);
|
||||||
|
newTags[DefaultTagID.Invite].push(room);
|
||||||
|
}
|
||||||
|
for (const room of memberships[EffectiveMembership.Leave]) {
|
||||||
|
console.log(`[DEBUG] "${room.name}" (${room.roomId}) is Historical`);
|
||||||
|
newTags[DefaultTagID.Archived].push(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now process all the joined rooms. This is a bit more complicated
|
||||||
|
for (const room of memberships[EffectiveMembership.Join]) {
|
||||||
|
const tags = Object.keys(room.tags || {});
|
||||||
|
|
||||||
|
let inTag = false;
|
||||||
|
if (tags.length > 0) {
|
||||||
|
for (const tag of tags) {
|
||||||
|
if (!isNullOrUndefined(newTags[tag])) {
|
||||||
|
console.log(`[DEBUG] "${room.name}" (${room.roomId}) is tagged as ${tag}`);
|
||||||
|
newTags[tag].push(room);
|
||||||
|
inTag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inTag) {
|
||||||
|
// TODO: Determine if DM and push there instead
|
||||||
|
newTags[DefaultTagID.Untagged].push(room);
|
||||||
|
console.log(`[DEBUG] "${room.name}" (${room.roomId}) is Untagged`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.generateFreshTags(newTags);
|
||||||
|
|
||||||
|
this.cached = newTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the Algorithm believes a complete regeneration of the existing
|
||||||
|
* lists is needed.
|
||||||
|
* @param {ITagMap} updatedTagMap The tag map which needs populating. Each tag
|
||||||
|
* will already have the rooms which belong to it - they just need ordering. Must
|
||||||
|
* be mutated in place.
|
||||||
|
* @returns {Promise<*>} A promise which resolves when complete.
|
||||||
|
*/
|
||||||
|
protected abstract generateFreshTags(updatedTagMap: ITagMap): Promise<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the Algorithm wants a whole tag to be reordered. Typically this will
|
||||||
|
* be done whenever the tag's scope changes (added/removed rooms).
|
||||||
|
* @param {TagID} tagId The tag ID which changed.
|
||||||
|
* @param {Room[]} rooms The rooms within the tag, unordered.
|
||||||
|
* @returns {Promise<Room[]>} Resolves to the ordered rooms in the tag.
|
||||||
|
*/
|
||||||
|
protected abstract regenerateTag(tagId: TagID, rooms: Room[]): Promise<Room[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the Algorithm to update its knowledge of a room. For example, when
|
||||||
|
* a user tags a room, joins/creates a room, or leaves a room the Algorithm
|
||||||
|
* should be told that the room's info might have changed. The Algorithm
|
||||||
|
* may no-op this request if no changes are required.
|
||||||
|
* @param {Room} room The room which might have affected sorting.
|
||||||
|
* @returns {Promise<boolean>} A promise which resolve to true or false
|
||||||
|
* depending on whether or not getOrderedRooms() should be called after
|
||||||
|
* processing.
|
||||||
|
*/
|
||||||
|
// TODO: Take a ReasonForChange to better predict the behaviour?
|
||||||
|
// TODO: Intercept here and handle tag changes automatically
|
||||||
|
public abstract handleRoomUpdate(room: Room): Promise<boolean>;
|
||||||
|
}
|
|
@ -14,89 +14,29 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IAlgorithm, ITagMap, ITagSortingMap, ListAlgorithm } from "./IAlgorithm";
|
import { Algorithm, ITagMap } from "./Algorithm";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
|
||||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
|
||||||
import { DefaultTagID } from "../models";
|
import { DefaultTagID } from "../models";
|
||||||
import { splitRoomsByMembership } from "../membership";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A demonstration/temporary algorithm to verify the API surface works.
|
* A demonstration/temporary algorithm to verify the API surface works.
|
||||||
* TODO: Remove this before shipping
|
* TODO: Remove this before shipping
|
||||||
*/
|
*/
|
||||||
export class ChaoticAlgorithm implements IAlgorithm {
|
export class ChaoticAlgorithm extends Algorithm {
|
||||||
|
|
||||||
private cached: ITagMap = {};
|
constructor() {
|
||||||
private sortAlgorithms: ITagSortingMap;
|
super();
|
||||||
private rooms: Room[] = [];
|
|
||||||
|
|
||||||
constructor(private representativeAlgorithm: ListAlgorithm) {
|
|
||||||
console.log("Constructed a ChaoticAlgorithm");
|
console.log("Constructed a ChaoticAlgorithm");
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrderedRooms(): ITagMap {
|
protected async generateFreshTags(updatedTagMap: ITagMap): Promise<any> {
|
||||||
return this.cached;
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
async populateTags(tagSortingMap: ITagSortingMap): Promise<any> {
|
protected async regenerateTag(tagId: string | DefaultTagID, rooms: []): Promise<[]> {
|
||||||
if (!tagSortingMap) throw new Error(`Map cannot be null or empty`);
|
return Promise.resolve(rooms);
|
||||||
this.sortAlgorithms = tagSortingMap;
|
|
||||||
this.setKnownRooms(this.rooms); // regenerate the room lists
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRoomUpdate(room): Promise<boolean> {
|
public async handleRoomUpdate(room): Promise<boolean> {
|
||||||
return undefined;
|
return Promise.resolve(false);
|
||||||
}
|
|
||||||
|
|
||||||
setKnownRooms(rooms: Room[]): Promise<any> {
|
|
||||||
if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`);
|
|
||||||
if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`);
|
|
||||||
|
|
||||||
this.rooms = rooms;
|
|
||||||
|
|
||||||
const newTags = {};
|
|
||||||
for (const tagId in this.sortAlgorithms) {
|
|
||||||
// noinspection JSUnfilteredForInLoop
|
|
||||||
newTags[tagId] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can avoid doing work, do so.
|
|
||||||
if (!rooms.length) {
|
|
||||||
this.cached = newTags;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove logging
|
|
||||||
const memberships = splitRoomsByMembership(rooms);
|
|
||||||
console.log({alg: this.representativeAlgorithm, memberships});
|
|
||||||
|
|
||||||
// Step through each room and determine which tags it should be in.
|
|
||||||
// We don't care about ordering or sorting here - we're simply organizing things.
|
|
||||||
for (const room of rooms) {
|
|
||||||
const tags = room.tags;
|
|
||||||
let inTag = false;
|
|
||||||
for (const tagId in tags) {
|
|
||||||
// noinspection JSUnfilteredForInLoop
|
|
||||||
if (isNullOrUndefined(newTags[tagId])) {
|
|
||||||
// skip the tag if we don't know about it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
inTag = true;
|
|
||||||
|
|
||||||
// noinspection JSUnfilteredForInLoop
|
|
||||||
newTags[tagId].push(room);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the room wasn't pushed to a tag, push it to the untagged tag.
|
|
||||||
if (!inTag) {
|
|
||||||
newTags[DefaultTagID.Untagged].push(room);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Do sorting
|
|
||||||
|
|
||||||
// Finally, assign the tags to our cache
|
|
||||||
this.cached = newTags;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { TagID } from "../models";
|
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
|
||||||
|
|
||||||
|
|
||||||
export enum SortAlgorithm {
|
|
||||||
Manual = "MANUAL",
|
|
||||||
Alphabetic = "ALPHABETIC",
|
|
||||||
Recent = "RECENT",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ListAlgorithm {
|
|
||||||
// Orders Red > Grey > Bold > Idle
|
|
||||||
Importance = "IMPORTANCE",
|
|
||||||
|
|
||||||
// Orders however the SortAlgorithm decides
|
|
||||||
Natural = "NATURAL",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITagSortingMap {
|
|
||||||
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
|
|
||||||
[tagId: TagID]: SortAlgorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITagMap {
|
|
||||||
// @ts-ignore - TypeScript really wants this to be [tagId: string] but we know better.
|
|
||||||
[tagId: TagID]: Room[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Convert IAlgorithm to an abstract class?
|
|
||||||
// TODO: Add locking support to avoid concurrent writes?
|
|
||||||
// TODO: EventEmitter support
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an algorithm for the RoomListStore to use
|
|
||||||
*/
|
|
||||||
export interface IAlgorithm {
|
|
||||||
/**
|
|
||||||
* Asks the Algorithm to regenerate all lists, using the tags given
|
|
||||||
* as reference for which lists to generate and which way to generate
|
|
||||||
* them.
|
|
||||||
* @param {ITagSortingMap} tagSortingMap The tags to generate.
|
|
||||||
* @returns {Promise<*>} A promise which resolves when complete.
|
|
||||||
*/
|
|
||||||
populateTags(tagSortingMap: ITagSortingMap): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an ordered set of rooms for the all known tags.
|
|
||||||
* @returns {ITagMap} The cached list of rooms, ordered,
|
|
||||||
* for each tag. May be empty, but never null/undefined.
|
|
||||||
*/
|
|
||||||
getOrderedRooms(): ITagMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Seeds the Algorithm with a set of rooms. The algorithm will discard all
|
|
||||||
* previously known information and instead use these rooms instead.
|
|
||||||
* @param {Room[]} rooms The rooms to force the algorithm to use.
|
|
||||||
* @returns {Promise<*>} A promise which resolves when complete.
|
|
||||||
*/
|
|
||||||
setKnownRooms(rooms: Room[]): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asks the Algorithm to update its knowledge of a room. For example, when
|
|
||||||
* a user tags a room, joins/creates a room, or leaves a room the Algorithm
|
|
||||||
* should be told that the room's info might have changed. The Algorithm
|
|
||||||
* may no-op this request if no changes are required.
|
|
||||||
* @param {Room} room The room which might have affected sorting.
|
|
||||||
* @returns {Promise<boolean>} A promise which resolve to true or false
|
|
||||||
* depending on whether or not getOrderedRooms() should be called after
|
|
||||||
* processing.
|
|
||||||
*/
|
|
||||||
handleRoomUpdate(room: Room): Promise<boolean>; // TODO: Take a ReasonForChange to better predict the behaviour?
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IAlgorithm, ITagMap, ITagSortingMap } from "./IAlgorithm";
|
import { Algorithm, ITagMap, ITagSortingMap } from "./Algorithm";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||||
import { DefaultTagID, TagID } from "../models";
|
import { DefaultTagID, TagID } from "../models";
|
||||||
|
@ -60,7 +60,7 @@ export enum Category {
|
||||||
* within the same category. For more information, see the comments contained
|
* within the same category. For more information, see the comments contained
|
||||||
* within the class.
|
* within the class.
|
||||||
*/
|
*/
|
||||||
export class ImportanceAlgorithm implements IAlgorithm {
|
export class ImportanceAlgorithm extends Algorithm {
|
||||||
|
|
||||||
// HOW THIS WORKS
|
// HOW THIS WORKS
|
||||||
// --------------
|
// --------------
|
||||||
|
@ -68,7 +68,7 @@ export class ImportanceAlgorithm implements IAlgorithm {
|
||||||
// This block of comments assumes you've read the README one level higher.
|
// This block of comments assumes you've read the README one level higher.
|
||||||
// You should do that if you haven't already.
|
// You should do that if you haven't already.
|
||||||
//
|
//
|
||||||
// Tags are fed into the algorithmic functions from the TagManager changes,
|
// Tags are fed into the algorithmic functions from the Algorithm superclass,
|
||||||
// which cause subsequent updates to the room list itself. Categories within
|
// which cause subsequent updates to the room list itself. Categories within
|
||||||
// those tags are tracked as index numbers within the array (zero = top), with
|
// those tags are tracked as index numbers within the array (zero = top), with
|
||||||
// each sticky room being tracked separately. Internally, the category index
|
// each sticky room being tracked separately. Internally, the category index
|
||||||
|
@ -84,9 +84,6 @@ export class ImportanceAlgorithm implements IAlgorithm {
|
||||||
// updated as needed and not recalculated often. For example, when a room needs to
|
// updated as needed and not recalculated often. For example, when a room needs to
|
||||||
// move within a tag, the array in `this.cached` will be spliced instead of iterated.
|
// move within a tag, the array in `this.cached` will be spliced instead of iterated.
|
||||||
|
|
||||||
private cached: ITagMap = {};
|
|
||||||
private sortAlgorithms: ITagSortingMap;
|
|
||||||
private rooms: Room[] = [];
|
|
||||||
private indices: {
|
private indices: {
|
||||||
// @ts-ignore - TS wants this to be a string but we know better than it
|
// @ts-ignore - TS wants this to be a string but we know better than it
|
||||||
[tag: TagID]: {
|
[tag: TagID]: {
|
||||||
|
@ -118,72 +115,19 @@ export class ImportanceAlgorithm implements IAlgorithm {
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
console.log("Constructed an ImportanceAlgorithm");
|
console.log("Constructed an ImportanceAlgorithm");
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrderedRooms(): ITagMap {
|
protected async generateFreshTags(updatedTagMap: ITagMap): Promise<any> {
|
||||||
return this.cached;
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
async populateTags(tagSortingMap: ITagSortingMap): Promise<any> {
|
protected async regenerateTag(tagId: string | DefaultTagID, rooms: []): Promise<[]> {
|
||||||
if (!tagSortingMap) throw new Error(`Map cannot be null or empty`);
|
return Promise.resolve(rooms);
|
||||||
this.sortAlgorithms = tagSortingMap;
|
|
||||||
this.setKnownRooms(this.rooms); // regenerate the room lists
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRoomUpdate(room): Promise<boolean> {
|
public async handleRoomUpdate(room): Promise<boolean> {
|
||||||
return undefined;
|
return Promise.resolve(false);
|
||||||
}
|
|
||||||
|
|
||||||
setKnownRooms(rooms: Room[]): Promise<any> {
|
|
||||||
if (isNullOrUndefined(rooms)) throw new Error(`Array of rooms cannot be null`);
|
|
||||||
if (!this.sortAlgorithms) throw new Error(`Cannot set known rooms without a tag sorting map`);
|
|
||||||
|
|
||||||
this.rooms = rooms;
|
|
||||||
|
|
||||||
const newTags = {};
|
|
||||||
for (const tagId in this.sortAlgorithms) {
|
|
||||||
// noinspection JSUnfilteredForInLoop
|
|
||||||
newTags[tagId] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can avoid doing work, do so.
|
|
||||||
if (!rooms.length) {
|
|
||||||
this.cached = newTags;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove logging
|
|
||||||
const memberships = splitRoomsByMembership(rooms);
|
|
||||||
console.log({memberships});
|
|
||||||
|
|
||||||
// Step through each room and determine which tags it should be in.
|
|
||||||
// We don't care about ordering or sorting here - we're simply organizing things.
|
|
||||||
for (const room of rooms) {
|
|
||||||
const tags = room.tags;
|
|
||||||
let inTag = false;
|
|
||||||
for (const tagId in tags) {
|
|
||||||
// noinspection JSUnfilteredForInLoop
|
|
||||||
if (isNullOrUndefined(newTags[tagId])) {
|
|
||||||
// skip the tag if we don't know about it
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
inTag = true;
|
|
||||||
|
|
||||||
// noinspection JSUnfilteredForInLoop
|
|
||||||
newTags[tagId].push(room);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the room wasn't pushed to a tag, push it to the untagged tag.
|
|
||||||
if (!inTag) {
|
|
||||||
newTags[DefaultTagID.Untagged].push(room);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Do sorting
|
|
||||||
|
|
||||||
// Finally, assign the tags to our cache
|
|
||||||
this.cached = newTags;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IAlgorithm, ListAlgorithm } from "./IAlgorithm";
|
import { Algorithm, ListAlgorithm } from "./Algorithm";
|
||||||
import { ChaoticAlgorithm } from "./ChaoticAlgorithm";
|
import { ChaoticAlgorithm } from "./ChaoticAlgorithm";
|
||||||
import { ImportanceAlgorithm } from "./ImportanceAlgorithm";
|
import { ImportanceAlgorithm } from "./ImportanceAlgorithm";
|
||||||
|
|
||||||
const ALGORITHM_FACTORIES: { [algorithm in ListAlgorithm]: () => IAlgorithm } = {
|
const ALGORITHM_FACTORIES: { [algorithm in ListAlgorithm]: () => Algorithm } = {
|
||||||
[ListAlgorithm.Natural]: () => new ChaoticAlgorithm(ListAlgorithm.Natural),
|
[ListAlgorithm.Natural]: () => new ChaoticAlgorithm(ListAlgorithm.Natural),
|
||||||
[ListAlgorithm.Importance]: () => new ImportanceAlgorithm(),
|
[ListAlgorithm.Importance]: () => new ImportanceAlgorithm(),
|
||||||
};
|
};
|
||||||
|
@ -26,9 +26,9 @@ const ALGORITHM_FACTORIES: { [algorithm in ListAlgorithm]: () => IAlgorithm } =
|
||||||
/**
|
/**
|
||||||
* Gets an instance of the defined algorithm
|
* Gets an instance of the defined algorithm
|
||||||
* @param {ListAlgorithm} algorithm The algorithm to get an instance of.
|
* @param {ListAlgorithm} algorithm The algorithm to get an instance of.
|
||||||
* @returns {IAlgorithm} The algorithm instance.
|
* @returns {Algorithm} The algorithm instance.
|
||||||
*/
|
*/
|
||||||
export function getAlgorithmInstance(algorithm: ListAlgorithm): IAlgorithm {
|
export function getAlgorithmInstance(algorithm: ListAlgorithm): Algorithm {
|
||||||
if (!ALGORITHM_FACTORIES[algorithm]) {
|
if (!ALGORITHM_FACTORIES[algorithm]) {
|
||||||
throw new Error(`${algorithm} is not a known algorithm`);
|
throw new Error(`${algorithm} is not a known algorithm`);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue