Initial support for notification settings

Signed-off-by: Travis Ralston <travpc@gmail.com>
This commit is contained in:
Travis Ralston 2017-11-04 21:47:18 -07:00
parent 8351ec7e73
commit 7ce4316cc8
16 changed files with 194 additions and 105 deletions

View file

@ -25,6 +25,7 @@ import dis from './dispatcher';
import sdk from './index';
import { _t } from './languageHandler';
import Modal from './Modal';
import SettingsStore, {SettingLevel} from "./settings/SettingsStore";
/*
* Dispatches:
@ -138,10 +139,8 @@ const Notifier = {
// make sure that we persist the current setting audio_enabled setting
// before changing anything
if (global.localStorage) {
if (global.localStorage.getItem('audio_notifications_enabled') === null) {
this.setAudioEnabled(this.isEnabled());
}
if (SettingsStore.isLevelSupported(SettingLevel.DEVICE)) {
SettingsStore.setValue("audioNotificationsEnabled", null, SettingLevel.DEVICE, this.isEnabled());
}
if (enable) {
@ -149,6 +148,7 @@ const Notifier = {
plaf.requestNotificationPermission().done((result) => {
if (result !== 'granted') {
// The permission request was dismissed or denied
// TODO: Support alternative branding in messaging
const description = result === 'denied'
? _t('Riot does not have permission to send you notifications - please check your browser settings')
: _t('Riot was not given permission to send notifications - please try again');
@ -160,10 +160,6 @@ const Notifier = {
return;
}
if (global.localStorage) {
global.localStorage.setItem('notifications_enabled', 'true');
}
if (callback) callback();
dis.dispatch({
action: "notifier_enabled",
@ -174,8 +170,6 @@ const Notifier = {
// disabled again in the future, we will show the banner again.
this.setToolbarHidden(false);
} else {
if (!global.localStorage) return;
global.localStorage.setItem('notifications_enabled', 'false');
dis.dispatch({
action: "notifier_enabled",
value: false,
@ -184,44 +178,24 @@ const Notifier = {
},
isEnabled: function() {
return this.isPossible() && SettingsStore.getValue("notificationsEnabled");
},
isPossible: function() {
const plaf = PlatformPeg.get();
if (!plaf) return false;
if (!plaf.supportsNotifications()) return false;
if (!plaf.maySendNotifications()) return false;
if (!global.localStorage) return true;
const enabled = global.localStorage.getItem('notifications_enabled');
if (enabled === null) return true;
return enabled === 'true';
},
setBodyEnabled: function(enable) {
if (!global.localStorage) return;
global.localStorage.setItem('notifications_body_enabled', enable ? 'true' : 'false');
return true; // possible, but not necessarily enabled
},
isBodyEnabled: function() {
if (!global.localStorage) return true;
const enabled = global.localStorage.getItem('notifications_body_enabled');
// default to true if the popups are enabled
if (enabled === null) return this.isEnabled();
return enabled === 'true';
return this.isEnabled() && SettingsStore.getValue("notificationBodyEnabled");
},
setAudioEnabled: function(enable) {
if (!global.localStorage) return;
global.localStorage.setItem('audio_notifications_enabled',
enable ? 'true' : 'false');
},
isAudioEnabled: function(enable) {
if (!global.localStorage) return true;
const enabled = global.localStorage.getItem(
'audio_notifications_enabled');
// default to true if the popups are enabled
if (enabled === null) return this.isEnabled();
return enabled === 'true';
isAudioEnabled: function() {
return this.isEnabled() && SettingsStore.getValue("audioNotificationsEnabled");
},
setToolbarHidden: function(hidden, persistent = true) {
@ -237,17 +211,15 @@ const Notifier = {
});
// update the info to localStorage for persistent settings
if (persistent && global.localStorage) {
global.localStorage.setItem('notifications_hidden', hidden);
if (persistent && SettingsStore.isLevelSupported(SettingLevel.DEVICE)) {
return SettingsStore.setValue("notificationToolbarHidden", null, SettingLevel.DEVICE, hidden);
}
},
isToolbarHidden: function() {
// Check localStorage for any such meta data
if (global.localStorage) {
if (global.localStorage.getItem('notifications_hidden') === 'true') {
return true;
}
if (SettingsStore.isLevelSupported(SettingLevel.DEVICE)) {
return SettingsStore.getValue("notificationToolbarHidden");
}
return this.toolbarHidden;

View file

@ -17,9 +17,6 @@ limitations under the License.
import Promise from 'bluebird';
import MatrixClientPeg from './MatrixClientPeg';
import Notifier from './Notifier';
import { _t, _td } from './languageHandler';
import SdkConfig from './SdkConfig';
/*
* TODO: Make things use this. This is all WIP - see UserSettings.js for usage.
@ -48,42 +45,6 @@ export default {
// TODO
},
// TODO: {Travis} Granular setting
getEnableNotifications: function() {
return Notifier.isEnabled();
},
// TODO: {Travis} Granular setting
setEnableNotifications: function(enable) {
if (!Notifier.supportsDesktopNotifications()) {
return;
}
Notifier.setEnabled(enable);
},
// TODO: {Travis} Granular setting
getEnableNotificationBody: function() {
return Notifier.isBodyEnabled();
},
// TODO: {Travis} Granular setting
setEnableNotificationBody: function(enable) {
if (!Notifier.supportsDesktopNotifications()) {
return;
}
Notifier.setBodyEnabled(enable);
},
// TODO: {Travis} Granular setting
getEnableAudioNotifications: function() {
return Notifier.isAudioEnabled();
},
// TODO: {Travis} Granular setting
setEnableAudioNotifications: function(enable) {
Notifier.setAudioEnabled(enable);
},
changePassword: function(oldPassword, newPassword) {
const cli = MatrixClientPeg.get();

View file

@ -415,10 +415,6 @@ module.exports = React.createClass({
dis.dispatch({action: 'password_changed'});
},
onEnableNotificationsChange: function(event) {
UserSettingsStore.setEnableNotifications(event.target.checked);
},
_onAddEmailEditFinished: function(value, shouldSubmit) {
if (!shouldSubmit) return;
this._addEmail();

View file

@ -62,6 +62,9 @@ export const SETTINGS = {
// default: {
// your: "value",
// },
//
// // Optional settings controller. See SettingsController for more information.
// controller: new MySettingController(),
// },
"feature_groups": {
isFeature: true,
@ -213,4 +216,23 @@ export const SETTINGS = {
secondary_color: null, // Hex string, eg: #000000
},
},
"notificationsEnabled": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
//controller: new NotificationsEnabledController(),
},
"notificationBodyEnabled": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
//controller: new NotificationBodyEnabledController(),
},
"audioNotificationsEnabled": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
//controller: new AudioNotificationsEnabledController(),
},
"notificationToolbarHidden": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
},
};

View file

@ -15,17 +15,17 @@ limitations under the License.
*/
import Promise from 'bluebird';
import DeviceSettingsHandler from "./DeviceSettingsHandler";
import RoomDeviceSettingsHandler from "./RoomDeviceSettingsHandler";
import DefaultSettingsHandler from "./DefaultSettingsHandler";
import RoomAccountSettingsHandler from "./RoomAccountSettingsHandler";
import AccountSettingsHandler from "./AccountSettingsHandler";
import RoomSettingsHandler from "./RoomSettingsHandler";
import ConfigSettingsHandler from "./ConfigSettingsHandler";
import DeviceSettingsHandler from "./handlers/DeviceSettingsHandler";
import RoomDeviceSettingsHandler from "./handlers/RoomDeviceSettingsHandler";
import DefaultSettingsHandler from "./handlers/DefaultSettingsHandler";
import RoomAccountSettingsHandler from "./handlers/RoomAccountSettingsHandler";
import AccountSettingsHandler from "./handlers/AccountSettingsHandler";
import RoomSettingsHandler from "./handlers/RoomSettingsHandler";
import ConfigSettingsHandler from "./handlers/ConfigSettingsHandler";
import {_t} from '../languageHandler';
import SdkConfig from "../SdkConfig";
import {SETTINGS} from "./Settings";
import LocalEchoWrapper from "./LocalEchoWrapper";
import LocalEchoWrapper from "./handlers/LocalEchoWrapper";
/**
* Represents the various setting levels supported by the SettingsStore.
@ -208,8 +208,9 @@ export default class SettingsStore {
if (explicit) {
let handler = handlers[level];
if (!handler) return null;
return handler.getValue(settingName, roomId);
if (!handler) return SettingsStore._tryControllerOverride(settingName, level, roomId, null);
const value = handler.getValue(settingName, roomId);
return SettingsStore._tryControllerOverride(settingName, level, roomId, value);
}
for (let i = minIndex; i < LEVEL_ORDER.length; i++) {
@ -219,10 +220,19 @@ export default class SettingsStore {
const value = handler.getValue(settingName, roomId);
if (value === null || value === undefined) continue;
return value;
return SettingsStore._tryControllerOverride(settingName, level, roomId, value);
}
return null;
return SettingsStore._tryControllerOverride(settingName, level, roomId, null);
}
static _tryControllerOverride(settingName, level, roomId, calculatedValue) {
const controller = SETTINGS[settingName].controller;
if (!controller) return calculatedValue;
const actualValue = controller.getValueOverride(level, roomId, calculatedValue);
if (actualValue !== undefined && actualValue !== null) return actualValue;
return calculatedValue;
}
/**
@ -251,7 +261,11 @@ export default class SettingsStore {
throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId);
}
return handler.setValue(settingName, roomId, value);
return handler.setValue(settingName, roomId, value).finally((() => {
const controller = SETTINGS[settingName].controller;
if (!controller) return;
controller.onChange(level, roomId, value);
}));
}
/**

View file

@ -0,0 +1,49 @@
/*
Copyright 2017 Travis Ralston
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 SettingController from "./SettingController";
// export class NotificationsEnabledController extends SettingController {
// getValueOverride(level, roomId, calculatedValue) {
// const Notifier = require('../../Notifier'); // avoids cyclical references
//
// return calculatedValue && Notifier.isPossible();
// }
//
// onChange(level, roomId, newValue) {
// const Notifier = require('../../Notifier'); // avoids cyclical references
//
// if (Notifier.supportsDesktopNotifications()) {
// Notifier.setBodyEnabled(newValue);
// }
// }
// }
//
// export class NotificationBodyEnabledController extends SettingController {
// getValueOverride(level, roomId, calculatedValue) {
// const Notifier = require('../../Notifier'); // avoids cyclical references
//
// return calculatedValue && Notifier.isEnabled();
// }
// }
//
// export class AudioNotificationsEnabledController extends SettingController {
// getValueOverride(level, roomId, calculatedValue) {
// const Notifier = require('../../Notifier'); // avoids cyclical references
//
// return calculatedValue && Notifier.isEnabled();
// }
// }

View file

@ -0,0 +1,49 @@
/*
Copyright 2017 Travis Ralston
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.
*/
/**
* Represents a controller for individual settings to alter the reading behaviour
* based upon environmental conditions, or to react to changes and therefore update
* the working environment.
*
* This is not intended to replace the functionality of a SettingsHandler, it is only
* intended to handle environmental factors for specific settings.
*/
export default class SettingController {
/**
* Gets the overridden value for the setting, if any. This must return null if the
* value is not to be overridden, otherwise it must return the new value.
* @param {string} level The level at which the value was requested at.
* @param {String} roomId The room ID, may be null.
* @param {*} calculatedValue The value that the handlers think the setting should be,
* may be null.
* @return {*} The value that should be used, or null if no override is applicable.
*/
getValueOverride(level, roomId, calculatedValue) {
return null; // no override
}
/**
* Called when the setting value has been changed.
* @param {string} level The level at which the setting has been modified.
* @param {String} roomId The room ID, may be null.
* @param {*} newValue The new value for the setting, may be null.
*/
onChange(level, roomId, newValue) {
// do nothing by default
}
}

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import SettingsHandler from "./SettingsHandler";
import MatrixClientPeg from '../MatrixClientPeg';
import MatrixClientPeg from '../../MatrixClientPeg';
/**
* Gets and sets settings at the "account" level for the current user.

View file

@ -16,7 +16,7 @@ limitations under the License.
import Promise from 'bluebird';
import SettingsHandler from "./SettingsHandler";
import SdkConfig from "../SdkConfig";
import SdkConfig from "../../SdkConfig";
/**
* Gets and sets settings at the "config" level. This handler does not make use of the

View file

@ -16,7 +16,7 @@ limitations under the License.
import Promise from 'bluebird';
import SettingsHandler from "./SettingsHandler";
import MatrixClientPeg from "../MatrixClientPeg";
import MatrixClientPeg from "../../MatrixClientPeg";
/**
* Gets and sets settings at the "device" level for the current device.
@ -38,6 +38,17 @@ export default class DeviceSettingsHandler extends SettingsHandler {
return this._readFeature(settingName);
}
// Special case notifications
if (settingName === "notificationsEnabled") {
return localStorage.getItem("notifications_enabled") === "true";
} else if (settingName === "notificationBodyEnabled") {
return localStorage.getItem("notifications_body_enabled") === "true";
} else if (settingName === "audioNotificationsEnabled") {
return localStorage.getItem("audio_notifications_enabled") === "true";
} else if (settingName === "notificationToolbarHidden") {
return localStorage.getItem("notifications_hidden") === "true";
}
return this._getSettings()[settingName];
}
@ -47,6 +58,21 @@ export default class DeviceSettingsHandler extends SettingsHandler {
return Promise.resolve();
}
// Special case notifications
if (settingName === "notificationsEnabled") {
localStorage.setItem("notifications_enabled", newValue);
return Promise.resolve();
} else if (settingName === "notificationBodyEnabled") {
localStorage.setItem("notifications_body_enabled", newValue);
return Promise.resolve();
} else if (settingName === "audioNotificationsEnabled") {
localStorage.setItem("audio_notifications_enabled", newValue);
return Promise.resolve();
} else if (settingName === "notificationToolbarHidden") {
localStorage.setItem("notifications_hidden", newValue);
return Promise.resolve();
}
const settings = this._getSettings();
settings[settingName] = newValue;
localStorage.setItem("mx_local_settings", JSON.stringify(settings));

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import SettingsHandler from "./SettingsHandler";
import MatrixClientPeg from '../MatrixClientPeg';
import MatrixClientPeg from '../../MatrixClientPeg';
/**
* Gets and sets settings at the "room-account" level for the current user.

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import SettingsHandler from "./SettingsHandler";
import MatrixClientPeg from '../MatrixClientPeg';
import MatrixClientPeg from '../../MatrixClientPeg';
/**
* Gets and sets settings at the "room" level.