From a768b7fe4be1a2e112d09e9a225663a37ffc531b Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 18 Jan 2016 00:16:31 +0000
Subject: [PATCH 1/6] bring back power badges

---
 src/components/views/rooms/MemberTile.js | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js
index 4752c4d539..3f12add550 100644
--- a/src/components/views/rooms/MemberTile.js
+++ b/src/components/views/rooms/MemberTile.js
@@ -120,8 +120,18 @@ module.exports = React.createClass({
             //     var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
             //     power = <img src={ img } className="mx_MemberTile_power" width="44" height="44" alt=""/>;
             // }
-        }
 
+            var power;
+            if (this.props.member) {
+                var powerLevel = this.props.member.powerLevel;
+                if (powerLevel >= 50 && powerLevel < 99) {
+                    power = <img src="img/mod.svg" className="mx_MemberTile_power" width="16" height="17" alt="Mod"/>;
+                }
+                if (powerLevel >= 99) {
+                    power = <img src="img/admin.svg" className="mx_MemberTile_power" width="16" height="17" alt="Admin"/>;
+                }
+            }
+        }
 
         var mainClassName = "mx_MemberTile ";
         mainClassName += presenceClass;
@@ -159,6 +169,7 @@ module.exports = React.createClass({
                 <div className="mx_MemberTile_avatar">
                     <MemberAvatar member={this.props.member} width={36} height={36}
                         customDisplayName={this.props.customDisplayName} />
+                    { power }
                 </div>
                 { nameEl }
             </div>

From 430c90f4a4ba1481630aef7bd9d36360acd86b88 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Sun, 17 Jan 2016 23:58:38 +0000
Subject: [PATCH 2/6] oops ,forgot PowerSelector

---
 .../views/elements/PowerSelector.js           | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 src/components/views/elements/PowerSelector.js

diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js
new file mode 100644
index 0000000000..ad258a7522
--- /dev/null
+++ b/src/components/views/elements/PowerSelector.js
@@ -0,0 +1,102 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+'use strict';
+
+var React = require('react');
+
+var roles = {
+    0: 'User',
+    50: 'Moderator',
+    100: 'Admin',
+};
+
+var reverseRoles = {};
+Object.keys(roles).forEach(function(key) {
+    reverseRoles[roles[key]] = key;
+});
+
+module.exports = React.createClass({
+    displayName: 'PowerSelector',
+
+    propTypes: {
+        value: React.PropTypes.number.isRequired,
+        disabled: React.PropTypes.bool,
+        onChange: React.PropTypes.func,
+    },
+
+    getInitialState: function() {
+        return {
+            custom: (roles[this.props.value] === undefined),
+        };
+    },
+
+    onSelectChange: function(event) {
+        this.state.custom = (event.target.value === "Custom");
+        this.props.onChange(this.getValue());
+    },
+
+    onCustomChange: function(event) {
+        this.props.onChange(this.getValue());
+    },
+
+    getValue: function() {
+        var value;
+        if (this.refs.select) {
+            value = reverseRoles[ this.refs.select.value ];
+            if (this.refs.custom) {
+                if (value === undefined) value = parseInt( this.refs.custom.value );
+            }
+        }
+        return value;
+    },
+
+    render: function() {
+        var customPicker;
+        if (this.state.custom) {
+            var input;
+            if (this.props.disabled) {
+                input = <span>{ this.props.value }</span>
+            }
+            else {
+                input = <input ref="custom" type="text" size="3" defaultValue={ this.props.value } onChange={ this.onCustomChange } />
+            }
+            customPicker = <span> of { input }</span>;
+        }
+
+        var selectValue = roles[this.props.value] || "Custom";
+        var select;
+        if (this.props.disabled) {
+            select = <span>{ selectValue }</span>;
+        }
+        else {
+            select =
+                <select ref="select" defaultValue={ selectValue } onChange={ this.onSelectChange }>
+                    <option value="User">User (0)</option>
+                    <option value="Moderator">Moderator (50)</option>
+                    <option value="Admin">Admin (100)</option>
+                    <option value="Custom">Custom level</option>
+                </select>
+        }
+
+        return (
+            <span className="mx_PowerSelector">
+                { select }
+                { customPicker }
+            </span>
+        );
+    }
+});

From 8e1ab8e6b49f0043509637b2498274eb60e0884e Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 18 Jan 2016 01:18:02 +0000
Subject: [PATCH 3/6] vaguely skin MemberInfo correctly

---
 src/component-index.js                        | 11 +--
 .../views/elements/PowerSelector.js           | 10 ++-
 src/components/views/rooms/MemberInfo.js      | 88 ++++++++++++++-----
 src/components/views/rooms/MemberTile.js      |  5 +-
 4 files changed, 83 insertions(+), 31 deletions(-)

diff --git a/src/component-index.js b/src/component-index.js
index 7ae15ba12c..19daeffb9e 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -23,15 +23,15 @@ limitations under the License.
 
 module.exports.components = {};
 module.exports.components['structures.CreateRoom'] = require('./components/structures/CreateRoom');
-module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword');
-module.exports.components['structures.login.Login'] = require('./components/structures/login/Login');
-module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
-module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
 module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat');
 module.exports.components['structures.RoomView'] = require('./components/structures/RoomView');
 module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel');
 module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar');
 module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings');
+module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword');
+module.exports.components['structures.login.Login'] = require('./components/structures/login/Login');
+module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
+module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
 module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
 module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
 module.exports.components['views.create_room.CreateRoomButton'] = require('./components/views/create_room/CreateRoomButton');
@@ -41,6 +41,7 @@ module.exports.components['views.dialogs.ErrorDialog'] = require('./components/v
 module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
 module.exports.components['views.dialogs.QuestionDialog'] = require('./components/views/dialogs/QuestionDialog');
 module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText');
+module.exports.components['views.elements.PowerSelector'] = require('./components/views/elements/PowerSelector');
 module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar');
 module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg');
 module.exports.components['views.elements.UserSelector'] = require('./components/views/elements/UserSelector');
@@ -52,10 +53,10 @@ module.exports.components['views.login.LoginHeader'] = require('./components/vie
 module.exports.components['views.login.PasswordLogin'] = require('./components/views/login/PasswordLogin');
 module.exports.components['views.login.RegistrationForm'] = require('./components/views/login/RegistrationForm');
 module.exports.components['views.login.ServerConfig'] = require('./components/views/login/ServerConfig');
-module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent');
 module.exports.components['views.messages.MFileBody'] = require('./components/views/messages/MFileBody');
 module.exports.components['views.messages.MImageBody'] = require('./components/views/messages/MImageBody');
 module.exports.components['views.messages.MVideoBody'] = require('./components/views/messages/MVideoBody');
+module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent');
 module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody');
 module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent');
 module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody');
diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js
index ad258a7522..c47c9f3809 100644
--- a/src/components/views/elements/PowerSelector.js
+++ b/src/components/views/elements/PowerSelector.js
@@ -49,10 +49,16 @@ module.exports = React.createClass({
         this.props.onChange(this.getValue());
     },
 
-    onCustomChange: function(event) {
+    onCustomBlur: function(event) {
         this.props.onChange(this.getValue());
     },
 
+    onCustomKeyDown: function(event) {
+        if (event.key == "Enter") {
+            this.props.onChange(this.getValue());
+        }
+    },
+
     getValue: function() {
         var value;
         if (this.refs.select) {
@@ -72,7 +78,7 @@ module.exports = React.createClass({
                 input = <span>{ this.props.value }</span>
             }
             else {
-                input = <input ref="custom" type="text" size="3" defaultValue={ this.props.value } onChange={ this.onCustomChange } />
+                input = <input ref="custom" type="text" size="3" defaultValue={ this.props.value } onBlur={ this.onCustomBlur } onKeyDown={ this.onCustomKeyDown }/>
             }
             customPicker = <span> of { input }</span>;
         }
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index b5f0b88b40..c08ba38ab0 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -154,6 +154,7 @@ module.exports = React.createClass({
         }
         var defaultLevel = powerLevelEvent.getContent().users_default;
         var modLevel = me.powerLevel - 1;
+        if (modLevel > 50 && defaultLevel < 50) modLevel = 50; // try to stick with the vector level defaults
         // toggle the level
         var newLevel = this.state.isTargetMod ? defaultLevel : modLevel;
         MatrixClientPeg.get().setPowerLevel(roomId, target, newLevel, powerLevelEvent).done(
@@ -170,6 +171,36 @@ module.exports = React.createClass({
         this.props.onFinished();        
     },
 
+    onPowerChange: function(powerLevel) {
+        var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+        var roomId = this.props.member.roomId;
+        var target = this.props.member.userId;
+        var room = MatrixClientPeg.get().getRoom(roomId);
+        if (!room) {
+            this.props.onFinished();
+            return;
+        }
+        var powerLevelEvent = room.currentState.getStateEvents(
+            "m.room.power_levels", ""
+        );
+        if (!powerLevelEvent) {
+            this.props.onFinished();
+            return;
+        }
+        MatrixClientPeg.get().setPowerLevel(roomId, target, powerLevel, 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("Power change success");
+        }, function(err) {
+            Modal.createDialog(ErrorDialog, {
+                title: "Failure to change power level",
+                description: err.message
+            });
+        });
+        this.props.onFinished();        
+    },    
+
     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.
@@ -317,12 +348,11 @@ module.exports = React.createClass({
     },
 
     render: function() {
-        var interactButton, kickButton, banButton, muteButton, giveModButton, spinner;
-        if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) {
-            interactButton = <div className="mx_MemberInfo_field" onClick={this.onLeaveClick}>Leave room</div>;
-        }
-        else {
-            interactButton = <div className="mx_MemberInfo_field" onClick={this.onChatClick}>Start chat</div>;
+        var startChat, kickButton, banButton, muteButton, giveModButton, spinner;
+        if (this.props.member.userId !== MatrixClientPeg.get().credentials.userId) {
+            // FIXME: we're referring to a vector component from react-sdk
+            var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile');
+            startChat = <BottomLeftMenuTile collapsed={ false } img="img/create-big.svg" label="Start chat" onClick={ this.onChatClick }/>
         }
 
         if (this.state.creatingRoom) {
@@ -347,34 +377,52 @@ module.exports = React.createClass({
             </div>;
         }
         if (this.state.can.modifyLevel) {
-            var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
+            var giveOpLabel = this.state.isTargetMod ? "Revoke Moderator" : "Make Moderator";
             giveModButton = <div className="mx_MemberInfo_field" onClick={this.onModToggle}>
                 {giveOpLabel}
             </div>
         }
 
+        var adminTools;
+        if (kickButton || banButton || muteButton || giveModButton) {
+            adminTools = 
+                <div>
+                    <h3>Admin tools</h3>
+
+                    <div className="mx_MemberInfo_buttons">
+                        {muteButton}
+                        {kickButton}
+                        {banButton}
+                        {giveModButton}
+                    </div>
+                </div>
+        }
+
         var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
+        var PowerSelector = sdk.getComponent('elements.PowerSelector');
         return (
             <div className="mx_MemberInfo">
                 <img className="mx_MemberInfo_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.onCancel}/>
                 <div className="mx_MemberInfo_avatar">
                     <MemberAvatar member={this.props.member} width={48} height={48} />
                 </div>
+
                 <h2>{ this.props.member.name }</h2>
-                <div className="mx_MemberInfo_profileField">
-                    { this.props.member.userId }
-                </div>
-                <div className="mx_MemberInfo_profileField">
-                    power: { this.props.member.powerLevelNorm }%
-                </div>
-                <div className="mx_MemberInfo_buttons">
-                    {interactButton}
-                    {muteButton}
-                    {kickButton}
-                    {banButton}
-                    {giveModButton}
-                    {spinner}
+
+                <div className="mx_MemberInfo_profile">
+                    <div className="mx_MemberInfo_profileField">
+                        { this.props.member.userId }
+                    </div>
+                    <div className="mx_MemberInfo_profileField">
+                        Level: <b><PowerSelector value={ parseInt(this.props.member.powerLevel) } disabled={ !this.state.can.modifyLevel } onChange={ this.onPowerChange }/></b>
+                    </div>
                 </div>
+
+                { startChat }
+
+                { adminTools }
+
+                { spinner }
             </div>
         );
     }
diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js
index 3f12add550..f1b64efa42 100644
--- a/src/components/views/rooms/MemberTile.js
+++ b/src/components/views/rooms/MemberTile.js
@@ -83,10 +83,7 @@ module.exports = React.createClass({
         if (!this.props.member) {
             return this._getDisplayName();
         }
-        var label = this.props.member.userId;
-        if (this.state.isTargetMod) {
-            label += " - Mod (" + this.props.member.powerLevelNorm + "%)";
-        }
+        var label = this.props.member.userId + " (power " + this.props.member.powerLevel + ")";
         return label;
     },
 

From 6e3245a3b0a252fd278a4af92e0a0fea818fc29a Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 18 Jan 2016 01:26:15 +0000
Subject: [PATCH 4/6] make linkify userid jump to memberinfo

---
 src/components/structures/MatrixChat.js  | 10 ++++++++++
 src/components/views/rooms/MemberInfo.js |  3 +++
 2 files changed, 13 insertions(+)

diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index e5af2a86b5..23be8d6587 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -636,6 +636,8 @@ module.exports = React.createClass({
 
     onUserClick: function(event, userId) {
         event.preventDefault();
+
+        /*
         var MemberInfo = sdk.getComponent('rooms.MemberInfo');
         var member = new Matrix.RoomMember(null, userId);
         ContextualMenu.createMenu(MemberInfo, {
@@ -643,6 +645,14 @@ module.exports = React.createClass({
             right: window.innerWidth - event.pageX,
             top: event.pageY
         });
+        */
+
+        var member = new Matrix.RoomMember(null, userId);
+        if (!member) { return; }
+        dis.dispatch({
+            action: 'view_user',
+            member: member,
+        });        
     },
 
     onLogoutClick: function(event) {
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index c08ba38ab0..d78fc5a005 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -383,6 +383,9 @@ module.exports = React.createClass({
             </div>
         }
 
+        // TODO: we should have an invite button if this MemberInfo is showing a user who isn't actually in the current room yet
+        // e.g. clicking on a linkified userid in a room
+
         var adminTools;
         if (kickButton || banButton || muteButton || giveModButton) {
             adminTools = 

From 8f83621c4cb4ae7decf4c64087cf1ca20d3e9b70 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 18 Jan 2016 01:40:19 +0000
Subject: [PATCH 5/6] hide Mod button for muted users

---
 src/components/views/rooms/MemberInfo.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index d78fc5a005..b601798f3e 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -322,9 +322,15 @@ module.exports = React.createClass({
             (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
             powerLevels.state_default
         );
+        var levelToSend = (
+            (powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
+            powerLevels.events_default
+        );
+
         can.kick = me.powerLevel >= powerLevels.kick;
         can.ban = me.powerLevel >= powerLevels.ban;
         can.mute = me.powerLevel >= editPowerLevel;
+        can.toggleMod = me.powerLevel > them.powerLevel && them.powerLevel >= levelToSend;
         can.modifyLevel = me.powerLevel > them.powerLevel;
         return can;
     },
@@ -376,7 +382,7 @@ module.exports = React.createClass({
                 {muteLabel}
             </div>;
         }
-        if (this.state.can.modifyLevel) {
+        if (this.state.can.toggleMod) {
             var giveOpLabel = this.state.isTargetMod ? "Revoke Moderator" : "Make Moderator";
             giveModButton = <div className="mx_MemberInfo_field" onClick={this.onModToggle}>
                 {giveOpLabel}

From aa412bed0f7303c8ca6cbe79980cf78a19da5591 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Mon, 18 Jan 2016 13:38:40 +0000
Subject: [PATCH 6/6] reindent promises as per dave's PR feedback

---
 src/components/views/rooms/MemberInfo.js | 134 ++++++++++++-----------
 1 file changed, 71 insertions(+), 63 deletions(-)

diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index b601798f3e..a8a601c2d6 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -58,15 +58,16 @@ module.exports = React.createClass({
         var roomId = this.props.member.roomId;
         var target = this.props.member.userId;
         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
-            });
-        });
+                // 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
+                });
+            }
+        );
         this.props.onFinished();
     },
 
@@ -74,16 +75,18 @@ module.exports = React.createClass({
         var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
         var roomId = this.props.member.roomId;
         var target = this.props.member.userId;
-        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
-            });
-        });
+        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
+                });
+            }
+        );
         this.props.onFinished();
     },
 
@@ -118,16 +121,17 @@ module.exports = React.createClass({
         }
 
         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
-            });
-        });
+            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
+                });
+            }
+        );
         this.props.onFinished();        
     },
 
@@ -158,16 +162,17 @@ module.exports = React.createClass({
         // 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
-            });
-        });
+            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
+                });
+            }
+        );
         this.props.onFinished();        
     },
 
