@@ -18,6 +18,7 @@ import React from "react";
1818import { IAnnotatedPushRule , IPusher , PushRuleAction , PushRuleKind , RuleId } from "matrix-js-sdk/src/@types/PushRules" ;
1919import { IThreepid , ThreepidMedium } from "matrix-js-sdk/src/@types/threepids" ;
2020import { logger } from "matrix-js-sdk/src/logger" ;
21+ import { LocalNotificationSettings } from "matrix-js-sdk/src/@types/local_notifications" ;
2122
2223import Spinner from "../elements/Spinner" ;
2324import { MatrixClientPeg } from "../../../MatrixClientPeg" ;
@@ -41,6 +42,7 @@ import AccessibleButton from "../elements/AccessibleButton";
4142import TagComposer from "../elements/TagComposer" ;
4243import { objectClone } from "../../../utils/objects" ;
4344import { arrayDiff } from "../../../utils/arrays" ;
45+ import { getLocalNotificationAccountDataEventType } from "../../../utils/notifications" ;
4446
4547// TODO: this "view" component still has far too much application logic in it,
4648// which should be factored out to other files.
@@ -106,6 +108,7 @@ interface IState {
106108 pushers ?: IPusher [ ] ;
107109 threepids ?: IThreepid [ ] ;
108110
111+ deviceNotificationsEnabled : boolean ;
109112 desktopNotifications : boolean ;
110113 desktopShowBody : boolean ;
111114 audioNotifications : boolean ;
@@ -119,6 +122,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
119122
120123 this . state = {
121124 phase : Phase . Loading ,
125+ deviceNotificationsEnabled : SettingsStore . getValue ( "deviceNotificationsEnabled" ) ?? false ,
122126 desktopNotifications : SettingsStore . getValue ( "notificationsEnabled" ) ,
123127 desktopShowBody : SettingsStore . getValue ( "notificationBodyEnabled" ) ,
124128 audioNotifications : SettingsStore . getValue ( "audioNotificationsEnabled" ) ,
@@ -128,6 +132,9 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
128132 SettingsStore . watchSetting ( "notificationsEnabled" , null , ( ...[ , , , , value ] ) =>
129133 this . setState ( { desktopNotifications : value as boolean } ) ,
130134 ) ,
135+ SettingsStore . watchSetting ( "deviceNotificationsEnabled" , null , ( ...[ , , , , value ] ) => {
136+ this . setState ( { deviceNotificationsEnabled : value as boolean } ) ;
137+ } ) ,
131138 SettingsStore . watchSetting ( "notificationBodyEnabled" , null , ( ...[ , , , , value ] ) =>
132139 this . setState ( { desktopShowBody : value as boolean } ) ,
133140 ) ,
@@ -148,12 +155,19 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
148155 public componentDidMount ( ) {
149156 // noinspection JSIgnoredPromiseFromCall
150157 this . refreshFromServer ( ) ;
158+ this . refreshFromAccountData ( ) ;
151159 }
152160
153161 public componentWillUnmount ( ) {
154162 this . settingWatchers . forEach ( watcher => SettingsStore . unwatchSetting ( watcher ) ) ;
155163 }
156164
165+ public componentDidUpdate ( prevProps : Readonly < IProps > , prevState : Readonly < IState > ) : void {
166+ if ( this . state . deviceNotificationsEnabled !== prevState . deviceNotificationsEnabled ) {
167+ this . persistLocalNotificationSettings ( this . state . deviceNotificationsEnabled ) ;
168+ }
169+ }
170+
157171 private async refreshFromServer ( ) {
158172 try {
159173 const newState = ( await Promise . all ( [
@@ -162,7 +176,9 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
162176 this . refreshThreepids ( ) ,
163177 ] ) ) . reduce ( ( p , c ) => Object . assign ( c , p ) , { } ) ;
164178
165- this . setState < keyof Omit < IState , "desktopNotifications" | "desktopShowBody" | "audioNotifications" > > ( {
179+ this . setState < keyof Omit < IState ,
180+ "deviceNotificationsEnabled" | "desktopNotifications" | "desktopShowBody" | "audioNotifications" >
181+ > ( {
166182 ...newState ,
167183 phase : Phase . Ready ,
168184 } ) ;
@@ -172,6 +188,22 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
172188 }
173189 }
174190
191+ private async refreshFromAccountData ( ) {
192+ const cli = MatrixClientPeg . get ( ) ;
193+ const settingsEvent = cli . getAccountData ( getLocalNotificationAccountDataEventType ( cli . deviceId ) ) ;
194+ if ( settingsEvent ) {
195+ const notificationsEnabled = ! ( settingsEvent . getContent ( ) as LocalNotificationSettings ) . is_silenced ;
196+ await this . updateDeviceNotifications ( notificationsEnabled ) ;
197+ }
198+ }
199+
200+ private persistLocalNotificationSettings ( enabled : boolean ) : Promise < { } > {
201+ const cli = MatrixClientPeg . get ( ) ;
202+ return cli . setAccountData ( getLocalNotificationAccountDataEventType ( cli . deviceId ) , {
203+ is_silenced : ! enabled ,
204+ } ) ;
205+ }
206+
175207 private async refreshRules ( ) : Promise < Partial < IState > > {
176208 const ruleSets = await MatrixClientPeg . get ( ) . getPushRules ( ) ;
177209 const categories = {
@@ -297,6 +329,10 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
297329 }
298330 } ;
299331
332+ private updateDeviceNotifications = async ( checked : boolean ) => {
333+ await SettingsStore . setValue ( "deviceNotificationsEnabled" , null , SettingLevel . DEVICE , checked ) ;
334+ } ;
335+
300336 private onEmailNotificationsChanged = async ( email : string , checked : boolean ) => {
301337 this . setState ( { phase : Phase . Persisting } ) ;
302338
@@ -497,7 +533,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
497533 const masterSwitch = < LabelledToggleSwitch
498534 data-test-id = 'notif-master-switch'
499535 value = { ! this . isInhibited }
500- label = { _t ( "Enable for this account" ) }
536+ label = { _t ( "Enable notifications for this account" ) }
537+ caption = { _t ( "Turn off to disable notifications on all your devices and sessions" ) }
501538 onChange = { this . onMasterRuleChanged }
502539 disabled = { this . state . phase === Phase . Persisting }
503540 /> ;
@@ -521,28 +558,36 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
521558 { masterSwitch }
522559
523560 < LabelledToggleSwitch
524- data-test-id = 'notif-setting-notificationsEnabled '
525- value = { this . state . desktopNotifications }
526- onChange = { this . onDesktopNotificationsChanged }
527- label = { _t ( 'Enable desktop notifications for this session' ) }
561+ data-test-id = 'notif-device-switch '
562+ value = { this . state . deviceNotificationsEnabled }
563+ label = { _t ( "Enable notifications for this device" ) }
564+ onChange = { checked => this . updateDeviceNotifications ( checked ) }
528565 disabled = { this . state . phase === Phase . Persisting }
529566 />
530567
531- < LabelledToggleSwitch
532- data-test-id = 'notif-setting-notificationBodyEnabled'
533- value = { this . state . desktopShowBody }
534- onChange = { this . onDesktopShowBodyChanged }
535- label = { _t ( 'Show message in desktop notification' ) }
536- disabled = { this . state . phase === Phase . Persisting }
537- />
538-
539- < LabelledToggleSwitch
540- data-test-id = 'notif-setting-audioNotificationsEnabled'
541- value = { this . state . audioNotifications }
542- onChange = { this . onAudioNotificationsChanged }
543- label = { _t ( 'Enable audible notifications for this session' ) }
544- disabled = { this . state . phase === Phase . Persisting }
545- />
568+ { this . state . deviceNotificationsEnabled && ( < >
569+ < LabelledToggleSwitch
570+ data-test-id = 'notif-setting-notificationsEnabled'
571+ value = { this . state . desktopNotifications }
572+ onChange = { this . onDesktopNotificationsChanged }
573+ label = { _t ( 'Enable desktop notifications for this session' ) }
574+ disabled = { this . state . phase === Phase . Persisting }
575+ />
576+ < LabelledToggleSwitch
577+ data-test-id = 'notif-setting-notificationBodyEnabled'
578+ value = { this . state . desktopShowBody }
579+ onChange = { this . onDesktopShowBodyChanged }
580+ label = { _t ( 'Show message in desktop notification' ) }
581+ disabled = { this . state . phase === Phase . Persisting }
582+ />
583+ < LabelledToggleSwitch
584+ data-test-id = 'notif-setting-audioNotificationsEnabled'
585+ value = { this . state . audioNotifications }
586+ onChange = { this . onAudioNotificationsChanged }
587+ label = { _t ( 'Enable audible notifications for this session' ) }
588+ disabled = { this . state . phase === Phase . Persisting }
589+ />
590+ </ > ) }
546591
547592 { emailSwitches }
548593 </ > ;
0 commit comments