UNPKG

matrix-react-sdk

Version:
204 lines (183 loc) 7.95 kB
/* Copyright 2024 New Vector Ltd. Copyright 2020 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import { Room } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { SortAlgorithm } from "../models"; import { sortRoomsWithAlgorithm } from "../tag-sorting"; import { OrderingAlgorithm } from "./OrderingAlgorithm"; import { RoomUpdateCause, TagID } from "../../models"; import { RoomNotificationStateStore } from "../../../notifications/RoomNotificationStateStore"; type NaturalCategorizedRoomMap = { defaultRooms: Room[]; mutedRooms: Room[]; }; /** * Uses the natural tag sorting algorithm order to determine tag ordering. No * additional behavioural changes are present. */ export class NaturalAlgorithm extends OrderingAlgorithm { private cachedCategorizedOrderedRooms: NaturalCategorizedRoomMap = { defaultRooms: [], mutedRooms: [], }; public constructor(tagId: TagID, initialSortingAlgorithm: SortAlgorithm) { super(tagId, initialSortingAlgorithm); } public setRooms(rooms: Room[]): void { const { defaultRooms, mutedRooms } = this.categorizeRooms(rooms); this.cachedCategorizedOrderedRooms = { defaultRooms: sortRoomsWithAlgorithm(defaultRooms, this.tagId, this.sortingAlgorithm), mutedRooms: sortRoomsWithAlgorithm(mutedRooms, this.tagId, this.sortingAlgorithm), }; this.buildCachedOrderedRooms(); } public handleRoomUpdate(room: Room, cause: RoomUpdateCause): boolean { const isSplice = cause === RoomUpdateCause.NewRoom || cause === RoomUpdateCause.RoomRemoved; const isInPlace = cause === RoomUpdateCause.Timeline || cause === RoomUpdateCause.ReadReceipt || cause === RoomUpdateCause.PossibleMuteChange; const isMuted = this.isMutedToBottom && this.getRoomIsMuted(room); if (!isSplice && !isInPlace) { throw new Error(`Unsupported update cause: ${cause}`); } if (cause === RoomUpdateCause.NewRoom) { if (isMuted) { this.cachedCategorizedOrderedRooms.mutedRooms = sortRoomsWithAlgorithm( [...this.cachedCategorizedOrderedRooms.mutedRooms, room], this.tagId, this.sortingAlgorithm, ); } else { this.cachedCategorizedOrderedRooms.defaultRooms = sortRoomsWithAlgorithm( [...this.cachedCategorizedOrderedRooms.defaultRooms, room], this.tagId, this.sortingAlgorithm, ); } this.buildCachedOrderedRooms(); return true; } else if (cause === RoomUpdateCause.RoomRemoved) { return this.removeRoom(room); } else if (cause === RoomUpdateCause.PossibleMuteChange) { if (this.isMutedToBottom) { return this.onPossibleMuteChange(room); } else { return false; } } // 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 if (isMuted) { this.cachedCategorizedOrderedRooms.mutedRooms = sortRoomsWithAlgorithm( this.cachedCategorizedOrderedRooms.mutedRooms, this.tagId, this.sortingAlgorithm, ); } else { this.cachedCategorizedOrderedRooms.defaultRooms = sortRoomsWithAlgorithm( this.cachedCategorizedOrderedRooms.defaultRooms, this.tagId, this.sortingAlgorithm, ); } this.buildCachedOrderedRooms(); return true; } /** * Remove a room from the cached room list * @param room Room to remove * @returns {boolean} true when room list should update as result */ private removeRoom(room: Room): boolean { const defaultIndex = this.cachedCategorizedOrderedRooms.defaultRooms.findIndex((r) => r.roomId === room.roomId); if (defaultIndex > -1) { this.cachedCategorizedOrderedRooms.defaultRooms.splice(defaultIndex, 1); this.buildCachedOrderedRooms(); return true; } const mutedIndex = this.cachedCategorizedOrderedRooms.mutedRooms.findIndex((r) => r.roomId === room.roomId); if (mutedIndex > -1) { this.cachedCategorizedOrderedRooms.mutedRooms.splice(mutedIndex, 1); this.buildCachedOrderedRooms(); return true; } logger.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`); // room was not in cached lists, no update return false; } /** * Sets cachedOrderedRooms from cachedCategorizedOrderedRooms */ private buildCachedOrderedRooms(): void { this.cachedOrderedRooms = [ ...this.cachedCategorizedOrderedRooms.defaultRooms, ...this.cachedCategorizedOrderedRooms.mutedRooms, ]; } private getRoomIsMuted(room: Room): boolean { // It's fine for us to call this a lot because it's cached, and we shouldn't be // wasting anything by doing so as the store holds single references const state = RoomNotificationStateStore.instance.getRoomState(room); return state.muted; } private categorizeRooms(rooms: Room[]): NaturalCategorizedRoomMap { if (!this.isMutedToBottom) { return { defaultRooms: rooms, mutedRooms: [] }; } return rooms.reduce<NaturalCategorizedRoomMap>( (acc, room: Room) => { if (this.getRoomIsMuted(room)) { acc.mutedRooms.push(room); } else { acc.defaultRooms.push(room); } return acc; }, { defaultRooms: [], mutedRooms: [] } as NaturalCategorizedRoomMap, ); } private onPossibleMuteChange(room: Room): boolean { const isMuted = this.getRoomIsMuted(room); if (isMuted) { const defaultIndex = this.cachedCategorizedOrderedRooms.defaultRooms.findIndex( (r) => r.roomId === room.roomId, ); // room has been muted if (defaultIndex > -1) { // remove from the default list this.cachedCategorizedOrderedRooms.defaultRooms.splice(defaultIndex, 1); // add to muted list and reorder this.cachedCategorizedOrderedRooms.mutedRooms = sortRoomsWithAlgorithm( [...this.cachedCategorizedOrderedRooms.mutedRooms, room], this.tagId, this.sortingAlgorithm, ); // rebuild this.buildCachedOrderedRooms(); return true; } } else { const mutedIndex = this.cachedCategorizedOrderedRooms.mutedRooms.findIndex((r) => r.roomId === room.roomId); // room has been unmuted if (mutedIndex > -1) { // remove from the muted list this.cachedCategorizedOrderedRooms.mutedRooms.splice(mutedIndex, 1); // add to default list and reorder this.cachedCategorizedOrderedRooms.defaultRooms = sortRoomsWithAlgorithm( [...this.cachedCategorizedOrderedRooms.defaultRooms, room], this.tagId, this.sortingAlgorithm, ); // rebuild this.buildCachedOrderedRooms(); return true; } } return false; } }