@@ -188,16 +193,17 @@ module.exports = React.createClass({
             return;
         }
         MatrixClientPeg.get().setPowerLevel(roomId, target, powerLevel, 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("Power change success");
-        }, function(err) {
-            Modal.createDialog(ErrorDialog, {
-                title: "Failure to change power level",
-                description: err.message
-            });
-        });
+            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("Power change success");
+            }, function(err) {
+                Modal.createDialog(ErrorDialog, {
+                    title: "Failure to change power level",
+                    description: err.message
+                });
+            }
+        );
         this.props.onFinished();        
     },    
 
@@ -240,20 +246,22 @@ module.exports = React.createClass({
             MatrixClientPeg.get().createRoom({
                 invite: [this.props.member.userId],
                 preset: "private_chat"
-            }).done(function(res) {
-                self.setState({ creatingRoom: false });
-                dis.dispatch({
-                    action: 'view_room',
-                    room_id: res.room_id
-                });
-                self.props.onFinished();
-            }, function(err) {
-                self.setState({ creatingRoom: false });
-                console.error(
-                    "Failed to create room: %s", JSON.stringify(err)
-                );
-                self.props.onFinished();
-            });
+            }).done(
+                function(res) {
+                    self.setState({ creatingRoom: false });
+                    dis.dispatch({
+                        action: 'view_room',
+                        room_id: res.room_id
+                    });
+                    self.props.onFinished();
+                }, function(err) {
+                    self.setState({ creatingRoom: false });
+                    console.error(
+                        "Failed to create room: %s", JSON.stringify(err)
+                    );
+                    self.props.onFinished();
+                }
+            );
         }
     },