Convert RoomNotifs to TS

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2021-09-27 14:32:04 +02:00
parent ff30dacc84
commit e2b6c2cbd6
No known key found for this signature in database
GPG key ID: 55C211A1226CB17D
5 changed files with 69 additions and 93 deletions

View file

@ -17,27 +17,31 @@ limitations under the License.
import { MatrixClientPeg } from './MatrixClientPeg';
import { PushProcessor } from 'matrix-js-sdk/src/pushprocessor';
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import { IAnnotatedPushRule, PushRuleKind } from "matrix-js-sdk/src/@types/PushRules";
export const ALL_MESSAGES_LOUD = 'all_messages_loud';
export const ALL_MESSAGES = 'all_messages';
export const MENTIONS_ONLY = 'mentions_only';
export const MUTE = 'mute';
export enum RoomNotifState {
AllMessagesLoud = 'all_messages_loud',
AllMessages = 'all_messages',
MentionsOnly = 'mentions_only',
Mute = 'mute',
}
export const BADGE_STATES = [ALL_MESSAGES, ALL_MESSAGES_LOUD];
export const MENTION_BADGE_STATES = [...BADGE_STATES, MENTIONS_ONLY];
export const BADGE_STATES = [RoomNotifState.AllMessages, RoomNotifState.AllMessagesLoud];
export const MENTION_BADGE_STATES = [...BADGE_STATES, RoomNotifState.MentionsOnly];
export function shouldShowNotifBadge(roomNotifState) {
export function shouldShowNotifBadge(roomNotifState: RoomNotifState): boolean {
return BADGE_STATES.includes(roomNotifState);
}
export function shouldShowMentionBadge(roomNotifState) {
export function shouldShowMentionBadge(roomNotifState: RoomNotifState): boolean {
return MENTION_BADGE_STATES.includes(roomNotifState);
}
export function aggregateNotificationCount(rooms) {
return rooms.reduce((result, room) => {
export function aggregateNotificationCount(rooms: Room[]): {count: number, highlight: boolean} {
return rooms.reduce<{count: number, highlight: boolean}>((result, room) => {
const roomNotifState = getRoomNotifsState(room.roomId);
const highlight = room.getUnreadNotificationCount('highlight') > 0;
const highlight = room.getUnreadNotificationCount(NotificationCountType.Highlight) > 0;
// use helper method to include highlights in the previous version of the room
const notificationCount = getUnreadNotificationCount(room);
@ -55,9 +59,9 @@ export function aggregateNotificationCount(rooms) {
}, { count: 0, highlight: false });
}
export function getRoomHasBadge(room) {
export function getRoomHasBadge(room: Room): boolean {
const roomNotifState = getRoomNotifsState(room.roomId);
const highlight = room.getUnreadNotificationCount('highlight') > 0;
const highlight = room.getUnreadNotificationCount(NotificationCountType.Highlight) > 0;
const notificationCount = room.getUnreadNotificationCount();
const notifBadges = notificationCount > 0 && shouldShowNotifBadge(roomNotifState);
@ -66,14 +70,14 @@ export function getRoomHasBadge(room) {
return notifBadges || mentionBadges;
}
export function getRoomNotifsState(roomId) {
if (MatrixClientPeg.get().isGuest()) return ALL_MESSAGES;
export function getRoomNotifsState(roomId: string): RoomNotifState {
if (MatrixClientPeg.get().isGuest()) return RoomNotifState.AllMessages;
// look through the override rules for a rule affecting this room:
// if one exists, it will take precedence.
const muteRule = findOverrideMuteRule(roomId);
if (muteRule) {
return MUTE;
return RoomNotifState.Mute;
}
// for everything else, look at the room rule.
@ -89,27 +93,27 @@ export function getRoomNotifsState(roomId) {
// XXX: We have to assume the default is to notify for all messages
// (in particular this will be 'wrong' for one to one rooms because
// they will notify loudly for all messages)
if (!roomRule || !roomRule.enabled) return ALL_MESSAGES;
if (!roomRule || !roomRule.enabled) return RoomNotifState.AllMessages;
// a mute at the room level will still allow mentions
// to notify
if (isMuteRule(roomRule)) return MENTIONS_ONLY;
if (isMuteRule(roomRule)) return RoomNotifState.MentionsOnly;
const actionsObject = PushProcessor.actionListToActionsObject(roomRule.actions);
if (actionsObject.tweaks.sound) return ALL_MESSAGES_LOUD;
if (actionsObject.tweaks.sound) return RoomNotifState.AllMessagesLoud;
return null;
}
export function setRoomNotifsState(roomId, newState) {
if (newState === MUTE) {
export function setRoomNotifsState(roomId: string, newState: RoomNotifState): Promise<void> {
if (newState === RoomNotifState.Mute) {
return setRoomNotifsStateMuted(roomId);
} else {
return setRoomNotifsStateUnmuted(roomId, newState);
}
}
export function getUnreadNotificationCount(room, type=null) {
export function getUnreadNotificationCount(room: Room, type: NotificationCountType = null): number {
let notificationCount = room.getUnreadNotificationCount(type);
// Check notification counts in the old room just in case there's some lost
@ -124,21 +128,21 @@ export function getUnreadNotificationCount(room, type=null) {
// notifying the user for unread messages because they would have extreme
// difficulty changing their notification preferences away from "All Messages"
// and "Noisy".
notificationCount += oldRoom.getUnreadNotificationCount("highlight");
notificationCount += oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight);
}
}
return notificationCount;
}
function setRoomNotifsStateMuted(roomId) {
function setRoomNotifsStateMuted(roomId: string): Promise<any> {
const cli = MatrixClientPeg.get();
const promises = [];
// delete the room rule
const roomRule = cli.getRoomPushRule('global', roomId);
if (roomRule) {
promises.push(cli.deletePushRule('global', 'room', roomRule.rule_id));
promises.push(cli.deletePushRule('global', PushRuleKind.RoomSpecific, roomRule.rule_id));
}
// add/replace an override rule to squelch everything in this room
@ -146,7 +150,7 @@ function setRoomNotifsStateMuted(roomId) {
// is an override rule, not a room rule: it still pertains to this room
// though, so using the room ID as the rule ID is logical and prevents
// duplicate copies of the rule.
promises.push(cli.addPushRule('global', 'override', roomId, {
promises.push(cli.addPushRule('global', PushRuleKind.Override, roomId, {
conditions: [
{
kind: 'event_match',
@ -162,30 +166,30 @@ function setRoomNotifsStateMuted(roomId) {
return Promise.all(promises);
}
function setRoomNotifsStateUnmuted(roomId, newState) {
function setRoomNotifsStateUnmuted(roomId: string, newState: RoomNotifState): Promise<any> {
const cli = MatrixClientPeg.get();
const promises = [];
const overrideMuteRule = findOverrideMuteRule(roomId);
if (overrideMuteRule) {
promises.push(cli.deletePushRule('global', 'override', overrideMuteRule.rule_id));
promises.push(cli.deletePushRule('global', PushRuleKind.Override, overrideMuteRule.rule_id));
}
if (newState === 'all_messages') {
if (newState === RoomNotifState.AllMessages) {
const roomRule = cli.getRoomPushRule('global', roomId);
if (roomRule) {
promises.push(cli.deletePushRule('global', 'room', roomRule.rule_id));
promises.push(cli.deletePushRule('global', PushRuleKind.RoomSpecific, roomRule.rule_id));
}
} else if (newState === 'mentions_only') {
promises.push(cli.addPushRule('global', 'room', roomId, {
} else if (newState === RoomNotifState.MentionsOnly) {
promises.push(cli.addPushRule('global', PushRuleKind.RoomSpecific, roomId, {
actions: [
'dont_notify',
],
}));
// https://matrix.org/jira/browse/SPEC-400
promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true));
} else if ('all_messages_loud') {
promises.push(cli.addPushRule('global', 'room', roomId, {
promises.push(cli.setPushRuleEnabled('global', PushRuleKind.RoomSpecific, roomId, true));
} else if (newState === RoomNotifState.AllMessagesLoud) {
promises.push(cli.addPushRule('global', PushRuleKind.RoomSpecific, roomId, {
actions: [
'notify',
{
@ -195,13 +199,13 @@ function setRoomNotifsStateUnmuted(roomId, newState) {
],
}));
// https://matrix.org/jira/browse/SPEC-400
promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true));
promises.push(cli.setPushRuleEnabled('global', PushRuleKind.RoomSpecific, roomId, true));
}
return Promise.all(promises);
}
function findOverrideMuteRule(roomId) {
function findOverrideMuteRule(roomId: string): IAnnotatedPushRule {
const cli = MatrixClientPeg.get();
if (!cli.pushRules ||
!cli.pushRules['global'] ||
@ -218,7 +222,7 @@ function findOverrideMuteRule(roomId) {
return null;
}
function isRuleForRoom(roomId, rule) {
function isRuleForRoom(roomId: string, rule: IAnnotatedPushRule): boolean {
if (rule.conditions.length !== 1) {
return false;
}
@ -226,6 +230,6 @@ function isRuleForRoom(roomId, rule) {
return (cond.kind === 'event_match' && cond.key === 'room_id' && cond.pattern === roomId);
}
function isMuteRule(rule) {
function isMuteRule(rule: IAnnotatedPushRule): boolean {
return (rule.actions.length === 1 && rule.actions[0] === 'dont_notify');
}

View file

@ -1,24 +0,0 @@
/*
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 {
ALL_MESSAGES,
ALL_MESSAGES_LOUD,
MENTIONS_ONLY,
MUTE,
} from "./RoomNotifs";
export type Volume = ALL_MESSAGES_LOUD | ALL_MESSAGES | MENTIONS_ONLY | MUTE;

View file

@ -29,10 +29,9 @@ import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextM
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
import { RoomNotifState } from "../../../RoomNotifs";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import NotificationBadge from "./NotificationBadge";
import { Volume } from "../../../RoomNotifsTypes";
import RoomListStore from "../../../stores/room-list/RoomListStore";
import RoomListActions from "../../../actions/RoomListActions";
import { ActionPayload } from "../../../dispatcher/payloads";
@ -364,7 +363,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
this.setState({ generalMenuPosition: null }); // hide the menu
};
private async saveNotifState(ev: ButtonEvent, newState: Volume) {
private async saveNotifState(ev: ButtonEvent, newState: RoomNotifState) {
ev.preventDefault();
ev.stopPropagation();
if (MatrixClientPeg.get().isGuest()) return;
@ -378,10 +377,10 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
}
}
private onClickAllNotifs = ev => this.saveNotifState(ev, ALL_MESSAGES);
private onClickAlertMe = ev => this.saveNotifState(ev, ALL_MESSAGES_LOUD);
private onClickMentions = ev => this.saveNotifState(ev, MENTIONS_ONLY);
private onClickMute = ev => this.saveNotifState(ev, MUTE);
private onClickAllNotifs = ev => this.saveNotifState(ev, RoomNotifState.AllMessages);
private onClickAlertMe = ev => this.saveNotifState(ev, RoomNotifState.AllMessagesLoud);
private onClickMentions = ev => this.saveNotifState(ev, RoomNotifState.MentionsOnly);
private onClickMute = ev => this.saveNotifState(ev, RoomNotifState.Mute);
private renderNotificationsMenu(isActive: boolean): React.ReactElement {
if (MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Archived ||
@ -404,25 +403,25 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
<IconizedContextMenuOptionList first>
<IconizedContextMenuRadio
label={_t("Use default")}
active={state === ALL_MESSAGES}
active={state === RoomNotifState.AllMessages}
iconClassName="mx_RoomTile_iconBell"
onClick={this.onClickAllNotifs}
/>
<IconizedContextMenuRadio
label={_t("All messages")}
active={state === ALL_MESSAGES_LOUD}
active={state === RoomNotifState.AllMessagesLoud}
iconClassName="mx_RoomTile_iconBellDot"
onClick={this.onClickAlertMe}
/>
<IconizedContextMenuRadio
label={_t("Mentions & Keywords")}
active={state === MENTIONS_ONLY}
active={state === RoomNotifState.MentionsOnly}
iconClassName="mx_RoomTile_iconBellMentions"
onClick={this.onClickMentions}
/>
<IconizedContextMenuRadio
label={_t("None")}
active={state === MUTE}
active={state === RoomNotifState.Mute}
iconClassName="mx_RoomTile_iconBellCrossed"
onClick={this.onClickMute}
/>
@ -432,14 +431,14 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
const classes = classNames("mx_RoomTile_notificationsButton", {
// Show bell icon for the default case too.
mx_RoomTile_iconBell: state === ALL_MESSAGES,
mx_RoomTile_iconBellDot: state === ALL_MESSAGES_LOUD,
mx_RoomTile_iconBellMentions: state === MENTIONS_ONLY,
mx_RoomTile_iconBellCrossed: state === MUTE,
mx_RoomTile_iconBell: state === RoomNotifState.AllMessages,
mx_RoomTile_iconBellDot: state === RoomNotifState.AllMessagesLoud,
mx_RoomTile_iconBellMentions: state === RoomNotifState.MentionsOnly,
mx_RoomTile_iconBellCrossed: state === RoomNotifState.Mute,
// Only show the icon by default if the room is overridden to muted.
// TODO: [FTUE Notifications] Probably need to detect global mute state
mx_RoomTile_notificationsButton_show: state === MUTE,
mx_RoomTile_notificationsButton_show: state === RoomNotifState.Mute,
});
return (

View file

@ -15,20 +15,17 @@ limitations under the License.
*/
import { GenericEchoChamber, implicitlyReverted, PROPERTY_UPDATED } from "./GenericEchoChamber";
import { getRoomNotifsState, setRoomNotifsState } from "../../RoomNotifs";
import { getRoomNotifsState, RoomNotifState, setRoomNotifsState } from "../../RoomNotifs";
import { RoomEchoContext } from "./RoomEchoContext";
import { _t } from "../../languageHandler";
import { Volume } from "../../RoomNotifsTypes";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
export type CachedRoomValues = Volume;
export enum CachedRoomKey {
NotificationVolume,
}
export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedRoomKey, CachedRoomValues> {
private properties = new Map<CachedRoomKey, CachedRoomValues>();
export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedRoomKey, RoomNotifState> {
private properties = new Map<CachedRoomKey, RoomNotifState>();
public constructor(context: RoomEchoContext) {
super(context, (k) => this.properties.get(k));
@ -50,8 +47,8 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
private onAccountData = (event: MatrixEvent) => {
if (event.getType() === "m.push_rules") {
const currentVolume = this.properties.get(CachedRoomKey.NotificationVolume) as Volume;
const newVolume = getRoomNotifsState(this.context.room.roomId) as Volume;
const currentVolume = this.properties.get(CachedRoomKey.NotificationVolume) as RoomNotifState;
const newVolume = getRoomNotifsState(this.context.room.roomId) as RoomNotifState;
if (currentVolume !== newVolume) {
this.updateNotificationVolume();
}
@ -66,11 +63,11 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
// ---- helpers below here ----
public get notificationVolume(): Volume {
public get notificationVolume(): RoomNotifState {
return this.getValue(CachedRoomKey.NotificationVolume);
}
public set notificationVolume(v: Volume) {
public set notificationVolume(v: RoomNotifState) {
this.setValue(_t("Change notification settings"), CachedRoomKey.NotificationVolume, v, async () => {
return setRoomNotifsState(this.context.room.roomId, v);
}, implicitlyReverted);

View file

@ -20,7 +20,7 @@ import { MatrixClientPeg } from "../../MatrixClientPeg";
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
import { readReceiptChangeIsFor } from "../../utils/read-receipts";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import * as RoomNotifs from '../../RoomNotifs';
import * as Unread from '../../Unread';
import { NotificationState } from "./NotificationState";
@ -91,7 +91,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy
this._color = NotificationColor.Unsent;
this._symbol = "!";
this._count = 1; // not used, technically
} else if (RoomNotifs.getRoomNotifsState(this.room.roomId) === RoomNotifs.MUTE) {
} else if (RoomNotifs.getRoomNotifsState(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;
@ -101,8 +101,8 @@ export class RoomNotificationState extends NotificationState implements IDestroy
this._symbol = "!";
this._count = 1; // not used, technically
} else {
const redNotifs = RoomNotifs.getUnreadNotificationCount(this.room, 'highlight');
const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.room, 'total');
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