Update the actions of default rules instead of overriding.
The Matrix CS API, and synapse now supports setting the actions for default rules. Doing that makes managing the rules much simpler from a vector persepctive since the ON/LOUD/OFF toggle buttons can be implemented by setting the actions and enabling/disabling the default rules rather than overidding them. Overriding the default rules was difficult because it was not possible to intermingle the evaluation of user-specified rules with the default rules. So even though you could add a rule with the same conditions as a default rule, it would evaluate before *all* the other default rules. Also creating new rules under a im.vector namespace creates challenges if we want vector to cooperate with other matrix clients that want to provide a similar set of toggle switches for the push rules.
This commit is contained in:
parent
c3b819b4da
commit
731d94eea4
|
@ -37,6 +37,40 @@ var PushRuleVectorState = {
|
|||
OFF: "off"
|
||||
};
|
||||
|
||||
|
||||
var ACTION_NOTIFY = [ "notify" ];
|
||||
|
||||
var ACTION_NOTIFY_DEFAULT_SOUND = [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
];
|
||||
|
||||
var ACTION_NOTIFY_RING_SOUND = [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "ring"
|
||||
}
|
||||
];
|
||||
|
||||
var ACTION_HIGHLIGHT_DEFAULT_SOUND = [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak":"highlight"
|
||||
}
|
||||
];
|
||||
|
||||
var ACTION_DONT_NOTIFY = [ "dont_notify" ];
|
||||
|
||||
var ACTION_DISABLED = null;
|
||||
|
||||
/**
|
||||
* The descriptions of rules managed by the Vector UI.
|
||||
* Each rule is described so that if the server does not have it in its default
|
||||
|
@ -48,225 +82,71 @@ var VectorPushRulesDefinitions = {
|
|||
|
||||
// Messages containing user's display name
|
||||
// (skip contains_user_name which is too geeky)
|
||||
"im.vector.rule.contains_display_name": {
|
||||
".m.rule.contains_display_name": {
|
||||
kind: "underride",
|
||||
hsDefaultRuleId: ".m.rule.contains_display_name",
|
||||
description: "Messages containing my name",
|
||||
conditions: [{
|
||||
"kind": "contains_display_name"
|
||||
}],
|
||||
vectorStateToActions: { // The actions for each vector state
|
||||
on: [
|
||||
"notify"
|
||||
],
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak":"highlight"
|
||||
}
|
||||
]
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: { // If it exists, the hs default push rule enabled expected value for each vector state
|
||||
on: undefined, // ON (and its actions) does not corresponds to the default hs push rule, so NA
|
||||
loud: true, // LOUD corresponds to the default rule when its enabled value is true
|
||||
off: false // OFF corresponds to the default rule when its enabled value is false
|
||||
},
|
||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||
on: ACTION_NOTIFY,
|
||||
loud: ACTION_HIGHLIGHT_DEFAULT,
|
||||
off: ACTION_DISABLED
|
||||
}
|
||||
},
|
||||
|
||||
// Messages just sent to the user in a 1:1 room
|
||||
"im.vector.rule.room_one_to_one": {
|
||||
".m.rule.room_one_to_one": {
|
||||
kind: "underride",
|
||||
hsDefaultRuleId: ".m.rule.room_one_to_one",
|
||||
description: "Messages in one-to-one chats",
|
||||
conditions: [{
|
||||
"is": "2",
|
||||
"kind": "room_member_count"
|
||||
}],
|
||||
vectorStateToActions: {
|
||||
on: [
|
||||
"notify"
|
||||
],
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
],
|
||||
off: [
|
||||
"dont_notify"
|
||||
]
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: {
|
||||
on: undefined,
|
||||
loud: true,
|
||||
off: undefined
|
||||
on: ACTION_NOTIFY,
|
||||
loud: ACTION_NOTIFY_DEFAULT_SOUND,
|
||||
off: ACTION_DONT_NOTIFY
|
||||
}
|
||||
},
|
||||
|
||||
// Messages just sent to a group chat room
|
||||
// 1:1 room messages are catched by the .m.rule.room_one_to_one rule if any defined
|
||||
// By opposition, all other room messages are from group chat rooms.
|
||||
"im.vector.rule.room_message": {
|
||||
".m.rule.room_message": {
|
||||
kind: "underride",
|
||||
description: "Messages in group chats",
|
||||
conditions: [{
|
||||
"pattern": "m.room.message",
|
||||
"kind": "event_match",
|
||||
"key": "type"
|
||||
}],
|
||||
hsDefaultRuleId: ".m.rule.message",
|
||||
vectorStateToActions: {
|
||||
on: [
|
||||
"notify"
|
||||
],
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
],
|
||||
off: [
|
||||
"dont_notify"
|
||||
]
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: {
|
||||
on: true,
|
||||
loud: undefined,
|
||||
off: undefined
|
||||
on: ACTION_NOTIFY,
|
||||
loud: ACTION_NOTIFY_DEFAULT_SOUND,
|
||||
off: ACTION_DONT_NOTIFY
|
||||
}
|
||||
},
|
||||
|
||||
// Invitation for the user
|
||||
"im.vector.rule.invite_for_me": {
|
||||
".m.rule.invite_for_me": {
|
||||
kind: "underride",
|
||||
hsDefaultRuleId: ".m.rule.invite_for_me",
|
||||
description: "When I'm invited to a room",
|
||||
conditions: [
|
||||
{
|
||||
"key": "type",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.room.member"
|
||||
},
|
||||
{
|
||||
"key": "content.membership",
|
||||
"kind": "event_match",
|
||||
"pattern": "invite"
|
||||
},
|
||||
{
|
||||
"key": "state_key",
|
||||
"kind": "event_match",
|
||||
"pattern": "" // It is updated at runtime the user id
|
||||
}
|
||||
],
|
||||
vectorStateToActions: {
|
||||
on: [
|
||||
"notify"
|
||||
],
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: {
|
||||
on: undefined,
|
||||
loud: true,
|
||||
off: false
|
||||
on: ACTION_NOTIFY,
|
||||
loud: ACTION_NOTIFY_DEFAULT_SOUND,
|
||||
off: ACTION_DISABLED
|
||||
}
|
||||
},
|
||||
|
||||
// When people join or leave a room
|
||||
/*"im.vector.rule.member_event": {
|
||||
hsDefaultRuleId: ".m.rule.member_event",
|
||||
description: "When people join or leave a room",
|
||||
conditions: [{
|
||||
"pattern": "m.room.member",
|
||||
"kind": "event_match",
|
||||
"key": "type"
|
||||
}],
|
||||
vectorStateToActions: {
|
||||
on: [
|
||||
"notify"
|
||||
],
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: {
|
||||
on: true,
|
||||
loud: undefined,
|
||||
off: false
|
||||
}
|
||||
},*/
|
||||
|
||||
// Incoming call
|
||||
"im.vector.rule.call": {
|
||||
".m.rule.call": {
|
||||
kind: "underride",
|
||||
hsDefaultRuleId: ".m.rule.call",
|
||||
description: "Call invitation",
|
||||
conditions: [{
|
||||
"pattern": "m.room.member",
|
||||
"kind": "event_match",
|
||||
"key": "type"
|
||||
}],
|
||||
vectorStateToActions: {
|
||||
on: [
|
||||
"notify"
|
||||
],
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "ring"
|
||||
}
|
||||
],
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: {
|
||||
on: undefined,
|
||||
loud: true,
|
||||
off: false
|
||||
on: ACTION_NOTIFY,
|
||||
loud: ACTION_NOTIFY_RING_SOUND,
|
||||
off: ACTION_DISABLED
|
||||
}
|
||||
},
|
||||
|
||||
// Notifications from bots
|
||||
"im.vector.rule.notices": {
|
||||
".m.rule.suppress_notices": {
|
||||
kind: "override",
|
||||
hsDefaultRuleId: ".m.rule.suppress_notices",
|
||||
description: "Messages sent by bot",
|
||||
conditions: [{
|
||||
"kind": "event_match",
|
||||
"key": "content.msgtype",
|
||||
"pattern": "m.notice"
|
||||
}],
|
||||
vectorStateToActions: {
|
||||
on: undefined, // ON for vector UI means that the .m.rule.suppress_notices rule is disabled.
|
||||
loud: [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "ring"
|
||||
}
|
||||
],
|
||||
off: [
|
||||
"dont_notify"
|
||||
]
|
||||
},
|
||||
vectorStateToHsDefaultRuleEnabled: {
|
||||
on: false, // .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI
|
||||
loud: undefined,
|
||||
off: true
|
||||
// .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI
|
||||
on: ACTION_DISABLED,
|
||||
loud: ACTION_NOTIFY_DEFAULT_SOUND,
|
||||
off: ACTION_DONT_NOTIFY,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -295,9 +175,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// Finalise the vector definitions
|
||||
VectorPushRulesDefinitions["im.vector.rule.invite_for_me"].conditions[2].pattern = MatrixClientPeg.get().credentials.userId;
|
||||
|
||||
this._refreshFromServer();
|
||||
},
|
||||
|
||||
|
@ -389,13 +266,10 @@ module.exports = React.createClass({
|
|||
|
||||
_actionsFor: function(pushRuleVectorState) {
|
||||
if (pushRuleVectorState === PushRuleVectorState.ON) {
|
||||
return ['notify'];
|
||||
return ACTIONS_NOTIFY;
|
||||
}
|
||||
else if (pushRuleVectorState === PushRuleVectorState.LOUD) {
|
||||
return ['notify',
|
||||
{'set_tweak': 'sound', 'value': 'default'},
|
||||
{'set_tweak': 'highlight', 'value': 'true'}
|
||||
];;
|
||||
return ACTIONS_HIGHLIGHT_DEFAULT_SOUND;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -437,36 +311,18 @@ module.exports = React.createClass({
|
|||
var ruleDefinition = VectorPushRulesDefinitions[rule.vectorRuleId];
|
||||
|
||||
if (rule.rule) {
|
||||
if (undefined !== ruleDefinition.vectorStateToHsDefaultRuleEnabled[newPushRuleVectorState] && rule.hsDefaultRule) {
|
||||
// The new state corresponds to the default hs rule
|
||||
// Enable or disable it according to the rule definition
|
||||
deferreds.push(cli.setPushRuleEnabled('global', rule.hsDefaultRule.kind, ruleDefinition.hsDefaultRuleId,
|
||||
ruleDefinition.vectorStateToHsDefaultRuleEnabled[newPushRuleVectorState]));
|
||||
var actions = ruleDefinition.vectorStateToActions(newPushRuleVectorState);
|
||||
|
||||
// Remove the vector rule if any
|
||||
if (!rule.isHSDefaultRule) {
|
||||
deferreds.push(cli.deletePushRule('global', rule.rule.kind, rule.rule.rule_id))
|
||||
}
|
||||
if (actions === ACTIONS_DISABLED) {
|
||||
// The new state corresponds to disabling the rule.
|
||||
deferreds.push(cli.setPushRuleEnabled('global', rule.rule.kind, rule.rule.rule_id, false));
|
||||
}
|
||||
else {
|
||||
// The new state (and its implied actions) does not correspond to a default hs rule
|
||||
// or the HS does not expose this default rule.
|
||||
if (rule.isHSDefaultRule) {
|
||||
// Create a new rule that will override the default one
|
||||
deferreds.push(this._addOverridingVectorPushRule(rule.vectorRuleId, newPushRuleVectorState));
|
||||
}
|
||||
else {
|
||||
// Change the actions of the existing overriding Vector rule
|
||||
deferreds.push(this._updatePushRuleActions(rule.rule, ruleDefinition.vectorStateToActions[newPushRuleVectorState]));
|
||||
}
|
||||
// The new state corresponds to enabling the rule and setting specific actions
|
||||
deferreds.push(this._updatePushRuleActions(rule.rule, actions, true));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is a Vector rule which does not exist yet server side
|
||||
// Create it
|
||||
deferreds.push(this._addOverridingVectorPushRule(rule.vectorRuleId, newPushRuleVectorState));
|
||||
}
|
||||
|
||||
|
||||
q.all(deferreds).done(function() {
|
||||
self._refreshFromServer();
|
||||
}, function(error) {
|
||||
|
@ -690,8 +546,6 @@ module.exports = React.createClass({
|
|||
|
||||
// HS default rules
|
||||
var defaultRules = {master: [], vector: {}, others: []};
|
||||
// Push rules defined py Vector to override hs default rules
|
||||
var vectorOverridingRules = {};
|
||||
// Content/keyword rules
|
||||
var contentRules = {on: [], on_but_disabled:[], loud: [], loud_but_disabled: [], other: []};
|
||||
|
||||
|
@ -701,29 +555,16 @@ module.exports = React.createClass({
|
|||
var cat = rule_categories[r.rule_id];
|
||||
r.kind = kind;
|
||||
if (r.rule_id[0] === '.') {
|
||||
if (cat) {
|
||||
if (cat === 'vector') {
|
||||
// Remove disabled, useless actions
|
||||
r.actions = r.actions.reduce(function(array, action){
|
||||
if (action.value !== false) {
|
||||
array.push(action);
|
||||
}
|
||||
return array;
|
||||
},[]);
|
||||
|
||||
defaultRules.vector[r.rule_id] = r;
|
||||
}
|
||||
else {
|
||||
defaultRules[cat].push(r);
|
||||
}
|
||||
if (cat === 'vector') {
|
||||
defaultRules.vector[r.rule_id] = r;
|
||||
}
|
||||
else if (cat === 'master') {
|
||||
defaultRules.master.push(r);
|
||||
}
|
||||
else {
|
||||
defaultRules['others'].push(r);
|
||||
}
|
||||
}
|
||||
else if (r.rule_id.startsWith('im.vector')) {
|
||||
vectorOverridingRules[r.rule_id] = r;
|
||||
}
|
||||
else if (kind === 'content') {
|
||||
switch (self._contentRuleVectorStateKind(r)) {
|
||||
case PushRuleVectorState.ON:
|
||||
|
@ -804,14 +645,14 @@ module.exports = React.createClass({
|
|||
self.state.vectorPushRules = [];
|
||||
|
||||
var vectorRuleIds = [
|
||||
'im.vector.rule.contains_display_name',
|
||||
'.m.rule.contains_display_name',
|
||||
'_keywords',
|
||||
'im.vector.rule.room_one_to_one',
|
||||
'im.vector.rule.room_message',
|
||||
'im.vector.rule.invite_for_me',
|
||||
'.m.rule.room_one_to_one',
|
||||
'.m.rule.room_message',
|
||||
'.m.rule.invite_for_me',
|
||||
//'im.vector.rule.member_event',
|
||||
'im.vector.rule.call',
|
||||
'im.vector.rule.notices'
|
||||
'.m.rule.call',
|
||||
'.m.rule.notices'
|
||||
];
|
||||
for (var i in vectorRuleIds) {
|
||||
var vectorRuleId = vectorRuleIds[i];
|
||||
|
@ -828,13 +669,7 @@ module.exports = React.createClass({
|
|||
});
|
||||
}
|
||||
else {
|
||||
var rule = vectorOverridingRules[vectorRuleId];
|
||||
var isHSDefaultRule = false;
|
||||
if (!rule) {
|
||||
// If the rule is not defined, look at the hs default one
|
||||
rule = defaultRules.vector[ruleDefinition.hsDefaultRuleId];
|
||||
isHSDefaultRule = true;
|
||||
}
|
||||
var rule = defaultRules.vector[vectorRuleId];
|
||||
|
||||
// Translate the rule actions and its enabled value into vector state
|
||||
var vectorState;
|
||||
|
@ -843,9 +678,9 @@ module.exports = React.createClass({
|
|||
var state = PushRuleVectorState[stateKey];
|
||||
var vectorStateToActions = ruleDefinition.vectorStateToActions[state];
|
||||
|
||||
if (!vectorStateToActions) {
|
||||
if (vectorStateToActions === ACTIONS_DISABLED) {
|
||||
// No defined actions means that this vector state expects a disabled default hs rule
|
||||
if (isHSDefaultRule && rule.enabled === ruleDefinition.vectorStateToHsDefaultRuleEnabled[state]) {
|
||||
if (rule.enabled === false) {
|
||||
vectorState = state;
|
||||
break;
|
||||
}
|
||||
|
@ -853,14 +688,8 @@ module.exports = React.createClass({
|
|||
else {
|
||||
// The actions must match to the ones expected by vector state
|
||||
if (JSON.stringify(rule.actions) === JSON.stringify(vectorStateToActions)) {
|
||||
if (isHSDefaultRule) {
|
||||
// In the case of a default hs push rule, the enabled value must also match
|
||||
if (rule.enabled === ruleDefinition.vectorStateToHsDefaultRuleEnabled[state]) {
|
||||
vectorState = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// And the rule must be enabled.
|
||||
if (rule.enabled === true) {
|
||||
vectorState = state;
|
||||
break;
|
||||
}
|
||||
|
@ -870,7 +699,7 @@ module.exports = React.createClass({
|
|||
|
||||
if (!vectorState) {
|
||||
console.error("Cannot translate rule actions into Vector rule state. Rule: " + rule);
|
||||
vectorState = PushRuleVectorState.OFF;
|
||||
vectorState = PushRuleVectorState.OFF;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -882,9 +711,7 @@ module.exports = React.createClass({
|
|||
"description" : ruleDefinition.description,
|
||||
"rule": rule,
|
||||
"vectorState": vectorState,
|
||||
"isHSDefaultRule": isHSDefaultRule,
|
||||
"hsDefaultRule": defaultRules.vector[ruleDefinition.hsDefaultRuleId]
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -913,39 +740,21 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_updatePushRuleActions: function(rule, actions, enabled) {
|
||||
// Workaround for SYN-590 : Push rule update fails
|
||||
// Remove the rule and recreate it with the new actions
|
||||
var cli = MatrixClientPeg.get();
|
||||
var deferred = q.defer();
|
||||
|
||||
cli.deletePushRule('global', rule.kind, rule.rule_id).done(function() {
|
||||
cli.addPushRule('global', rule.kind, rule.rule_id, {
|
||||
conditions: rule.conditions,
|
||||
actions: actions,
|
||||
pattern: rule.pattern
|
||||
}).done(function() {
|
||||
|
||||
// Then, if requested, enabled or disabled the rule
|
||||
if (undefined != enabled) {
|
||||
cli.setPushRuleEnabled('global', rule.kind, rule.rule_id, enabled).done(function() {
|
||||
deferred.resolve();
|
||||
}, function(err) {
|
||||
deferred.reject(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
deferred.resolve();
|
||||
}
|
||||
}, function(err) {
|
||||
deferred.reject(err);
|
||||
});
|
||||
}, function(err) {
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
return cli.setPushRuleActions(
|
||||
'global', rule.kind, rule.rule_id, actions
|
||||
).then( function() {
|
||||
// Then, if requested, enabled or disabled the rule
|
||||
if (undefined != enabled) {
|
||||
return cli.setPushRuleEnabled(
|
||||
'global', rule.kind, rule.rule_id, enabled
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
renderNotifRulesTableRow: function(title, className, pushRuleVectorState) {
|
||||
return (
|
||||
<tr key = {className}>
|
||||
|
|
Loading…
Reference in New Issue