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 { MatrixClientPeg } from './MatrixClientPeg';
import { PushProcessor } from 'matrix-js-sdk/src/pushprocessor'; 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 enum RoomNotifState {
export const ALL_MESSAGES = 'all_messages'; AllMessagesLoud = 'all_messages_loud',
export const MENTIONS_ONLY = 'mentions_only'; AllMessages = 'all_messages',
export const MUTE = 'mute'; MentionsOnly = 'mentions_only',
Mute = 'mute',
}
export const BADGE_STATES = [ALL_MESSAGES, ALL_MESSAGES_LOUD]; export const BADGE_STATES = [RoomNotifState.AllMessages, RoomNotifState.AllMessagesLoud];
export const MENTION_BADGE_STATES = [...BADGE_STATES, MENTIONS_ONLY]; export const MENTION_BADGE_STATES = [...BADGE_STATES, RoomNotifState.MentionsOnly];
export function shouldShowNotifBadge(roomNotifState) { export function shouldShowNotifBadge(roomNotifState: RoomNotifState): boolean {
return BADGE_STATES.includes(roomNotifState); return BADGE_STATES.includes(roomNotifState);
} }
export function shouldShowMentionBadge(roomNotifState) { export function shouldShowMentionBadge(roomNotifState: RoomNotifState): boolean {
return MENTION_BADGE_STATES.includes(roomNotifState); return MENTION_BADGE_STATES.includes(roomNotifState);
} }
export function aggregateNotificationCount(rooms) { export function aggregateNotificationCount(rooms: Room[]): {count: number, highlight: boolean} {
return rooms.reduce((result, room) => { return rooms.reduce<{count: number, highlight: boolean}>((result, room) => {
const roomNotifState = getRoomNotifsState(room.roomId); 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 // use helper method to include highlights in the previous version of the room
const notificationCount = getUnreadNotificationCount(room); const notificationCount = getUnreadNotificationCount(room);
@ -55,9 +59,9 @@ export function aggregateNotificationCount(rooms) {
}, { count: 0, highlight: false }); }, { count: 0, highlight: false });
} }
export function getRoomHasBadge(room) { export function getRoomHasBadge(room: Room): boolean {
const roomNotifState = getRoomNotifsState(room.roomId); const roomNotifState = getRoomNotifsState(room.roomId);
const highlight = room.getUnreadNotificationCount('highlight') > 0; const highlight = room.getUnreadNotificationCount(NotificationCountType.Highlight) > 0;
const notificationCount = room.getUnreadNotificationCount(); const notificationCount = room.getUnreadNotificationCount();
const notifBadges = notificationCount > 0 && shouldShowNotifBadge(roomNotifState); const notifBadges = notificationCount > 0 && shouldShowNotifBadge(roomNotifState);
@ -66,14 +70,14 @@ export function getRoomHasBadge(room) {
return notifBadges || mentionBadges; return notifBadges || mentionBadges;
} }
export function getRoomNotifsState(roomId) { export function getRoomNotifsState(roomId: string): RoomNotifState {
if (MatrixClientPeg.get().isGuest()) return ALL_MESSAGES; if (MatrixClientPeg.get().isGuest()) return RoomNotifState.AllMessages;
// look through the override rules for a rule affecting this room: // look through the override rules for a rule affecting this room:
// if one exists, it will take precedence. // if one exists, it will take precedence.
const muteRule = findOverrideMuteRule(roomId); const muteRule = findOverrideMuteRule(roomId);
if (muteRule) { if (muteRule) {
return MUTE; return RoomNotifState.Mute;
} }
// for everything else, look at the room rule. // 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 // 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 // (in particular this will be 'wrong' for one to one rooms because
// they will notify loudly for all messages) // 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 // a mute at the room level will still allow mentions
// to notify // to notify
if (isMuteRule(roomRule)) return MENTIONS_ONLY; if (isMuteRule(roomRule)) return RoomNotifState.MentionsOnly;
const actionsObject = PushProcessor.actionListToActionsObject(roomRule.actions); const actionsObject = PushProcessor.actionListToActionsObject(roomRule.actions);
if (actionsObject.tweaks.sound) return ALL_MESSAGES_LOUD; if (actionsObject.tweaks.sound) return RoomNotifState.AllMessagesLoud;
return null; return null;
} }
export function setRoomNotifsState(roomId, newState) { export function setRoomNotifsState(roomId: string, newState: RoomNotifState): Promise<void> {
if (newState === MUTE) { if (newState === RoomNotifState.Mute) {
return setRoomNotifsStateMuted(roomId); return setRoomNotifsStateMuted(roomId);
} else { } else {
return setRoomNotifsStateUnmuted(roomId, newState); return setRoomNotifsStateUnmuted(roomId, newState);
} }
} }
export function getUnreadNotificationCount(room, type=null) { export function getUnreadNotificationCount(room: Room, type: NotificationCountType = null): number {
let notificationCount = room.getUnreadNotificationCount(type); let notificationCount = room.getUnreadNotificationCount(type);
// Check notification counts in the old room just in case there's some lost // 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 // notifying the user for unread messages because they would have extreme
// difficulty changing their notification preferences away from "All Messages" // difficulty changing their notification preferences away from "All Messages"
// and "Noisy". // and "Noisy".
notificationCount += oldRoom.getUnreadNotificationCount("highlight"); notificationCount += oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight);
} }
} }
return notificationCount; return notificationCount;
} }
function setRoomNotifsStateMuted(roomId) { function setRoomNotifsStateMuted(roomId: string): Promise<any> {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const promises = []; const promises = [];
// delete the room rule // delete the room rule
const roomRule = cli.getRoomPushRule('global', roomId); const roomRule = cli.getRoomPushRule('global', roomId);
if (roomRule) { 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 // 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 // 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 // though, so using the room ID as the rule ID is logical and prevents
// duplicate copies of the rule. // duplicate copies of the rule.
promises.push(cli.addPushRule('global', 'override', roomId, { promises.push(cli.addPushRule('global', PushRuleKind.Override, roomId, {
conditions: [ conditions: [
{ {
kind: 'event_match', kind: 'event_match',
@ -162,30 +166,30 @@ function setRoomNotifsStateMuted(roomId) {
return Promise.all(promises); return Promise.all(promises);
} }
function setRoomNotifsStateUnmuted(roomId, newState) { function setRoomNotifsStateUnmuted(roomId: string, newState: RoomNotifState): Promise<any> {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const promises = []; const promises = [];
const overrideMuteRule = findOverrideMuteRule(roomId); const overrideMuteRule = findOverrideMuteRule(roomId);
if (overrideMuteRule) { 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); const roomRule = cli.getRoomPushRule('global', roomId);
if (roomRule) { 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') { } else if (newState === RoomNotifState.MentionsOnly) {
promises.push(cli.addPushRule('global', 'room', roomId, { promises.push(cli.addPushRule('global', PushRuleKind.RoomSpecific, roomId, {
actions: [ actions: [
'dont_notify', 'dont_notify',
], ],
})); }));
// https://matrix.org/jira/browse/SPEC-400 // https://matrix.org/jira/browse/SPEC-400
promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true)); promises.push(cli.setPushRuleEnabled('global', PushRuleKind.RoomSpecific, roomId, true));
} else if ('all_messages_loud') { } else if (newState === RoomNotifState.AllMessagesLoud) {
promises.push(cli.addPushRule('global', 'room', roomId, { promises.push(cli.addPushRule('global', PushRuleKind.RoomSpecific, roomId, {
actions: [ actions: [
'notify', 'notify',
{ {
@ -195,13 +199,13 @@ function setRoomNotifsStateUnmuted(roomId, newState) {
], ],
})); }));
// https://matrix.org/jira/browse/SPEC-400 // 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); return Promise.all(promises);
} }
function findOverrideMuteRule(roomId) { function findOverrideMuteRule(roomId: string): IAnnotatedPushRule {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if (!cli.pushRules || if (!cli.pushRules ||
!cli.pushRules['global'] || !cli.pushRules['global'] ||
@ -218,7 +222,7 @@ function findOverrideMuteRule(roomId) {
return null; return null;
} }
function isRuleForRoom(roomId, rule) { function isRuleForRoom(roomId: string, rule: IAnnotatedPushRule): boolean {
if (rule.conditions.length !== 1) { if (rule.conditions.length !== 1) {
return false; return false;
} }
@ -226,6 +230,6 @@ function isRuleForRoom(roomId, rule) {
return (cond.kind === 'event_match' && cond.key === 'room_id' && cond.pattern === roomId); 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'); 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 { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore"; import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; 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 { MatrixClientPeg } from "../../../MatrixClientPeg";
import NotificationBadge from "./NotificationBadge"; import NotificationBadge from "./NotificationBadge";
import { Volume } from "../../../RoomNotifsTypes";
import RoomListStore from "../../../stores/room-list/RoomListStore"; import RoomListStore from "../../../stores/room-list/RoomListStore";
import RoomListActions from "../../../actions/RoomListActions"; import RoomListActions from "../../../actions/RoomListActions";
import { ActionPayload } from "../../../dispatcher/payloads"; 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 this.setState({ generalMenuPosition: null }); // hide the menu
}; };
private async saveNotifState(ev: ButtonEvent, newState: Volume) { private async saveNotifState(ev: ButtonEvent, newState: RoomNotifState) {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
if (MatrixClientPeg.get().isGuest()) return; 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 onClickAllNotifs = ev => this.saveNotifState(ev, RoomNotifState.AllMessages);
private onClickAlertMe = ev => this.saveNotifState(ev, ALL_MESSAGES_LOUD); private onClickAlertMe = ev => this.saveNotifState(ev, RoomNotifState.AllMessagesLoud);
private onClickMentions = ev => this.saveNotifState(ev, MENTIONS_ONLY); private onClickMentions = ev => this.saveNotifState(ev, RoomNotifState.MentionsOnly);
private onClickMute = ev => this.saveNotifState(ev, MUTE); private onClickMute = ev => this.saveNotifState(ev, RoomNotifState.Mute);
private renderNotificationsMenu(isActive: boolean): React.ReactElement { private renderNotificationsMenu(isActive: boolean): React.ReactElement {
if (MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Archived || if (MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Archived ||
@ -404,25 +403,25 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
<IconizedContextMenuOptionList first> <IconizedContextMenuOptionList first>
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("Use default")} label={_t("Use default")}
active={state === ALL_MESSAGES} active={state === RoomNotifState.AllMessages}
iconClassName="mx_RoomTile_iconBell" iconClassName="mx_RoomTile_iconBell"
onClick={this.onClickAllNotifs} onClick={this.onClickAllNotifs}
/> />
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("All messages")} label={_t("All messages")}
active={state === ALL_MESSAGES_LOUD} active={state === RoomNotifState.AllMessagesLoud}
iconClassName="mx_RoomTile_iconBellDot" iconClassName="mx_RoomTile_iconBellDot"
onClick={this.onClickAlertMe} onClick={this.onClickAlertMe}
/> />
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("Mentions & Keywords")} label={_t("Mentions & Keywords")}
active={state === MENTIONS_ONLY} active={state === RoomNotifState.MentionsOnly}
iconClassName="mx_RoomTile_iconBellMentions" iconClassName="mx_RoomTile_iconBellMentions"
onClick={this.onClickMentions} onClick={this.onClickMentions}
/> />
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("None")} label={_t("None")}
active={state === MUTE} active={state === RoomNotifState.Mute}
iconClassName="mx_RoomTile_iconBellCrossed" iconClassName="mx_RoomTile_iconBellCrossed"
onClick={this.onClickMute} onClick={this.onClickMute}
/> />
@ -432,14 +431,14 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
const classes = classNames("mx_RoomTile_notificationsButton", { const classes = classNames("mx_RoomTile_notificationsButton", {
// Show bell icon for the default case too. // Show bell icon for the default case too.
mx_RoomTile_iconBell: state === ALL_MESSAGES, mx_RoomTile_iconBell: state === RoomNotifState.AllMessages,
mx_RoomTile_iconBellDot: state === ALL_MESSAGES_LOUD, mx_RoomTile_iconBellDot: state === RoomNotifState.AllMessagesLoud,
mx_RoomTile_iconBellMentions: state === MENTIONS_ONLY, mx_RoomTile_iconBellMentions: state === RoomNotifState.MentionsOnly,
mx_RoomTile_iconBellCrossed: state === MUTE, mx_RoomTile_iconBellCrossed: state === RoomNotifState.Mute,
// Only show the icon by default if the room is overridden to muted. // Only show the icon by default if the room is overridden to muted.
// TODO: [FTUE Notifications] Probably need to detect global mute state // TODO: [FTUE Notifications] Probably need to detect global mute state
mx_RoomTile_notificationsButton_show: state === MUTE, mx_RoomTile_notificationsButton_show: state === RoomNotifState.Mute,
}); });
return ( return (

View file

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

View file

@ -20,7 +20,7 @@ import { MatrixClientPeg } from "../../MatrixClientPeg";
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership"; import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
import { readReceiptChangeIsFor } from "../../utils/read-receipts"; import { readReceiptChangeIsFor } from "../../utils/read-receipts";
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; 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 RoomNotifs from '../../RoomNotifs';
import * as Unread from '../../Unread'; import * as Unread from '../../Unread';
import { NotificationState } from "./NotificationState"; import { NotificationState } from "./NotificationState";
@ -91,7 +91,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy
this._color = NotificationColor.Unsent; this._color = NotificationColor.Unsent;
this._symbol = "!"; this._symbol = "!";
this._count = 1; // not used, technically 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. // When muted we suppress all notification states, even if we have context on them.
this._color = NotificationColor.None; this._color = NotificationColor.None;
this._symbol = null; this._symbol = null;
@ -101,8 +101,8 @@ export class RoomNotificationState extends NotificationState implements IDestroy
this._symbol = "!"; this._symbol = "!";
this._count = 1; // not used, technically this._count = 1; // not used, technically
} else { } else {
const redNotifs = RoomNotifs.getUnreadNotificationCount(this.room, 'highlight'); const redNotifs = RoomNotifs.getUnreadNotificationCount(this.room, NotificationCountType.Highlight);
const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.room, 'total'); const greyNotifs = RoomNotifs.getUnreadNotificationCount(this.room, NotificationCountType.Total);
// For a 'true count' we pick the grey notifications first because they include the // 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 // red notifications. If we don't have a grey count for some reason we use the red