Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into spaces-jump-to-room
This commit is contained in:
@@ -22,6 +22,7 @@ import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import { arrayHasDiff } from "../utils/arrays";
|
||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
import SpaceStore from "./SpaceStore";
|
||||
|
||||
const MAX_ROOMS = 20; // arbitrary
|
||||
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up
|
||||
@@ -122,7 +123,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
private async appendRoom(room: Room) {
|
||||
if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) return; // hide space rooms
|
||||
if (SpaceStore.spacesEnabled && room.isSpaceRoom()) return; // hide space rooms
|
||||
let updated = false;
|
||||
const rooms = (this.state.rooms || []).slice(); // cheap clone
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class GroupFilterOrderStore extends Store {
|
||||
this.__emitChange();
|
||||
}
|
||||
|
||||
__onDispatch(payload) {
|
||||
__onDispatch(payload) { // eslint-disable-line @typescript-eslint/naming-convention
|
||||
switch (payload.action) {
|
||||
// Initialise state after initial sync
|
||||
case 'view_room': {
|
||||
|
||||
@@ -44,7 +44,7 @@ class LifecycleStore extends Store<ActionPayload> {
|
||||
this.__emitChange();
|
||||
}
|
||||
|
||||
protected __onDispatch(payload: ActionPayload) {
|
||||
protected __onDispatch(payload: ActionPayload) { // eslint-disable-line @typescript-eslint/naming-convention
|
||||
switch (payload.action) {
|
||||
case 'do_after_sync_prepared':
|
||||
this.setState({
|
||||
|
||||
@@ -22,7 +22,6 @@ import { RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS } from "./RightPanelStoreP
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
import { Action } from '../dispatcher/actions';
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
import RoomViewStore from './RoomViewStore';
|
||||
|
||||
interface RightPanelStoreState {
|
||||
// Whether or not to show the right panel at all. We split out rooms and groups
|
||||
@@ -68,6 +67,7 @@ const MEMBER_INFO_PHASES = [
|
||||
export default class RightPanelStore extends Store<ActionPayload> {
|
||||
private static instance: RightPanelStore;
|
||||
private state: RightPanelStoreState;
|
||||
private lastRoomId: string;
|
||||
|
||||
constructor() {
|
||||
super(dis);
|
||||
@@ -144,11 +144,13 @@ export default class RightPanelStore extends Store<ActionPayload> {
|
||||
this.__emitChange();
|
||||
}
|
||||
|
||||
__onDispatch(payload: ActionPayload) {
|
||||
__onDispatch(payload: ActionPayload) { // eslint-disable-line @typescript-eslint/naming-convention
|
||||
switch (payload.action) {
|
||||
case 'view_room':
|
||||
if (payload.room_id === this.lastRoomId) break; // skip this transition, probably a permalink
|
||||
// fallthrough
|
||||
case 'view_group':
|
||||
if (payload.room_id === RoomViewStore.getRoomId()) break; // skip this transition, probably a permalink
|
||||
this.lastRoomId = payload.room_id;
|
||||
|
||||
// Reset to the member list if we're viewing member info
|
||||
if (MEMBER_INFO_PHASES.includes(this.state.lastRoomPhase)) {
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stores where the user has scrolled to in each room
|
||||
*/
|
||||
class RoomScrollStateStore {
|
||||
constructor() {
|
||||
// A map from room id to scroll state.
|
||||
//
|
||||
// If there is no special scroll state (ie, we are following the live
|
||||
// timeline), the scroll state is null. Otherwise, it is an object with
|
||||
// the following properties:
|
||||
//
|
||||
// focussedEvent: the ID of the 'focussed' event. Typically this is
|
||||
// the last event fully visible in the viewport, though if we
|
||||
// have done an explicit scroll to an explicit event, it will be
|
||||
// that event.
|
||||
//
|
||||
// pixelOffset: the number of pixels the window is scrolled down
|
||||
// from the focussedEvent.
|
||||
this._scrollStateMap = {};
|
||||
}
|
||||
|
||||
getScrollState(roomId) {
|
||||
return this._scrollStateMap[roomId];
|
||||
}
|
||||
|
||||
setScrollState(roomId, scrollState) {
|
||||
this._scrollStateMap[roomId] = scrollState;
|
||||
}
|
||||
}
|
||||
|
||||
if (global.mx_RoomScrollStateStore === undefined) {
|
||||
global.mx_RoomScrollStateStore = new RoomScrollStateStore();
|
||||
}
|
||||
export default global.mx_RoomScrollStateStore;
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
export interface ScrollState {
|
||||
focussedEvent: string;
|
||||
pixelOffset: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores where the user has scrolled to in each room
|
||||
*/
|
||||
export class RoomScrollStateStore {
|
||||
// A map from room id to scroll state.
|
||||
//
|
||||
// If there is no special scroll state (ie, we are following the live
|
||||
// timeline), the scroll state is null. Otherwise, it is an object with
|
||||
// the following properties:
|
||||
//
|
||||
// focussedEvent: the ID of the 'focussed' event. Typically this is
|
||||
// the last event fully visible in the viewport, though if we
|
||||
// have done an explicit scroll to an explicit event, it will be
|
||||
// that event.
|
||||
//
|
||||
// pixelOffset: the number of pixels the window is scrolled down
|
||||
// from the focussedEvent.
|
||||
private scrollStateMap = new Map<string, ScrollState>();
|
||||
|
||||
public getScrollState(roomId: string): ScrollState {
|
||||
return this.scrollStateMap.get(roomId);
|
||||
}
|
||||
|
||||
setScrollState(roomId: string, scrollState: ScrollState): void {
|
||||
this.scrollStateMap.set(roomId, scrollState);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.mxRoomScrollStateStore === undefined) {
|
||||
window.mxRoomScrollStateStore = new RoomScrollStateStore();
|
||||
}
|
||||
export default window.mxRoomScrollStateStore;
|
||||
@@ -96,7 +96,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||
this.__emitChange();
|
||||
}
|
||||
|
||||
__onDispatch(payload) {
|
||||
__onDispatch(payload) { // eslint-disable-line @typescript-eslint/naming-convention
|
||||
switch (payload.action) {
|
||||
// view_room:
|
||||
// - room_alias: '#somealias:matrix.org'
|
||||
@@ -164,6 +164,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||
}
|
||||
break;
|
||||
case 'open_room_settings': {
|
||||
// FIXME: Using an import will result in test failures
|
||||
const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
|
||||
Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
|
||||
roomId: payload.room_id || this.state.roomId,
|
||||
@@ -324,8 +325,8 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||
msg = _t("There was an error joining the room");
|
||||
} else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') {
|
||||
msg = <div>
|
||||
{_t("Sorry, your homeserver is too old to participate in this room.")}<br />
|
||||
{_t("Please contact your homeserver administrator.")}
|
||||
{ _t("Sorry, your homeserver is too old to participate in this room.") }<br />
|
||||
{ _t("Please contact your homeserver administrator.") }
|
||||
</div>;
|
||||
} else if (err.httpStatus === 404) {
|
||||
const invitingUserId = this.getInvitingUserId(this.state.roomId);
|
||||
@@ -340,6 +341,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Using an import will result in test failures
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
|
||||
title: _t("Failed to join room"),
|
||||
@@ -427,7 +429,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||
}
|
||||
}
|
||||
|
||||
let singletonRoomViewStore = null;
|
||||
let singletonRoomViewStore: RoomViewStore = null;
|
||||
if (!singletonRoomViewStore) {
|
||||
singletonRoomViewStore = new RoomViewStore();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
import EventEmitter from 'events';
|
||||
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/matrix";
|
||||
import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api";
|
||||
import { PHASE_DONE as VERIF_PHASE_DONE } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||
|
||||
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||
|
||||
+27
-19
@@ -18,6 +18,7 @@ import { ListIteratee, Many, sortBy, throttle } from "lodash";
|
||||
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { ISpaceSummaryRoom } from "matrix-js-sdk/src/@types/spaces";
|
||||
|
||||
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
@@ -31,7 +32,6 @@ import { RoomNotificationStateStore } from "./notifications/RoomNotificationStat
|
||||
import { DefaultTagID } from "./room-list/models";
|
||||
import { EnhancedMap, mapDiff } from "../utils/maps";
|
||||
import { setHasDiff } from "../utils/sets";
|
||||
import { ISpaceSummaryEvent, ISpaceSummaryRoom } from "../components/structures/SpaceRoomDirectory";
|
||||
import RoomViewStore from "./RoomViewStore";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { arrayHasDiff } from "../utils/arrays";
|
||||
@@ -60,7 +60,13 @@ export interface ISuggestedRoom extends ISpaceSummaryRoom {
|
||||
|
||||
const MAX_SUGGESTED_ROOMS = 20;
|
||||
|
||||
const homeSpaceKey = SettingsStore.getValue("feature_spaces.all_rooms") ? "ALL_ROOMS" : "HOME_SPACE";
|
||||
// All of these settings cause the page to reload and can be costly if read frequently, so read them here only
|
||||
const spacesEnabled = SettingsStore.getValue("feature_spaces");
|
||||
const spacesTweakAllRoomsEnabled = SettingsStore.getValue("feature_spaces.all_rooms");
|
||||
const spacesTweakSpaceMemberDMsEnabled = SettingsStore.getValue("feature_spaces.space_member_dms");
|
||||
const spacesTweakSpaceDMBadgesEnabled = SettingsStore.getValue("feature_spaces.space_dm_badges");
|
||||
|
||||
const homeSpaceKey = spacesTweakAllRoomsEnabled ? "ALL_ROOMS" : "HOME_SPACE";
|
||||
const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || homeSpaceKey}`;
|
||||
|
||||
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms]
|
||||
@@ -220,10 +226,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
|
||||
public fetchSuggestedRooms = async (space: Room, limit = MAX_SUGGESTED_ROOMS): Promise<ISuggestedRoom[]> => {
|
||||
try {
|
||||
const data: {
|
||||
rooms: ISpaceSummaryRoom[];
|
||||
events: ISpaceSummaryEvent[];
|
||||
} = await this.matrixClient.getSpaceSummary(space.roomId, 0, true, false, limit);
|
||||
const data = await this.matrixClient.getSpaceSummary(space.roomId, 0, true, false, limit);
|
||||
|
||||
const viaMap = new EnhancedMap<string, Set<string>>();
|
||||
data.events.forEach(ev => {
|
||||
@@ -299,7 +302,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
|
||||
if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
if (!space && spacesTweakAllRoomsEnabled) {
|
||||
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
|
||||
}
|
||||
return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set();
|
||||
@@ -396,7 +399,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
};
|
||||
|
||||
private showInHomeSpace = (room: Room) => {
|
||||
if (SettingsStore.getValue("feature_spaces.all_rooms")) return true;
|
||||
if (spacesTweakAllRoomsEnabled) return true;
|
||||
if (room.isSpaceRoom()) return false;
|
||||
return !this.parentMap.get(room.roomId)?.size // put all orphaned rooms in the Home Space
|
||||
|| DMRoomMap.shared().getUserIdForRoomId(room.roomId) // put all DMs in the Home Space
|
||||
@@ -428,7 +431,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
const oldFilteredRooms = this.spaceFilteredRooms;
|
||||
this.spaceFilteredRooms = new Map();
|
||||
|
||||
if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
if (!spacesTweakAllRoomsEnabled) {
|
||||
// put all room invites in the Home Space
|
||||
const invites = visibleRooms.filter(r => !r.isSpaceRoom() && r.getMyMembership() === "invite");
|
||||
this.spaceFilteredRooms.set(HOME_SPACE, new Set<string>(invites.map(room => room.roomId)));
|
||||
@@ -455,7 +458,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
const roomIds = new Set(childRooms.map(r => r.roomId));
|
||||
const space = this.matrixClient?.getRoom(spaceId);
|
||||
|
||||
if (SettingsStore.getValue("feature_spaces.space_member_dms")) {
|
||||
if (spacesTweakSpaceMemberDMsEnabled) {
|
||||
// Add relevant DMs
|
||||
space?.getMembers().forEach(member => {
|
||||
if (member.membership !== "join" && member.membership !== "invite") return;
|
||||
@@ -489,7 +492,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// Update NotificationStates
|
||||
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
|
||||
if (roomIds.has(room.roomId)) {
|
||||
if (s !== HOME_SPACE && SettingsStore.getValue("feature_spaces.space_dm_badges")) return true;
|
||||
if (s !== HOME_SPACE && spacesTweakSpaceDMBadgesEnabled) return true;
|
||||
|
||||
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|
||||
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
|
||||
@@ -588,7 +591,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// TODO confirm this after implementing parenting behaviour
|
||||
if (room.isSpaceRoom()) {
|
||||
this.onSpaceUpdate();
|
||||
} else if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
} else if (!spacesTweakAllRoomsEnabled) {
|
||||
this.onRoomUpdate(room);
|
||||
}
|
||||
this.emit(room.roomId);
|
||||
@@ -612,7 +615,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
if (order !== lastOrder) {
|
||||
this.notifyIfOrderChanged();
|
||||
}
|
||||
} else if (ev.getType() === EventType.Tag && !SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
} else if (ev.getType() === EventType.Tag && !spacesTweakAllRoomsEnabled) {
|
||||
// If the room was in favourites and now isn't or the opposite then update its position in the trees
|
||||
const oldTags = lastEv?.getContent()?.tags || {};
|
||||
const newTags = ev.getContent()?.tags || {};
|
||||
@@ -652,13 +655,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
protected async onNotReady() {
|
||||
if (!SettingsStore.getValue("feature_spaces")) return;
|
||||
if (!SpaceStore.spacesEnabled) return;
|
||||
if (this.matrixClient) {
|
||||
this.matrixClient.removeListener("Room", this.onRoom);
|
||||
this.matrixClient.removeListener("Room.myMembership", this.onRoom);
|
||||
this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData);
|
||||
this.matrixClient.removeListener("RoomState.events", this.onRoomState);
|
||||
if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
if (!spacesTweakAllRoomsEnabled) {
|
||||
this.matrixClient.removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
}
|
||||
@@ -666,12 +669,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
protected async onReady() {
|
||||
if (!SettingsStore.getValue("feature_spaces")) return;
|
||||
if (!spacesEnabled) return;
|
||||
this.matrixClient.on("Room", this.onRoom);
|
||||
this.matrixClient.on("Room.myMembership", this.onRoom);
|
||||
this.matrixClient.on("Room.accountData", this.onRoomAccountData);
|
||||
this.matrixClient.on("RoomState.events", this.onRoomState);
|
||||
if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
if (!spacesTweakAllRoomsEnabled) {
|
||||
this.matrixClient.on("accountData", this.onAccountData);
|
||||
}
|
||||
|
||||
@@ -685,7 +688,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
protected async onAction(payload: ActionPayload) {
|
||||
if (!SettingsStore.getValue("feature_spaces")) return;
|
||||
if (!spacesEnabled) return;
|
||||
switch (payload.action) {
|
||||
case "view_room": {
|
||||
// Don't auto-switch rooms when reacting to a context-switch
|
||||
@@ -699,7 +702,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// as it will cause you to end up in the wrong room
|
||||
this.setActiveSpace(room, false);
|
||||
} else if (
|
||||
(!SettingsStore.getValue("feature_spaces.all_rooms") || this.activeSpace) &&
|
||||
(!spacesTweakAllRoomsEnabled || this.activeSpace) &&
|
||||
!this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)
|
||||
) {
|
||||
this.switchToRelatedSpace(roomId);
|
||||
@@ -791,6 +794,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
export default class SpaceStore {
|
||||
public static spacesEnabled = spacesEnabled;
|
||||
public static spacesTweakAllRoomsEnabled = spacesTweakAllRoomsEnabled;
|
||||
public static spacesTweakSpaceMemberDMsEnabled = spacesTweakSpaceMemberDMsEnabled;
|
||||
public static spacesTweakSpaceDMBadgesEnabled = spacesTweakSpaceDMBadgesEnabled;
|
||||
|
||||
private static internalInstance = new SpaceStoreClass();
|
||||
|
||||
public static get instance(): SpaceStoreClass {
|
||||
|
||||
@@ -45,6 +45,16 @@ export interface IThreepidInvite {
|
||||
inviterName: string;
|
||||
}
|
||||
|
||||
// Any data about the room that would normally come from the homeserver
|
||||
// but has been passed out-of-band, eg. the room name and avatar URL
|
||||
// from an email invite (a workaround for the fact that we can't
|
||||
// get this information from the HS using an email invite).
|
||||
export interface IOOBData {
|
||||
name?: string; // The room's name
|
||||
avatarUrl?: string; // The mxc:// avatar URL for the room
|
||||
inviterName?: string; // The display name of the person who invited us to the room
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX = "mx_threepid_invite_";
|
||||
|
||||
export default class ThreepidInviteStore extends EventEmitter {
|
||||
|
||||
@@ -27,10 +27,10 @@ const TYPING_SERVER_TIMEOUT = 30000;
|
||||
export default class TypingStore {
|
||||
private typingStates: {
|
||||
[roomId: string]: {
|
||||
isTyping: boolean,
|
||||
userTimer: Timer,
|
||||
serverTimer: Timer,
|
||||
},
|
||||
isTyping: boolean;
|
||||
userTimer: Timer;
|
||||
serverTimer: Timer;
|
||||
};
|
||||
};
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -15,7 +15,11 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import EventEmitter from "events";
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
// XXX: resize-observer-polyfill has types that now conflict with typescript's
|
||||
// own DOM types: https://github.com/que-etc/resize-observer-polyfill/issues/80
|
||||
// Using require here rather than import is a horrenous workaround. We should
|
||||
// be able to remove the polyfill once Safari 14 is released.
|
||||
const ResizeObserverPolyfill = require('resize-observer-polyfill'); // eslint-disable-line @typescript-eslint/no-var-requires
|
||||
import ResizeObserverEntry from 'resize-observer-polyfill/src/ResizeObserverEntry';
|
||||
|
||||
export enum UI_EVENTS {
|
||||
@@ -43,7 +47,7 @@ export default class UIStore extends EventEmitter {
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
this.windowHeight = window.innerHeight;
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.resizeObserverCallback);
|
||||
this.resizeObserver = new ResizeObserverPolyfill(this.resizeObserverCallback);
|
||||
this.resizeObserver.observe(document.body);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ import { WidgetType } from "../widgets/WidgetType";
|
||||
class WidgetEchoStore extends EventEmitter {
|
||||
private roomWidgetEcho: {
|
||||
[roomId: string]: {
|
||||
[widgetId: string]: IWidget,
|
||||
},
|
||||
[widgetId: string]: IWidget;
|
||||
};
|
||||
};
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -63,7 +63,7 @@ const PREVIEWS = {
|
||||
const MAX_EVENTS_BACKWARDS = 50;
|
||||
|
||||
// type merging ftw
|
||||
type TAG_ANY = "im.vector.any";
|
||||
type TAG_ANY = "im.vector.any"; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
const TAG_ANY: TAG_ANY = "im.vector.any";
|
||||
|
||||
interface IState {
|
||||
|
||||
@@ -35,6 +35,7 @@ import { NameFilterCondition } from "./filters/NameFilterCondition";
|
||||
import { RoomNotificationStateStore } from "../notifications/RoomNotificationStateStore";
|
||||
import { VisibilityProvider } from "./filters/VisibilityProvider";
|
||||
import { SpaceWatcher } from "./SpaceWatcher";
|
||||
import SpaceStore from "../SpaceStore";
|
||||
|
||||
interface IState {
|
||||
tagsEnabled?: boolean;
|
||||
@@ -73,10 +74,11 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
|
||||
constructor() {
|
||||
super(defaultDispatcher);
|
||||
this.setMaxListeners(20); // CustomRoomTagStore + RoomList + LeftPanel + 8xRoomSubList + spares
|
||||
}
|
||||
|
||||
private setupWatchers() {
|
||||
if (SettingsStore.getValue("feature_spaces")) {
|
||||
if (SpaceStore.spacesEnabled) {
|
||||
this.spaceWatcher = new SpaceWatcher(this);
|
||||
} else {
|
||||
this.tagWatcher = new TagWatcher(this);
|
||||
@@ -130,8 +132,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// Update any settings here, as some may have happened before we were logically ready.
|
||||
console.log("Regenerating room lists: Startup");
|
||||
await this.readAndCacheSettingsFromStore();
|
||||
await this.regenerateAllLists({ trigger: false });
|
||||
await this.handleRVSUpdate({ trigger: false }); // fake an RVS update to adjust sticky room, if needed
|
||||
this.regenerateAllLists({ trigger: false });
|
||||
this.handleRVSUpdate({ trigger: false }); // fake an RVS update to adjust sticky room, if needed
|
||||
|
||||
this.updateFn.mark(); // we almost certainly want to trigger an update.
|
||||
this.updateFn.trigger();
|
||||
@@ -148,7 +150,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
await this.updateState({
|
||||
tagsEnabled,
|
||||
});
|
||||
await this.updateAlgorithmInstances();
|
||||
this.updateAlgorithmInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,23 +158,23 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
* @param trigger Set to false to prevent a list update from being sent. Should only
|
||||
* be used if the calling code will manually trigger the update.
|
||||
*/
|
||||
private async handleRVSUpdate({ trigger = true }) {
|
||||
private handleRVSUpdate({ trigger = true }) {
|
||||
if (!this.matrixClient) return; // We assume there won't be RVS updates without a client
|
||||
|
||||
const activeRoomId = RoomViewStore.getRoomId();
|
||||
if (!activeRoomId && this.algorithm.stickyRoom) {
|
||||
await this.algorithm.setStickyRoom(null);
|
||||
this.algorithm.setStickyRoom(null);
|
||||
} else if (activeRoomId) {
|
||||
const activeRoom = this.matrixClient.getRoom(activeRoomId);
|
||||
if (!activeRoom) {
|
||||
console.warn(`${activeRoomId} is current in RVS but missing from client - clearing sticky room`);
|
||||
await this.algorithm.setStickyRoom(null);
|
||||
this.algorithm.setStickyRoom(null);
|
||||
} else if (activeRoom !== this.algorithm.stickyRoom) {
|
||||
if (SettingsStore.getValue("advancedRoomListLogging")) {
|
||||
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
|
||||
console.log(`Changing sticky room to ${activeRoomId}`);
|
||||
}
|
||||
await this.algorithm.setStickyRoom(activeRoom);
|
||||
this.algorithm.setStickyRoom(activeRoom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +226,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
console.log("Regenerating room lists: Settings changed");
|
||||
await this.readAndCacheSettingsFromStore();
|
||||
|
||||
await this.regenerateAllLists({ trigger: false }); // regenerate the lists now
|
||||
this.regenerateAllLists({ trigger: false }); // regenerate the lists now
|
||||
this.updateFn.trigger();
|
||||
}
|
||||
}
|
||||
@@ -366,7 +368,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
|
||||
console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`);
|
||||
}
|
||||
await this.algorithm.setStickyRoom(null);
|
||||
this.algorithm.setStickyRoom(null);
|
||||
}
|
||||
|
||||
// Note: we hit the algorithm instead of our handleRoomUpdate() function to
|
||||
@@ -375,7 +377,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
|
||||
console.log(`[RoomListDebug] Removing previous room from room list`);
|
||||
}
|
||||
await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved);
|
||||
this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +433,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
return; // don't do anything on new/moved rooms which ought not to be shown
|
||||
}
|
||||
|
||||
const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause);
|
||||
const shouldUpdate = this.algorithm.handleRoomUpdate(room, cause);
|
||||
if (shouldUpdate) {
|
||||
if (SettingsStore.getValue("advancedRoomListLogging")) {
|
||||
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
|
||||
@@ -460,13 +462,13 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
|
||||
// Reset the sticky room before resetting the known rooms so the algorithm
|
||||
// doesn't freak out.
|
||||
await this.algorithm.setStickyRoom(null);
|
||||
await this.algorithm.setKnownRooms(rooms);
|
||||
this.algorithm.setStickyRoom(null);
|
||||
this.algorithm.setKnownRooms(rooms);
|
||||
|
||||
// Set the sticky room back, if needed, now that we have updated the store.
|
||||
// This will use relative stickyness to the new room set.
|
||||
if (stickyIsStillPresent) {
|
||||
await this.algorithm.setStickyRoom(currentSticky);
|
||||
this.algorithm.setStickyRoom(currentSticky);
|
||||
}
|
||||
|
||||
// Finally, mark an update and resume updates from the algorithm
|
||||
@@ -475,12 +477,12 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
public async setTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
||||
await this.setAndPersistTagSorting(tagId, sort);
|
||||
this.setAndPersistTagSorting(tagId, sort);
|
||||
this.updateFn.trigger();
|
||||
}
|
||||
|
||||
private async setAndPersistTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
||||
await this.algorithm.setTagSorting(tagId, sort);
|
||||
private setAndPersistTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
||||
this.algorithm.setTagSorting(tagId, sort);
|
||||
// TODO: Per-account? https://github.com/vector-im/element-web/issues/14114
|
||||
localStorage.setItem(`mx_tagSort_${tagId}`, sort);
|
||||
}
|
||||
@@ -518,13 +520,13 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
return tagSort;
|
||||
}
|
||||
|
||||
public async setListOrder(tagId: TagID, order: ListAlgorithm) {
|
||||
await this.setAndPersistListOrder(tagId, order);
|
||||
public setListOrder(tagId: TagID, order: ListAlgorithm) {
|
||||
this.setAndPersistListOrder(tagId, order);
|
||||
this.updateFn.trigger();
|
||||
}
|
||||
|
||||
private async setAndPersistListOrder(tagId: TagID, order: ListAlgorithm) {
|
||||
await this.algorithm.setListOrdering(tagId, order);
|
||||
private setAndPersistListOrder(tagId: TagID, order: ListAlgorithm) {
|
||||
this.algorithm.setListOrdering(tagId, order);
|
||||
// TODO: Per-account? https://github.com/vector-im/element-web/issues/14114
|
||||
localStorage.setItem(`mx_listOrder_${tagId}`, order);
|
||||
}
|
||||
@@ -561,7 +563,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
return listOrder;
|
||||
}
|
||||
|
||||
private async updateAlgorithmInstances() {
|
||||
private updateAlgorithmInstances() {
|
||||
// We'll require an update, so mark for one. Marking now also prevents the calls
|
||||
// to setTagSorting and setListOrder from causing triggers.
|
||||
this.updateFn.mark();
|
||||
@@ -574,10 +576,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
const listOrder = this.calculateListOrder(tag);
|
||||
|
||||
if (tagSort !== definedSort) {
|
||||
await this.setAndPersistTagSorting(tag, tagSort);
|
||||
this.setAndPersistTagSorting(tag, tagSort);
|
||||
}
|
||||
if (listOrder !== definedOrder) {
|
||||
await this.setAndPersistListOrder(tag, listOrder);
|
||||
this.setAndPersistListOrder(tag, listOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,9 +610,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
|
||||
// if spaces are enabled only consider the prefilter conditions when there are no runtime conditions
|
||||
// for the search all spaces feature
|
||||
if (this.prefilterConditions.length > 0
|
||||
&& (!SettingsStore.getValue("feature_spaces") || !this.filterConditions.length)
|
||||
) {
|
||||
if (this.prefilterConditions.length > 0 && (!SpaceStore.spacesEnabled || !this.filterConditions.length)) {
|
||||
rooms = rooms.filter(r => {
|
||||
for (const filter of this.prefilterConditions) {
|
||||
if (!filter.isVisible(r)) {
|
||||
@@ -632,7 +632,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
* @param trigger Set to false to prevent a list update from being sent. Should only
|
||||
* be used if the calling code will manually trigger the update.
|
||||
*/
|
||||
public async regenerateAllLists({ trigger = true }) {
|
||||
public regenerateAllLists({ trigger = true }) {
|
||||
console.warn("Regenerating all room lists");
|
||||
|
||||
const rooms = this.getPlausibleRooms();
|
||||
@@ -656,8 +656,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
RoomListLayoutStore.instance.ensureLayoutExists(tagId);
|
||||
}
|
||||
|
||||
await this.algorithm.populateTags(sorts, orders);
|
||||
await this.algorithm.setKnownRooms(rooms);
|
||||
this.algorithm.populateTags(sorts, orders);
|
||||
this.algorithm.setKnownRooms(rooms);
|
||||
|
||||
this.initialListsGenerated = true;
|
||||
|
||||
@@ -682,7 +682,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
} else {
|
||||
this.filterConditions.push(filter);
|
||||
// Runtime filters with spaces disable prefiltering for the search all spaces feature
|
||||
if (SettingsStore.getValue("feature_spaces")) {
|
||||
if (SpaceStore.spacesEnabled) {
|
||||
// this has to be awaited so that `setKnownRooms` is called in time for the `addFilterCondition` below
|
||||
// this way the runtime filters are only evaluated on one dataset and not both.
|
||||
await this.recalculatePrefiltering();
|
||||
@@ -715,7 +715,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
|
||||
this.algorithm.removeFilterCondition(filter);
|
||||
}
|
||||
// Runtime filters with spaces disable prefiltering for the search all spaces feature
|
||||
if (SettingsStore.getValue("feature_spaces")) {
|
||||
if (SpaceStore.spacesEnabled) {
|
||||
promise = this.recalculatePrefiltering();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { RoomListStoreClass } from "./RoomListStore";
|
||||
import { SpaceFilterCondition } from "./filters/SpaceFilterCondition";
|
||||
import SpaceStore, { UPDATE_SELECTED_SPACE } from "../SpaceStore";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
|
||||
/**
|
||||
* Watches for changes in spaces to manage the filter on the provided RoomListStore
|
||||
@@ -29,7 +28,7 @@ export class SpaceWatcher {
|
||||
private activeSpace: Room = SpaceStore.instance.activeSpace;
|
||||
|
||||
constructor(private store: RoomListStoreClass) {
|
||||
if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
if (!SpaceStore.spacesTweakAllRoomsEnabled) {
|
||||
this.filter = new SpaceFilterCondition();
|
||||
this.updateFilter();
|
||||
store.addFilter(this.filter);
|
||||
@@ -41,7 +40,7 @@ export class SpaceWatcher {
|
||||
this.activeSpace = activeSpace;
|
||||
|
||||
if (this.filter) {
|
||||
if (activeSpace || !SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||
if (activeSpace || !SpaceStore.spacesTweakAllRoomsEnabled) {
|
||||
this.updateFilter();
|
||||
} else {
|
||||
this.store.removeFilter(this.filter);
|
||||
|
||||
@@ -16,8 +16,9 @@ limitations under the License.
|
||||
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import { arrayDiff, arrayHasDiff } from "../../../utils/arrays";
|
||||
import { DefaultTagID, RoomUpdateCause, TagID } from "../models";
|
||||
import {
|
||||
@@ -34,6 +35,7 @@ import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm";
|
||||
import { getListAlgorithmInstance } from "./list-ordering";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { VisibilityProvider } from "../filters/VisibilityProvider";
|
||||
import SpaceStore from "../../SpaceStore";
|
||||
|
||||
/**
|
||||
* Fired when the Algorithm has determined a list has been updated.
|
||||
@@ -121,8 +123,12 @@ export class Algorithm extends EventEmitter {
|
||||
* Awaitable version of the sticky room setter.
|
||||
* @param val The new room to sticky.
|
||||
*/
|
||||
public async setStickyRoom(val: Room) {
|
||||
await this.updateStickyRoom(val);
|
||||
public setStickyRoom(val: Room) {
|
||||
try {
|
||||
this.updateStickyRoom(val);
|
||||
} catch (e) {
|
||||
console.warn("Failed to update sticky room", e);
|
||||
}
|
||||
}
|
||||
|
||||
public getTagSorting(tagId: TagID): SortAlgorithm {
|
||||
@@ -130,13 +136,13 @@ export class Algorithm extends EventEmitter {
|
||||
return this.sortAlgorithms[tagId];
|
||||
}
|
||||
|
||||
public async setTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
||||
public setTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
||||
if (!tagId) throw new Error("Tag ID must be defined");
|
||||
if (!sort) throw new Error("Algorithm must be defined");
|
||||
this.sortAlgorithms[tagId] = sort;
|
||||
|
||||
const algorithm: OrderingAlgorithm = this.algorithms[tagId];
|
||||
await algorithm.setSortAlgorithm(sort);
|
||||
algorithm.setSortAlgorithm(sort);
|
||||
this._cachedRooms[tagId] = algorithm.orderedRooms;
|
||||
this.recalculateFilteredRoomsForTag(tagId); // update filter to re-sort the list
|
||||
this.recalculateStickyRoom(tagId); // update sticky room to make sure it appears if needed
|
||||
@@ -147,7 +153,7 @@ export class Algorithm extends EventEmitter {
|
||||
return this.listAlgorithms[tagId];
|
||||
}
|
||||
|
||||
public async setListOrdering(tagId: TagID, order: ListAlgorithm) {
|
||||
public setListOrdering(tagId: TagID, order: ListAlgorithm) {
|
||||
if (!tagId) throw new Error("Tag ID must be defined");
|
||||
if (!order) throw new Error("Algorithm must be defined");
|
||||
this.listAlgorithms[tagId] = order;
|
||||
@@ -155,7 +161,7 @@ export class Algorithm extends EventEmitter {
|
||||
const algorithm = getListAlgorithmInstance(order, tagId, this.sortAlgorithms[tagId]);
|
||||
this.algorithms[tagId] = algorithm;
|
||||
|
||||
await algorithm.setRooms(this._cachedRooms[tagId]);
|
||||
algorithm.setRooms(this._cachedRooms[tagId]);
|
||||
this._cachedRooms[tagId] = algorithm.orderedRooms;
|
||||
this.recalculateFilteredRoomsForTag(tagId); // update filter to re-sort the list
|
||||
this.recalculateStickyRoom(tagId); // update sticky room to make sure it appears if needed
|
||||
@@ -182,31 +188,25 @@ export class Algorithm extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleFilterChange() {
|
||||
await this.recalculateFilteredRooms();
|
||||
private handleFilterChange() {
|
||||
this.recalculateFilteredRooms();
|
||||
|
||||
// re-emit the update so the list store can fire an off-cycle update if needed
|
||||
if (this.updatesInhibited) return;
|
||||
this.emit(FILTER_CHANGED);
|
||||
}
|
||||
|
||||
private async updateStickyRoom(val: Room) {
|
||||
try {
|
||||
return await this.doUpdateStickyRoom(val);
|
||||
} finally {
|
||||
this._lastStickyRoom = null; // clear to indicate we're done changing
|
||||
}
|
||||
private updateStickyRoom(val: Room) {
|
||||
this.doUpdateStickyRoom(val);
|
||||
this._lastStickyRoom = null; // clear to indicate we're done changing
|
||||
}
|
||||
|
||||
private async doUpdateStickyRoom(val: Room) {
|
||||
if (SettingsStore.getValue("feature_spaces") && val?.isSpaceRoom() && val.getMyMembership() !== "invite") {
|
||||
private doUpdateStickyRoom(val: Room) {
|
||||
if (SpaceStore.spacesEnabled && val?.isSpaceRoom() && val.getMyMembership() !== "invite") {
|
||||
// no-op sticky rooms for spaces - they're effectively virtual rooms
|
||||
val = null;
|
||||
}
|
||||
|
||||
// Note throughout: We need async so we can wait for handleRoomUpdate() to do its thing,
|
||||
// otherwise we risk duplicating rooms.
|
||||
|
||||
if (val && !VisibilityProvider.instance.isRoomVisible(val)) {
|
||||
val = null; // the room isn't visible - lie to the rest of this function
|
||||
}
|
||||
@@ -222,7 +222,7 @@ export class Algorithm extends EventEmitter {
|
||||
this._stickyRoom = null; // clear before we go to update the algorithm
|
||||
|
||||
// Lie to the algorithm and re-add the room to the algorithm
|
||||
await this.handleRoomUpdate(stickyRoom, RoomUpdateCause.NewRoom);
|
||||
this.handleRoomUpdate(stickyRoom, RoomUpdateCause.NewRoom);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
@@ -268,10 +268,10 @@ export class Algorithm extends EventEmitter {
|
||||
// referential checks as the references can differ through the lifecycle.
|
||||
if (lastStickyRoom && lastStickyRoom.room && lastStickyRoom.room.roomId !== val.roomId) {
|
||||
// Lie to the algorithm and re-add the room to the algorithm
|
||||
await this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom);
|
||||
this.handleRoomUpdate(lastStickyRoom.room, RoomUpdateCause.NewRoom);
|
||||
}
|
||||
// Lie to the algorithm and remove the room from it's field of view
|
||||
await this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved);
|
||||
this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
// Check for tag & position changes while we're here. We also check the room to ensure
|
||||
// it is still the same room.
|
||||
@@ -461,9 +461,8 @@ export class Algorithm extends EventEmitter {
|
||||
* them.
|
||||
* @param {ITagSortingMap} tagSortingMap The tags to generate.
|
||||
* @param {IListOrderingMap} listOrderingMap The ordering of those tags.
|
||||
* @returns {Promise<*>} A promise which resolves when complete.
|
||||
*/
|
||||
public async populateTags(tagSortingMap: ITagSortingMap, listOrderingMap: IListOrderingMap): Promise<any> {
|
||||
public populateTags(tagSortingMap: ITagSortingMap, listOrderingMap: IListOrderingMap): void {
|
||||
if (!tagSortingMap) throw new Error(`Sorting map cannot be null or empty`);
|
||||
if (!listOrderingMap) throw new Error(`Ordering ma cannot be null or empty`);
|
||||
if (arrayHasDiff(Object.keys(tagSortingMap), Object.keys(listOrderingMap))) {
|
||||
@@ -512,9 +511,8 @@ export class Algorithm extends EventEmitter {
|
||||
* 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> {
|
||||
public setKnownRooms(rooms: Room[]): void {
|
||||
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`);
|
||||
|
||||
@@ -528,7 +526,7 @@ export class Algorithm extends EventEmitter {
|
||||
// Before we go any further we need to clear (but remember) the sticky room to
|
||||
// avoid accidentally duplicating it in the list.
|
||||
const oldStickyRoom = this._stickyRoom;
|
||||
await this.updateStickyRoom(null);
|
||||
if (oldStickyRoom) this.updateStickyRoom(null);
|
||||
|
||||
this.rooms = rooms;
|
||||
|
||||
@@ -540,7 +538,7 @@ export class Algorithm extends EventEmitter {
|
||||
|
||||
// If we can avoid doing work, do so.
|
||||
if (!rooms.length) {
|
||||
await this.generateFreshTags(newTags); // just in case it wants to do something
|
||||
this.generateFreshTags(newTags); // just in case it wants to do something
|
||||
this.cachedRooms = newTags;
|
||||
return;
|
||||
}
|
||||
@@ -577,7 +575,7 @@ export class Algorithm extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
await this.generateFreshTags(newTags);
|
||||
this.generateFreshTags(newTags);
|
||||
|
||||
this.cachedRooms = newTags; // this recalculates the filtered rooms for us
|
||||
this.updateTagsFromCache();
|
||||
@@ -586,7 +584,7 @@ export class Algorithm extends EventEmitter {
|
||||
// it was. It's entirely possible that it changed lists though, so if it did then
|
||||
// we also have to update the position of it.
|
||||
if (oldStickyRoom && oldStickyRoom.room) {
|
||||
await this.updateStickyRoom(oldStickyRoom.room);
|
||||
this.updateStickyRoom(oldStickyRoom.room);
|
||||
if (this._stickyRoom && this._stickyRoom.room) { // just in case the update doesn't go according to plan
|
||||
if (this._stickyRoom.tag !== oldStickyRoom.tag) {
|
||||
// We put the sticky room at the top of the list to treat it as an obvious tag change.
|
||||
@@ -651,16 +649,15 @@ export class Algorithm extends EventEmitter {
|
||||
* @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.
|
||||
*/
|
||||
private async generateFreshTags(updatedTagMap: ITagMap): Promise<any> {
|
||||
private generateFreshTags(updatedTagMap: ITagMap): void {
|
||||
if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from");
|
||||
|
||||
for (const tag of Object.keys(updatedTagMap)) {
|
||||
const algorithm: OrderingAlgorithm = this.algorithms[tag];
|
||||
if (!algorithm) throw new Error(`No algorithm for ${tag}`);
|
||||
|
||||
await algorithm.setRooms(updatedTagMap[tag]);
|
||||
algorithm.setRooms(updatedTagMap[tag]);
|
||||
updatedTagMap[tag] = algorithm.orderedRooms;
|
||||
}
|
||||
}
|
||||
@@ -672,11 +669,10 @@ export class Algorithm extends EventEmitter {
|
||||
* may no-op this request if no changes are required.
|
||||
* @param {Room} room The room which might have affected sorting.
|
||||
* @param {RoomUpdateCause} cause The reason for the update being triggered.
|
||||
* @returns {Promise<boolean>} A promise which resolve to true or false
|
||||
* depending on whether or not getOrderedRooms() should be called after
|
||||
* processing.
|
||||
* @returns {Promise<boolean>} A boolean of whether or not getOrderedRooms()
|
||||
* should be called after processing.
|
||||
*/
|
||||
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||
public handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean {
|
||||
if (SettingsStore.getValue("advancedRoomListLogging")) {
|
||||
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
|
||||
console.log(`Handle room update for ${room.roomId} called with cause ${cause}`);
|
||||
@@ -684,9 +680,9 @@ export class Algorithm extends EventEmitter {
|
||||
if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from");
|
||||
|
||||
// Note: check the isSticky against the room ID just in case the reference is wrong
|
||||
const isSticky = this._stickyRoom && this._stickyRoom.room && this._stickyRoom.room.roomId === room.roomId;
|
||||
const isSticky = this._stickyRoom?.room?.roomId === room.roomId;
|
||||
if (cause === RoomUpdateCause.NewRoom) {
|
||||
const isForLastSticky = this._lastStickyRoom && this._lastStickyRoom.room === room;
|
||||
const isForLastSticky = this._lastStickyRoom?.room === room;
|
||||
const roomTags = this.roomIdsToTags[room.roomId];
|
||||
const hasTags = roomTags && roomTags.length > 0;
|
||||
|
||||
@@ -743,7 +739,7 @@ export class Algorithm extends EventEmitter {
|
||||
}
|
||||
const algorithm: OrderingAlgorithm = this.algorithms[rmTag];
|
||||
if (!algorithm) throw new Error(`No algorithm for ${rmTag}`);
|
||||
await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved);
|
||||
algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved);
|
||||
this._cachedRooms[rmTag] = algorithm.orderedRooms;
|
||||
this.recalculateFilteredRoomsForTag(rmTag); // update filter to re-sort the list
|
||||
this.recalculateStickyRoom(rmTag); // update sticky room to make sure it moves if needed
|
||||
@@ -755,7 +751,7 @@ export class Algorithm extends EventEmitter {
|
||||
}
|
||||
const algorithm: OrderingAlgorithm = this.algorithms[addTag];
|
||||
if (!algorithm) throw new Error(`No algorithm for ${addTag}`);
|
||||
await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom);
|
||||
algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom);
|
||||
this._cachedRooms[addTag] = algorithm.orderedRooms;
|
||||
}
|
||||
|
||||
@@ -788,7 +784,7 @@ export class Algorithm extends EventEmitter {
|
||||
};
|
||||
} else {
|
||||
// We have to clear the lock as the sticky room change will trigger updates.
|
||||
await this.setStickyRoom(room);
|
||||
this.setStickyRoom(room);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -851,7 +847,7 @@ export class Algorithm extends EventEmitter {
|
||||
const algorithm: OrderingAlgorithm = this.algorithms[tag];
|
||||
if (!algorithm) throw new Error(`No algorithm for ${tag}`);
|
||||
|
||||
await algorithm.handleRoomUpdate(room, cause);
|
||||
algorithm.handleRoomUpdate(room, cause);
|
||||
this._cachedRooms[tag] = algorithm.orderedRooms;
|
||||
|
||||
// Flag that we've done something
|
||||
|
||||
@@ -94,15 +94,15 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
||||
return state.color;
|
||||
}
|
||||
|
||||
public async setRooms(rooms: Room[]): Promise<any> {
|
||||
public setRooms(rooms: Room[]): void {
|
||||
if (this.sortingAlgorithm === SortAlgorithm.Manual) {
|
||||
this.cachedOrderedRooms = await sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm);
|
||||
this.cachedOrderedRooms = sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm);
|
||||
} else {
|
||||
// Every other sorting type affects the categories, not the whole tag.
|
||||
const categorized = this.categorizeRooms(rooms);
|
||||
for (const category of Object.keys(categorized)) {
|
||||
const roomsToOrder = categorized[category];
|
||||
categorized[category] = await sortRoomsWithAlgorithm(roomsToOrder, this.tagId, this.sortingAlgorithm);
|
||||
categorized[category] = sortRoomsWithAlgorithm(roomsToOrder, this.tagId, this.sortingAlgorithm);
|
||||
}
|
||||
|
||||
const newlyOrganized: Room[] = [];
|
||||
@@ -118,12 +118,12 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSplice(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||
private handleSplice(room: Room, cause: RoomUpdateCause): boolean {
|
||||
if (cause === RoomUpdateCause.NewRoom) {
|
||||
const category = this.getRoomCategory(room);
|
||||
this.alterCategoryPositionBy(category, 1, this.indices);
|
||||
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
||||
await this.sortCategory(category);
|
||||
this.sortCategory(category);
|
||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||
const roomIdx = this.getRoomIndex(room);
|
||||
if (roomIdx === -1) {
|
||||
@@ -141,55 +141,49 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||
try {
|
||||
await this.updateLock.acquireAsync();
|
||||
|
||||
if (cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved) {
|
||||
return this.handleSplice(room, cause);
|
||||
}
|
||||
|
||||
if (cause !== RoomUpdateCause.Timeline && cause !== RoomUpdateCause.ReadReceipt) {
|
||||
throw new Error(`Unsupported update cause: ${cause}`);
|
||||
}
|
||||
|
||||
const category = this.getRoomCategory(room);
|
||||
if (this.sortingAlgorithm === SortAlgorithm.Manual) {
|
||||
return; // Nothing to do here.
|
||||
}
|
||||
|
||||
const roomIdx = this.getRoomIndex(room);
|
||||
if (roomIdx === -1) {
|
||||
throw new Error(`Room ${room.roomId} has no index in ${this.tagId}`);
|
||||
}
|
||||
|
||||
// Try to avoid doing array operations if we don't have to: only move rooms within
|
||||
// the categories if we're jumping categories
|
||||
const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
|
||||
if (oldCategory !== category) {
|
||||
// Move the room and update the indices
|
||||
this.moveRoomIndexes(1, oldCategory, category, this.indices);
|
||||
this.cachedOrderedRooms.splice(roomIdx, 1); // splice out the old index (fixed position)
|
||||
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
||||
// Note: if moveRoomIndexes() is called after the splice then the insert operation
|
||||
// will happen in the wrong place. Because we would have already adjusted the index
|
||||
// for the category, we don't need to determine how the room is moving in the list.
|
||||
// If we instead tried to insert before updating the indices, we'd have to determine
|
||||
// whether the room was moving later (towards IDLE) or earlier (towards RED) from its
|
||||
// current position, as it'll affect the category's start index after we remove the
|
||||
// room from the array.
|
||||
}
|
||||
|
||||
// Sort the category now that we've dumped the room in
|
||||
await this.sortCategory(category);
|
||||
|
||||
return true; // change made
|
||||
} finally {
|
||||
await this.updateLock.release();
|
||||
public handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean {
|
||||
if (cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved) {
|
||||
return this.handleSplice(room, cause);
|
||||
}
|
||||
|
||||
if (cause !== RoomUpdateCause.Timeline && cause !== RoomUpdateCause.ReadReceipt) {
|
||||
throw new Error(`Unsupported update cause: ${cause}`);
|
||||
}
|
||||
|
||||
const category = this.getRoomCategory(room);
|
||||
if (this.sortingAlgorithm === SortAlgorithm.Manual) {
|
||||
return; // Nothing to do here.
|
||||
}
|
||||
|
||||
const roomIdx = this.getRoomIndex(room);
|
||||
if (roomIdx === -1) {
|
||||
throw new Error(`Room ${room.roomId} has no index in ${this.tagId}`);
|
||||
}
|
||||
|
||||
// Try to avoid doing array operations if we don't have to: only move rooms within
|
||||
// the categories if we're jumping categories
|
||||
const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
|
||||
if (oldCategory !== category) {
|
||||
// Move the room and update the indices
|
||||
this.moveRoomIndexes(1, oldCategory, category, this.indices);
|
||||
this.cachedOrderedRooms.splice(roomIdx, 1); // splice out the old index (fixed position)
|
||||
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
||||
// Note: if moveRoomIndexes() is called after the splice then the insert operation
|
||||
// will happen in the wrong place. Because we would have already adjusted the index
|
||||
// for the category, we don't need to determine how the room is moving in the list.
|
||||
// If we instead tried to insert before updating the indices, we'd have to determine
|
||||
// whether the room was moving later (towards IDLE) or earlier (towards RED) from its
|
||||
// current position, as it'll affect the category's start index after we remove the
|
||||
// room from the array.
|
||||
}
|
||||
|
||||
// Sort the category now that we've dumped the room in
|
||||
this.sortCategory(category);
|
||||
|
||||
return true; // change made
|
||||
}
|
||||
|
||||
private async sortCategory(category: NotificationColor) {
|
||||
private sortCategory(category: NotificationColor) {
|
||||
// This should be relatively quick because the room is usually inserted at the top of the
|
||||
// category, and most popular sorting algorithms will deal with trying to keep the active
|
||||
// room at the top/start of the category. For the few algorithms that will have to move the
|
||||
@@ -201,7 +195,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
||||
const startIdx = this.indices[category];
|
||||
const numSort = nextCategoryStartIdx - startIdx; // splice() returns up to the max, so MAX_SAFE_INT is fine
|
||||
const unsortedSlice = this.cachedOrderedRooms.splice(startIdx, numSort);
|
||||
const sorted = await sortRoomsWithAlgorithm(unsortedSlice, this.tagId, this.sortingAlgorithm);
|
||||
const sorted = sortRoomsWithAlgorithm(unsortedSlice, this.tagId, this.sortingAlgorithm);
|
||||
this.cachedOrderedRooms.splice(startIdx, 0, ...sorted);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,42 +29,32 @@ export class NaturalAlgorithm extends OrderingAlgorithm {
|
||||
super(tagId, initialSortingAlgorithm);
|
||||
}
|
||||
|
||||
public async setRooms(rooms: Room[]): Promise<any> {
|
||||
this.cachedOrderedRooms = await sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm);
|
||||
public setRooms(rooms: Room[]): void {
|
||||
this.cachedOrderedRooms = sortRoomsWithAlgorithm(rooms, this.tagId, this.sortingAlgorithm);
|
||||
}
|
||||
|
||||
public async handleRoomUpdate(room, cause): Promise<boolean> {
|
||||
try {
|
||||
await this.updateLock.acquireAsync();
|
||||
|
||||
const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved;
|
||||
const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt;
|
||||
if (!isSplice && !isInPlace) {
|
||||
throw new Error(`Unsupported update cause: ${cause}`);
|
||||
}
|
||||
|
||||
if (cause === RoomUpdateCause.NewRoom) {
|
||||
this.cachedOrderedRooms.push(room);
|
||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||
const idx = this.getRoomIndex(room);
|
||||
if (idx >= 0) {
|
||||
this.cachedOrderedRooms.splice(idx, 1);
|
||||
} else {
|
||||
console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Optimize this to avoid useless operations: https://github.com/vector-im/element-web/issues/14457
|
||||
// For example, we can skip updates to alphabetic (sometimes) and manually ordered tags
|
||||
this.cachedOrderedRooms = await sortRoomsWithAlgorithm(
|
||||
this.cachedOrderedRooms,
|
||||
this.tagId,
|
||||
this.sortingAlgorithm,
|
||||
);
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
await this.updateLock.release();
|
||||
public handleRoomUpdate(room, cause): boolean {
|
||||
const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved;
|
||||
const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt;
|
||||
if (!isSplice && !isInPlace) {
|
||||
throw new Error(`Unsupported update cause: ${cause}`);
|
||||
}
|
||||
|
||||
if (cause === RoomUpdateCause.NewRoom) {
|
||||
this.cachedOrderedRooms.push(room);
|
||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||
const idx = this.getRoomIndex(room);
|
||||
if (idx >= 0) {
|
||||
this.cachedOrderedRooms.splice(idx, 1);
|
||||
} else {
|
||||
console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Optimize this to avoid useless operations: https://github.com/vector-im/element-web/issues/14457
|
||||
// For example, we can skip updates to alphabetic (sometimes) and manually ordered tags
|
||||
this.cachedOrderedRooms = sortRoomsWithAlgorithm(this.cachedOrderedRooms, this.tagId, this.sortingAlgorithm);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { RoomUpdateCause, TagID } from "../../models";
|
||||
import { SortAlgorithm } from "../models";
|
||||
import AwaitLock from "await-lock";
|
||||
|
||||
/**
|
||||
* Represents a list ordering algorithm. Subclasses should populate the
|
||||
@@ -26,7 +25,6 @@ import AwaitLock from "await-lock";
|
||||
export abstract class OrderingAlgorithm {
|
||||
protected cachedOrderedRooms: Room[];
|
||||
protected sortingAlgorithm: SortAlgorithm;
|
||||
protected readonly updateLock = new AwaitLock();
|
||||
|
||||
protected constructor(protected tagId: TagID, initialSortingAlgorithm: SortAlgorithm) {
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
@@ -45,21 +43,20 @@ export abstract class OrderingAlgorithm {
|
||||
* @param newAlgorithm The new algorithm. Must be defined.
|
||||
* @returns Resolves when complete.
|
||||
*/
|
||||
public async setSortAlgorithm(newAlgorithm: SortAlgorithm) {
|
||||
public setSortAlgorithm(newAlgorithm: SortAlgorithm) {
|
||||
if (!newAlgorithm) throw new Error("A sorting algorithm must be defined");
|
||||
this.sortingAlgorithm = newAlgorithm;
|
||||
|
||||
// Force regeneration of the rooms
|
||||
await this.setRooms(this.orderedRooms);
|
||||
this.setRooms(this.orderedRooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rooms the algorithm should be handling, implying a reconstruction
|
||||
* of the ordering.
|
||||
* @param rooms The rooms to use going forward.
|
||||
* @returns Resolves when complete.
|
||||
*/
|
||||
public abstract setRooms(rooms: Room[]): Promise<any>;
|
||||
public abstract setRooms(rooms: Room[]): void;
|
||||
|
||||
/**
|
||||
* Handle a room update. The Algorithm will only call this for causes which
|
||||
@@ -69,7 +66,7 @@ export abstract class OrderingAlgorithm {
|
||||
* @param cause The cause of the update.
|
||||
* @returns True if the update requires the Algorithm to update the presentation layers.
|
||||
*/
|
||||
public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean>;
|
||||
public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean;
|
||||
|
||||
protected getRoomIndex(room: Room): number {
|
||||
let roomIdx = this.cachedOrderedRooms.indexOf(room);
|
||||
|
||||
@@ -23,7 +23,7 @@ import { compare } from "../../../../utils/strings";
|
||||
* Sorts rooms according to the browser's determination of alphabetic.
|
||||
*/
|
||||
export class AlphabeticAlgorithm implements IAlgorithm {
|
||||
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
|
||||
public sortRooms(rooms: Room[], tagId: TagID): Room[] {
|
||||
return rooms.sort((a, b) => {
|
||||
return compare(a.name, b.name);
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface IAlgorithm {
|
||||
* Sorts the given rooms according to the sorting rules of the algorithm.
|
||||
* @param {Room[]} rooms The rooms to sort.
|
||||
* @param {TagID} tagId The tag ID in which the rooms are being sorted.
|
||||
* @returns {Promise<Room[]>} Resolves to the sorted rooms.
|
||||
* @returns {Room[]} Returns the sorted rooms.
|
||||
*/
|
||||
sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]>;
|
||||
sortRooms(rooms: Room[], tagId: TagID): Room[];
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { IAlgorithm } from "./IAlgorithm";
|
||||
* Sorts rooms according to the tag's `order` property on the room.
|
||||
*/
|
||||
export class ManualAlgorithm implements IAlgorithm {
|
||||
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
|
||||
public sortRooms(rooms: Room[], tagId: TagID): Room[] {
|
||||
const getOrderProp = (r: Room) => r.tags[tagId].order || 0;
|
||||
return rooms.sort((a, b) => {
|
||||
return getOrderProp(a) - getOrderProp(b);
|
||||
|
||||
@@ -97,7 +97,7 @@ export const sortRooms = (rooms: Room[]): Room[] => {
|
||||
* useful to the user.
|
||||
*/
|
||||
export class RecentAlgorithm implements IAlgorithm {
|
||||
public async sortRooms(rooms: Room[], tagId: TagID): Promise<Room[]> {
|
||||
public sortRooms(rooms: Room[], tagId: TagID): Room[] {
|
||||
return sortRooms(rooms);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ export function getSortingAlgorithmInstance(algorithm: SortAlgorithm): IAlgorith
|
||||
* @param {Room[]} rooms The rooms to sort.
|
||||
* @param {TagID} tagId The tag in which the sorting is occurring.
|
||||
* @param {SortAlgorithm} algorithm The algorithm to use for sorting.
|
||||
* @returns {Promise<Room[]>} Resolves to the sorted rooms.
|
||||
* @returns {Room[]} Returns the sorted rooms.
|
||||
*/
|
||||
export function sortRoomsWithAlgorithm(rooms: Room[], tagId: TagID, algorithm: SortAlgorithm): Promise<Room[]> {
|
||||
export function sortRoomsWithAlgorithm(rooms: Room[], tagId: TagID, algorithm: SortAlgorithm): Room[] {
|
||||
return getSortingAlgorithmInstance(algorithm).sortRooms(rooms, tagId);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import CallHandler from "../../../CallHandler";
|
||||
import { RoomListCustomisations } from "../../../customisations/RoomList";
|
||||
import VoipUserMapper from "../../../VoipUserMapper";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import SpaceStore from "../../SpaceStore";
|
||||
|
||||
export class VisibilityProvider {
|
||||
private static internalInstance: VisibilityProvider;
|
||||
@@ -50,7 +50,7 @@ export class VisibilityProvider {
|
||||
}
|
||||
|
||||
// hide space rooms as they'll be shown in the SpacePanel
|
||||
if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) {
|
||||
if (SpaceStore.spacesEnabled && room.isSpaceRoom()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
|
||||
import { getCustomTheme } from "../../theme";
|
||||
import CountlyAnalytics from "../../CountlyAnalytics";
|
||||
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
||||
import { MatrixEvent, IEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { ELEMENT_CLIENT_ID } from "../../identifiers";
|
||||
import { getUserLanguage } from "../../languageHandler";
|
||||
|
||||
@@ -415,7 +415,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
private feedEvent(ev: MatrixEvent) {
|
||||
if (!this.messaging) return;
|
||||
|
||||
const raw = ev.event as IEvent;
|
||||
const raw = ev.getEffectiveEvent();
|
||||
this.messaging.feedEvent(raw).catch(e => {
|
||||
console.error("Error sending event to widget: ", e);
|
||||
});
|
||||
|
||||
@@ -159,12 +159,12 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
if (results.length >= limit) break;
|
||||
|
||||
const ev = events[i];
|
||||
if (ev.getType() !== eventType) continue;
|
||||
if (ev.getType() !== eventType || ev.isState()) continue;
|
||||
if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()['msgtype']) continue;
|
||||
results.push(ev);
|
||||
}
|
||||
|
||||
return results.map(e => e.event);
|
||||
return results.map(e => e.getEffectiveEvent());
|
||||
}
|
||||
|
||||
public async readStateEvents(eventType: string, stateKey: string | undefined, limit: number): Promise<object[]> {
|
||||
|
||||
Reference in New Issue
Block a user