Unify unread notification state determination (#9941)
* Add tests for unread notification facilities Add some tests to guarantee some consistency in `useUnreadNotifications` and `RoomNotificationState`. Signed-off-by: Clark Fischer <clark.fischer@gmail.com> * Add RoomNotifs#determineUnreadState Intended as a singular replacement for the divergent implementations before. Signed-off-by: Clark Fischer <clark.fischer@gmail.com> * Unify room unread state determination Have both the class-based facility and the hook use the new unified logic in `RoomNotifs#determineUnreadState`. Addresses https://github.com/vector-im/element-web/issues/24229 Signed-off-by: Clark Fischer <clark.fischer@gmail.com> --------- Signed-off-by: Clark Fischer <clark.fischer@gmail.com> Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
53a9b6447b
commit
431afaafc6
9 changed files with 499 additions and 231 deletions
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2016, 2019, 2023 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.
|
||||
|
@ -16,18 +15,18 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
|
||||
import {
|
||||
ConditionKind,
|
||||
IPushRule,
|
||||
PushRuleActionName,
|
||||
PushRuleKind,
|
||||
TweakName,
|
||||
} from "matrix-js-sdk/src/@types/PushRules";
|
||||
import { NotificationCountType } from "matrix-js-sdk/src/models/room";
|
||||
import { ConditionKind, PushRuleActionName, PushRuleKind, TweakName } from "matrix-js-sdk/src/@types/PushRules";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import type { IPushRule } from "matrix-js-sdk/src/@types/PushRules";
|
||||
import type { Room } from "matrix-js-sdk/src/models/room";
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||
import { NotificationColor } from "./stores/notifications/NotificationColor";
|
||||
import { getUnsentMessages } from "./components/structures/RoomStatusBar";
|
||||
import { doesRoomHaveUnreadMessages, doesRoomOrThreadHaveUnreadMessages } from "./Unread";
|
||||
import { getEffectiveMembership, EffectiveMembership } from "./utils/membership";
|
||||
|
||||
export enum RoomNotifState {
|
||||
AllMessagesLoud = "all_messages_loud",
|
||||
|
@ -36,7 +35,7 @@ export enum RoomNotifState {
|
|||
Mute = "mute",
|
||||
}
|
||||
|
||||
export function getRoomNotifsState(client: MatrixClient, roomId: string): RoomNotifState {
|
||||
export function getRoomNotifsState(client: MatrixClient, roomId: string): RoomNotifState | null {
|
||||
if (client.isGuest()) return RoomNotifState.AllMessages;
|
||||
|
||||
// look through the override rules for a rule affecting this room:
|
||||
|
@ -177,7 +176,7 @@ function setRoomNotifsStateUnmuted(roomId: string, newState: RoomNotifState): Pr
|
|||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function findOverrideMuteRule(roomId: string): IPushRule {
|
||||
function findOverrideMuteRule(roomId: string): IPushRule | null {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (!cli?.pushRules?.global?.override) {
|
||||
return null;
|
||||
|
@ -201,3 +200,44 @@ function isRuleForRoom(roomId: string, rule: IPushRule): boolean {
|
|||
function isMuteRule(rule: IPushRule): boolean {
|
||||
return rule.actions.length === 1 && rule.actions[0] === PushRuleActionName.DontNotify;
|
||||
}
|
||||
|
||||
export function determineUnreadState(
|
||||
room: Room,
|
||||
threadId?: string,
|
||||
): { color: NotificationColor; symbol: string | null; count: number } {
|
||||
if (getUnsentMessages(room, threadId).length > 0) {
|
||||
return { symbol: "!", count: 1, color: NotificationColor.Unsent };
|
||||
}
|
||||
|
||||
if (getEffectiveMembership(room.getMyMembership()) === EffectiveMembership.Invite) {
|
||||
return { symbol: "!", count: 1, color: NotificationColor.Red };
|
||||
}
|
||||
|
||||
if (getRoomNotifsState(room.client, room.roomId) === RoomNotifState.Mute) {
|
||||
return { symbol: null, count: 0, color: NotificationColor.None };
|
||||
}
|
||||
|
||||
const redNotifs = getUnreadNotificationCount(room, NotificationCountType.Highlight, threadId);
|
||||
const greyNotifs = getUnreadNotificationCount(room, NotificationCountType.Total, threadId);
|
||||
|
||||
const trueCount = greyNotifs || redNotifs;
|
||||
if (redNotifs > 0) {
|
||||
return { symbol: null, count: trueCount, color: NotificationColor.Red };
|
||||
}
|
||||
|
||||
if (greyNotifs > 0) {
|
||||
return { symbol: null, count: trueCount, color: NotificationColor.Grey };
|
||||
}
|
||||
|
||||
// We don't have any notified messages, but we might have unread messages. Let's
|
||||
// find out.
|
||||
let hasUnread = false;
|
||||
if (threadId) hasUnread = doesRoomOrThreadHaveUnreadMessages(room.getThread(threadId)!);
|
||||
else hasUnread = doesRoomHaveUnreadMessages(room);
|
||||
|
||||
return {
|
||||
symbol: null,
|
||||
count: trueCount,
|
||||
color: hasUnread ? NotificationColor.Bold : NotificationColor.None,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022 - 2023 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.
|
||||
|
@ -14,15 +14,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { NotificationCount, NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { Thread } from "matrix-js-sdk/src/models/thread";
|
||||
import { RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { getUnsentMessages } from "../components/structures/RoomStatusBar";
|
||||
import { getRoomNotifsState, getUnreadNotificationCount, RoomNotifState } from "../RoomNotifs";
|
||||
import { NotificationColor } from "../stores/notifications/NotificationColor";
|
||||
import { doesRoomOrThreadHaveUnreadMessages } from "../Unread";
|
||||
import { EffectiveMembership, getEffectiveMembership } from "../utils/membership";
|
||||
import type { NotificationCount, Room } from "matrix-js-sdk/src/models/room";
|
||||
import { determineUnreadState } from "../RoomNotifs";
|
||||
import type { NotificationColor } from "../stores/notifications/NotificationColor";
|
||||
import { useEventEmitter } from "./useEventEmitter";
|
||||
|
||||
export const useUnreadNotifications = (
|
||||
|
@ -53,40 +50,10 @@ export const useUnreadNotifications = (
|
|||
useEventEmitter(room, RoomEvent.MyMembership, () => updateNotificationState());
|
||||
|
||||
const updateNotificationState = useCallback(() => {
|
||||
if (getUnsentMessages(room, threadId).length > 0) {
|
||||
setSymbol("!");
|
||||
setCount(1);
|
||||
setColor(NotificationColor.Unsent);
|
||||
} else if (getEffectiveMembership(room.getMyMembership()) === EffectiveMembership.Invite) {
|
||||
setSymbol("!");
|
||||
setCount(1);
|
||||
setColor(NotificationColor.Red);
|
||||
} else if (getRoomNotifsState(room.client, room.roomId) === RoomNotifState.Mute) {
|
||||
setSymbol(null);
|
||||
setCount(0);
|
||||
setColor(NotificationColor.None);
|
||||
} else {
|
||||
const redNotifs = getUnreadNotificationCount(room, NotificationCountType.Highlight, threadId);
|
||||
const greyNotifs = getUnreadNotificationCount(room, NotificationCountType.Total, threadId);
|
||||
|
||||
const trueCount = greyNotifs || redNotifs;
|
||||
setCount(trueCount);
|
||||
setSymbol(null);
|
||||
if (redNotifs > 0) {
|
||||
setColor(NotificationColor.Red);
|
||||
} else if (greyNotifs > 0) {
|
||||
setColor(NotificationColor.Grey);
|
||||
} else {
|
||||
// We don't have any notified messages, but we might have unread messages. Let's
|
||||
// find out.
|
||||
let roomOrThread: Room | Thread = room;
|
||||
if (threadId) {
|
||||
roomOrThread = room.getThread(threadId)!;
|
||||
}
|
||||
const hasUnread = doesRoomOrThreadHaveUnreadMessages(roomOrThread);
|
||||
setColor(hasUnread ? NotificationColor.Bold : NotificationColor.None);
|
||||
}
|
||||
}
|
||||
const { symbol, count, color } = determineUnreadState(room, threadId);
|
||||
setSymbol(symbol);
|
||||
setCount(count);
|
||||
setColor(color);
|
||||
}, [room, threadId]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -58,10 +58,9 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
|
|||
};
|
||||
|
||||
private updateNotificationVolume(): void {
|
||||
this.properties.set(
|
||||
CachedRoomKey.NotificationVolume,
|
||||
getRoomNotifsState(this.matrixClient, this.context.room.roomId),
|
||||
);
|
||||
const state = getRoomNotifsState(this.matrixClient, this.context.room.roomId);
|
||||
if (state) this.properties.set(CachedRoomKey.NotificationVolume, state);
|
||||
else this.properties.delete(CachedRoomKey.NotificationVolume);
|
||||
this.markEchoReceived(CachedRoomKey.NotificationVolume);
|
||||
this.emit(PROPERTY_UPDATED, CachedRoomKey.NotificationVolume);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2020, 2023 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.
|
||||
|
@ -14,21 +14,19 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEventEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { ClientEvent } from "matrix-js-sdk/src/client";
|
||||
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
|
||||
|
||||
import { NotificationColor } from "./NotificationColor";
|
||||
import { IDestroyable } from "../../utils/IDestroyable";
|
||||
import type { Room } from "matrix-js-sdk/src/models/room";
|
||||
import type { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import type { IDestroyable } from "../../utils/IDestroyable";
|
||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
|
||||
import { readReceiptChangeIsFor } from "../../utils/read-receipts";
|
||||
import * as RoomNotifs from "../../RoomNotifs";
|
||||
import * as Unread from "../../Unread";
|
||||
import { NotificationState, NotificationStateEvents } from "./NotificationState";
|
||||
import { getUnsentMessages } from "../../components/structures/RoomStatusBar";
|
||||
import { ThreadsRoomNotificationState } from "./ThreadsRoomNotificationState";
|
||||
import type { ThreadsRoomNotificationState } from "./ThreadsRoomNotificationState";
|
||||
|
||||
export class RoomNotificationState extends NotificationState implements IDestroyable {
|
||||
public constructor(public readonly room: Room, private readonly threadsState?: ThreadsRoomNotificationState) {
|
||||
|
@ -49,10 +47,6 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
|||
this.updateNotificationState();
|
||||
}
|
||||
|
||||
private get roomIsInvite(): boolean {
|
||||
return getEffectiveMembership(this.room.getMyMembership()) === EffectiveMembership.Invite;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
super.destroy();
|
||||
const cli = this.room.client;
|
||||
|
@ -112,58 +106,10 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
|||
private updateNotificationState(): void {
|
||||
const snapshot = this.snapshot();
|
||||
|
||||
if (getUnsentMessages(this.room).length > 0) {
|
||||
// When there are unsent messages we show a red `!`
|
||||
this._color = NotificationColor.Unsent;
|
||||
this._symbol = "!";
|
||||
this._count = 1; // not used, technically
|
||||
} else if (
|
||||
RoomNotifs.getRoomNotifsState(this.room.client, this.room.roomId) === RoomNotifs.RoomNotifState.Mute
|
||||
) {
|
||||
// When muted we suppress all notification states, even if we have context on them.
|
||||
this._color = NotificationColor.None;
|
||||
this._symbol = null;
|
||||
this._count = 0;
|
||||
} else if (this.roomIsInvite) {
|
||||
this._color = NotificationColor.Red;
|
||||
this._symbol = "!";
|
||||
this._count = 1; // not used, technically
|
||||
} else {
|
||||
const redNotifs = RoomNotifs.getUnreadNotificationCount(this.room, NotificationCountType.Highlight);
|
||||
const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.room, NotificationCountType.Total);
|
||||
|
||||
// For a 'true count' we pick the grey notifications first because they include the
|
||||
// red notifications. If we don't have a grey count for some reason we use the red
|
||||
// count. If that count is broken for some reason, assume zero. This avoids us showing
|
||||
// a badge for 'NaN' (which formats as 'NaNB' for NaN Billion).
|
||||
const trueCount = greyNotifs ? greyNotifs : redNotifs ? redNotifs : 0;
|
||||
|
||||
// Note: we only set the symbol if we have an actual count. We don't want to show
|
||||
// zero on badges.
|
||||
|
||||
if (redNotifs > 0) {
|
||||
this._color = NotificationColor.Red;
|
||||
this._count = trueCount;
|
||||
this._symbol = null; // symbol calculated by component
|
||||
} else if (greyNotifs > 0) {
|
||||
this._color = NotificationColor.Grey;
|
||||
this._count = trueCount;
|
||||
this._symbol = null; // symbol calculated by component
|
||||
} else {
|
||||
// We don't have any notified messages, but we might have unread messages. Let's
|
||||
// find out.
|
||||
const hasUnread = Unread.doesRoomHaveUnreadMessages(this.room);
|
||||
if (hasUnread) {
|
||||
this._color = NotificationColor.Bold;
|
||||
} else {
|
||||
this._color = NotificationColor.None;
|
||||
}
|
||||
|
||||
// no symbol or count for this state
|
||||
this._count = 0;
|
||||
this._symbol = null;
|
||||
}
|
||||
}
|
||||
const { color, symbol, count } = RoomNotifs.determineUnreadState(this.room);
|
||||
this._color = color;
|
||||
this._symbol = symbol;
|
||||
this._count = count;
|
||||
|
||||
// finally, publish an update if needed
|
||||
this.emitIfUpdated(snapshot);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022 - 2023 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.
|
||||
|
@ -15,21 +15,29 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import { ConditionKind, PushRuleActionName, TweakName } from "matrix-js-sdk/src/@types/PushRules";
|
||||
import { PushRuleActionName, TweakName } from "matrix-js-sdk/src/@types/PushRules";
|
||||
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
|
||||
import { EventStatus, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { mkEvent, stubClient } from "./test-utils";
|
||||
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||
import { getRoomNotifsState, RoomNotifState, getUnreadNotificationCount } from "../src/RoomNotifs";
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { mkEvent, mkRoom, muteRoom, stubClient } from "./test-utils";
|
||||
import {
|
||||
getRoomNotifsState,
|
||||
RoomNotifState,
|
||||
getUnreadNotificationCount,
|
||||
determineUnreadState,
|
||||
} from "../src/RoomNotifs";
|
||||
import { NotificationColor } from "../src/stores/notifications/NotificationColor";
|
||||
|
||||
describe("RoomNotifs test", () => {
|
||||
let client: jest.Mocked<MatrixClient>;
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
client = stubClient() as jest.Mocked<MatrixClient>;
|
||||
});
|
||||
|
||||
it("getRoomNotifsState handles rules with no conditions", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
mocked(cli).pushRules = {
|
||||
mocked(client).pushRules = {
|
||||
global: {
|
||||
override: [
|
||||
{
|
||||
|
@ -41,70 +49,47 @@ describe("RoomNotifs test", () => {
|
|||
],
|
||||
},
|
||||
};
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(null);
|
||||
expect(getRoomNotifsState(client, "!roomId:server")).toBe(null);
|
||||
});
|
||||
|
||||
it("getRoomNotifsState handles guest users", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
mocked(cli).isGuest.mockReturnValue(true);
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(RoomNotifState.AllMessages);
|
||||
mocked(client).isGuest.mockReturnValue(true);
|
||||
expect(getRoomNotifsState(client, "!roomId:server")).toBe(RoomNotifState.AllMessages);
|
||||
});
|
||||
|
||||
it("getRoomNotifsState handles mute state", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.pushRules = {
|
||||
global: {
|
||||
override: [
|
||||
{
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
conditions: [
|
||||
{
|
||||
kind: ConditionKind.EventMatch,
|
||||
key: "room_id",
|
||||
pattern: "!roomId:server",
|
||||
},
|
||||
],
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(RoomNotifState.Mute);
|
||||
const room = mkRoom(client, "!roomId:server");
|
||||
muteRoom(room);
|
||||
expect(getRoomNotifsState(client, room.roomId)).toBe(RoomNotifState.Mute);
|
||||
});
|
||||
|
||||
it("getRoomNotifsState handles mentions only", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.getRoomPushRule = () => ({
|
||||
(client as any).getRoomPushRule = () => ({
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
});
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(RoomNotifState.MentionsOnly);
|
||||
expect(getRoomNotifsState(client, "!roomId:server")).toBe(RoomNotifState.MentionsOnly);
|
||||
});
|
||||
|
||||
it("getRoomNotifsState handles noisy", () => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.getRoomPushRule = () => ({
|
||||
(client as any).getRoomPushRule = () => ({
|
||||
rule_id: "!roomId:server",
|
||||
enabled: true,
|
||||
default: false,
|
||||
actions: [{ set_tweak: TweakName.Sound, value: "default" }],
|
||||
});
|
||||
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(RoomNotifState.AllMessagesLoud);
|
||||
expect(getRoomNotifsState(client, "!roomId:server")).toBe(RoomNotifState.AllMessagesLoud);
|
||||
});
|
||||
|
||||
describe("getUnreadNotificationCount", () => {
|
||||
const ROOM_ID = "!roomId:example.org";
|
||||
const THREAD_ID = "$threadId";
|
||||
|
||||
let cli;
|
||||
let room: Room;
|
||||
beforeEach(() => {
|
||||
cli = MatrixClientPeg.get();
|
||||
room = new Room(ROOM_ID, cli, cli.getUserId());
|
||||
room = new Room(ROOM_ID, client, client.getUserId()!);
|
||||
});
|
||||
|
||||
it("counts room notification type", () => {
|
||||
|
@ -125,19 +110,19 @@ describe("RoomNotifs test", () => {
|
|||
room.setUnreadNotificationCount(NotificationCountType.Highlight, 1);
|
||||
|
||||
const OLD_ROOM_ID = "!oldRoomId:example.org";
|
||||
const oldRoom = new Room(OLD_ROOM_ID, cli, cli.getUserId());
|
||||
const oldRoom = new Room(OLD_ROOM_ID, client, client.getUserId()!);
|
||||
oldRoom.setUnreadNotificationCount(NotificationCountType.Total, 10);
|
||||
oldRoom.setUnreadNotificationCount(NotificationCountType.Highlight, 6);
|
||||
|
||||
cli.getRoom.mockReset().mockReturnValue(oldRoom);
|
||||
client.getRoom.mockReset().mockReturnValue(oldRoom);
|
||||
|
||||
const predecessorEvent = mkEvent({
|
||||
event: true,
|
||||
type: "m.room.create",
|
||||
room: ROOM_ID,
|
||||
user: cli.getUserId(),
|
||||
user: client.getUserId()!,
|
||||
content: {
|
||||
creator: cli.getUserId(),
|
||||
creator: client.getUserId(),
|
||||
room_version: "5",
|
||||
predecessor: {
|
||||
room_id: OLD_ROOM_ID,
|
||||
|
@ -165,4 +150,78 @@ describe("RoomNotifs test", () => {
|
|||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, THREAD_ID)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("determineUnreadState", () => {
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
room = new Room("!room-id:example.com", client, "@user:example.com", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
});
|
||||
|
||||
it("shows nothing by default", async () => {
|
||||
const { color, symbol, count } = determineUnreadState(room);
|
||||
|
||||
expect(symbol).toBe(null);
|
||||
expect(color).toBe(NotificationColor.None);
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it("indicates if there are unsent messages", async () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
type: "m.message",
|
||||
user: "@user:example.org",
|
||||
content: {},
|
||||
});
|
||||
event.status = EventStatus.NOT_SENT;
|
||||
room.addPendingEvent(event, "txn");
|
||||
|
||||
const { color, symbol, count } = determineUnreadState(room);
|
||||
|
||||
expect(symbol).toBe("!");
|
||||
expect(color).toBe(NotificationColor.Unsent);
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("indicates the user has been invited to a channel", async () => {
|
||||
room.updateMyMembership("invite");
|
||||
|
||||
const { color, symbol, count } = determineUnreadState(room);
|
||||
|
||||
expect(symbol).toBe("!");
|
||||
expect(color).toBe(NotificationColor.Red);
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("shows nothing for muted channels", async () => {
|
||||
room.setUnreadNotificationCount(NotificationCountType.Highlight, 99);
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, 99);
|
||||
muteRoom(room);
|
||||
|
||||
const { color, count } = determineUnreadState(room);
|
||||
|
||||
expect(color).toBe(NotificationColor.None);
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it("uses the correct number of unreads", async () => {
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, 999);
|
||||
|
||||
const { color, count } = determineUnreadState(room);
|
||||
|
||||
expect(color).toBe(NotificationColor.Grey);
|
||||
expect(count).toBe(999);
|
||||
});
|
||||
|
||||
it("uses the correct number of highlights", async () => {
|
||||
room.setUnreadNotificationCount(NotificationCountType.Highlight, 888);
|
||||
|
||||
const { color, count } = determineUnreadState(room);
|
||||
|
||||
expect(color).toBe(NotificationColor.Red);
|
||||
expect(count).toBe(888);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022 - 2023 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.
|
||||
|
@ -23,36 +23,26 @@ import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
|
|||
import { EventStatus } from "matrix-js-sdk/src/models/event-status";
|
||||
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { mkThread } from "../../../../test-utils/threads";
|
||||
import { UnreadNotificationBadge } from "../../../../../src/components/views/rooms/NotificationBadge/UnreadNotificationBadge";
|
||||
import { mkEvent, mkMessage, stubClient } from "../../../../test-utils/test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { mkEvent, mkMessage, muteRoom, stubClient } from "../../../../test-utils/test-utils";
|
||||
import * as RoomNotifs from "../../../../../src/RoomNotifs";
|
||||
|
||||
jest.mock("../../../../../src/RoomNotifs");
|
||||
jest.mock("../../../../../src/RoomNotifs", () => ({
|
||||
...(jest.requireActual("../../../../../src/RoomNotifs") as Object),
|
||||
getRoomNotifsState: jest.fn(),
|
||||
}));
|
||||
|
||||
const ROOM_ID = "!roomId:example.org";
|
||||
let THREAD_ID: string;
|
||||
|
||||
describe("UnreadNotificationBadge", () => {
|
||||
stubClient();
|
||||
const client = MatrixClientPeg.get();
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
function getComponent(threadId?: string) {
|
||||
return <UnreadNotificationBadge room={room} threadId={threadId} />;
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
client.supportsThreads = () => true;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
client = stubClient();
|
||||
client.supportsThreads = () => true;
|
||||
|
||||
room = new Room(ROOM_ID, client, client.getUserId()!, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
|
@ -145,41 +135,39 @@ describe("UnreadNotificationBadge", () => {
|
|||
});
|
||||
|
||||
it("adds a warning for invites", () => {
|
||||
jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
|
||||
room.updateMyMembership("invite");
|
||||
render(getComponent());
|
||||
expect(screen.queryByText("!")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("hides counter for muted rooms", () => {
|
||||
jest.spyOn(RoomNotifs, "getRoomNotifsState").mockReset().mockReturnValue(RoomNotifs.RoomNotifState.Mute);
|
||||
muteRoom(room);
|
||||
|
||||
const { container } = render(getComponent());
|
||||
expect(container.querySelector(".mx_NotificationBadge")).toBeNull();
|
||||
});
|
||||
|
||||
it("activity renders unread notification badge", () => {
|
||||
act(() => {
|
||||
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 0);
|
||||
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 0);
|
||||
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total, 0);
|
||||
room.setThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight, 0);
|
||||
|
||||
// Add another event on the thread which is not sent by us.
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
user: "@alice:server.org",
|
||||
room: room.roomId,
|
||||
content: {
|
||||
"msgtype": MsgType.Text,
|
||||
"body": "Hello from Bob",
|
||||
"m.relates_to": {
|
||||
event_id: THREAD_ID,
|
||||
rel_type: RelationType.Thread,
|
||||
},
|
||||
// Add another event on the thread which is not sent by us.
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
user: "@alice:server.org",
|
||||
room: room.roomId,
|
||||
content: {
|
||||
"msgtype": MsgType.Text,
|
||||
"body": "Hello from Bob",
|
||||
"m.relates_to": {
|
||||
event_id: THREAD_ID,
|
||||
rel_type: RelationType.Thread,
|
||||
},
|
||||
ts: 5,
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
},
|
||||
ts: 5,
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
|
||||
const { container } = render(getComponent(THREAD_ID));
|
||||
expect(container.querySelector(".mx_NotificationBadge_dot")).toBeTruthy();
|
||||
|
|
110
test/hooks/useUnreadNotifications-test.ts
Normal file
110
test/hooks/useUnreadNotifications-test.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2023 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 { renderHook } from "@testing-library/react-hooks";
|
||||
import { EventStatus, NotificationCountType, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { useUnreadNotifications } from "../../src/hooks/useUnreadNotifications";
|
||||
import { NotificationColor } from "../../src/stores/notifications/NotificationColor";
|
||||
import { mkEvent, muteRoom, stubClient } from "../test-utils";
|
||||
|
||||
describe("useUnreadNotifications", () => {
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
client = stubClient();
|
||||
room = new Room("!room:example.org", client, "@user:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
});
|
||||
|
||||
function setUnreads(greys: number, reds: number): void {
|
||||
room.setUnreadNotificationCount(NotificationCountType.Highlight, reds);
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, greys);
|
||||
}
|
||||
|
||||
it("shows nothing by default", async () => {
|
||||
const { result } = renderHook(() => useUnreadNotifications(room));
|
||||
const { color, symbol, count } = result.current;
|
||||
|
||||
expect(symbol).toBe(null);
|
||||
expect(color).toBe(NotificationColor.None);
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it("indicates if there are unsent messages", async () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
type: "m.message",
|
||||
user: "@user:example.org",
|
||||
content: {},
|
||||
});
|
||||
event.status = EventStatus.NOT_SENT;
|
||||
room.addPendingEvent(event, "txn");
|
||||
|
||||
const { result } = renderHook(() => useUnreadNotifications(room));
|
||||
const { color, symbol, count } = result.current;
|
||||
|
||||
expect(symbol).toBe("!");
|
||||
expect(color).toBe(NotificationColor.Unsent);
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("indicates the user has been invited to a channel", async () => {
|
||||
room.updateMyMembership("invite");
|
||||
|
||||
const { result } = renderHook(() => useUnreadNotifications(room));
|
||||
const { color, symbol, count } = result.current;
|
||||
|
||||
expect(symbol).toBe("!");
|
||||
expect(color).toBe(NotificationColor.Red);
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("shows nothing for muted channels", async () => {
|
||||
setUnreads(999, 999);
|
||||
muteRoom(room);
|
||||
|
||||
const { result } = renderHook(() => useUnreadNotifications(room));
|
||||
const { color, count } = result.current;
|
||||
|
||||
expect(color).toBe(NotificationColor.None);
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it("uses the correct number of unreads", async () => {
|
||||
setUnreads(999, 0);
|
||||
|
||||
const { result } = renderHook(() => useUnreadNotifications(room));
|
||||
const { color, count } = result.current;
|
||||
|
||||
expect(color).toBe(NotificationColor.Grey);
|
||||
expect(count).toBe(999);
|
||||
});
|
||||
|
||||
it("uses the correct number of highlights", async () => {
|
||||
setUnreads(0, 888);
|
||||
|
||||
const { result } = renderHook(() => useUnreadNotifications(room));
|
||||
const { color, count } = result.current;
|
||||
|
||||
expect(color).toBe(NotificationColor.Red);
|
||||
expect(count).toBe(888);
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022 - 2023 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.
|
||||
|
@ -15,38 +15,165 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEventEvent, MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
MatrixEventEvent,
|
||||
PendingEventOrdering,
|
||||
EventStatus,
|
||||
NotificationCountType,
|
||||
EventType,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { stubClient } from "../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { mkEvent, muteRoom, stubClient } from "../../test-utils";
|
||||
import { RoomNotificationState } from "../../../src/stores/notifications/RoomNotificationState";
|
||||
import * as testUtils from "../../test-utils";
|
||||
import { NotificationStateEvents } from "../../../src/stores/notifications/NotificationState";
|
||||
import { NotificationColor } from "../../../src/stores/notifications/NotificationColor";
|
||||
import { createMessageEventContent } from "../../test-utils/events";
|
||||
|
||||
describe("RoomNotificationState", () => {
|
||||
let testRoom: Room;
|
||||
let room: Room;
|
||||
let client: MatrixClient;
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
client = MatrixClientPeg.get();
|
||||
testRoom = testUtils.mkStubRoom("$aroomid", "Test room", client);
|
||||
client = stubClient();
|
||||
room = new Room("!room:example.com", client, "@user:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
});
|
||||
|
||||
function addThread(room: Room): void {
|
||||
const threadId = "thread_id";
|
||||
jest.spyOn(room, "eventShouldLiveIn").mockReturnValue({
|
||||
shouldLiveInRoom: true,
|
||||
shouldLiveInThread: true,
|
||||
threadId,
|
||||
});
|
||||
const thread = room.createThread(
|
||||
threadId,
|
||||
new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: "event_root_1",
|
||||
type: EventType.RoomMessage,
|
||||
sender: "userId",
|
||||
content: createMessageEventContent("RootEvent"),
|
||||
}),
|
||||
[],
|
||||
true,
|
||||
);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
thread.addEvent(
|
||||
new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
event_id: "event_reply_1" + i,
|
||||
type: EventType.RoomMessage,
|
||||
sender: "userId",
|
||||
content: createMessageEventContent("ReplyEvent" + 1),
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setUnreads(room: Room, greys: number, reds: number): void {
|
||||
room.setUnreadNotificationCount(NotificationCountType.Highlight, reds);
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, greys);
|
||||
}
|
||||
|
||||
it("Updates on event decryption", () => {
|
||||
const roomNotifState = new RoomNotificationState(testRoom as any as Room);
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
const listener = jest.fn();
|
||||
roomNotifState.addListener(NotificationStateEvents.Update, listener);
|
||||
const testEvent = {
|
||||
getRoomId: () => testRoom.roomId,
|
||||
getRoomId: () => room.roomId,
|
||||
} as unknown as MatrixEvent;
|
||||
testRoom.getUnreadNotificationCount = jest.fn().mockReturnValue(1);
|
||||
room.getUnreadNotificationCount = jest.fn().mockReturnValue(1);
|
||||
client.emit(MatrixEventEvent.Decrypted, testEvent);
|
||||
expect(listener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("removes listeners", () => {
|
||||
const roomNotifState = new RoomNotificationState(testRoom as any as Room);
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
expect(() => roomNotifState.destroy()).not.toThrow();
|
||||
});
|
||||
|
||||
it("suggests an 'unread' ! if there are unsent messages", () => {
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
type: "m.message",
|
||||
user: "@user:example.org",
|
||||
content: {},
|
||||
});
|
||||
event.status = EventStatus.NOT_SENT;
|
||||
room.addPendingEvent(event, "txn");
|
||||
|
||||
expect(roomNotifState.color).toBe(NotificationColor.Unsent);
|
||||
expect(roomNotifState.symbol).toBe("!");
|
||||
expect(roomNotifState.count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("suggests nothing if the room is muted", () => {
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
|
||||
muteRoom(room);
|
||||
setUnreads(room, 1234, 0);
|
||||
room.updateMyMembership("join"); // emit
|
||||
|
||||
expect(roomNotifState.color).toBe(NotificationColor.None);
|
||||
expect(roomNotifState.symbol).toBe(null);
|
||||
expect(roomNotifState.count).toBe(0);
|
||||
});
|
||||
|
||||
it("suggests a red ! if the user has been invited to a room", () => {
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
|
||||
room.updateMyMembership("invite"); // emit
|
||||
|
||||
expect(roomNotifState.color).toBe(NotificationColor.Red);
|
||||
expect(roomNotifState.symbol).toBe("!");
|
||||
expect(roomNotifState.count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("returns a proper count and color for regular unreads", () => {
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
|
||||
setUnreads(room, 4321, 0);
|
||||
room.updateMyMembership("join"); // emit
|
||||
|
||||
expect(roomNotifState.color).toBe(NotificationColor.Grey);
|
||||
expect(roomNotifState.symbol).toBe(null);
|
||||
expect(roomNotifState.count).toBe(4321);
|
||||
});
|
||||
|
||||
it("returns a proper count and color for highlights", () => {
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
|
||||
setUnreads(room, 0, 69);
|
||||
room.updateMyMembership("join"); // emit
|
||||
|
||||
expect(roomNotifState.color).toBe(NotificationColor.Red);
|
||||
expect(roomNotifState.symbol).toBe(null);
|
||||
expect(roomNotifState.count).toBe(69);
|
||||
});
|
||||
|
||||
it("includes threads", async () => {
|
||||
const roomNotifState = new RoomNotificationState(room);
|
||||
|
||||
room.timeline.push(
|
||||
new MatrixEvent({
|
||||
room_id: room.roomId,
|
||||
type: EventType.RoomMessage,
|
||||
sender: "userId",
|
||||
content: createMessageEventContent("timeline event"),
|
||||
}),
|
||||
);
|
||||
|
||||
addThread(room);
|
||||
room.updateMyMembership("join"); // emit
|
||||
|
||||
expect(roomNotifState.color).toBe(NotificationColor.Bold);
|
||||
expect(roomNotifState.symbol).toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022 - 2023 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.
|
||||
|
@ -33,6 +33,9 @@ import {
|
|||
IPusher,
|
||||
RoomType,
|
||||
KNOWN_SAFE_ROOM_VERSION,
|
||||
ConditionKind,
|
||||
PushRuleActionName,
|
||||
IPushRules,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { normalize } from "matrix-js-sdk/src/utils";
|
||||
import { ReEmitter } from "matrix-js-sdk/src/ReEmitter";
|
||||
|
@ -139,7 +142,7 @@ export function createTestClient(): MatrixClient {
|
|||
getThirdpartyUser: jest.fn().mockResolvedValue([]),
|
||||
getAccountData: jest.fn().mockImplementation((type) => {
|
||||
return mkEvent({
|
||||
user: undefined,
|
||||
user: "@user:example.com",
|
||||
room: undefined,
|
||||
type,
|
||||
event: true,
|
||||
|
@ -480,8 +483,12 @@ export function mkMessage({
|
|||
return mkEvent(event);
|
||||
}
|
||||
|
||||
export function mkStubRoom(roomId: string = null, name: string, client: MatrixClient): Room {
|
||||
const stubTimeline = { getEvents: () => [] as MatrixEvent[] } as unknown as EventTimeline;
|
||||
export function mkStubRoom(
|
||||
roomId: string | null | undefined = null,
|
||||
name: string | undefined,
|
||||
client: MatrixClient | undefined,
|
||||
): Room {
|
||||
const stubTimeline = { getEvents: (): MatrixEvent[] => [] } as unknown as EventTimeline;
|
||||
return {
|
||||
canInvite: jest.fn(),
|
||||
client,
|
||||
|
@ -565,22 +572,25 @@ export function mkServerConfig(hsUrl: string, isUrl: string) {
|
|||
// These methods make some use of some private methods on the AsyncStoreWithClient to simplify getting into a consistent
|
||||
// ready state without needing to wire up a dispatcher and pretend to be a js-sdk client.
|
||||
|
||||
export const setupAsyncStoreWithClient = async <T = unknown>(store: AsyncStoreWithClient<T>, client: MatrixClient) => {
|
||||
// @ts-ignore
|
||||
export const setupAsyncStoreWithClient = async <T extends Object = any>(
|
||||
store: AsyncStoreWithClient<T>,
|
||||
client: MatrixClient,
|
||||
) => {
|
||||
// @ts-ignore protected access
|
||||
store.readyStore.useUnitTestClient(client);
|
||||
// @ts-ignore
|
||||
// @ts-ignore protected access
|
||||
await store.onReady();
|
||||
};
|
||||
|
||||
export const resetAsyncStoreWithClient = async <T = unknown>(store: AsyncStoreWithClient<T>) => {
|
||||
// @ts-ignore
|
||||
export const resetAsyncStoreWithClient = async <T extends Object = any>(store: AsyncStoreWithClient<T>) => {
|
||||
// @ts-ignore protected access
|
||||
await store.onNotReady();
|
||||
};
|
||||
|
||||
export const mockStateEventImplementation = (events: MatrixEvent[]) => {
|
||||
const stateMap = new EnhancedMap<string, Map<string, MatrixEvent>>();
|
||||
events.forEach((event) => {
|
||||
stateMap.getOrCreate(event.getType(), new Map()).set(event.getStateKey(), event);
|
||||
stateMap.getOrCreate(event.getType(), new Map()).set(event.getStateKey()!, event);
|
||||
});
|
||||
|
||||
// recreate the overloading in RoomState
|
||||
|
@ -617,7 +627,7 @@ export const upsertRoomStateEvents = (room: Room, events: MatrixEvent[]): void =
|
|||
if (!acc.has(eventType)) {
|
||||
acc.set(eventType, new Map());
|
||||
}
|
||||
acc.get(eventType).set(event.getStateKey(), event);
|
||||
acc.get(eventType)?.set(event.getStateKey()!, event);
|
||||
return acc;
|
||||
}, room.currentState.events || new Map<string, Map<string, MatrixEvent>>());
|
||||
|
||||
|
@ -674,3 +684,25 @@ export const mkPusher = (extra: Partial<IPusher> = {}): IPusher => ({
|
|||
pushkey: "pushpush",
|
||||
...extra,
|
||||
});
|
||||
|
||||
/** Add a mute rule for a room. */
|
||||
export function muteRoom(room: Room): void {
|
||||
const client = room.client!;
|
||||
client.pushRules = client.pushRules ?? ({ global: [] } as IPushRules);
|
||||
client.pushRules.global = client.pushRules.global ?? {};
|
||||
client.pushRules.global.override = [
|
||||
{
|
||||
default: true,
|
||||
enabled: true,
|
||||
rule_id: "rule_id",
|
||||
conditions: [
|
||||
{
|
||||
kind: ConditionKind.EventMatch,
|
||||
key: "room_id",
|
||||
pattern: room.roomId,
|
||||
},
|
||||
],
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue