From b9af446c1bcd28e9c3d99364c8f9b7cdb5f0df83 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Nov 2020 19:42:57 -0700 Subject: [PATCH 1/4] Make it possible in-code to hide rooms from the room list Fixes https://github.com/vector-im/element-web/issues/15745 This was surprisingly easy given the number of errors I remember last time, but here it is. This also includes an over-engineered VisibilityProvider with the intention that it'll get used in the future for things like Spaces and other X as Rooms stuff. --- src/customisations/RoomList.ts | 45 ++++++++++++++ src/stores/room-list/RoomListStore.ts | 4 +- src/stores/room-list/algorithms/Algorithm.ts | 5 ++ .../room-list/filters/VisibilityProvider.ts | 59 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/customisations/RoomList.ts create mode 100644 src/stores/room-list/filters/VisibilityProvider.ts diff --git a/src/customisations/RoomList.ts b/src/customisations/RoomList.ts new file mode 100644 index 0000000000..758b212aa2 --- /dev/null +++ b/src/customisations/RoomList.ts @@ -0,0 +1,45 @@ +/* + * 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 { Room } from "matrix-js-sdk/src/models/room"; + +// Populate this file with the details of your customisations when copying it. + +/** + * Determines if a room is visible in the room list or not. By default, + * all rooms are visible. Where special handling is performed by Element, + * those rooms will not be able to override their visibility in the room + * list - Element will make the decision without calling this function. + * + * This function should be as fast as possible to avoid slowing down the + * client. + * @param {Room} room The room to check the visibility of. + * @returns {boolean} True if the room should be visible, false otherwise. + */ +function isRoomVisible(room: Room): boolean { + return true; +} + +// This interface summarises all available customisation points and also marks +// them all as optional. This allows customisers to only define and export the +// customisations they need while still maintaining type safety. +export interface IRoomListCustomisations { + isRoomVisible?: typeof isRoomVisible; +} + +// A real customisation module will define and export one or more of the +// customisation points that make up the interface above. +export const RoomListCustomisations: IRoomListCustomisations = {}; diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 0f3138fe9e..c60f35118b 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -34,6 +34,7 @@ import { MarkedExecution } from "../../utils/MarkedExecution"; import { AsyncStoreWithClient } from "../AsyncStoreWithClient"; import { NameFilterCondition } from "./filters/NameFilterCondition"; import { RoomNotificationStateStore } from "../notifications/RoomNotificationStateStore"; +import { VisibilityProvider } from "./filters/VisibilityProvider"; interface IState { tagsEnabled?: boolean; @@ -544,7 +545,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient { public async regenerateAllLists({trigger = true}) { console.warn("Regenerating all room lists"); - const rooms = this.matrixClient.getVisibleRooms(); + const rooms = this.matrixClient.getVisibleRooms() + .filter(r => VisibilityProvider.instance.isRoomVisible(r)); const customTags = new Set(); if (this.state.tagsEnabled) { for (const room of rooms) { diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 439141edb4..25059aabe7 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -34,6 +34,7 @@ import { EffectiveMembership, getEffectiveMembership, splitRoomsByMembership } f import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm"; import { getListAlgorithmInstance } from "./list-ordering"; import SettingsStore from "../../../settings/SettingsStore"; +import { VisibilityProvider } from "../filters/VisibilityProvider"; /** * Fired when the Algorithm has determined a list has been updated. @@ -188,6 +189,10 @@ export class Algorithm extends EventEmitter { // 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 + } + // Set the last sticky room to indicate that we're in a change. The code throughout the // class can safely handle a null room, so this should be safe to do as a backup. this._lastStickyRoom = this._stickyRoom || {}; diff --git a/src/stores/room-list/filters/VisibilityProvider.ts b/src/stores/room-list/filters/VisibilityProvider.ts new file mode 100644 index 0000000000..def2c20514 --- /dev/null +++ b/src/stores/room-list/filters/VisibilityProvider.ts @@ -0,0 +1,59 @@ +/* + * 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 {Room} from "matrix-js-sdk/src/models/room"; +import { RoomListCustomisations } from "../../../customisations/RoomList"; + +export class VisibilityProvider { + private static internalInstance: VisibilityProvider; + + private constructor() { + } + + public static get instance(): VisibilityProvider { + if (!VisibilityProvider.internalInstance) { + VisibilityProvider.internalInstance = new VisibilityProvider(); + } + return VisibilityProvider.internalInstance; + } + + public isRoomVisible(room: Room): boolean { + let isVisible = true; // Returned at the end of this function + let forced = false; // When true, this function won't bother calling the customisation points + + // ------ + // TODO: The `if` statements to control visibility of custom room types + // would go here. The remainder of this function assumes that the statements + // will be here. + + // An example of how the `if` statements mentioned above would look follows. + // A real check would probably check for a `type` or something instead of the room ID. + // Note: the room ID here is intentionally invalid to prevent accidental hiding of someone's room. + // TODO: Remove this statement once we have a statement to replace it (just keeping the reference count up) + if (room.roomId === '~!JFmkoouJANxFGtmMYC:localhost') { + isVisible = false; + forced = true; + } + // ------ + + const isVisibleFn = RoomListCustomisations.isRoomVisible; + if (!forced && isVisibleFn) { + isVisible = isVisibleFn(room); + } + + return isVisible; + } +} From 80b93e0843c01eeb5196dc5ece41218921b682da Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Nov 2020 20:03:58 -0700 Subject: [PATCH 2/4] Mute all updates from rooms that are invisible --- src/stores/room-list/RoomListStore.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index c60f35118b..b2fe630760 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -402,6 +402,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise { + if (!VisibilityProvider.instance.isRoomVisible(room)) { + return; // don't do anything on rooms that aren't visible + } + const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause); if (shouldUpdate) { if (SettingsStore.getValue("advancedRoomListLogging")) { From b9c57f47b04df359eb840a53ce94f08778864e7a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 26 Nov 2020 08:01:38 -0700 Subject: [PATCH 3/4] Remove example --- src/stores/room-list/filters/VisibilityProvider.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/stores/room-list/filters/VisibilityProvider.ts b/src/stores/room-list/filters/VisibilityProvider.ts index def2c20514..2e4eb485c0 100644 --- a/src/stores/room-list/filters/VisibilityProvider.ts +++ b/src/stores/room-list/filters/VisibilityProvider.ts @@ -38,15 +38,6 @@ export class VisibilityProvider { // TODO: The `if` statements to control visibility of custom room types // would go here. The remainder of this function assumes that the statements // will be here. - - // An example of how the `if` statements mentioned above would look follows. - // A real check would probably check for a `type` or something instead of the room ID. - // Note: the room ID here is intentionally invalid to prevent accidental hiding of someone's room. - // TODO: Remove this statement once we have a statement to replace it (just keeping the reference count up) - if (room.roomId === '~!JFmkoouJANxFGtmMYC:localhost') { - isVisible = false; - forced = true; - } // ------ const isVisibleFn = RoomListCustomisations.isRoomVisible; From c2c328e23c66f4d1a50e163432b1fd029fbbe0cd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 26 Nov 2020 08:06:48 -0700 Subject: [PATCH 4/4] Appease the linter --- src/stores/room-list/filters/VisibilityProvider.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stores/room-list/filters/VisibilityProvider.ts b/src/stores/room-list/filters/VisibilityProvider.ts index 2e4eb485c0..553dd33ce0 100644 --- a/src/stores/room-list/filters/VisibilityProvider.ts +++ b/src/stores/room-list/filters/VisibilityProvider.ts @@ -31,13 +31,17 @@ export class VisibilityProvider { } public isRoomVisible(room: Room): boolean { + /* eslint-disable prefer-const */ let isVisible = true; // Returned at the end of this function let forced = false; // When true, this function won't bother calling the customisation points + /* eslint-enable prefer-const */ // ------ // TODO: The `if` statements to control visibility of custom room types // would go here. The remainder of this function assumes that the statements // will be here. + // + // When removing this comment block, please remove the lint disable lines in the area. // ------ const isVisibleFn = RoomListCustomisations.isRoomVisible;