diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js index 4c880760ba..dc46e099a2 100644 --- a/src/components/views/settings/Notifications.js +++ b/src/components/views/settings/Notifications.js @@ -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 (