diff --git a/skins/base/views/molecules/MemberInfo.js b/skins/base/views/molecules/MemberInfo.js
index 3a81185ba9..8e1e383f69 100644
--- a/skins/base/views/molecules/MemberInfo.js
+++ b/skins/base/views/molecules/MemberInfo.js
@@ -67,7 +67,35 @@ module.exports = React.createClass({
if (this.state.active >= 0) {
activeAgo = this.getDuration(this.state.active);
}
+ var kickButton, banButton, muteButton, giveModButton;
+ 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}
+
;
+ }
+ if (this.state.can.modifyLevel) {
+ var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
+ giveModButton =
+ {giveOpLabel}
+
+ }
+ var opLabel;
+ if (this.state.isTargetMod) {
+ var level = this.props.member.powerLevelNorm + "%";
+ opLabel = Moderator ({level})
+ }
return (
@@ -78,9 +106,14 @@ module.exports = React.createClass({
width="128" height="128" alt=""/>
{this.props.member.userId}
+ {opLabel}
Presence: {this.state.presence}
Last active: {activeAgo}
Start chat
+ {muteButton}
+ {kickButton}
+ {banButton}
+ {giveModButton}
);
}
diff --git a/src/controllers/molecules/MemberInfo.js b/src/controllers/molecules/MemberInfo.js
index 96f8bb8ca8..4f222c5dbf 100644
--- a/src/controllers/molecules/MemberInfo.js
+++ b/src/controllers/molecules/MemberInfo.js
@@ -18,15 +18,27 @@ 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,
+ * modifyLevel: boolean
+ * },
+ * 'muted': boolean,
+ * 'isTargetMod': 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 +52,145 @@ 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;
}
- this.setState({
- presence: usr.presence,
- active: usr.lastActiveAgo
- });
+ // 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(self._calculateOpsPermissions());
+ }
+ 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) || {};
+ var memberState = this._calculateOpsPermissions();
+ memberState.presence = usr.presence || "offline";
+ memberState.active = usr.lastActiveAgo || -1;
+ this.setState(memberState);
}
},
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
+ });
+ });
+ },
+
+ onModToggle: function() {
+ var roomId = this.props.member.roomId;
+ var target = this.props.member.userId;
+ var room = MatrixClientPeg.get().getRoom(roomId);
+ if (!room) {
+ return;
+ }
+ var powerLevelEvent = room.currentState.getStateEvents(
+ "m.room.power_levels", ""
+ );
+ if (!powerLevelEvent) {
+ return;
+ }
+ var me = room.getMember(MatrixClientPeg.get().credentials.userId);
+ if (!me) {
+ return;
+ }
+ var defaultLevel = powerLevelEvent.getContent().users_default;
+ var modLevel = me.powerLevel - 1;
+ // toggle the level
+ var newLevel = this.state.isTargetMod ? defaultLevel : modLevel;
+ MatrixClientPeg.get().setPowerLevel(roomId, target, newLevel, 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("Mod toggle success");
+ }, function(err) {
+ Modal.createDialog(ErrorDialog, {
+ title: "Mod error",
+ description: err.message
+ });
+ });
},
onChatClick: function() {
@@ -108,8 +245,77 @@ module.exports = {
getInitialState: function() {
return {
presence: "offline",
- active: -1
+ active: -1,
+ can: {
+ kick: false,
+ ban: false,
+ mute: false,
+ modifyLevel: false
+ },
+ muted: false,
+ isTargetMod: false
}
+ },
+
+ _calculateOpsPermissions: function() {
+ var defaultPerms = {
+ can: {},
+ muted: false,
+ modifyLevel: false
+ };
+ var room = MatrixClientPeg.get().getRoom(this.props.member.roomId);
+ if (!room) {
+ return defaultPerms;
+ }
+ var powerLevels = room.currentState.getStateEvents(
+ "m.room.power_levels", ""
+ );
+ if (!powerLevels) {
+ return defaultPerms;
+ }
+ var me = room.getMember(MatrixClientPeg.get().credentials.userId);
+ var them = this.props.member;
+ return {
+ can: this._calculateCanPermissions(
+ me, them, powerLevels.getContent()
+ ),
+ muted: this._isMuted(them, powerLevels.getContent()),
+ isTargetMod: them.powerLevel > powerLevels.getContent().users_default
+ };
+ },
+
+ _calculateCanPermissions: function(me, them, powerLevels) {
+ var can = {
+ kick: false,
+ ban: false,
+ mute: false,
+ modifyLevel: false
+ };
+ 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;
+ can.modifyLevel = me.powerLevel > them.powerLevel;
+ return can;
+ },
+
+ _isMuted: function(member, powerLevelContent) {
+ if (!powerLevelContent || !member) {
+ return false;
+ }
+ var levelToSend = (
+ (powerLevelContent.events ? powerLevelContent.events["m.room.message"] : null) ||
+ powerLevelContent.events_default
+ );
+ return member.powerLevel < levelToSend;
}
};