From 2e2cecdd4fc72b56614d7548293cd8dda810b3ff Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 21 Jul 2015 11:26:02 +0100 Subject: [PATCH] Implement kick/ban/[un]mute buttons on member list dialogs. --- skins/base/views/molecules/MemberInfo.js | 20 +++ src/controllers/molecules/MemberInfo.js | 182 ++++++++++++++++++++++- 2 files changed, 196 insertions(+), 6 deletions(-) diff --git a/skins/base/views/molecules/MemberInfo.js b/skins/base/views/molecules/MemberInfo.js index 3a81185ba9..036a3fd62d 100644 --- a/skins/base/views/molecules/MemberInfo.js +++ b/skins/base/views/molecules/MemberInfo.js @@ -67,6 +67,23 @@ module.exports = React.createClass({ if (this.state.active >= 0) { activeAgo = this.getDuration(this.state.active); } + var kickButton, banButton, muteButton; + if (this.state.can.kick) { + kickButton =
+ Kick +
; + } + if (this.state.can.ban) { + banButton =
+ Ban +
; + } + if (this.state.can.mute) { + var muteLabel = this.state.muted ? "Unmute" : "Mute"; + muteButton =
+ {muteLabel} +
; + } return (
@@ -81,6 +98,9 @@ module.exports = React.createClass({
Presence: {this.state.presence}
Last active: {activeAgo}
Start chat
+ {muteButton} + {kickButton} + {banButton}
); } diff --git a/src/controllers/molecules/MemberInfo.js b/src/controllers/molecules/MemberInfo.js index 96f8bb8ca8..fd04df4d55 100644 --- a/src/controllers/molecules/MemberInfo.js +++ b/src/controllers/molecules/MemberInfo.js @@ -18,15 +18,25 @@ limitations under the License. * State vars: * 'presence' : string (online|offline|unavailable etc) * 'active' : number (ms ago; can be -1) + * 'can': { + * kick: boolean, + * ban: boolean, + * mute: boolean + * }, + * 'muted': boolean */ 'use strict'; var MatrixClientPeg = require("../../MatrixClientPeg"); var dis = require("../../dispatcher"); +var Modal = require("../../Modal"); +var ComponentBroker = require('../../ComponentBroker'); +var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog"); module.exports = { componentDidMount: function() { var self = this; + // listen for presence changes function updateUserState(event, user) { if (!self.props.member) { return; } @@ -40,20 +50,116 @@ module.exports = { MatrixClientPeg.get().on("User.presence", updateUserState); this.userPresenceFn = updateUserState; - if (this.props.member) { - var usr = MatrixClientPeg.get().getUser(this.props.member.userId); - if (!usr) { + // listen for power level changes + function updatePowerLevel(event, member) { + if (!self.props.member) { return; } + + if (member.roomId !== self.props.member.roomId) { return; } + // only interested in changes to us or them + var myUserId = MatrixClientPeg.get().credentials.userId; + if ([myUserId, self.props.member.userId].indexOf(member.userId) === -1) { + return; + } + self.setState({ + can: self._calculateOpsPermissions(), + muted: self._isMuted(self.props.member) + }); + } + MatrixClientPeg.get().on("RoomMember.powerLevel", updatePowerLevel); + this.updatePowerLevelFn = updatePowerLevel; + + // work out the current state + if (this.props.member) { + var usr = MatrixClientPeg.get().getUser(this.props.member.userId) || {}; this.setState({ - presence: usr.presence, - active: usr.lastActiveAgo + presence: usr.presence || "offline", + active: usr.lastActiveAgo || -1, + can: this._calculateOpsPermissions(), + muted: this._isMuted(this.props.member) }); } }, componentWillUnmount: function() { MatrixClientPeg.get().removeListener("User.presence", this.userPresenceFn); + MatrixClientPeg.get().removeListener( + "RoomMember.powerLevel", this.updatePowerLevelFn + ); + }, + + onKick: function() { + var roomId = this.props.member.roomId; + var target = this.props.member.userId; + var self = this; + MatrixClientPeg.get().kick(roomId, target).done(function() { + // NO-OP; rely on the m.room.member event coming down else we could + // get out of sync if we force setState here! + console.log("Kick success"); + }, function(err) { + Modal.createDialog(ErrorDialog, { + title: "Kick error", + description: err.message + }); + }); + }, + + onBan: function() { + var roomId = this.props.member.roomId; + var target = this.props.member.userId; + var self = this; + MatrixClientPeg.get().ban(roomId, target).done(function() { + // NO-OP; rely on the m.room.member event coming down else we could + // get out of sync if we force setState here! + console.log("Ban success"); + }, function(err) { + Modal.createDialog(ErrorDialog, { + title: "Ban error", + description: err.message + }); + }); + }, + + onMuteToggle: function() { + var roomId = this.props.member.roomId; + var target = this.props.member.userId; + var self = this; + var room = MatrixClientPeg.get().getRoom(roomId); + if (!room) { + return; + } + var powerLevelEvent = room.currentState.getStateEvents( + "m.room.power_levels", "" + ); + if (!powerLevelEvent) { + return; + } + var isMuted = this.state.muted; + var powerLevels = powerLevelEvent.getContent(); + var levelToSend = ( + (powerLevels.events ? powerLevels.events["m.room.message"] : null) || + powerLevels.events_default + ); + var level; + if (isMuted) { // unmute + level = levelToSend; + } + else { // mute + level = levelToSend - 1; + } + + MatrixClientPeg.get().setPowerLevel(roomId, target, level, powerLevelEvent).done( + function() { + // NO-OP; rely on the m.room.member event coming down else we could + // get out of sync if we force setState here! + console.log("Mute toggle success"); + }, function(err) { + Modal.createDialog(ErrorDialog, { + title: "Mute error", + description: err.message + }); + }); }, onChatClick: function() { @@ -108,8 +214,72 @@ module.exports = { getInitialState: function() { return { presence: "offline", - active: -1 + active: -1, + can: { + kick: false, + ban: false, + mute: false + }, + muted: false } + }, + + _isMuted: function(member) { + var room = MatrixClientPeg.get().getRoom(member.roomId); + if (!room) { + return false; + } + var powerLevels = room.currentState.getStateEvents( + "m.room.power_levels", "" + ); + if (!powerLevels) { + return false; + } + powerLevels = powerLevels.getContent(); + var levelToSend = ( + (powerLevels.events ? powerLevels.events["m.room.message"] : null) || + powerLevels.events_default + ); + return member.powerLevel < levelToSend; + }, + + _calculateOpsPermissions: function() { + var can = { + kick: false, + ban: false, + mute: false + }; + var them = this.props.member; + var room = MatrixClientPeg.get().getRoom(this.props.member.roomId); + if (!room) { + console.error("No room found"); + return can; + } + var myUserId = MatrixClientPeg.get().credentials.userId; + var me = room.getMember(myUserId); + var powerLevels = room.currentState.getStateEvents( + "m.room.power_levels", "" + ); + if (powerLevels) { + powerLevels = powerLevels.getContent(); + } + else { + console.log("No power level event found in %s", room.roomId); + return can; // no power level event, don't allow anything. + } + var canAffectUser = them.powerLevel < me.powerLevel; + if (!canAffectUser) { + console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel); + return can; + } + var editPowerLevel = ( + (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) || + powerLevels.state_default + ); + can.kick = me.powerLevel >= powerLevels.kick; + can.ban = me.powerLevel >= powerLevels.ban; + can.mute = me.powerLevel >= editPowerLevel; + return can; } };