mirror of https://github.com/vector-im/riot-web
				
				
				
			Replace countRoomsWithNotif with a dedicated NotificationState
Fixes https://github.com/vector-im/riot-web/issues/14694 Instead of spending 10-1000ms in a function iterating over a whole lot of room events, we can use our cached state from the Notification State Store. This commit sets up a structure that could be applied to communities in the TagPanel too, as that could probably use a similar optimization. This reduces the updateStatusIndicator() time to just 4ms on average.pull/21833/head
							parent
							
								
									6a29cd33c1
								
							
						
					
					
						commit
						dd16ec070c
					
				|  | @ -34,27 +34,6 @@ export function shouldShowMentionBadge(roomNotifState) { | |||
|     return MENTION_BADGE_STATES.includes(roomNotifState); | ||||
| } | ||||
| 
 | ||||
| export function countRoomsWithNotif(rooms) { | ||||
|     return rooms.reduce((result, room, index) => { | ||||
|         const roomNotifState = getRoomNotifsState(room.roomId); | ||||
|         const highlight = room.getUnreadNotificationCount('highlight') > 0; | ||||
|         const notificationCount = room.getUnreadNotificationCount(); | ||||
| 
 | ||||
|         const notifBadges = notificationCount > 0 && shouldShowNotifBadge(roomNotifState); | ||||
|         const mentionBadges = highlight && shouldShowMentionBadge(roomNotifState); | ||||
|         const isInvite = room.hasMembershipState(MatrixClientPeg.get().credentials.userId, 'invite'); | ||||
|         const badges = notifBadges || mentionBadges || isInvite; | ||||
| 
 | ||||
|         if (badges) { | ||||
|             result.count++; | ||||
|             if (highlight) { | ||||
|                 result.highlight = true; | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     }, {count: 0, highlight: false}); | ||||
| } | ||||
| 
 | ||||
| export function aggregateNotificationCount(rooms) { | ||||
|     return rooms.reduce((result, room) => { | ||||
|         const roomNotifState = getRoomNotifsState(room.roomId); | ||||
|  |  | |||
|  | @ -58,7 +58,6 @@ import { messageForSyncError } from '../../utils/ErrorUtils'; | |||
| import ResizeNotifier from "../../utils/ResizeNotifier"; | ||||
| import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils"; | ||||
| import DMRoomMap from '../../utils/DMRoomMap'; | ||||
| import { countRoomsWithNotif } from '../../RoomNotifs'; | ||||
| import ThemeWatcher from "../../settings/watchers/ThemeWatcher"; | ||||
| import { FontWatcher } from '../../settings/watchers/FontWatcher'; | ||||
| import { storeRoomAliasInCache } from '../../RoomAliasCache'; | ||||
|  | @ -75,6 +74,7 @@ import { | |||
| import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast"; | ||||
| import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; | ||||
| import ErrorDialog from "../views/dialogs/ErrorDialog"; | ||||
| import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore"; | ||||
| 
 | ||||
| /** constants for MatrixChat.state.view */ | ||||
| export enum Views { | ||||
|  | @ -1844,21 +1844,20 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { | |||
|     } | ||||
| 
 | ||||
|     updateStatusIndicator(state: string, prevState: string) { | ||||
|         // only count visible rooms to not torment the user with notification counts in rooms they can't see
 | ||||
|         // it will include highlights from the previous version of the room internally
 | ||||
|         const notifCount = countRoomsWithNotif(MatrixClientPeg.get().getVisibleRooms()).count; | ||||
|         const notificationState = RoomNotificationStateStore.instance.globalState; | ||||
|         const numUnreadRooms = notificationState.numUnreadStates; // we know that states === rooms here
 | ||||
| 
 | ||||
|         if (PlatformPeg.get()) { | ||||
|             PlatformPeg.get().setErrorStatus(state === 'ERROR'); | ||||
|             PlatformPeg.get().setNotificationCount(notifCount); | ||||
|             PlatformPeg.get().setNotificationCount(numUnreadRooms); | ||||
|         } | ||||
| 
 | ||||
|         this.subTitleStatus = ''; | ||||
|         if (state === "ERROR") { | ||||
|             this.subTitleStatus += `[${_t("Offline")}] `; | ||||
|         } | ||||
|         if (notifCount > 0) { | ||||
|             this.subTitleStatus += `[${notifCount}]`; | ||||
|         if (numUnreadRooms > 0) { | ||||
|             this.subTitleStatus += `[${numUnreadRooms}]`; | ||||
|         } | ||||
| 
 | ||||
|         this.setPageSubtitle(); | ||||
|  |  | |||
|  | @ -21,6 +21,8 @@ import { DefaultTagID, TagID } from "../room-list/models"; | |||
| import { FetchRoomFn, ListNotificationState } from "./ListNotificationState"; | ||||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { RoomNotificationState } from "./RoomNotificationState"; | ||||
| import { NotificationState } from "./NotificationState"; | ||||
| import { SummarizedNotificationState } from "./SummarizedNotificationState"; | ||||
| 
 | ||||
| interface IState {} | ||||
| 
 | ||||
|  | @ -33,6 +35,23 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> { | |||
|         super(defaultDispatcher, {}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets a snapshot of notification state for all visible rooms. The number of states recorded | ||||
|      * on the SummarizedNotificationState is equivalent to rooms. | ||||
|      */ | ||||
|     public get globalState(): SummarizedNotificationState { | ||||
|         // If we're not ready yet, just return an empty state
 | ||||
|         if (!this.matrixClient) return new SummarizedNotificationState(); | ||||
| 
 | ||||
|         // Only count visible rooms to not torment the user with notification counts in rooms they can't see.
 | ||||
|         // This will include highlights from the previous version of the room internally
 | ||||
|         const globalState = new SummarizedNotificationState(); | ||||
|         for (const room of this.matrixClient.getVisibleRooms()) { | ||||
|             globalState.add(this.getRoomState(room)); | ||||
|         } | ||||
|         return globalState; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new list notification state. The consumer is expected to set the rooms | ||||
|      * on the notification state, and destroy the state when it no longer needs it. | ||||
|  |  | |||
|  | @ -0,0 +1,62 @@ | |||
| /* | ||||
| 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 { NotificationColor } from "./NotificationColor"; | ||||
| import { NotificationState } from "./NotificationState"; | ||||
| 
 | ||||
| /** | ||||
|  * Summarizes a number of states into a unique snapshot. To populate, call | ||||
|  * the add() function with the notification states to be included. | ||||
|  * | ||||
|  * Useful for community notification counts, global notification counts, etc. | ||||
|  */ | ||||
| export class SummarizedNotificationState extends NotificationState { | ||||
|     private _totalStatesWithUnread = 0; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
|         this._symbol = null; | ||||
|         this._count = 0; | ||||
|         this._color = NotificationColor.None; | ||||
|     } | ||||
| 
 | ||||
|     public get numUnreadStates(): number { | ||||
|         return this._totalStatesWithUnread; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Append a notification state to this snapshot, taking the loudest NotificationColor | ||||
|      * of the two. By default this will not adopt the symbol of the other notification | ||||
|      * state to prevent the count from being lost in typical usage. | ||||
|      * @param other The other notification state to append. | ||||
|      * @param includeSymbol If true, the notification state's symbol will be taken if one | ||||
|      * is present. | ||||
|      */ | ||||
|     public add(other: NotificationState, includeSymbol = false) { | ||||
|         if (other.symbol && includeSymbol) { | ||||
|             this._symbol = other.symbol; | ||||
|         } | ||||
|         if (other.count) { | ||||
|             this._count += other.count; | ||||
|         } | ||||
|         if (other.color > this.color) { | ||||
|             this._color = other.color; | ||||
|         } | ||||
|         if (other.hasUnreadCount) { | ||||
|             this._totalStatesWithUnread++; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston