Inhibit local notifications when local notifications are silenced (#9328)
parent
951cad98d3
commit
a49603b9b8
|
@ -46,6 +46,7 @@ import { mediaFromMxc } from "./customisations/Media";
|
||||||
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||||
import LegacyCallHandler from "./LegacyCallHandler";
|
import LegacyCallHandler from "./LegacyCallHandler";
|
||||||
import VoipUserMapper from "./VoipUserMapper";
|
import VoipUserMapper from "./VoipUserMapper";
|
||||||
|
import { localNotificationsAreSilenced } from "./utils/notifications";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dispatches:
|
* Dispatches:
|
||||||
|
@ -90,8 +91,9 @@ export const Notifier = {
|
||||||
return TextForEvent.textForEvent(ev);
|
return TextForEvent.textForEvent(ev);
|
||||||
},
|
},
|
||||||
|
|
||||||
_displayPopupNotification: function(ev: MatrixEvent, room: Room) {
|
_displayPopupNotification: function(ev: MatrixEvent, room: Room): void {
|
||||||
const plaf = PlatformPeg.get();
|
const plaf = PlatformPeg.get();
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
if (!plaf) {
|
if (!plaf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +101,10 @@ export const Notifier = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (localNotificationsAreSilenced(cli)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let msg = this.notificationMessageForEvent(ev);
|
let msg = this.notificationMessageForEvent(ev);
|
||||||
if (!msg) return;
|
if (!msg) return;
|
||||||
|
|
||||||
|
@ -170,7 +176,12 @@ export const Notifier = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
_playAudioNotification: async function(ev: MatrixEvent, room: Room) {
|
_playAudioNotification: async function(ev: MatrixEvent, room: Room): Promise<void> {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
if (localNotificationsAreSilenced(cli)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const sound = this.getSoundForRoom(room.roomId);
|
const sound = this.getSoundForRoom(room.roomId);
|
||||||
logger.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
|
logger.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
|
||||||
|
|
||||||
|
@ -325,7 +336,7 @@ export const Notifier = {
|
||||||
}
|
}
|
||||||
const isGuest = client.isGuest();
|
const isGuest = client.isGuest();
|
||||||
return !isGuest && this.supportsDesktopNotifications() && !isPushNotifyDisabled() &&
|
return !isGuest && this.supportsDesktopNotifications() && !isPushNotifyDisabled() &&
|
||||||
!this.isEnabled() && !this._isPromptHidden();
|
!localNotificationsAreSilenced(client) && !this.isEnabled() && !this._isPromptHidden();
|
||||||
},
|
},
|
||||||
|
|
||||||
_isPromptHidden: function() {
|
_isPromptHidden: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { LOCAL_NOTIFICATION_SETTINGS_PREFIX } from "matrix-js-sdk/src/@types/event";
|
import { LOCAL_NOTIFICATION_SETTINGS_PREFIX } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { LocalNotificationSettings } from "matrix-js-sdk/src/@types/local_notifications";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
|
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
|
@ -32,7 +33,6 @@ export function getLocalNotificationAccountDataEventType(deviceId: string): stri
|
||||||
export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient): Promise<void> {
|
export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient): Promise<void> {
|
||||||
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
|
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
|
||||||
const event = cli.getAccountData(eventType);
|
const event = cli.getAccountData(eventType);
|
||||||
|
|
||||||
// New sessions will create an account data event to signify they support
|
// New sessions will create an account data event to signify they support
|
||||||
// remote toggling of push notifications on this device. Default `is_silenced=true`
|
// remote toggling of push notifications on this device. Default `is_silenced=true`
|
||||||
// For backwards compat purposes, older sessions will need to check settings value
|
// For backwards compat purposes, older sessions will need to check settings value
|
||||||
|
@ -47,3 +47,9 @@ export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function localNotificationsAreSilenced(cli: MatrixClient): boolean {
|
||||||
|
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
|
||||||
|
const event = cli.getAccountData(eventType);
|
||||||
|
return event?.getContent<LocalNotificationSettings>()?.is_silenced ?? true;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 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 { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
|
import Notifier from "../src/Notifier";
|
||||||
|
import { getLocalNotificationAccountDataEventType } from "../src/utils/notifications";
|
||||||
|
import { getMockClientWithEventEmitter, mkEvent, mkRoom, mockPlatformPeg } from "./test-utils";
|
||||||
|
|
||||||
|
describe("Notifier", () => {
|
||||||
|
let MockPlatform;
|
||||||
|
let accountDataStore = {};
|
||||||
|
|
||||||
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
|
getUserId: jest.fn().mockReturnValue("@bob:example.org"),
|
||||||
|
isGuest: jest.fn().mockReturnValue(false),
|
||||||
|
getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]),
|
||||||
|
setAccountData: jest.fn().mockImplementation((eventType, content) => {
|
||||||
|
accountDataStore[eventType] = new MatrixEvent({
|
||||||
|
type: eventType,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
|
||||||
|
const roomId = "!room1:server";
|
||||||
|
const testEvent = mkEvent({
|
||||||
|
event: true,
|
||||||
|
type: "m.room.message",
|
||||||
|
user: "@user1:server",
|
||||||
|
room: roomId,
|
||||||
|
content: {},
|
||||||
|
});
|
||||||
|
const testRoom = mkRoom(mockClient, roomId);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
accountDataStore = {};
|
||||||
|
MockPlatform = mockPlatformPeg({
|
||||||
|
supportsNotifications: jest.fn().mockReturnValue(true),
|
||||||
|
maySendNotifications: jest.fn().mockReturnValue(true),
|
||||||
|
displayNotification: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Notifier.isBodyEnabled = jest.fn().mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("_displayPopupNotification", () => {
|
||||||
|
it.each([
|
||||||
|
{ silenced: true, count: 0 },
|
||||||
|
{ silenced: false, count: 1 },
|
||||||
|
])("does not dispatch when notifications are silenced", ({ silenced, count }) => {
|
||||||
|
mockClient.setAccountData(accountDataEventKey, { is_silenced: silenced });
|
||||||
|
Notifier._displayPopupNotification(testEvent, testRoom);
|
||||||
|
expect(MockPlatform.displayNotification).toHaveBeenCalledTimes(count);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("_playAudioNotification", () => {
|
||||||
|
it.each([
|
||||||
|
{ silenced: true, count: 0 },
|
||||||
|
{ silenced: false, count: 1 },
|
||||||
|
])("does not dispatch when notifications are silenced", ({ silenced, count }) => {
|
||||||
|
// It's not ideal to only look at whether this function has been called
|
||||||
|
// but avoids starting to look into DOM stuff
|
||||||
|
Notifier.getSoundForRoom = jest.fn();
|
||||||
|
|
||||||
|
mockClient.setAccountData(accountDataEventKey, { is_silenced: silenced });
|
||||||
|
Notifier._playAudioNotification(testEvent, testRoom);
|
||||||
|
expect(Notifier.getSoundForRoom).toHaveBeenCalledTimes(count);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -18,6 +18,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
localNotificationsAreSilenced,
|
||||||
createLocalNotificationSettingsIfNeeded,
|
createLocalNotificationSettingsIfNeeded,
|
||||||
getLocalNotificationAccountDataEventType,
|
getLocalNotificationAccountDataEventType,
|
||||||
} from "../../src/utils/notifications";
|
} from "../../src/utils/notifications";
|
||||||
|
@ -27,7 +28,7 @@ import { getMockClientWithEventEmitter } from "../test-utils/client";
|
||||||
jest.mock("../../src/settings/SettingsStore");
|
jest.mock("../../src/settings/SettingsStore");
|
||||||
|
|
||||||
describe('notifications', () => {
|
describe('notifications', () => {
|
||||||
const accountDataStore = {};
|
let accountDataStore = {};
|
||||||
const mockClient = getMockClientWithEventEmitter({
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
isGuest: jest.fn().mockReturnValue(false),
|
isGuest: jest.fn().mockReturnValue(false),
|
||||||
getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]),
|
getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]),
|
||||||
|
@ -42,6 +43,7 @@ describe('notifications', () => {
|
||||||
const accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
|
const accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
accountDataStore = {};
|
||||||
mocked(SettingsStore).getValue.mockReturnValue(false);
|
mocked(SettingsStore).getValue.mockReturnValue(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,4 +78,17 @@ describe('notifications', () => {
|
||||||
expect(event?.getContent().is_silenced).toBe(false);
|
expect(event?.getContent().is_silenced).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('localNotificationsAreSilenced', () => {
|
||||||
|
it('defaults to true when no setting exists', () => {
|
||||||
|
expect(localNotificationsAreSilenced(mockClient)).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('checks the persisted value', () => {
|
||||||
|
mockClient.setAccountData(accountDataEventKey, { is_silenced: true });
|
||||||
|
expect(localNotificationsAreSilenced(mockClient)).toBeTruthy();
|
||||||
|
|
||||||
|
mockClient.setAccountData(accountDataEventKey, { is_silenced: false });
|
||||||
|
expect(localNotificationsAreSilenced(mockClient)).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue