Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Fix notification badge for All Rooms space #7401

Merged
merged 2 commits into from
Dec 17, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ 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";
import {
RoomNotificationStateStore,
UPDATE_STATUS_INDICATOR,
} from "../../stores/notifications/RoomNotificationStateStore";
import { SettingLevel } from "../../settings/SettingLevel";
import { leaveRoomBehaviour } from "../../utils/membership";
import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog";
Expand Down Expand Up @@ -115,6 +118,7 @@ import InfoDialog from "../views/dialogs/InfoDialog";
import FeedbackDialog from "../views/dialogs/FeedbackDialog";
import AccessibleButton from "../views/elements/AccessibleButton";
import { ActionPayload } from "../../dispatcher/payloads";
import { SummarizedNotificationState } from "../../stores/notifications/SummarizedNotificationState";

/** constants for MatrixChat.state.view */
export enum Views {
Expand Down Expand Up @@ -322,6 +326,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// For PersistentElement
this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize);

RoomNotificationStateStore.instance.on(UPDATE_STATUS_INDICATOR, this.onUpdateStatusIndicator);

// Force users to go through the soft logout page if they're soft logged out
if (Lifecycle.isSoftLogout()) {
// When the session loads it'll be detected as soft logged out and a dispatch
Expand Down Expand Up @@ -1504,7 +1510,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.setState({ syncError: null });
}

this.updateStatusIndicator(state, prevState);
if (state === SyncState.Syncing && prevState === SyncState.Syncing) {
return;
}
Expand Down Expand Up @@ -2025,8 +2030,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
}

private updateStatusIndicator(state: SyncState, prevState?: SyncState): void {
const notificationState = RoomNotificationStateStore.instance.globalState;
private onUpdateStatusIndicator = (notificationState: SummarizedNotificationState, state: SyncState): void => {
const numUnreadRooms = notificationState.numUnreadStates; // we know that states === rooms here

if (PlatformPeg.get()) {
Expand All @@ -2043,7 +2047,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}

this.setPageSubtitle();
}
};

