Merge pull request #5088 from matrix-org/t3chguy/notifications/14880
Decouple Audible notifications from Desktop notifications
This commit is contained in:
commit
fe2f29d78e
5 changed files with 31 additions and 31 deletions
6
src/@types/global.d.ts
vendored
6
src/@types/global.d.ts
vendored
|
@ -27,6 +27,7 @@ import {IntegrationManagers} from "../integrations/IntegrationManagers";
|
||||||
import {ModalManager} from "../Modal";
|
import {ModalManager} from "../Modal";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import {ActiveRoomObserver} from "../ActiveRoomObserver";
|
import {ActiveRoomObserver} from "../ActiveRoomObserver";
|
||||||
|
import {Notifier} from "../Notifier";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -47,6 +48,7 @@ declare global {
|
||||||
mxIntegrationManagers: typeof IntegrationManagers;
|
mxIntegrationManagers: typeof IntegrationManagers;
|
||||||
singletonModalManager: ModalManager;
|
singletonModalManager: ModalManager;
|
||||||
mxSettingsStore: SettingsStore;
|
mxSettingsStore: SettingsStore;
|
||||||
|
mxNotifier: typeof Notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround for https://github.com/microsoft/TypeScript/issues/30933
|
// workaround for https://github.com/microsoft/TypeScript/issues/30933
|
||||||
|
@ -79,4 +81,8 @@ declare global {
|
||||||
interface PromiseConstructor {
|
interface PromiseConstructor {
|
||||||
allSettled<T>(promises: Promise<T>[]): Promise<Array<ISettledFulfilled<T> | ISettledRejected>>;
|
allSettled<T>(promises: Promise<T>[]): Promise<Array<ISettledFulfilled<T> | ISettledRejected>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface HTMLAudioElement {
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,8 @@ export default abstract class BasePlatform {
|
||||||
|
|
||||||
loudNotification(ev: Event, room: Object) {
|
loudNotification(ev: Event, room: Object) {
|
||||||
}
|
}
|
||||||
|
clearNotification(notif: Notification) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a promise that resolves to a string representing the current version of the application.
|
* Returns a promise that resolves to a string representing the current version of the application.
|
||||||
|
|
|
@ -17,6 +17,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import SdkConfig from './SdkConfig';
|
import SdkConfig from './SdkConfig';
|
||||||
import PlatformPeg from './PlatformPeg';
|
import PlatformPeg from './PlatformPeg';
|
||||||
|
@ -28,9 +31,7 @@ import * as sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import {
|
import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast";
|
||||||
hideToast as hideNotificationsToast,
|
|
||||||
} from "./toasts/DesktopNotificationsToast";
|
|
||||||
import {SettingLevel} from "./settings/SettingLevel";
|
import {SettingLevel} from "./settings/SettingLevel";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -55,7 +56,7 @@ const typehandlers = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Notifier = {
|
export const Notifier = {
|
||||||
notifsByRoom: {},
|
notifsByRoom: {},
|
||||||
|
|
||||||
// A list of event IDs that we've received but need to wait until
|
// A list of event IDs that we've received but need to wait until
|
||||||
|
@ -63,14 +64,14 @@ const Notifier = {
|
||||||
// or not
|
// or not
|
||||||
pendingEncryptedEventIds: [],
|
pendingEncryptedEventIds: [],
|
||||||
|
|
||||||
notificationMessageForEvent: function(ev) {
|
notificationMessageForEvent: function(ev: MatrixEvent) {
|
||||||
if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
|
if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
|
||||||
return typehandlers[ev.getContent().msgtype](ev);
|
return typehandlers[ev.getContent().msgtype](ev);
|
||||||
}
|
}
|
||||||
return TextForEvent.textForEvent(ev);
|
return TextForEvent.textForEvent(ev);
|
||||||
},
|
},
|
||||||
|
|
||||||
_displayPopupNotification: function(ev, room) {
|
_displayPopupNotification: function(ev: MatrixEvent, room: Room) {
|
||||||
const plaf = PlatformPeg.get();
|
const plaf = PlatformPeg.get();
|
||||||
if (!plaf) {
|
if (!plaf) {
|
||||||
return;
|
return;
|
||||||
|
@ -125,7 +126,7 @@ const Notifier = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getSoundForRoom: function(roomId) {
|
getSoundForRoom: function(roomId: string) {
|
||||||
// We do no caching here because the SDK caches setting
|
// We do no caching here because the SDK caches setting
|
||||||
// and the browser will cache the sound.
|
// and the browser will cache the sound.
|
||||||
const content = SettingsStore.getValue("notificationSound", roomId);
|
const content = SettingsStore.getValue("notificationSound", roomId);
|
||||||
|
@ -153,12 +154,13 @@ const Notifier = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
_playAudioNotification: async function(ev, room) {
|
_playAudioNotification: async function(ev: MatrixEvent, room: Room) {
|
||||||
const sound = this.getSoundForRoom(room.roomId);
|
const sound = this.getSoundForRoom(room.roomId);
|
||||||
console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
|
console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio");
|
const selector =
|
||||||
|
document.querySelector<HTMLAudioElement>(sound ? `audio[src='${sound.url}']` : "#messageAudio");
|
||||||
let audioElement = selector;
|
let audioElement = selector;
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
if (!sound) {
|
if (!sound) {
|
||||||
|
@ -207,7 +209,7 @@ const Notifier = {
|
||||||
return plaf && plaf.supportsNotifications();
|
return plaf && plaf.supportsNotifications();
|
||||||
},
|
},
|
||||||
|
|
||||||
setEnabled: function(enable, callback) {
|
setEnabled: function(enable: boolean, callback?: () => void) {
|
||||||
const plaf = PlatformPeg.get();
|
const plaf = PlatformPeg.get();
|
||||||
if (!plaf) return;
|
if (!plaf) return;
|
||||||
|
|
||||||
|
@ -277,10 +279,11 @@ const Notifier = {
|
||||||
},
|
},
|
||||||
|
|
||||||
isAudioEnabled: function() {
|
isAudioEnabled: function() {
|
||||||
return this.isEnabled() && SettingsStore.getValue("audioNotificationsEnabled");
|
// We don't route Audio via the HTML Notifications API so it is possible regardless of other things
|
||||||
|
return SettingsStore.getValue("audioNotificationsEnabled");
|
||||||
},
|
},
|
||||||
|
|
||||||
setToolbarHidden: function(hidden, persistent = true) {
|
setToolbarHidden: function(hidden: boolean, persistent = true) {
|
||||||
this.toolbarHidden = hidden;
|
this.toolbarHidden = hidden;
|
||||||
|
|
||||||
Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden);
|
Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden);
|
||||||
|
@ -289,7 +292,7 @@ const Notifier = {
|
||||||
|
|
||||||
// update the info to localStorage for persistent settings
|
// update the info to localStorage for persistent settings
|
||||||
if (persistent && global.localStorage) {
|
if (persistent && global.localStorage) {
|
||||||
global.localStorage.setItem("notifications_hidden", hidden);
|
global.localStorage.setItem("notifications_hidden", String(hidden));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -312,7 +315,7 @@ const Notifier = {
|
||||||
return this.toolbarHidden;
|
return this.toolbarHidden;
|
||||||
},
|
},
|
||||||
|
|
||||||
onSyncStateChange: function(state) {
|
onSyncStateChange: function(state: string) {
|
||||||
if (state === "SYNCING") {
|
if (state === "SYNCING") {
|
||||||
this.isSyncing = true;
|
this.isSyncing = true;
|
||||||
} else if (state === "STOPPED" || state === "ERROR") {
|
} else if (state === "STOPPED" || state === "ERROR") {
|
||||||
|
@ -320,7 +323,7 @@ const Notifier = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onEvent: function(ev) {
|
onEvent: function(ev: MatrixEvent) {
|
||||||
if (!this.isSyncing) return; // don't alert for any messages initially
|
if (!this.isSyncing) return; // don't alert for any messages initially
|
||||||
if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return;
|
if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return;
|
||||||
|
|
||||||
|
@ -338,7 +341,7 @@ const Notifier = {
|
||||||
this._evaluateEvent(ev);
|
this._evaluateEvent(ev);
|
||||||
},
|
},
|
||||||
|
|
||||||
onEventDecrypted: function(ev) {
|
onEventDecrypted: function(ev: MatrixEvent) {
|
||||||
// 'decrypted' means the decryption process has finished: it may have failed,
|
// 'decrypted' means the decryption process has finished: it may have failed,
|
||||||
// in which case it might decrypt soon if the keys arrive
|
// in which case it might decrypt soon if the keys arrive
|
||||||
if (ev.isDecryptionFailure()) return;
|
if (ev.isDecryptionFailure()) return;
|
||||||
|
@ -350,7 +353,7 @@ const Notifier = {
|
||||||
this._evaluateEvent(ev);
|
this._evaluateEvent(ev);
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomReceipt: function(ev, room) {
|
onRoomReceipt: function(ev: MatrixEvent, room: Room) {
|
||||||
if (room.getUnreadNotificationCount() === 0) {
|
if (room.getUnreadNotificationCount() === 0) {
|
||||||
// ideally we would clear each notification when it was read,
|
// ideally we would clear each notification when it was read,
|
||||||
// but we have no way, given a read receipt, to know whether
|
// but we have no way, given a read receipt, to know whether
|
||||||
|
@ -383,8 +386,8 @@ const Notifier = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!global.mxNotifier) {
|
if (!window.mxNotifier) {
|
||||||
global.mxNotifier = Notifier;
|
window.mxNotifier = Notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default global.mxNotifier;
|
export default window.mxNotifier;
|
|
@ -19,7 +19,6 @@ import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||||
|
|
||||||
import { _td } from '../languageHandler';
|
import { _td } from '../languageHandler';
|
||||||
import {
|
import {
|
||||||
AudioNotificationsEnabledController,
|
|
||||||
NotificationBodyEnabledController,
|
NotificationBodyEnabledController,
|
||||||
NotificationsEnabledController,
|
NotificationsEnabledController,
|
||||||
} from "./controllers/NotificationControllers";
|
} from "./controllers/NotificationControllers";
|
||||||
|
@ -460,7 +459,6 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
"audioNotificationsEnabled": {
|
"audioNotificationsEnabled": {
|
||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||||
default: true,
|
default: true,
|
||||||
controller: new AudioNotificationsEnabledController(),
|
|
||||||
},
|
},
|
||||||
"enableWidgetScreenshots": {
|
"enableWidgetScreenshots": {
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
|
|
|
@ -79,12 +79,3 @@ export class NotificationBodyEnabledController extends SettingController {
|
||||||
return calculatedValue;
|
return calculatedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AudioNotificationsEnabledController extends SettingController {
|
|
||||||
public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any): any {
|
|
||||||
if (!getNotifier().isPossible()) return false;
|
|
||||||
|
|
||||||
// Note: Audio notifications are *not* enabled by default.
|
|
||||||
return calculatedValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue