2018-04-12 01:58:04 +03:00
|
|
|
|
/*
|
|
|
|
|
Copyright 2016 OpenMarket Ltd
|
|
|
|
|
|
|
|
|
|
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 React from 'react';
|
2019-09-06 20:37:43 +03:00
|
|
|
|
import createReactClass from 'create-react-class';
|
2018-04-13 02:43:44 +03:00
|
|
|
|
import sdk from '../../../index';
|
|
|
|
|
import { _t } from '../../../languageHandler';
|
|
|
|
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
|
|
|
|
import SettingsStore, {SettingLevel} from '../../../settings/SettingsStore';
|
|
|
|
|
import Modal from '../../../Modal';
|
2018-04-12 01:58:04 +03:00
|
|
|
|
import {
|
|
|
|
|
NotificationUtils,
|
|
|
|
|
VectorPushRulesDefinitions,
|
|
|
|
|
PushRuleVectorState,
|
2018-10-12 06:43:59 +03:00
|
|
|
|
ContentRules,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
} from '../../../notifications';
|
2019-02-22 01:18:54 +03:00
|
|
|
|
import SdkConfig from "../../../SdkConfig";
|
2019-01-24 06:13:43 +03:00
|
|
|
|
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
2019-05-13 01:24:12 +03:00
|
|
|
|
import AccessibleButton from "../elements/AccessibleButton";
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
|
|
// TODO: this "view" component still has far too much application logic in it,
|
|
|
|
|
// which should be factored out to other files.
|
|
|
|
|
|
|
|
|
|
// TODO: this component also does a lot of direct poking into this.state, which
|
|
|
|
|
// is VERY NAUGHTY.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Rules that Vector used to set in order to override the actions of default rules.
|
|
|
|
|
* These are used to port peoples existing overrides to match the current API.
|
|
|
|
|
* These can be removed and forgotten once everyone has moved to the new client.
|
|
|
|
|
*/
|
|
|
|
|
const LEGACY_RULES = {
|
|
|
|
|
"im.vector.rule.contains_display_name": ".m.rule.contains_display_name",
|
|
|
|
|
"im.vector.rule.room_one_to_one": ".m.rule.room_one_to_one",
|
|
|
|
|
"im.vector.rule.room_message": ".m.rule.message",
|
|
|
|
|
"im.vector.rule.invite_for_me": ".m.rule.invite_for_me",
|
|
|
|
|
"im.vector.rule.call": ".m.rule.call",
|
2018-10-12 06:43:59 +03:00
|
|
|
|
"im.vector.rule.notices": ".m.rule.suppress_notices",
|
2018-04-12 01:58:04 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function portLegacyActions(actions) {
|
|
|
|
|
const decoded = NotificationUtils.decodeActions(actions);
|
|
|
|
|
if (decoded !== null) {
|
|
|
|
|
return NotificationUtils.encodeActions(decoded);
|
|
|
|
|
} else {
|
|
|
|
|
// We don't recognise one of the actions here, so we don't try to
|
|
|
|
|
// canonicalise them.
|
|
|
|
|
return actions;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-06 20:37:43 +03:00
|
|
|
|
module.exports = createReactClass({
|
2018-04-12 01:58:04 +03:00
|
|
|
|
displayName: 'Notifications',
|
|
|
|
|
|
|
|
|
|
phases: {
|
|
|
|
|
LOADING: "LOADING", // The component is loading or sending data to the hs
|
|
|
|
|
DISPLAY: "DISPLAY", // The component is ready and display data
|
2018-10-27 06:50:35 +03:00
|
|
|
|
ERROR: "ERROR", // There was an error
|
2018-04-12 01:58:04 +03:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
|
return {
|
|
|
|
|
phase: this.phases.LOADING,
|
2018-10-27 06:50:35 +03:00
|
|
|
|
masterPushRule: undefined, // The master rule ('.m.rule.master')
|
|
|
|
|
vectorPushRules: [], // HS default push rules displayed in Vector UI
|
|
|
|
|
vectorContentRules: { // Keyword push rules displayed in Vector UI
|
2018-04-12 01:58:04 +03:00
|
|
|
|
vectorState: PushRuleVectorState.ON,
|
2018-10-12 06:43:59 +03:00
|
|
|
|
rules: [],
|
2018-04-12 01:58:04 +03:00
|
|
|
|
},
|
2018-10-27 06:50:35 +03:00
|
|
|
|
externalPushRules: [], // Push rules (except content rule) that have been defined outside Vector UI
|
|
|
|
|
externalContentRules: [], // Keyword push rules that have been defined outside Vector UI
|
2019-01-24 06:13:43 +03:00
|
|
|
|
threepids: [], // used for email notifications
|
2018-04-12 01:58:04 +03:00
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
|
|
this._refreshFromServer();
|
|
|
|
|
},
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
onEnableNotificationsChange: function(checked) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const self = this;
|
|
|
|
|
this.setState({
|
2018-10-12 06:43:59 +03:00
|
|
|
|
phase: this.phases.LOADING,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
|
2019-11-18 13:03:05 +03:00
|
|
|
|
MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !checked).then(function() {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
self._refreshFromServer();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
onEnableDesktopNotificationsChange: function(checked) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
SettingsStore.setValue(
|
|
|
|
|
"notificationsEnabled", null,
|
|
|
|
|
SettingLevel.DEVICE,
|
2019-01-24 06:13:43 +03:00
|
|
|
|
checked,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
).finally(() => {
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
onEnableDesktopNotificationBodyChange: function(checked) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
SettingsStore.setValue(
|
|
|
|
|
"notificationBodyEnabled", null,
|
|
|
|
|
SettingLevel.DEVICE,
|
2019-01-24 06:13:43 +03:00
|
|
|
|
checked,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
).finally(() => {
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
onEnableAudioNotificationsChange: function(checked) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
SettingsStore.setValue(
|
|
|
|
|
"audioNotificationsEnabled", null,
|
|
|
|
|
SettingLevel.DEVICE,
|
2019-01-24 06:13:43 +03:00
|
|
|
|
checked,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
).finally(() => {
|
|
|
|
|
this.forceUpdate();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2019-03-01 05:57:45 +03:00
|
|
|
|
/*
|
|
|
|
|
* Returns the email pusher (pusher of type 'email') for a given
|
|
|
|
|
* email address. Email pushers all have the same app ID, so since
|
|
|
|
|
* pushers are unique over (app ID, pushkey), there will be at most
|
|
|
|
|
* one such pusher.
|
|
|
|
|
*/
|
|
|
|
|
getEmailPusher: function(pushers, address) {
|
|
|
|
|
if (pushers === undefined) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < pushers.length; ++i) {
|
|
|
|
|
if (pushers[i].kind === 'email' && pushers[i].pushkey === address) {
|
|
|
|
|
return pushers[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
},
|
|
|
|
|
|
2019-02-22 01:18:11 +03:00
|
|
|
|
onEnableEmailNotificationsChange: function(address, checked) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
let emailPusherPromise;
|
2019-02-22 01:18:11 +03:00
|
|
|
|
if (checked) {
|
2018-10-12 06:43:59 +03:00
|
|
|
|
const data = {};
|
2019-01-24 05:31:49 +03:00
|
|
|
|
data['brand'] = SdkConfig.get().brand || 'Riot';
|
2019-03-01 05:57:45 +03:00
|
|
|
|
emailPusherPromise = MatrixClientPeg.get().setPusher({
|
|
|
|
|
kind: 'email',
|
|
|
|
|
app_id: 'm.email',
|
|
|
|
|
pushkey: address,
|
|
|
|
|
app_display_name: 'Email Notifications',
|
|
|
|
|
device_display_name: address,
|
|
|
|
|
lang: navigator.language,
|
|
|
|
|
data: data,
|
|
|
|
|
append: true, // We always append for email pushers since we don't want to stop other accounts notifying to the same email address
|
|
|
|
|
});
|
2018-04-12 01:58:04 +03:00
|
|
|
|
} else {
|
2019-03-01 05:57:45 +03:00
|
|
|
|
const emailPusher = this.getEmailPusher(this.state.pushers, address);
|
2018-04-12 01:58:04 +03:00
|
|
|
|
emailPusher.kind = null;
|
|
|
|
|
emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher);
|
|
|
|
|
}
|
2019-11-18 13:03:05 +03:00
|
|
|
|
emailPusherPromise.then(() => {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
this._refreshFromServer();
|
|
|
|
|
}, (error) => {
|
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
|
Modal.createTrackedDialog('Error saving email notification preferences', '', ErrorDialog, {
|
|
|
|
|
title: _t('Error saving email notification preferences'),
|
|
|
|
|
description: _t('An error occurred whilst saving your email notification preferences.'),
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onNotifStateButtonClicked: function(event) {
|
|
|
|
|
// FIXME: use .bind() rather than className metadata here surely
|
|
|
|
|
const vectorRuleId = event.target.className.split("-")[0];
|
|
|
|
|
const newPushRuleVectorState = event.target.className.split("-")[1];
|
|
|
|
|
|
|
|
|
|
if ("_keywords" === vectorRuleId) {
|
2018-10-12 06:43:59 +03:00
|
|
|
|
this._setKeywordsPushRuleVectorState(newPushRuleVectorState);
|
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.getRule(vectorRuleId);
|
|
|
|
|
if (rule) {
|
|
|
|
|
this._setPushRuleVectorState(rule, newPushRuleVectorState);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onKeywordsClicked: function(event) {
|
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
|
|
// Compute the keywords list to display
|
|
|
|
|
let keywords = [];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in this.state.vectorContentRules.rules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.state.vectorContentRules.rules[i];
|
|
|
|
|
keywords.push(rule.pattern);
|
|
|
|
|
}
|
|
|
|
|
if (keywords.length) {
|
|
|
|
|
// As keeping the order of per-word push rules hs side is a bit tricky to code,
|
|
|
|
|
// display the keywords in alphabetical order to the user
|
|
|
|
|
keywords.sort();
|
|
|
|
|
|
|
|
|
|
keywords = keywords.join(", ");
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
keywords = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
|
|
|
|
|
Modal.createTrackedDialog('Keywords Dialog', '', TextInputDialog, {
|
|
|
|
|
title: _t('Keywords'),
|
|
|
|
|
description: _t('Enter keywords separated by a comma:'),
|
|
|
|
|
button: _t('OK'),
|
|
|
|
|
value: keywords,
|
|
|
|
|
onFinished: function onFinished(should_leave, newValue) {
|
|
|
|
|
if (should_leave && newValue !== keywords) {
|
|
|
|
|
let newKeywords = newValue.split(',');
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in newKeywords) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
newKeywords[i] = newKeywords[i].trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove duplicates and empty
|
2018-10-12 06:43:59 +03:00
|
|
|
|
newKeywords = newKeywords.reduce(function(array, keyword) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
if (keyword !== "" && array.indexOf(keyword) < 0) {
|
|
|
|
|
array.push(keyword);
|
|
|
|
|
}
|
|
|
|
|
return array;
|
2018-10-12 06:43:59 +03:00
|
|
|
|
}, []);
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
|
|
self._setKeywords(newKeywords);
|
|
|
|
|
}
|
2018-10-12 06:43:59 +03:00
|
|
|
|
},
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
getRule: function(vectorRuleId) {
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in this.state.vectorPushRules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.state.vectorPushRules[i];
|
|
|
|
|
if (rule.vectorRuleId === vectorRuleId) {
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_setPushRuleVectorState: function(rule, newPushRuleVectorState) {
|
|
|
|
|
if (rule && rule.vectorState !== newPushRuleVectorState) {
|
|
|
|
|
this.setState({
|
2018-10-12 06:43:59 +03:00
|
|
|
|
phase: this.phases.LOADING,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const self = this;
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
const deferreds = [];
|
|
|
|
|
const ruleDefinition = VectorPushRulesDefinitions[rule.vectorRuleId];
|
|
|
|
|
|
|
|
|
|
if (rule.rule) {
|
|
|
|
|
const actions = ruleDefinition.vectorStateToActions[newPushRuleVectorState];
|
|
|
|
|
|
|
|
|
|
if (!actions) {
|
|
|
|
|
// The new state corresponds to disabling the rule.
|
|
|
|
|
deferreds.push(cli.setPushRuleEnabled('global', rule.rule.kind, rule.rule.rule_id, false));
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
// The new state corresponds to enabling the rule and setting specific actions
|
|
|
|
|
deferreds.push(this._updatePushRuleActions(rule.rule, actions, true));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 13:03:05 +03:00
|
|
|
|
Promise.all(deferreds).then(function() {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
self._refreshFromServer();
|
|
|
|
|
}, function(error) {
|
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
|
console.error("Failed to change settings: " + error);
|
|
|
|
|
Modal.createTrackedDialog('Failed to change settings', '', ErrorDialog, {
|
|
|
|
|
title: _t('Failed to change settings'),
|
|
|
|
|
description: ((error && error.message) ? error.message : _t('Operation failed')),
|
2018-10-12 06:43:59 +03:00
|
|
|
|
onFinished: self._refreshFromServer,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_setKeywordsPushRuleVectorState: function(newPushRuleVectorState) {
|
|
|
|
|
// Is there really a change?
|
|
|
|
|
if (this.state.vectorContentRules.vectorState === newPushRuleVectorState
|
|
|
|
|
|| this.state.vectorContentRules.rules.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const self = this;
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
|
|
|
|
|
this.setState({
|
2018-10-12 06:43:59 +03:00
|
|
|
|
phase: this.phases.LOADING,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update all rules in self.state.vectorContentRules
|
|
|
|
|
const deferreds = [];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in this.state.vectorContentRules.rules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.state.vectorContentRules.rules[i];
|
|
|
|
|
|
2018-10-27 06:50:35 +03:00
|
|
|
|
let enabled; let actions;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
switch (newPushRuleVectorState) {
|
|
|
|
|
case PushRuleVectorState.ON:
|
|
|
|
|
if (rule.actions.length !== 1) {
|
|
|
|
|
actions = PushRuleVectorState.actionsFor(PushRuleVectorState.ON);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) {
|
|
|
|
|
enabled = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PushRuleVectorState.LOUD:
|
|
|
|
|
if (rule.actions.length !== 3) {
|
|
|
|
|
actions = PushRuleVectorState.actionsFor(PushRuleVectorState.LOUD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) {
|
|
|
|
|
enabled = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PushRuleVectorState.OFF:
|
|
|
|
|
enabled = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (actions) {
|
|
|
|
|
// Note that the workaround in _updatePushRuleActions will automatically
|
|
|
|
|
// enable the rule
|
|
|
|
|
deferreds.push(this._updatePushRuleActions(rule, actions, enabled));
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else if (enabled != undefined) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
deferreds.push(cli.setPushRuleEnabled('global', rule.kind, rule.rule_id, enabled));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 13:03:05 +03:00
|
|
|
|
Promise.all(deferreds).then(function(resps) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
self._refreshFromServer();
|
|
|
|
|
}, function(error) {
|
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
|
console.error("Can't update user notification settings: " + error);
|
|
|
|
|
Modal.createTrackedDialog('Can\'t update user notifcation settings', '', ErrorDialog, {
|
|
|
|
|
title: _t('Can\'t update user notification settings'),
|
|
|
|
|
description: ((error && error.message) ? error.message : _t('Operation failed')),
|
2018-10-12 06:43:59 +03:00
|
|
|
|
onFinished: self._refreshFromServer,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_setKeywords: function(newKeywords) {
|
|
|
|
|
this.setState({
|
2018-10-12 06:43:59 +03:00
|
|
|
|
phase: this.phases.LOADING,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const self = this;
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
const removeDeferreds = [];
|
|
|
|
|
|
|
|
|
|
// Remove per-word push rules of keywords that are no more in the list
|
|
|
|
|
const vectorContentRulesPatterns = [];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in self.state.vectorContentRules.rules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = self.state.vectorContentRules.rules[i];
|
|
|
|
|
|
|
|
|
|
vectorContentRulesPatterns.push(rule.pattern);
|
|
|
|
|
|
|
|
|
|
if (newKeywords.indexOf(rule.pattern) < 0) {
|
|
|
|
|
removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the keyword is part of `externalContentRules`, remove the rule
|
|
|
|
|
// before recreating it in the right Vector path
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in self.state.externalContentRules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = self.state.externalContentRules[i];
|
|
|
|
|
|
|
|
|
|
if (newKeywords.indexOf(rule.pattern) >= 0) {
|
|
|
|
|
removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const onError = function(error) {
|
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
|
console.error("Failed to update keywords: " + error);
|
|
|
|
|
Modal.createTrackedDialog('Failed to update keywords', '', ErrorDialog, {
|
|
|
|
|
title: _t('Failed to update keywords'),
|
|
|
|
|
description: ((error && error.message) ? error.message : _t('Operation failed')),
|
2018-10-12 06:43:59 +03:00
|
|
|
|
onFinished: self._refreshFromServer,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
2018-10-12 06:43:59 +03:00
|
|
|
|
};
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
|
|
// Then, add the new ones
|
2019-11-18 13:03:05 +03:00
|
|
|
|
Promise.all(removeDeferreds).then(function(resps) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const deferreds = [];
|
|
|
|
|
|
|
|
|
|
let pushRuleVectorStateKind = self.state.vectorContentRules.vectorState;
|
|
|
|
|
if (pushRuleVectorStateKind === PushRuleVectorState.OFF) {
|
|
|
|
|
// When the current global keywords rule is OFF, we need to look at
|
|
|
|
|
// the flavor of rules in 'vectorContentRules' to apply the same actions
|
|
|
|
|
// when creating the new rule.
|
|
|
|
|
// Thus, this new rule will join the 'vectorContentRules' set.
|
|
|
|
|
if (self.state.vectorContentRules.rules.length) {
|
|
|
|
|
pushRuleVectorStateKind = PushRuleVectorState.contentRuleVectorStateKind(self.state.vectorContentRules.rules[0]);
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
// ON is default
|
2018-10-12 06:43:59 +03:00
|
|
|
|
pushRuleVectorStateKind = PushRuleVectorState.ON;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in newKeywords) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const keyword = newKeywords[i];
|
|
|
|
|
|
|
|
|
|
if (vectorContentRulesPatterns.indexOf(keyword) < 0) {
|
|
|
|
|
if (self.state.vectorContentRules.vectorState !== PushRuleVectorState.OFF) {
|
|
|
|
|
deferreds.push(cli.addPushRule
|
|
|
|
|
('global', 'content', keyword, {
|
|
|
|
|
actions: PushRuleVectorState.actionsFor(pushRuleVectorStateKind),
|
2018-10-12 06:43:59 +03:00
|
|
|
|
pattern: keyword,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
}));
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
deferreds.push(self._addDisabledPushRule('global', 'content', keyword, {
|
|
|
|
|
actions: PushRuleVectorState.actionsFor(pushRuleVectorStateKind),
|
2018-10-12 06:43:59 +03:00
|
|
|
|
pattern: keyword,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 13:03:05 +03:00
|
|
|
|
Promise.all(deferreds).then(function(resps) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
self._refreshFromServer();
|
|
|
|
|
}, onError);
|
|
|
|
|
}, onError);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Create a push rule but disabled
|
|
|
|
|
_addDisabledPushRule: function(scope, kind, ruleId, body) {
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
return cli.addPushRule(scope, kind, ruleId, body).then(() =>
|
2018-10-12 06:43:59 +03:00
|
|
|
|
cli.setPushRuleEnabled(scope, kind, ruleId, false),
|
2018-04-12 01:58:04 +03:00
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Check if any legacy im.vector rules need to be ported to the new API
|
|
|
|
|
// for overriding the actions of default rules.
|
|
|
|
|
_portRulesToNewAPI: function(rulesets) {
|
|
|
|
|
const needsUpdate = [];
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const kind in rulesets.global) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const ruleset = rulesets.global[kind];
|
|
|
|
|
for (let i = 0; i < ruleset.length; ++i) {
|
|
|
|
|
const rule = ruleset[i];
|
|
|
|
|
if (rule.rule_id in LEGACY_RULES) {
|
|
|
|
|
console.log("Porting legacy rule", rule);
|
|
|
|
|
needsUpdate.push( function(kind, rule) {
|
|
|
|
|
return cli.setPushRuleActions(
|
2018-10-12 06:43:59 +03:00
|
|
|
|
'global', kind, LEGACY_RULES[rule.rule_id], portLegacyActions(rule.actions),
|
2018-04-12 01:58:04 +03:00
|
|
|
|
).then(() =>
|
2018-10-12 06:43:59 +03:00
|
|
|
|
cli.deletePushRule('global', kind, rule.rule_id),
|
2018-04-12 01:58:04 +03:00
|
|
|
|
).catch( (e) => {
|
|
|
|
|
console.warn(`Error when porting legacy rule: ${e}`);
|
|
|
|
|
});
|
|
|
|
|
}(kind, rule));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needsUpdate.length > 0) {
|
|
|
|
|
// If some of the rules need to be ported then wait for the porting
|
|
|
|
|
// to happen and then fetch the rules again.
|
|
|
|
|
return Promise.all(needsUpdate).then(() =>
|
2018-10-12 06:43:59 +03:00
|
|
|
|
cli.getPushRules(),
|
2018-04-12 01:58:04 +03:00
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
// Otherwise return the rules that we already have.
|
|
|
|
|
return rulesets;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_refreshFromServer: function() {
|
|
|
|
|
const self = this;
|
|
|
|
|
const pushRulesPromise = MatrixClientPeg.get().getPushRules().then(self._portRulesToNewAPI).then(function(rulesets) {
|
|
|
|
|
/// XXX seriously? wtf is this?
|
|
|
|
|
MatrixClientPeg.get().pushRules = rulesets;
|
|
|
|
|
|
|
|
|
|
// Get homeserver default rules and triage them by categories
|
|
|
|
|
const rule_categories = {
|
|
|
|
|
// The master rule (all notifications disabling)
|
|
|
|
|
'.m.rule.master': 'master',
|
|
|
|
|
|
|
|
|
|
// The default push rules displayed by Vector UI
|
|
|
|
|
'.m.rule.contains_display_name': 'vector',
|
|
|
|
|
'.m.rule.contains_user_name': 'vector',
|
2018-12-11 22:01:27 +03:00
|
|
|
|
'.m.rule.roomnotif': 'vector',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
'.m.rule.room_one_to_one': 'vector',
|
2018-12-11 22:01:27 +03:00
|
|
|
|
'.m.rule.encrypted_room_one_to_one': 'vector',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
'.m.rule.message': 'vector',
|
2018-12-11 22:01:27 +03:00
|
|
|
|
'.m.rule.encrypted': 'vector',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
'.m.rule.invite_for_me': 'vector',
|
|
|
|
|
//'.m.rule.member_event': 'vector',
|
|
|
|
|
'.m.rule.call': 'vector',
|
2018-10-12 06:43:59 +03:00
|
|
|
|
'.m.rule.suppress_notices': 'vector',
|
2019-03-15 23:13:15 +03:00
|
|
|
|
'.m.rule.tombstone': 'vector',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
|
|
// Others go to others
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// HS default rules
|
|
|
|
|
const defaultRules = {master: [], vector: {}, others: []};
|
|
|
|
|
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const kind in rulesets.global) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
for (let i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) {
|
|
|
|
|
const r = rulesets.global[kind][i];
|
|
|
|
|
const cat = rule_categories[r.rule_id];
|
|
|
|
|
r.kind = kind;
|
|
|
|
|
|
|
|
|
|
if (r.rule_id[0] === '.') {
|
|
|
|
|
if (cat === 'vector') {
|
|
|
|
|
defaultRules.vector[r.rule_id] = r;
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else if (cat === 'master') {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
defaultRules.master.push(r);
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
defaultRules['others'].push(r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the master rule if any defined by the hs
|
|
|
|
|
if (defaultRules.master.length > 0) {
|
|
|
|
|
self.state.masterPushRule = defaultRules.master[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse the keyword rules into our state
|
|
|
|
|
const contentRules = ContentRules.parseContentRules(rulesets);
|
|
|
|
|
self.state.vectorContentRules = {
|
|
|
|
|
vectorState: contentRules.vectorState,
|
|
|
|
|
rules: contentRules.rules,
|
|
|
|
|
};
|
|
|
|
|
self.state.externalContentRules = contentRules.externalRules;
|
|
|
|
|
|
|
|
|
|
// Build the rules displayed in the Vector UI matrix table
|
|
|
|
|
self.state.vectorPushRules = [];
|
|
|
|
|
self.state.externalPushRules = [];
|
|
|
|
|
|
|
|
|
|
const vectorRuleIds = [
|
|
|
|
|
'.m.rule.contains_display_name',
|
|
|
|
|
'.m.rule.contains_user_name',
|
2018-12-11 22:01:27 +03:00
|
|
|
|
'.m.rule.roomnotif',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
'_keywords',
|
|
|
|
|
'.m.rule.room_one_to_one',
|
2018-12-11 22:01:27 +03:00
|
|
|
|
'.m.rule.encrypted_room_one_to_one',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
'.m.rule.message',
|
2018-12-11 22:01:27 +03:00
|
|
|
|
'.m.rule.encrypted',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
'.m.rule.invite_for_me',
|
|
|
|
|
//'im.vector.rule.member_event',
|
|
|
|
|
'.m.rule.call',
|
2018-10-12 06:43:59 +03:00
|
|
|
|
'.m.rule.suppress_notices',
|
2019-03-15 23:13:15 +03:00
|
|
|
|
'.m.rule.tombstone',
|
2018-04-12 01:58:04 +03:00
|
|
|
|
];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in vectorRuleIds) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const vectorRuleId = vectorRuleIds[i];
|
|
|
|
|
|
|
|
|
|
if (vectorRuleId === '_keywords') {
|
|
|
|
|
// keywords needs a special handling
|
|
|
|
|
// For Vector UI, this is a single global push rule but translated in Matrix,
|
|
|
|
|
// it corresponds to all content push rules (stored in self.state.vectorContentRule)
|
|
|
|
|
self.state.vectorPushRules.push({
|
|
|
|
|
"vectorRuleId": "_keywords",
|
2018-10-12 06:43:59 +03:00
|
|
|
|
"description": (
|
2018-04-12 01:58:04 +03:00
|
|
|
|
<span>
|
|
|
|
|
{ _t('Messages containing <span>keywords</span>',
|
|
|
|
|
{},
|
|
|
|
|
{ 'span': (sub) =>
|
2018-10-12 06:43:59 +03:00
|
|
|
|
<span className="mx_UserNotifSettings_keywords" onClick={ self.onKeywordsClicked }>{sub}</span>,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
},
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
),
|
2018-10-12 06:43:59 +03:00
|
|
|
|
"vectorState": self.state.vectorContentRules.vectorState,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
2018-10-12 06:43:59 +03:00
|
|
|
|
} else {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const ruleDefinition = VectorPushRulesDefinitions[vectorRuleId];
|
|
|
|
|
const rule = defaultRules.vector[vectorRuleId];
|
|
|
|
|
|
|
|
|
|
const vectorState = ruleDefinition.ruleToVectorState(rule);
|
|
|
|
|
|
|
|
|
|
//console.log("Refreshing vectorPushRules for " + vectorRuleId +", "+ ruleDefinition.description +", " + rule +", " + vectorState);
|
|
|
|
|
|
|
|
|
|
self.state.vectorPushRules.push({
|
|
|
|
|
"vectorRuleId": vectorRuleId,
|
2018-10-12 06:43:59 +03:00
|
|
|
|
"description": _t(ruleDefinition.description), // Text from VectorPushRulesDefinitions.js
|
2018-04-12 01:58:04 +03:00
|
|
|
|
"rule": rule,
|
|
|
|
|
"vectorState": vectorState,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// if there was a rule which we couldn't parse, add it to the external list
|
|
|
|
|
if (rule && !vectorState) {
|
|
|
|
|
rule.description = ruleDefinition.description;
|
|
|
|
|
self.state.externalPushRules.push(rule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build the rules not managed by Vector UI
|
|
|
|
|
const otherRulesDescriptions = {
|
|
|
|
|
'.m.rule.message': _t('Notify for all other messages/rooms'),
|
|
|
|
|
'.m.rule.fallback': _t('Notify me for anything else'),
|
|
|
|
|
};
|
|
|
|
|
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in defaultRules.others) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = defaultRules.others[i];
|
|
|
|
|
const ruleDescription = otherRulesDescriptions[rule.rule_id];
|
|
|
|
|
|
|
|
|
|
// Show enabled default rules that was modified by the user
|
|
|
|
|
if (ruleDescription && rule.enabled && !rule.default) {
|
|
|
|
|
rule.description = ruleDescription;
|
|
|
|
|
self.state.externalPushRules.push(rule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const pushersPromise = MatrixClientPeg.get().getPushers().then(function(resp) {
|
|
|
|
|
self.setState({pushers: resp.pushers});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Promise.all([pushRulesPromise, pushersPromise]).then(function() {
|
|
|
|
|
self.setState({
|
2018-10-12 06:43:59 +03:00
|
|
|
|
phase: self.phases.DISPLAY,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
}, function(error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
self.setState({
|
2018-10-12 06:43:59 +03:00
|
|
|
|
phase: self.phases.ERROR,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
// actually explicitly update our state having been deep-manipulating it
|
|
|
|
|
self.setState({
|
|
|
|
|
masterPushRule: self.state.masterPushRule,
|
|
|
|
|
vectorContentRules: self.state.vectorContentRules,
|
|
|
|
|
vectorPushRules: self.state.vectorPushRules,
|
|
|
|
|
externalContentRules: self.state.externalContentRules,
|
|
|
|
|
externalPushRules: self.state.externalPushRules,
|
|
|
|
|
});
|
2019-11-18 13:03:05 +03:00
|
|
|
|
});
|
2019-01-24 06:13:43 +03:00
|
|
|
|
|
|
|
|
|
MatrixClientPeg.get().getThreePids().then((r) => this.setState({threepids: r.threepids}));
|
2018-04-12 01:58:04 +03:00
|
|
|
|
},
|
|
|
|
|
|
2019-05-13 01:24:12 +03:00
|
|
|
|
_onClearNotifications: function() {
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
|
|
|
|
|
cli.getRooms().forEach(r => {
|
|
|
|
|
if (r.getUnreadNotificationCount() > 0) {
|
|
|
|
|
const events = r.getLiveTimeline().getEvents();
|
|
|
|
|
if (events.length) cli.sendReadReceipt(events.pop());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2018-04-12 01:58:04 +03:00
|
|
|
|
_updatePushRuleActions: function(rule, actions, enabled) {
|
|
|
|
|
const cli = MatrixClientPeg.get();
|
|
|
|
|
|
|
|
|
|
return cli.setPushRuleActions(
|
2018-10-12 06:43:59 +03:00
|
|
|
|
'global', rule.kind, rule.rule_id, actions,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
).then( function() {
|
|
|
|
|
// Then, if requested, enabled or disabled the rule
|
|
|
|
|
if (undefined != enabled) {
|
|
|
|
|
return cli.setPushRuleEnabled(
|
2018-10-12 06:43:59 +03:00
|
|
|
|
'global', rule.kind, rule.rule_id, enabled,
|
2018-04-12 01:58:04 +03:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
renderNotifRulesTableRow: function(title, className, pushRuleVectorState) {
|
|
|
|
|
return (
|
|
|
|
|
<tr key={ className }>
|
|
|
|
|
<th>
|
|
|
|
|
{ title }
|
|
|
|
|
</th>
|
|
|
|
|
|
|
|
|
|
<th>
|
|
|
|
|
<input className= {className + "-" + PushRuleVectorState.OFF}
|
|
|
|
|
type="radio"
|
|
|
|
|
checked={ pushRuleVectorState === PushRuleVectorState.OFF }
|
|
|
|
|
onChange={ this.onNotifStateButtonClicked } />
|
|
|
|
|
</th>
|
|
|
|
|
|
|
|
|
|
<th>
|
|
|
|
|
<input className= {className + "-" + PushRuleVectorState.ON}
|
|
|
|
|
type="radio"
|
|
|
|
|
checked={ pushRuleVectorState === PushRuleVectorState.ON }
|
|
|
|
|
onChange={ this.onNotifStateButtonClicked } />
|
|
|
|
|
</th>
|
|
|
|
|
|
|
|
|
|
<th>
|
|
|
|
|
<input className= {className + "-" + PushRuleVectorState.LOUD}
|
|
|
|
|
type="radio"
|
|
|
|
|
checked={ pushRuleVectorState === PushRuleVectorState.LOUD }
|
|
|
|
|
onChange={ this.onNotifStateButtonClicked } />
|
|
|
|
|
</th>
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
renderNotifRulesTableRows: function() {
|
|
|
|
|
const rows = [];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in this.state.vectorPushRules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.state.vectorPushRules[i];
|
2019-03-15 23:14:00 +03:00
|
|
|
|
if (rule.rule === undefined && rule.vectorRuleId.startsWith(".m.")) {
|
|
|
|
|
console.warn(`Skipping render of rule ${rule.vectorRuleId} due to no underlying rule`);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-04-12 01:58:04 +03:00
|
|
|
|
//console.log("rendering: " + rule.description + ", " + rule.vectorRuleId + ", " + rule.vectorState);
|
|
|
|
|
rows.push(this.renderNotifRulesTableRow(rule.description, rule.vectorRuleId, rule.vectorState));
|
|
|
|
|
}
|
|
|
|
|
return rows;
|
|
|
|
|
},
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
hasEmailPusher: function(pushers, address) {
|
|
|
|
|
if (pushers === undefined) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < pushers.length; ++i) {
|
|
|
|
|
if (pushers[i].kind === 'email' && pushers[i].pushkey === address) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
|
2018-04-12 01:58:04 +03:00
|
|
|
|
emailNotificationsRow: function(address, label) {
|
2019-01-24 06:13:43 +03:00
|
|
|
|
return <LabelledToggleSwitch value={this.hasEmailPusher(this.state.pushers, address)}
|
|
|
|
|
onChange={this.onEnableEmailNotificationsChange.bind(this, address)}
|
2019-03-01 05:57:45 +03:00
|
|
|
|
label={label} key={`emailNotif_${label}`} />;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
|
let spinner;
|
|
|
|
|
if (this.state.phase === this.phases.LOADING) {
|
|
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
|
|
|
|
spinner = <Loader />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let masterPushRuleDiv;
|
|
|
|
|
if (this.state.masterPushRule) {
|
2019-01-24 06:13:43 +03:00
|
|
|
|
masterPushRuleDiv = <LabelledToggleSwitch value={!this.state.masterPushRule.enabled}
|
|
|
|
|
onChange={this.onEnableNotificationsChange}
|
|
|
|
|
label={_t('Enable notifications for this account')}/>;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-13 01:24:12 +03:00
|
|
|
|
let clearNotificationsButton;
|
|
|
|
|
if (MatrixClientPeg.get().getRooms().some(r => r.getUnreadNotificationCount() > 0)) {
|
|
|
|
|
clearNotificationsButton = <AccessibleButton onClick={this._onClearNotifications} kind='danger'>
|
|
|
|
|
{_t("Clear notifications")}
|
|
|
|
|
</AccessibleButton>;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-12 01:58:04 +03:00
|
|
|
|
// When enabled, the master rule inhibits all existing rules
|
|
|
|
|
// So do not show all notification settings
|
|
|
|
|
if (this.state.masterPushRule && this.state.masterPushRule.enabled) {
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
{masterPushRuleDiv}
|
|
|
|
|
|
2019-02-04 23:25:26 +03:00
|
|
|
|
<div className="mx_UserNotifSettings_notifTable">
|
2019-01-24 06:13:43 +03:00
|
|
|
|
{ _t('All notifications are currently disabled for all targets.') }
|
2018-04-12 01:58:04 +03:00
|
|
|
|
</div>
|
2019-05-13 01:24:12 +03:00
|
|
|
|
|
|
|
|
|
{clearNotificationsButton}
|
2018-04-12 01:58:04 +03:00
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
const emailThreepids = this.state.threepids.filter((tp) => tp.medium === "email");
|
2019-03-01 05:57:45 +03:00
|
|
|
|
let emailNotificationsRows;
|
2018-04-12 01:58:04 +03:00
|
|
|
|
if (emailThreepids.length === 0) {
|
2019-03-01 05:57:45 +03:00
|
|
|
|
emailNotificationsRows = <div>
|
2019-01-24 06:13:43 +03:00
|
|
|
|
{ _t('Add an email address to configure email notifications') }
|
2018-04-12 01:58:04 +03:00
|
|
|
|
</div>;
|
|
|
|
|
} else {
|
2019-03-01 05:57:45 +03:00
|
|
|
|
emailNotificationsRows = emailThreepids.map((threePid) => this.emailNotificationsRow(
|
|
|
|
|
threePid.address, `${_t('Enable email notifications')} (${threePid.address})`,
|
|
|
|
|
));
|
2018-04-12 01:58:04 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build external push rules
|
|
|
|
|
const externalRules = [];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in this.state.externalPushRules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.state.externalPushRules[i];
|
|
|
|
|
externalRules.push(<li>{ _t(rule.description) }</li>);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show keywords not displayed by the vector UI as a single external push rule
|
|
|
|
|
let externalKeywords = [];
|
2018-10-12 06:43:59 +03:00
|
|
|
|
for (const i in this.state.externalContentRules) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
const rule = this.state.externalContentRules[i];
|
|
|
|
|
externalKeywords.push(rule.pattern);
|
|
|
|
|
}
|
|
|
|
|
if (externalKeywords.length) {
|
|
|
|
|
externalKeywords = externalKeywords.join(", ");
|
|
|
|
|
externalRules.push(<li>{ _t('Notifications on the following keywords follow rules which can’t be displayed here:') } { externalKeywords }</li>);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let devicesSection;
|
|
|
|
|
if (this.state.pushers === undefined) {
|
2018-10-12 06:43:59 +03:00
|
|
|
|
devicesSection = <div className="error">{ _t('Unable to fetch notification target list') }</div>;
|
2019-01-24 06:13:43 +03:00
|
|
|
|
} else if (this.state.pushers.length === 0) {
|
2018-04-12 01:58:04 +03:00
|
|
|
|
devicesSection = null;
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: It would be great to be able to delete pushers from here too,
|
|
|
|
|
// and this wouldn't be hard to add.
|
|
|
|
|
const rows = [];
|
|
|
|
|
for (let i = 0; i < this.state.pushers.length; ++i) {
|
|
|
|
|
rows.push(<tr key={ i }>
|
|
|
|
|
<td>{this.state.pushers[i].app_display_name}</td>
|
|
|
|
|
<td>{this.state.pushers[i].device_display_name}</td>
|
|
|
|
|
</tr>);
|
|
|
|
|
}
|
2019-02-04 23:25:26 +03:00
|
|
|
|
devicesSection = (<table className="mx_UserNotifSettings_devicesTable">
|
2018-04-12 01:58:04 +03:00
|
|
|
|
<tbody>
|
|
|
|
|
{rows}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>);
|
|
|
|
|
}
|
|
|
|
|
if (devicesSection) {
|
|
|
|
|
devicesSection = (<div>
|
|
|
|
|
<h3>{ _t('Notification targets') }</h3>
|
|
|
|
|
{ devicesSection }
|
|
|
|
|
</div>);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let advancedSettings;
|
|
|
|
|
if (externalRules.length) {
|
|
|
|
|
advancedSettings = (
|
|
|
|
|
<div>
|
|
|
|
|
<h3>{ _t('Advanced notification settings') }</h3>
|
2018-10-12 06:43:59 +03:00
|
|
|
|
{ _t('There are advanced notifications which are not shown here') }.<br />
|
2018-04-12 01:58:04 +03:00
|
|
|
|
{ _t('You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply') }.
|
|
|
|
|
<ul>
|
|
|
|
|
{ externalRules }
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
|
|
{masterPushRuleDiv}
|
|
|
|
|
|
2019-02-04 23:25:26 +03:00
|
|
|
|
<div className="mx_UserNotifSettings_notifTable">
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
|
|
{ spinner }
|
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
<LabelledToggleSwitch value={SettingsStore.getValue("notificationsEnabled")}
|
|
|
|
|
onChange={this.onEnableDesktopNotificationsChange}
|
2019-03-26 13:09:04 +03:00
|
|
|
|
label={_t('Enable desktop notifications for this device')} />
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
<LabelledToggleSwitch value={SettingsStore.getValue("notificationBodyEnabled")}
|
|
|
|
|
onChange={this.onEnableDesktopNotificationBodyChange}
|
|
|
|
|
label={_t('Show message in desktop notification')} />
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
2019-01-24 06:13:43 +03:00
|
|
|
|
<LabelledToggleSwitch value={SettingsStore.getValue("audioNotificationsEnabled")}
|
|
|
|
|
onChange={this.onEnableAudioNotificationsChange}
|
2019-03-26 13:09:04 +03:00
|
|
|
|
label={_t('Enable audible notifications for this device')} />
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
2019-03-01 05:57:45 +03:00
|
|
|
|
{ emailNotificationsRows }
|
2018-04-12 01:58:04 +03:00
|
|
|
|
|
|
|
|
|
<div className="mx_UserNotifSettings_pushRulesTableWrapper">
|
|
|
|
|
<table className="mx_UserNotifSettings_pushRulesTable">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th width="55%"></th>
|
|
|
|
|
<th width="15%">{ _t('Off') }</th>
|
|
|
|
|
<th width="15%">{ _t('On') }</th>
|
|
|
|
|
<th width="15%">{ _t('Noisy') }</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
|
|
|
|
|
{ this.renderNotifRulesTableRows() }
|
|
|
|
|
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{ advancedSettings }
|
|
|
|
|
|
|
|
|
|
{ devicesSection }
|
|
|
|
|
|
2019-05-13 01:24:12 +03:00
|
|
|
|
{ clearNotificationsButton }
|
2018-04-12 01:58:04 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2018-10-12 06:43:59 +03:00
|
|
|
|
},
|
2018-04-12 01:58:04 +03:00
|
|
|
|
});
|