WIP fixing up the member list - just needs CSS and testing
This commit is contained in:
parent
80c3b2c8a3
commit
e3798e1b85
Binary file not shown.
After Width: | Height: | Size: 498 B |
|
@ -34,11 +34,6 @@ module.exports = React.createClass({
|
||||||
displayName: 'MemberTile',
|
displayName: 'MemberTile',
|
||||||
mixins: [MemberTileController],
|
mixins: [MemberTileController],
|
||||||
|
|
||||||
// XXX: should these be in the controller?
|
|
||||||
getInitialState: function() {
|
|
||||||
return { 'hover': false };
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseEnter: function(e) {
|
mouseEnter: function(e) {
|
||||||
this.setState({ 'hover': true });
|
this.setState({ 'hover': true });
|
||||||
},
|
},
|
||||||
|
@ -47,6 +42,11 @@ module.exports = React.createClass({
|
||||||
this.setState({ 'hover': false });
|
this.setState({ 'hover': false });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onClick: function(e) {
|
||||||
|
this.setState({ 'menu': true });
|
||||||
|
this.setState(this._calculateOpsPermissions());
|
||||||
|
},
|
||||||
|
|
||||||
getDuration: function(time) {
|
getDuration: function(time) {
|
||||||
if (!time) return;
|
if (!time) return;
|
||||||
var t = parseInt(time / 1000);
|
var t = parseInt(time / 1000);
|
||||||
|
@ -78,6 +78,14 @@ module.exports = React.createClass({
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPowerLabel: function() {
|
||||||
|
var label = this.props.member.userId;
|
||||||
|
if (this.state.isTargetMod) {
|
||||||
|
label += " - Mod (" + this.props.member.powerLevelNorm + "%)";
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
||||||
|
|
||||||
|
@ -102,7 +110,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = this.props.member.name;
|
var name = this.props.member.name;
|
||||||
if (isMyUser) name += " (me)";
|
// if (isMyUser) name += " (me)"; // this does nothing other than introduce line wrapping and pain
|
||||||
var leave = isMyUser ? <img className="mx_MemberTile_leave" src="img/delete.png" width="10" height="10" onClick={this.onLeaveClick}/> : null;
|
var leave = isMyUser ? <img className="mx_MemberTile_leave" src="img/delete.png" width="10" height="10" onClick={this.onLeaveClick}/> : null;
|
||||||
|
|
||||||
var nameClass = "mx_MemberTile_name";
|
var nameClass = "mx_MemberTile_name";
|
||||||
|
@ -110,6 +118,41 @@ module.exports = React.createClass({
|
||||||
nameClass += " mx_MemberTile_zalgo";
|
nameClass += " mx_MemberTile_zalgo";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var menu;
|
||||||
|
if (this.state.menu) {
|
||||||
|
var kickButton, banButton, muteButton, giveModButton;
|
||||||
|
if (this.state.can.kick) {
|
||||||
|
kickButton = <div className="mx_MemberTile_menuItem" onClick={this.onKick}>
|
||||||
|
Kick
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
if (this.state.can.ban) {
|
||||||
|
banButton = <div className="mx_MemberTile_menuItem" onClick={this.onBan}>
|
||||||
|
Ban
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
if (this.state.can.mute) {
|
||||||
|
var muteLabel = this.state.muted ? "Unmute" : "Mute";
|
||||||
|
muteButton = <div className="mx_MemberTile_menuItem" onClick={this.onMuteToggle}>
|
||||||
|
{muteLabel}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
if (this.state.can.modifyLevel) {
|
||||||
|
var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
|
||||||
|
giveModButton = <div className="mx_MemberTile_menuItem" onClick={this.onModToggle}>
|
||||||
|
{giveOpLabel}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
menu = <div className="mx_MemberTile_menu">
|
||||||
|
<img className="mx_MemberTile_chevron" src="img/chevron-right.png" width="9" height="16" />
|
||||||
|
<div className="mx_MemberTile_menuItem" onClick={this.onChatClick}>Chat</div>
|
||||||
|
{muteButton}
|
||||||
|
{kickButton}
|
||||||
|
{banButton}
|
||||||
|
{giveModButton}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
var nameEl;
|
var nameEl;
|
||||||
if (this.state.hover) {
|
if (this.state.hover) {
|
||||||
var presence;
|
var presence;
|
||||||
|
@ -122,11 +165,10 @@ module.exports = React.createClass({
|
||||||
presence = <div className="mx_MemberTile_presence">{ this.getPrettyPresence(this.props.member.user) }</div>;
|
presence = <div className="mx_MemberTile_presence">{ this.getPrettyPresence(this.props.member.user) }</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// <MemberInfo member={this.props.member} />
|
|
||||||
nameEl =
|
nameEl =
|
||||||
<div className="mx_MemberTile_details">
|
<div className="mx_MemberTile_details">
|
||||||
{ leave }
|
{ leave }
|
||||||
<div className="mx_MemberTile_userId" title={ this.props.member.userId }>{ this.props.member.userId }</div>
|
<div className="mx_MemberTile_userId">{ this.props.member.userId }</div>
|
||||||
{ presence }
|
{ presence }
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -138,7 +180,8 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={mainClassName} onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
|
<div className={mainClassName} title={ this.getPowerLabel } onClick={ this.onClick } onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
|
||||||
|
{ menu }
|
||||||
<div className="mx_MemberTile_avatar">
|
<div className="mx_MemberTile_avatar">
|
||||||
<MemberAvatar member={this.props.member} />
|
<MemberAvatar member={this.props.member} />
|
||||||
{ power }
|
{ power }
|
||||||
|
|
|
@ -72,7 +72,7 @@ module.exports = React.createClass({
|
||||||
if (!this.state.numUnreadMessages) {
|
if (!this.state.numUnreadMessages) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return this.state.numUnreadMessages + " new messages";
|
return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : "");
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollToBottom: function() {
|
scrollToBottom: function() {
|
||||||
|
|
|
@ -25,11 +25,167 @@ var Loader = require("react-loader");
|
||||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
onClick: function() {
|
// onClick: function() {
|
||||||
dis.dispatch({
|
// dis.dispatch({
|
||||||
action: 'view_user',
|
// action: 'view_user',
|
||||||
user_id: this.props.member.userId
|
// 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() {
|
onLeaveClick: function() {
|
||||||
|
@ -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;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,7 +61,9 @@ module.exports = {
|
||||||
function updateUserState(event, user) {
|
function updateUserState(event, user) {
|
||||||
var tile = self.refs[user.userId];
|
var tile = self.refs[user.userId];
|
||||||
if (tile) {
|
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);
|
MatrixClientPeg.get().on("User.presence", updateUserState);
|
||||||
|
|
Loading…
Reference in New Issue