From e3798e1b8572693f1471fe9f544b8470a85613b7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 15 Aug 2015 03:06:21 +0100 Subject: [PATCH] WIP fixing up the member list - just needs CSS and testing --- skins/base/img/edit.png | Bin 0 -> 498 bytes skins/base/views/molecules/MemberTile.js | 61 +++++- skins/base/views/organisms/RoomView.js | 2 +- src/controllers/molecules/MemberTile.js | 245 ++++++++++++++++++++++- src/controllers/organisms/MemberList.js | 4 +- 5 files changed, 296 insertions(+), 16 deletions(-) create mode 100644 skins/base/img/edit.png diff --git a/skins/base/img/edit.png b/skins/base/img/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..2686885f792ed69bdb88518bfe6850301719e7f0 GIT binary patch literal 498 zcmVPx$tVu*cR5%f(l)+BIKoEwfO92Z+Lr{4D6%!NV(UTtZDbRyF8YUWIV)P00Y@!!0 zCIz`G2_n)KOUu^%*OY950?|3N&CYy(XJ%(tqbXK1t>d$G^MmkYTF;*(&li78Hv5fv3;`jlz`kdE0p6&6#bX@=BbZ4cZ^78wt z-aEW*THVIyV8}TS;tW2-Ahu?H9YlnX2^?>>AIDFuBNhx;h%r1+&R!vvD`rF|W|-{- z^w9DMWg->~SRgtYi=J#O>rymMr{{4xZ+uhR4M-Dh+h0o(7_;M-puw;^XsiZrjHx24 zOB(Iwbr}f;EHoIl7`qdrLE|E4n+AnD2}ZI)L&uz`4pk5$RZ-4|C9N}SnrPL|AK~CFgUys : null; var nameClass = "mx_MemberTile_name"; @@ -110,6 +118,41 @@ module.exports = React.createClass({ nameClass += " mx_MemberTile_zalgo"; } + var menu; + if (this.state.menu) { + 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} +
+ } + menu =
+ +
Chat
+ {muteButton} + {kickButton} + {banButton} + {giveModButton} +
; + } + var nameEl; if (this.state.hover) { var presence; @@ -122,11 +165,10 @@ module.exports = React.createClass({ presence =
{ this.getPrettyPresence(this.props.member.user) }
; } - // nameEl =
{ leave } -
{ this.props.member.userId }
+
{ this.props.member.userId }
{ presence }
} @@ -138,7 +180,8 @@ module.exports = React.createClass({ } return ( -
+
+ { menu }
{ power } diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index b6bfedf190..eff25d965a 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -72,7 +72,7 @@ module.exports = React.createClass({ if (!this.state.numUnreadMessages) { return ""; } - return this.state.numUnreadMessages + " new messages"; + return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : ""); }, scrollToBottom: function() { diff --git a/src/controllers/molecules/MemberTile.js b/src/controllers/molecules/MemberTile.js index ae4682847a..19c08731c8 100644 --- a/src/controllers/molecules/MemberTile.js +++ b/src/controllers/molecules/MemberTile.js @@ -25,13 +25,169 @@ var Loader = require("react-loader"); var MatrixClientPeg = require("../../MatrixClientPeg"); module.exports = { - onClick: function() { - dis.dispatch({ - action: 'view_user', - user_id: this.props.member.userId + // onClick: function() { + // dis.dispatch({ + // action: 'view_user', + // user_id: this.props.member.userId + // }); + // }, + + 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() { + // check if there are any existing rooms with just us and them (1:1) + // If so, just view that room. If not, create a private room with them. + var rooms = MatrixClientPeg.get().getRooms(); + var userIds = [ + this.props.member.userId, + MatrixClientPeg.get().credentials.userId + ]; + var existingRoomId = null; + for (var i = 0; i < rooms.length; i++) { + var members = rooms[i].getJoinedMembers(); + if (members.length === 2) { + var hasTargetUsers = true; + for (var j = 0; j < members.length; j++) { + if (userIds.indexOf(members[j].userId) === -1) { + hasTargetUsers = false; + break; + } + } + if (hasTargetUsers) { + existingRoomId = rooms[i].roomId; + break; + } + } + } + + if (existingRoomId) { + dis.dispatch({ + action: 'view_room', + room_id: existingRoomId + }); + } + else { + MatrixClientPeg.get().createRoom({ + invite: [this.props.member.userId], + preset: "private_chat" + }).done(function(res) { + dis.dispatch({ + action: 'view_room', + room_id: res.room_id + }); + }, function(err) { + console.error( + "Failed to create room: %s", JSON.stringify(err) + ); + }); + } + }, + onLeaveClick: function() { var roomId = this.props.member.roomId; Modal.createDialog(QuestionDialog, { @@ -56,5 +212,84 @@ module.exports = { } } }); - } + }, + + getInitialState: function() { + return { + hover: false, + menu: false, + + // presence: "offline", + // 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; + }, }; diff --git a/src/controllers/organisms/MemberList.js b/src/controllers/organisms/MemberList.js index 912b142a13..3eef007ed4 100644 --- a/src/controllers/organisms/MemberList.js +++ b/src/controllers/organisms/MemberList.js @@ -61,7 +61,9 @@ module.exports = { function updateUserState(event, user) { var tile = self.refs[user.userId]; if (tile) { - tile.forceUpdate(); + // update the whole list to get the order right, not just this cell... + self.forceUpdate(); + // tile.forceUpdate(); } } MatrixClientPeg.get().on("User.presence", updateUserState);