2020-03-20 23:38:20 +03:00
|
|
|
/*
|
|
|
|
Copyright 2018, 2019 New Vector Ltd
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
2020-03-20 23:38:20 +03:00
|
|
|
import SettingsStore from "../../settings/SettingsStore";
|
2020-06-12 06:24:25 +03:00
|
|
|
import { OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models";
|
2020-03-20 23:38:20 +03:00
|
|
|
import TagOrderStore from "../TagOrderStore";
|
2020-04-28 00:25:04 +03:00
|
|
|
import { AsyncStore } from "../AsyncStore";
|
2020-05-04 18:06:34 +03:00
|
|
|
import { Room } from "matrix-js-sdk/src/models/room";
|
2020-06-12 06:24:25 +03:00
|
|
|
import { IListOrderingMap, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models";
|
2020-05-14 22:45:17 +03:00
|
|
|
import { ActionPayload } from "../../dispatcher/payloads";
|
|
|
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
2020-05-26 00:54:02 +03:00
|
|
|
import { readReceiptChangeIsFor } from "../../utils/read-receipts";
|
2020-05-29 16:59:06 +03:00
|
|
|
import { IFilterCondition } from "./filters/IFilterCondition";
|
|
|
|
import { TagWatcher } from "./TagWatcher";
|
2020-06-06 03:44:05 +03:00
|
|
|
import RoomViewStore from "../RoomViewStore";
|
2020-06-12 06:24:25 +03:00
|
|
|
import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
|
2020-06-30 23:23:00 +03:00
|
|
|
import { EffectiveMembership, getEffectiveMembership } from "./membership";
|
2020-03-20 23:38:20 +03:00
|
|
|
|
|
|
|
interface IState {
|
|
|
|
tagsEnabled?: boolean;
|
|
|
|
}
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
/**
|
|
|
|
* The event/channel which is called when the room lists have been changed. Raised
|
|
|
|
* with one argument: the instance of the store.
|
|
|
|
*/
|
|
|
|
export const LISTS_UPDATE_EVENT = "lists_update";
|
|
|
|
|
2020-05-29 16:59:06 +03:00
|
|
|
export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
|
|
|
private _matrixClient: MatrixClient;
|
2020-03-20 23:38:20 +03:00
|
|
|
private initialListsGenerated = false;
|
|
|
|
private enabled = false;
|
2020-06-12 06:24:25 +03:00
|
|
|
private algorithm = new Algorithm();
|
2020-05-29 16:59:06 +03:00
|
|
|
private filterConditions: IFilterCondition[] = [];
|
|
|
|
private tagWatcher = new TagWatcher(this);
|
2020-03-20 23:38:20 +03:00
|
|
|
|
|
|
|
private readonly watchedSettings = [
|
|
|
|
'RoomList.orderAlphabetically',
|
|
|
|
'RoomList.orderByImportance',
|
|
|
|
'feature_custom_tags',
|
|
|
|
];
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super(defaultDispatcher);
|
|
|
|
|
|
|
|
this.checkEnabled();
|
|
|
|
for (const settingName of this.watchedSettings) SettingsStore.monitorSetting(settingName, null);
|
2020-06-06 03:44:05 +03:00
|
|
|
RoomViewStore.addListener(this.onRVSUpdate);
|
2020-06-12 06:24:25 +03:00
|
|
|
this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated);
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public get orderedLists(): ITagMap {
|
|
|
|
if (!this.algorithm) return {}; // No tags yet.
|
|
|
|
return this.algorithm.getOrderedRooms();
|
|
|
|
}
|
|
|
|
|
2020-05-29 16:59:06 +03:00
|
|
|
public get matrixClient(): MatrixClient {
|
|
|
|
return this._matrixClient;
|
|
|
|
}
|
|
|
|
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove enabled flag with the old RoomListStore: https://github.com/vector-im/riot-web/issues/14231
|
2020-03-20 23:38:20 +03:00
|
|
|
private checkEnabled() {
|
|
|
|
this.enabled = SettingsStore.isFeatureEnabled("feature_new_room_list");
|
|
|
|
if (this.enabled) {
|
2020-05-14 22:01:51 +03:00
|
|
|
console.log("⚡ new room list store engaged");
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
private async readAndCacheSettingsFromStore() {
|
2020-03-20 23:38:20 +03:00
|
|
|
const tagsEnabled = SettingsStore.isFeatureEnabled("feature_custom_tags");
|
2020-04-28 00:25:04 +03:00
|
|
|
await this.updateState({
|
2020-03-20 23:38:20 +03:00
|
|
|
tagsEnabled,
|
|
|
|
});
|
2020-06-12 06:24:25 +03:00
|
|
|
await this.updateAlgorithmInstances();
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
2020-06-06 03:44:05 +03:00
|
|
|
private onRVSUpdate = () => {
|
2020-06-29 05:03:04 +03:00
|
|
|
if (!this.enabled) return; // TODO: Remove with https://github.com/vector-im/riot-web/issues/14231
|
2020-06-06 03:44:05 +03:00
|
|
|
if (!this.matrixClient) return; // We assume there won't be RVS updates without a client
|
|
|
|
|
|
|
|
const activeRoomId = RoomViewStore.getRoomId();
|
|
|
|
if (!activeRoomId && this.algorithm.stickyRoom) {
|
|
|
|
this.algorithm.stickyRoom = null;
|
|
|
|
} else if (activeRoomId) {
|
|
|
|
const activeRoom = this.matrixClient.getRoom(activeRoomId);
|
2020-06-25 06:31:44 +03:00
|
|
|
if (!activeRoom) {
|
|
|
|
console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`);
|
|
|
|
this.algorithm.stickyRoom = null;
|
|
|
|
} else if (activeRoom !== this.algorithm.stickyRoom) {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-06-06 03:44:05 +03:00
|
|
|
console.log(`Changing sticky room to ${activeRoomId}`);
|
|
|
|
this.algorithm.stickyRoom = activeRoom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
protected async onDispatch(payload: ActionPayload) {
|
2020-03-20 23:38:20 +03:00
|
|
|
if (payload.action === 'MatrixActions.sync') {
|
|
|
|
// Filter out anything that isn't the first PREPARED sync.
|
|
|
|
if (!(payload.prevState === 'PREPARED' && payload.state !== 'PREPARED')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove with https://github.com/vector-im/riot-web/issues/14231
|
2020-03-20 23:38:20 +03:00
|
|
|
this.checkEnabled();
|
|
|
|
if (!this.enabled) return;
|
|
|
|
|
2020-05-29 16:59:06 +03:00
|
|
|
this._matrixClient = payload.matrixClient;
|
2020-03-20 23:38:20 +03:00
|
|
|
|
|
|
|
// Update any settings here, as some may have happened before we were logically ready.
|
2020-04-28 00:25:04 +03:00
|
|
|
console.log("Regenerating room lists: Startup");
|
|
|
|
await this.readAndCacheSettingsFromStore();
|
|
|
|
await this.regenerateAllLists();
|
2020-06-06 03:44:05 +03:00
|
|
|
this.onRVSUpdate(); // fake an RVS update to adjust sticky room, if needed
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Remove this once the RoomListStore becomes default
|
|
|
|
if (!this.enabled) return;
|
|
|
|
|
|
|
|
if (payload.action === 'on_client_not_viable' || payload.action === 'on_logged_out') {
|
|
|
|
// Reset state without causing updates as the client will have been destroyed
|
|
|
|
// and downstream code will throw NPE errors.
|
2020-04-28 00:25:04 +03:00
|
|
|
this.reset(null, true);
|
2020-05-29 16:59:06 +03:00
|
|
|
this._matrixClient = null;
|
2020-03-20 23:38:20 +03:00
|
|
|
this.initialListsGenerated = false; // we'll want to regenerate them
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything below here requires a MatrixClient or some sort of logical readiness.
|
|
|
|
const logicallyReady = this.matrixClient && this.initialListsGenerated;
|
|
|
|
if (!logicallyReady) return;
|
|
|
|
|
|
|
|
if (payload.action === 'setting_updated') {
|
|
|
|
if (this.watchedSettings.includes(payload.settingName)) {
|
2020-04-28 00:25:04 +03:00
|
|
|
console.log("Regenerating room lists: Settings changed");
|
|
|
|
await this.readAndCacheSettingsFromStore();
|
2020-03-20 23:38:20 +03:00
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
await this.regenerateAllLists(); // regenerate the lists now
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
2020-05-04 18:06:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.algorithm) {
|
|
|
|
// This shouldn't happen because `initialListsGenerated` implies we have an algorithm.
|
|
|
|
throw new Error("Room list store has no algorithm to process dispatcher update with");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (payload.action === 'MatrixActions.Room.receipt') {
|
2020-03-20 23:38:20 +03:00
|
|
|
// First see if the receipt event is for our own user. If it was, trigger
|
|
|
|
// a room update (we probably read the room on a different device).
|
2020-05-26 00:54:02 +03:00
|
|
|
if (readReceiptChangeIsFor(payload.event, this.matrixClient)) {
|
2020-06-23 00:12:30 +03:00
|
|
|
const room = payload.room;
|
2020-06-06 05:12:32 +03:00
|
|
|
if (!room) {
|
2020-06-23 00:12:30 +03:00
|
|
|
console.warn(`Own read receipt was in unknown room ${room.roomId}`);
|
2020-06-06 05:12:32 +03:00
|
|
|
return;
|
|
|
|
}
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-06-23 00:12:30 +03:00
|
|
|
console.log(`[RoomListDebug] Got own read receipt in ${room.roomId}`);
|
2020-06-06 05:12:32 +03:00
|
|
|
await this.handleRoomUpdate(room, RoomUpdateCause.ReadReceipt);
|
2020-05-26 00:54:02 +03:00
|
|
|
return;
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
} else if (payload.action === 'MatrixActions.Room.tags') {
|
2020-06-06 05:12:32 +03:00
|
|
|
const roomPayload = (<any>payload); // TODO: Type out the dispatcher types
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-06-06 05:12:32 +03:00
|
|
|
console.log(`[RoomListDebug] Got tag change in ${roomPayload.room.roomId}`);
|
|
|
|
await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange);
|
2020-05-04 18:06:34 +03:00
|
|
|
} else if (payload.action === 'MatrixActions.Room.timeline') {
|
|
|
|
const eventPayload = (<any>payload); // TODO: Type out the dispatcher types
|
|
|
|
|
|
|
|
// Ignore non-live events (backfill)
|
|
|
|
if (!eventPayload.isLiveEvent || !payload.isLiveUnfilteredRoomTimelineEvent) return;
|
|
|
|
|
|
|
|
const roomId = eventPayload.event.getRoomId();
|
|
|
|
const room = this.matrixClient.getRoom(roomId);
|
2020-05-29 16:59:06 +03:00
|
|
|
const tryUpdate = async (updatedRoom: Room) => {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-29 16:59:06 +03:00
|
|
|
console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()} in ${updatedRoom.roomId}`);
|
2020-06-16 05:11:06 +03:00
|
|
|
if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-06-25 06:31:44 +03:00
|
|
|
console.log(`[RoomListDebug] Got tombstone event - trying to remove now-dead room`);
|
|
|
|
const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']);
|
|
|
|
if (newRoom) {
|
|
|
|
// If we have the new room, then the new room check will have seen the predecessor
|
|
|
|
// and did the required updates, so do nothing here.
|
|
|
|
return;
|
|
|
|
}
|
2020-06-16 05:11:06 +03:00
|
|
|
}
|
2020-05-29 16:59:06 +03:00
|
|
|
await this.handleRoomUpdate(updatedRoom, RoomUpdateCause.Timeline);
|
|
|
|
};
|
|
|
|
if (!room) {
|
|
|
|
console.warn(`Live timeline event ${eventPayload.event.getId()} received without associated room`);
|
|
|
|
console.warn(`Queuing failed room update for retry as a result.`);
|
|
|
|
setTimeout(async () => {
|
|
|
|
const updatedRoom = this.matrixClient.getRoom(roomId);
|
|
|
|
await tryUpdate(updatedRoom);
|
|
|
|
}, 100); // 100ms should be enough for the room to show up
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
await tryUpdate(room);
|
|
|
|
}
|
2020-03-20 23:38:20 +03:00
|
|
|
} else if (payload.action === 'MatrixActions.Event.decrypted') {
|
2020-05-08 20:59:03 +03:00
|
|
|
const eventPayload = (<any>payload); // TODO: Type out the dispatcher types
|
|
|
|
const roomId = eventPayload.event.getRoomId();
|
|
|
|
const room = this.matrixClient.getRoom(roomId);
|
|
|
|
if (!room) {
|
|
|
|
console.warn(`Event ${eventPayload.event.getId()} was decrypted in an unknown room ${roomId}`);
|
|
|
|
return;
|
|
|
|
}
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-08 20:59:03 +03:00
|
|
|
console.log(`[RoomListDebug] Decrypted timeline event ${eventPayload.event.getId()} in ${roomId}`);
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Verify that e2e rooms are handled on init: https://github.com/vector-im/riot-web/issues/14238
|
2020-05-08 20:59:03 +03:00
|
|
|
// It seems like when viewing the room the timeline is decrypted, rather than at startup. This could
|
|
|
|
// cause inaccuracies with the list ordering. We may have to decrypt the last N messages of every room :(
|
|
|
|
await this.handleRoomUpdate(room, RoomUpdateCause.Timeline);
|
2020-03-20 23:38:20 +03:00
|
|
|
} else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') {
|
2020-06-06 05:12:32 +03:00
|
|
|
const eventPayload = (<any>payload); // TODO: Type out the dispatcher types
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-06-06 05:12:32 +03:00
|
|
|
console.log(`[RoomListDebug] Received updated DM map`);
|
|
|
|
const dmMap = eventPayload.event.getContent();
|
|
|
|
for (const userId of Object.keys(dmMap)) {
|
|
|
|
const roomIds = dmMap[userId];
|
|
|
|
for (const roomId of roomIds) {
|
|
|
|
const room = this.matrixClient.getRoom(roomId);
|
|
|
|
if (!room) {
|
|
|
|
console.warn(`${roomId} was found in DMs but the room is not in the store`);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect this RoomUpdateCause to no-op if there's no change, and we don't expect
|
|
|
|
// the user to have hundreds of rooms to update in one event. As such, we just hammer
|
|
|
|
// away at updates until the problem is solved. If we were expecting more than a couple
|
|
|
|
// of rooms to be updated at once, we would consider batching the rooms up.
|
|
|
|
await this.handleRoomUpdate(room, RoomUpdateCause.PossibleTagChange);
|
|
|
|
}
|
|
|
|
}
|
2020-03-20 23:38:20 +03:00
|
|
|
} else if (payload.action === 'MatrixActions.Room.myMembership') {
|
2020-05-29 16:59:06 +03:00
|
|
|
const membershipPayload = (<any>payload); // TODO: Type out the dispatcher types
|
2020-06-30 23:23:00 +03:00
|
|
|
const oldMembership = getEffectiveMembership(membershipPayload.oldMembership);
|
|
|
|
const newMembership = getEffectiveMembership(membershipPayload.membership);
|
|
|
|
if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-29 16:59:06 +03:00
|
|
|
console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`);
|
2020-06-25 06:31:44 +03:00
|
|
|
|
|
|
|
// If we're joining an upgraded room, we'll want to make sure we don't proliferate
|
|
|
|
// the dead room in the list.
|
|
|
|
const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", "");
|
|
|
|
if (createEvent && createEvent.getContent()['predecessor']) {
|
|
|
|
console.log(`[RoomListDebug] Room has a predecessor`);
|
|
|
|
const prevRoom = this.matrixClient.getRoom(createEvent.getContent()['predecessor']['room_id']);
|
|
|
|
if (prevRoom) {
|
|
|
|
const isSticky = this.algorithm.stickyRoom === prevRoom;
|
|
|
|
if (isSticky) {
|
|
|
|
console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`);
|
|
|
|
await this.algorithm.setStickyRoomAsync(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: we hit the algorithm instead of our handleRoomUpdate() function to
|
|
|
|
// avoid redundant updates.
|
|
|
|
console.log(`[RoomListDebug] Removing previous room from room list`);
|
|
|
|
await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(`[RoomListDebug] Adding new room to room list`);
|
|
|
|
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
|
2020-06-06 05:12:32 +03:00
|
|
|
return;
|
2020-05-29 16:59:06 +03:00
|
|
|
}
|
|
|
|
|
2020-06-30 23:23:00 +03:00
|
|
|
if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) {
|
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
|
|
|
console.log(`[RoomListDebug] Handling invite to ${membershipPayload.room.roomId}`);
|
|
|
|
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-06 05:12:32 +03:00
|
|
|
// If it's not a join, it's transitioning into a different list (possibly historical)
|
2020-06-30 23:23:00 +03:00
|
|
|
if (oldMembership !== newMembership) {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-06-06 05:12:32 +03:00
|
|
|
console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`);
|
2020-06-25 06:31:44 +03:00
|
|
|
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange);
|
2020-06-06 05:12:32 +03:00
|
|
|
return;
|
|
|
|
}
|
2020-05-04 18:06:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<any> {
|
|
|
|
const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause);
|
|
|
|
if (shouldUpdate) {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-04 18:06:34 +03:00
|
|
|
console.log(`[DEBUG] Room "${room.name}" (${room.roomId}) triggered by ${cause} requires list update`);
|
|
|
|
this.emit(LISTS_UPDATE_EVENT, this);
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 06:24:25 +03:00
|
|
|
public async setTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
|
|
|
await this.algorithm.setTagSorting(tagId, sort);
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
|
2020-06-12 06:24:25 +03:00
|
|
|
localStorage.setItem(`mx_tagSort_${tagId}`, sort);
|
|
|
|
}
|
|
|
|
|
|
|
|
public getTagSorting(tagId: TagID): SortAlgorithm {
|
|
|
|
return this.algorithm.getTagSorting(tagId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// noinspection JSMethodCanBeStatic
|
|
|
|
private getStoredTagSorting(tagId: TagID): SortAlgorithm {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
|
2020-06-12 06:24:25 +03:00
|
|
|
return <SortAlgorithm>localStorage.getItem(`mx_tagSort_${tagId}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async setListOrder(tagId: TagID, order: ListAlgorithm) {
|
|
|
|
await this.algorithm.setListOrdering(tagId, order);
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
|
2020-06-12 06:24:25 +03:00
|
|
|
localStorage.setItem(`mx_listOrder_${tagId}`, order);
|
|
|
|
}
|
|
|
|
|
|
|
|
public getListOrder(tagId: TagID): ListAlgorithm {
|
|
|
|
return this.algorithm.getListOrdering(tagId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// noinspection JSMethodCanBeStatic
|
|
|
|
private getStoredListOrder(tagId: TagID): ListAlgorithm {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
|
2020-06-12 06:24:25 +03:00
|
|
|
return <ListAlgorithm>localStorage.getItem(`mx_listOrder_${tagId}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async updateAlgorithmInstances() {
|
|
|
|
const orderByImportance = SettingsStore.getValue("RoomList.orderByImportance");
|
|
|
|
const orderAlphabetically = SettingsStore.getValue("RoomList.orderAlphabetically");
|
|
|
|
|
|
|
|
const defaultSort = orderAlphabetically ? SortAlgorithm.Alphabetic : SortAlgorithm.Recent;
|
|
|
|
const defaultOrder = orderByImportance ? ListAlgorithm.Importance : ListAlgorithm.Natural;
|
|
|
|
|
|
|
|
for (const tag of Object.keys(this.orderedLists)) {
|
|
|
|
const definedSort = this.getTagSorting(tag);
|
|
|
|
const definedOrder = this.getListOrder(tag);
|
|
|
|
|
|
|
|
const storedSort = this.getStoredTagSorting(tag);
|
|
|
|
const storedOrder = this.getStoredListOrder(tag);
|
|
|
|
|
|
|
|
const tagSort = storedSort ? storedSort : (definedSort ? definedSort : defaultSort);
|
|
|
|
const listOrder = storedOrder ? storedOrder : (definedOrder ? definedOrder : defaultOrder);
|
|
|
|
|
|
|
|
if (tagSort !== definedSort) {
|
|
|
|
await this.setTagSorting(tag, tagSort);
|
|
|
|
}
|
|
|
|
if (listOrder !== definedOrder) {
|
|
|
|
await this.setListOrder(tag, listOrder);
|
|
|
|
}
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
protected async updateState(newState: IState) {
|
2020-03-20 23:38:20 +03:00
|
|
|
if (!this.enabled) return;
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
await super.updateState(newState);
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
2020-05-29 16:59:06 +03:00
|
|
|
private onAlgorithmListUpdated = () => {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-29 16:59:06 +03:00
|
|
|
console.log("Underlying algorithm has triggered a list update - refiring");
|
|
|
|
this.emit(LISTS_UPDATE_EVENT, this);
|
|
|
|
};
|
|
|
|
|
2020-03-20 23:38:20 +03:00
|
|
|
private async regenerateAllLists() {
|
2020-04-28 00:25:04 +03:00
|
|
|
console.warn("Regenerating all room lists");
|
2020-05-29 16:59:06 +03:00
|
|
|
|
2020-06-12 06:24:25 +03:00
|
|
|
const sorts: ITagSortingMap = {};
|
|
|
|
const orders: IListOrderingMap = {};
|
2020-03-20 23:38:20 +03:00
|
|
|
for (const tagId of OrderedDefaultTagIDs) {
|
2020-06-12 06:24:25 +03:00
|
|
|
sorts[tagId] = this.getStoredTagSorting(tagId) || SortAlgorithm.Alphabetic;
|
|
|
|
orders[tagId] = this.getStoredListOrder(tagId) || ListAlgorithm.Natural;
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.state.tagsEnabled) {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Fix custom tags: https://github.com/vector-im/riot-web/issues/14091
|
2020-03-20 23:38:20 +03:00
|
|
|
const roomTags = TagOrderStore.getOrderedTags() || [];
|
2020-06-29 05:03:04 +03:00
|
|
|
|
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-03-20 23:38:20 +03:00
|
|
|
console.log("rtags", roomTags);
|
|
|
|
}
|
|
|
|
|
2020-06-12 06:24:25 +03:00
|
|
|
await this.algorithm.populateTags(sorts, orders);
|
2020-06-16 05:11:06 +03:00
|
|
|
await this.algorithm.setKnownRooms(this.matrixClient.getVisibleRooms());
|
2020-03-20 23:38:20 +03:00
|
|
|
|
|
|
|
this.initialListsGenerated = true;
|
|
|
|
|
2020-04-28 00:25:04 +03:00
|
|
|
this.emit(LISTS_UPDATE_EVENT, this);
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
2020-05-29 16:59:06 +03:00
|
|
|
|
|
|
|
public addFilter(filter: IFilterCondition): void {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-29 16:59:06 +03:00
|
|
|
console.log("Adding filter condition:", filter);
|
|
|
|
this.filterConditions.push(filter);
|
|
|
|
if (this.algorithm) {
|
|
|
|
this.algorithm.addFilterCondition(filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public removeFilter(filter: IFilterCondition): void {
|
2020-06-29 05:03:04 +03:00
|
|
|
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
2020-05-29 16:59:06 +03:00
|
|
|
console.log("Removing filter condition:", filter);
|
|
|
|
const idx = this.filterConditions.indexOf(filter);
|
|
|
|
if (idx >= 0) {
|
|
|
|
this.filterConditions.splice(idx, 1);
|
|
|
|
|
|
|
|
if (this.algorithm) {
|
|
|
|
this.algorithm.removeFilterCondition(filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export default class RoomListStore {
|
2020-05-29 16:59:06 +03:00
|
|
|
private static internalInstance: RoomListStore2;
|
2020-03-20 23:38:20 +03:00
|
|
|
|
2020-05-29 16:59:06 +03:00
|
|
|
public static get instance(): RoomListStore2 {
|
2020-03-20 23:38:20 +03:00
|
|
|
if (!RoomListStore.internalInstance) {
|
2020-05-29 16:59:06 +03:00
|
|
|
RoomListStore.internalInstance = new RoomListStore2();
|
2020-03-20 23:38:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return RoomListStore.internalInstance;
|
|
|
|
}
|
|
|
|
}
|
2020-05-29 16:59:06 +03:00
|
|
|
|
|
|
|
window.mx_RoomListStore2 = RoomListStore.instance;
|