diff --git a/src/components/views/context_menus/NotificationStateContextMenu.js b/src/components/views/context_menus/NotificationStateContextMenu.js index 21b17fe6..f263f44a 100644 --- a/src/components/views/context_menus/NotificationStateContextMenu.js +++ b/src/components/views/context_menus/NotificationStateContextMenu.js @@ -32,78 +32,86 @@ module.exports = React.createClass({ onFinished: React.PropTypes.func, }, - _save: function( areNotifsMuted ) { - var self = this; + getInitialState() { + return { + vectorRoomNotifState: RoomNotifs.getVectorRoomNotifsState(this.props.room.roomId), + } + }, + + _save: function(newState) { + const oldState = this.state.vectorRoomNotifState; const roomId = this.props.room.roomId; var cli = MatrixClientPeg.get(); if (!cli.isGuest()) { // Wrapping this in a q promise, as setRoomMutePushRule can return // a promise or a value - q(cli.setRoomMutePushRule("global", roomId, areNotifsMuted)) - .then(function() { - self.setState({areNotifsMuted: areNotifsMuted}); - + this.setState({ + vectorRoomNotifState: newState, + }); + RoomNotifs.setVectorRoomNotifsState(this.props.room.roomId, newState).done(() => { // delay slightly so that the user can see their state change // before closing the menu - return q.delay(500).then(function() { + return q.delay(500).then(() => { // tell everyone that wants to know of the change in // notification state dis.dispatch({ action: 'notification_change', - roomId: self.props.room.roomId, - areNotifsMuted: areNotifsMuted, + roomId: this.props.room.roomId, + //areNotifsMuted: areNotifsMuted, }); // Close the context menu - if (self.props.onFinished) { - self.props.onFinished(); + if (this.props.onFinished) { + this.props.onFinished(); }; }); - }).fail(function(error) { + }, (error) => { // TODO: some form of error notification to the user // to inform them that their state change failed. + // For now we at least set the state back + this.setState({ + vectorRoomNotifState: oldState, + }); }); } }, _onClickAlertMe: function() { - // Placeholder + this._save('all_messages_loud'); }, _onClickAllNotifs: function() { - this._save(false); + this._save('all_messages'); }, _onClickMentions: function() { - this._save(true); + this._save('mentions_only'); }, _onClickMute: function() { - // Placeholder + this._save('mute'); }, render: function() { - const vectorRoomNotifState = RoomNotifs.getVectorRoomNotifsState(this.props.room.roomId); - var alertMeClasses = classNames({ 'mx_NotificationStateContextMenu_field': true, - 'mx_NotificationStateContextMenu_fieldSet': vectorRoomNotifState == 'all_messages_loud', + 'mx_NotificationStateContextMenu_fieldSet': this.state.vectorRoomNotifState == 'all_messages_loud', }); var allNotifsClasses = classNames({ 'mx_NotificationStateContextMenu_field': true, - 'mx_NotificationStateContextMenu_fieldSet': vectorRoomNotifState == 'all_messages', + 'mx_NotificationStateContextMenu_fieldSet': this.state.vectorRoomNotifState == 'all_messages', }); var mentionsClasses = classNames({ 'mx_NotificationStateContextMenu_field': true, - 'mx_NotificationStateContextMenu_fieldSet': vectorRoomNotifState == 'mentions_only', + 'mx_NotificationStateContextMenu_fieldSet': this.state.vectorRoomNotifState == 'mentions_only', }); var muteNotifsClasses = classNames({ 'mx_NotificationStateContextMenu_field': true, - 'mx_NotificationStateContextMenu_fieldDisabled': vectorRoomNotifState == 'mute', + 'mx_NotificationStateContextMenu_fieldSet': this.state.vectorRoomNotifState == 'mute', }); return ( diff --git a/src/notifications/RoomNotifs.js b/src/notifications/RoomNotifs.js index 35b1f125..e07b3ac8 100644 --- a/src/notifications/RoomNotifs.js +++ b/src/notifications/RoomNotifs.js @@ -16,16 +16,14 @@ limitations under the License. import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg'; import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; +import q from 'q'; export function getVectorRoomNotifsState(roomId) { // look through the override rules for a rule affecting this room: // if one exists, it will take precedence. - for (const rule of MatrixClientPeg.get().pushRules['global'].override) { - if (isRuleForRoom(roomId, rule)) { - if (isMuteRule(rule)) { - return 'mute'; - } - } + const muteRule = findOverrideMuteRule(roomId); + if (muteRule && muteRule.enabled) { + return 'mute'; } // for everything else, look at the room rule. @@ -34,7 +32,7 @@ export function getVectorRoomNotifsState(roomId) { // XXX: We have to assume the default is to notify for all messages // (in particular this will be 'wrong' for one to one rooms because // they will notify loudly for all messages) - if (!roomRule) return 'all_messages'; + if (!roomRule || !roomRule.enabled) return 'all_messages'; // a mute at the room level will still allow mentions // to notify @@ -46,6 +44,75 @@ export function getVectorRoomNotifsState(roomId) { return null; } +export function setVectorRoomNotifsState(roomId, newState) { + const cli = MatrixClientPeg.get(); + const promises = []; + + if (newState == 'mute') { + // delete the room rule + const roomRule = MatrixClientPeg.get().getRoomPushRule('global', roomId); + if (roomRule) { + promises.push(cli.deletePushRule('global', 'room', roomRule.rule_id)); + } + + // add an override rule to squelch everything in this room + promises.push(cli.addPushRule('global', 'override', roomId, { + conditions: [ + { + kind: 'event_match', + key: 'room_id', + pattern: roomId, + } + ], + actions: [ + 'dont_notify', + ] + })); + } else { + const overrideMuteRule = findOverrideMuteRule(roomId); + if (overrideMuteRule) { + promises.push(cli.deletePushRule('global', 'override', overrideMuteRule.rule_id)); + } + + if (newState == 'all_messages') { + promises.push(cli.deletePushRule('global', 'room', roomId)); + } else if (newState == 'mentions_only') { + promises.push(cli.addPushRule('global', 'room', roomId, { + actions: [ + 'dont_notify', + ] + })); + // https://matrix.org/jira/browse/SPEC-400 + promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true)); + } else if ('all_messages_loud') { + promises.push(cli.addPushRule('global', 'room', roomId, { + actions: [ + 'notify', + { + set_tweak: 'sound', + value: 'default', + } + ] + })); + // https://matrix.org/jira/browse/SPEC-400 + promises.push(cli.setPushRuleEnabled('global', 'room', roomId, true)); + } + } + + return q.all(promises); +} + +function findOverrideMuteRule(roomId) { + for (const rule of MatrixClientPeg.get().pushRules['global'].override) { + if (isRuleForRoom(roomId, rule)) { + if (isMuteRule(rule)) { + return rule; + } + } + } + return null; +} + function isRuleForRoom(roomId, rule) { if (rule.conditions.length !== 1) { return false; diff --git a/src/notifications/VectorPushRulesDefinitions.js b/src/notifications/VectorPushRulesDefinitions.js index dfbc06c0..2e90e576 100644 --- a/src/notifications/VectorPushRulesDefinitions.js +++ b/src/notifications/VectorPushRulesDefinitions.js @@ -65,7 +65,7 @@ module.exports = { // Messages containing user's display name // (skip contains_user_name which is too geeky) ".m.rule.contains_display_name": new VectorPushRuleDefinition({ - kind: "underride", + kind: "override", description: "Messages containing my name", vectorStateToActions: { // The actions for each vector state, or null to disable the rule. on: StandardActions.ACTION_NOTIFY,