private onServerConfigChange = (serverConfig: ValidatedServerConfig) => {
this.setState({ serverConfig });
Expand Down
25 changes: 20 additions & 5 deletions src/components/views/spaces/SpacePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import React, {
Dispatch,
ReactNode,
SetStateAction,
useCallback,
useEffect,
useLayoutEffect,
useRef,
Expand All @@ -33,7 +34,7 @@ import { useContextMenu } from "../../structures/ContextMenu";
import SpaceCreateMenu from "./SpaceCreateMenu";
import { SpaceButton, SpaceItem } from "./SpaceTreeLevel";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import { useEventEmitter, useEventEmitterState } from "../../../hooks/useEventEmitter";
import SpaceStore from "../../../stores/spaces/SpaceStore";
import {
getMetaSpaceName,
Expand All @@ -45,7 +46,10 @@ import {
UPDATE_TOP_LEVEL_SPACES,
} from "../../../stores/spaces";
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import {
RoomNotificationStateStore,
UPDATE_STATUS_INDICATOR,
} from "../../../stores/notifications/RoomNotificationStateStore";
import SpaceContextMenu from "../context_menus/SpaceContextMenu";
import IconizedContextMenu, {
IconizedContextMenuCheckbox,
Expand All @@ -63,6 +67,7 @@ import { useDispatcher } from "../../../hooks/useDispatcher";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { ActionPayload } from "../../../dispatcher/payloads";
import { Action } from "../../../dispatcher/actions";
import { NotificationState } from "../../../stores/notifications/NotificationState";

const useSpaces = (): [Room[], MetaSpace[], Room[], SpaceKey] => {
const invites = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_INVITED_SPACES, () => {
Expand Down Expand Up @@ -136,20 +141,30 @@ const MetaSpaceButton = ({ selected, isPanelCollapsed, ...props }: IMetaSpaceBut
</li>;
};

const getHomeNotificationState = (): NotificationState => {
return SpaceStore.instance.allRoomsInHome
? RoomNotificationStateStore.instance.globalState
: SpaceStore.instance.getNotificationState(MetaSpace.Home);
};

const HomeButton = ({ selected, isPanelCollapsed }: MetaSpaceButtonProps) => {
const allRoomsInHome = useEventEmitterState(SpaceStore.instance, UPDATE_HOME_BEHAVIOUR, () => {
return SpaceStore.instance.allRoomsInHome;
});
const [notificationState, setNotificationState] = useState(getHomeNotificationState());
const updateNotificationState = useCallback(() => {
setNotificationState(getHomeNotificationState());
}, []);
useEffect(updateNotificationState, [updateNotificationState, allRoomsInHome]);
useEventEmitter(RoomNotificationStateStore.instance, UPDATE_STATUS_INDICATOR, updateNotificationState);

return <MetaSpaceButton
spaceKey={MetaSpace.Home}
className="mx_SpaceButton_home"
selected={selected}
isPanelCollapsed={isPanelCollapsed}
label={getMetaSpaceName(MetaSpace.Home, allRoomsInHome)}
notificationState={allRoomsInHome
? RoomNotificationStateStore.instance.globalState
: SpaceStore.instance.getNotificationState(MetaSpace.Home)}
notificationState={notificationState}
ContextMenuComponent={HomeButtonContextMenu}
contextMenuTooltip={_t("Options")}
/>;
Expand Down
43 changes: 30 additions & 13 deletions src/stores/notifications/RoomNotificationStateStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
*/

import { Room } from "matrix-js-sdk/src/models/room";
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";

import { ActionPayload } from "../../dispatcher/payloads";
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
Expand All @@ -23,17 +24,20 @@ import { DefaultTagID, TagID } from "../room-list/models";
import { FetchRoomFn, ListNotificationState } from "./ListNotificationState";
import { RoomNotificationState } from "./RoomNotificationState";
import { SummarizedNotificationState } from "./SummarizedNotificationState";
import { VisibilityProvider } from "../room-list/filters/VisibilityProvider";
import { ThreadsRoomNotificationState } from "./ThreadsRoomNotificationState";
import { VisibilityProvider } from "../room-list/filters/VisibilityProvider";

interface IState {}

export const UPDATE_STATUS_INDICATOR = Symbol("update-status-indicator");

export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
private static internalInstance = new RoomNotificationStateStore();

private roomMap = new Map<Room, RoomNotificationState>();
private roomThreadsMap = new Map<Room, ThreadsRoomNotificationState>();
private listMap = new Map<TagID, ListNotificationState>();
private _globalState = new SummarizedNotificationState();

private constructor() {
super(defaultDispatcher, {});
Expand All @@ -44,18 +48,7 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
* 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()) {
if (VisibilityProvider.instance.isRoomVisible(room)) {
globalState.add(this.getRoomState(room));
}
}
return globalState;
return this._globalState;
}

/**
Expand Down Expand Up @@ -108,6 +101,30 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
return RoomNotificationStateStore.internalInstance;
}

private onSync = (state: SyncState, prevState?: SyncState, data?: ISyncStateData) => {
// 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()) {
if (VisibilityProvider.instance.isRoomVisible(room)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume getVisibleRooms and VisibilityProvider check different things?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, getVisibleRooms is the js-sdk's interpretations which just filters out rooms which were upgraded.
VisibilityProvider is for react-sdk and element-web specifics, like hiding special Voip virtual rooms and whatnot

globalState.add(this.getRoomState(room));
}
}

if (this.globalState.symbol !== globalState.symbol ||
this.globalState.count !== globalState.count ||
this.globalState.color !== globalState.color ||
this.globalState.numUnreadStates !== globalState.numUnreadStates
) {
this._globalState = globalState;
this.emit(UPDATE_STATUS_INDICATOR, globalState, state, prevState, data);
}
};

protected async onReady() {
this.matrixClient.on("sync", this.onSync);
}

protected async onNotReady(): Promise<any> {
for (const roomState of this.roomMap.values()) {
roomState.destroy();
Expand Down