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

Commit a49603b

Browse files
author
Germain
authored
Inhibit local notifications when local notifications are silenced (#9328)
1 parent 951cad9 commit a49603b

File tree

4 files changed

+122
-5
lines changed

4 files changed

+122
-5
lines changed

src/Notifier.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { mediaFromMxc } from "./customisations/Media";
4646
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
4747
import LegacyCallHandler from "./LegacyCallHandler";
4848
import VoipUserMapper from "./VoipUserMapper";
49+
import { localNotificationsAreSilenced } from "./utils/notifications";
4950

5051
/*
5152
* Dispatches:
@@ -90,15 +91,20 @@ export const Notifier = {
9091
return TextForEvent.textForEvent(ev);
9192
},
9293

93-
_displayPopupNotification: function(ev: MatrixEvent, room: Room) {
94+
_displayPopupNotification: function(ev: MatrixEvent, room: Room): void {
9495
const plaf = PlatformPeg.get();
96+
const cli = MatrixClientPeg.get();
9597
if (!plaf) {
9698
return;
9799
}
98100
if (!plaf.supportsNotifications() || !plaf.maySendNotifications()) {
99101
return;
100102
}
101103

104+
if (localNotificationsAreSilenced(cli)) {
105+
return;
106+
}
107+
102108
let msg = this.notificationMessageForEvent(ev);
103109
if (!msg) return;
104110

@@ -170,7 +176,12 @@ export const Notifier = {
170176
};
171177
},
172178

173-
_playAudioNotification: async function(ev: MatrixEvent, room: Room) {
179+
_playAudioNotification: async function(ev: MatrixEvent, room: Room): Promise<void> {
180+
const cli = MatrixClientPeg.get();
181+
if (localNotificationsAreSilenced(cli)) {
182+
return;
183+
}
184+
174185
const sound = this.getSoundForRoom(room.roomId);
175186
logger.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
176187

@@ -325,7 +336,7 @@ export const Notifier = {
325336
}
326337
const isGuest = client.isGuest();
327338
return !isGuest && this.supportsDesktopNotifications() && !isPushNotifyDisabled() &&
328-
!this.isEnabled() && !this._isPromptHidden();
339+
!localNotificationsAreSilenced(client) && !this.isEnabled() && !this._isPromptHidden();
329340
},
330341

331342
_isPromptHidden: function() {

src/utils/notifications.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import { LOCAL_NOTIFICATION_SETTINGS_PREFIX } from "matrix-js-sdk/src/@types/event";
18+
import { LocalNotificationSettings } from "matrix-js-sdk/src/@types/local_notifications";
1819
import { MatrixClient } from "matrix-js-sdk/src/client";
1920

2021
import SettingsStore from "../settings/SettingsStore";
@@ -32,7 +33,6 @@ export function getLocalNotificationAccountDataEventType(deviceId: string): stri
3233
export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient): Promise<void> {
3334
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
3435
const event = cli.getAccountData(eventType);
35-
3636
// New sessions will create an account data event to signify they support
3737
// remote toggling of push notifications on this device. Default `is_silenced=true`
3838
// For backwards compat purposes, older sessions will need to check settings value
@@ -47,3 +47,9 @@ export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient)
4747
});
4848
}
4949
}
50+
51+
export function localNotificationsAreSilenced(cli: MatrixClient): boolean {
52+
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
53+
const event = cli.getAccountData(eventType);
54+
return event?.getContent<LocalNotificationSettings>()?.is_silenced ?? true;
55+
}

test/Notifier-test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
18+
19+
import Notifier from "../src/Notifier";
20+
import { getLocalNotificationAccountDataEventType } from "../src/utils/notifications";
21+
import { getMockClientWithEventEmitter, mkEvent, mkRoom, mockPlatformPeg } from "./test-utils";
22+
23+
describe("Notifier", () => {
24+
let MockPlatform;
25+
let accountDataStore = {};
26+
27+
const mockClient = getMockClientWithEventEmitter({
28+
getUserId: jest.fn().mockReturnValue("@bob:example.org"),
29+
isGuest: jest.fn().mockReturnValue(false),
30+
getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]),
31+
setAccountData: jest.fn().mockImplementation((eventType, content) => {
32+
accountDataStore[eventType] = new MatrixEvent({
33+
type: eventType,
34+
content,
35+
});
36+
}),
37+
});
38+
const accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
39+
const roomId = "!room1:server";
40+
const testEvent = mkEvent({
41+
event: true,
42+
type: "m.room.message",
43+
user: "@user1:server",
44+
room: roomId,
45+
content: {},
46+
});
47+
const testRoom = mkRoom(mockClient, roomId);
48+
49+
beforeEach(() => {
50+
accountDataStore = {};
51+
MockPlatform = mockPlatformPeg({
52+
supportsNotifications: jest.fn().mockReturnValue(true),
53+
maySendNotifications: jest.fn().mockReturnValue(true),
54+
displayNotification: jest.fn(),
55+
});
56+
57+
Notifier.isBodyEnabled = jest.fn().mockReturnValue(true);
58+
});
59+
60+
describe("_displayPopupNotification", () => {
61+
it.each([
62+
{ silenced: true, count: 0 },
63+
{ silenced: false, count: 1 },
64+
])("does not dispatch when notifications are silenced", ({ silenced, count }) => {
65+
mockClient.setAccountData(accountDataEventKey, { is_silenced: silenced });
66+
Notifier._displayPopupNotification(testEvent, testRoom);
67+
expect(MockPlatform.displayNotification).toHaveBeenCalledTimes(count);
68+
});
69+
});
70+
71+
describe("_playAudioNotification", () => {
72+
it.each([
73+
{ silenced: true, count: 0 },
74+
{ silenced: false, count: 1 },
75+
])("does not dispatch when notifications are silenced", ({ silenced, count }) => {
76+
// It's not ideal to only look at whether this function has been called
77+
// but avoids starting to look into DOM stuff
78+
Notifier.getSoundForRoom = jest.fn();
79+
80+
mockClient.setAccountData(accountDataEventKey, { is_silenced: silenced });
81+
Notifier._playAudioNotification(testEvent, testRoom);
82+
expect(Notifier.getSoundForRoom).toHaveBeenCalledTimes(count);
83+
});
84+
});
85+
});

test/utils/notifications-test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
1818
import { mocked } from "jest-mock";
1919

2020
import {
21+
localNotificationsAreSilenced,
2122
createLocalNotificationSettingsIfNeeded,
2223
getLocalNotificationAccountDataEventType,
2324
} from "../../src/utils/notifications";
@@ -27,7 +28,7 @@ import { getMockClientWithEventEmitter } from "../test-utils/client";
2728
jest.mock("../../src/settings/SettingsStore");
2829

2930
describe('notifications', () => {
30-
const accountDataStore = {};
31+
let accountDataStore = {};
3132
const mockClient = getMockClientWithEventEmitter({
3233
isGuest: jest.fn().mockReturnValue(false),
3334
getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]),
@@ -42,6 +43,7 @@ describe('notifications', () => {
4243
const accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
4344

4445
beforeEach(() => {
46+
accountDataStore = {};
4547
mocked(SettingsStore).getValue.mockReturnValue(false);
4648
});
4749

@@ -76,4 +78,17 @@ describe('notifications', () => {
7678
expect(event?.getContent().is_silenced).toBe(false);
7779
});
7880
});
81+
82+
describe('localNotificationsAreSilenced', () => {
83+
it('defaults to true when no setting exists', () => {
84+
expect(localNotificationsAreSilenced(mockClient)).toBeTruthy();
85+
});
86+
it('checks the persisted value', () => {
87+
mockClient.setAccountData(accountDataEventKey, { is_silenced: true });
88+
expect(localNotificationsAreSilenced(mockClient)).toBeTruthy();
89+
90+
mockClient.setAccountData(accountDataEventKey, { is_silenced: false });
91+
expect(localNotificationsAreSilenced(mockClient)).toBeFalsy();
92+
});
93+
});
7994
});

0 commit comments

Comments
 (0)