Make MemberAvatar and MemberTile work without RoomMember objects

pull/21833/head
Kegan Dougal 2016-01-13 16:55:28 +00:00
parent c0e5d1d13b
commit 8c9352c484
3 changed files with 98 additions and 78 deletions

View File

@ -24,10 +24,16 @@ module.exports = React.createClass({
displayName: 'MemberAvatar', displayName: 'MemberAvatar',
propTypes: { propTypes: {
member: React.PropTypes.object.isRequired, member: React.PropTypes.object,
width: React.PropTypes.number, width: React.PropTypes.number,
height: React.PropTypes.number, height: React.PropTypes.number,
resizeMethod: React.PropTypes.string, resizeMethod: React.PropTypes.string,
/**
* The custom display name to use for this member. This can serve as a
* drop in replacement for RoomMember objects, or as a clobber name on
* an existing RoomMember. Used for 3pid invites.
*/
customDisplayName: React.PropTypes.string
}, },
getDefaultProps: function() { getDefaultProps: function() {
@ -38,64 +44,68 @@ module.exports = React.createClass({
} }
}, },
getInitialState: function() {
var defaultImageUrl = Avatar.defaultAvatarUrlForString(
this.props.customDisplayName || this.props.member.userId
)
return {
imageUrl: this._getMemberImageUrl() || defaultImageUrl,
defaultImageUrl: defaultImageUrl
};
},
componentWillReceiveProps: function(nextProps) { componentWillReceiveProps: function(nextProps) {
this.refreshUrl(); this.refreshUrl();
}, },
defaultAvatarUrl: function(member, width, height, resizeMethod) {
return Avatar.defaultAvatarUrlForString(member.userId);
},
onError: function(ev) { onError: function(ev) {
// don't tightloop if the browser can't load a data url // don't tightloop if the browser can't load a data url
if (ev.target.src == this.defaultAvatarUrl(this.props.member)) { if (ev.target.src == this.state.defaultImageUrl) {
return; return;
} }
this.setState({ this.setState({
imageUrl: this.defaultAvatarUrl(this.props.member) imageUrl: this.state.defaultImageUrl
}); });
}, },
_computeUrl: function() { _getMemberImageUrl: function() {
if (!this.props.member) { return null; }
return Avatar.avatarUrlForMember(this.props.member, return Avatar.avatarUrlForMember(this.props.member,
this.props.width, this.props.width,
this.props.height, this.props.height,
this.props.resizeMethod); this.props.resizeMethod);
}, },
_getInitialLetter: function() {
var name = this.props.customDisplayName || this.props.member.name;
var initial = name[0];
if (initial === '@' && name[1]) {
initial = name[1];
}
return initial.toUpperCase();
},
refreshUrl: function() { refreshUrl: function() {
var newUrl = this._computeUrl(); var newUrl = this._getMemberImageUrl();
if (newUrl != this.currentUrl) { if (newUrl != this.currentUrl) {
this.currentUrl = newUrl; this.currentUrl = newUrl;
this.setState({imageUrl: newUrl}); this.setState({imageUrl: newUrl});
} }
}, },
getInitialState: function() {
return {
imageUrl: this._computeUrl()
};
},
///////////////
render: function() { render: function() {
// XXX: recalculates default avatar url constantly var name = this.props.customDisplayName || this.props.member.name;
if (this.state.imageUrl === this.defaultAvatarUrl(this.props.member)) {
var initial; if (this.state.imageUrl === this.state.defaultImageUrl) {
if (this.props.member.name[0]) var initialLetter = this._getInitialLetter();
initial = this.props.member.name[0].toUpperCase();
if (initial === '@' && this.props.member.name[1])
initial = this.props.member.name[1].toUpperCase();
return ( return (
<span className="mx_MemberAvatar" {...this.props}> <span className="mx_MemberAvatar" {...this.props}>
<span className="mx_MemberAvatar_initial" aria-hidden="true" <span className="mx_MemberAvatar_initial" aria-hidden="true"
style={{ fontSize: (this.props.width * 0.65) + "px", style={{ fontSize: (this.props.width * 0.65) + "px",
width: this.props.width + "px", width: this.props.width + "px",
lineHeight: this.props.height + "px" }}>{ initial }</span> lineHeight: this.props.height + "px" }}>{ initialLetter }</span>
<img className="mx_MemberAvatar_image" src={this.state.imageUrl} title={this.props.member.name} <img className="mx_MemberAvatar_image" src={this.state.imageUrl} title={name}
onError={this.onError} width={this.props.width} height={this.props.height} /> onError={this.onError} width={this.props.width} height={this.props.height} />
</span> </span>
); );
@ -104,9 +114,8 @@ module.exports = React.createClass({
<img className="mx_MemberAvatar mx_MemberAvatar_image" src={this.state.imageUrl} <img className="mx_MemberAvatar mx_MemberAvatar_image" src={this.state.imageUrl}
onError={this.onError} onError={this.onError}
width={this.props.width} height={this.props.height} width={this.props.width} height={this.props.height}
title={this.props.member.name} title={name}
{...this.props} {...this.props} />
/>
); );
} }
}); });

View File

@ -15,6 +15,7 @@ limitations under the License.
*/ */
var React = require('react'); var React = require('react');
var classNames = require('classnames'); var classNames = require('classnames');
var Matrix = require("matrix-js-sdk");
var MatrixClientPeg = require("../../../MatrixClientPeg"); var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal"); var Modal = require("../../../Modal");
var sdk = require('../../../index'); var sdk = require('../../../index');
@ -253,14 +254,12 @@ module.exports = React.createClass({
// already added them. // already added them.
var memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey()); var memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey());
if (memberEvent) { if (memberEvent) {
console.log("Found match => %s", memberEvent.getStateKey());
return; return;
} }
console.log("Display match => ");
/*
memberList.push( memberList.push(
<MemberTile key={e.getStateKey()} ref={e.getStateKey()} member={}/> <MemberTile key={e.getStateKey()} ref={e.getStateKey()}
) */ customDisplayName={e.getContent().display_name} />
)
}) })
} }
} }

View File

@ -27,24 +27,18 @@ module.exports = React.createClass({
displayName: 'MemberTile', displayName: 'MemberTile',
propTypes: { propTypes: {
member: React.PropTypes.any.isRequired, // RoomMember member: React.PropTypes.any, // RoomMember
onFinished: React.PropTypes.func onFinished: React.PropTypes.func,
customDisplayName: React.PropTypes.string // for 3pid invites
}, },
getInitialState: function() { getInitialState: function() {
return {}; return {};
}, },
onLeaveClick: function() {
dis.dispatch({
action: 'leave_room',
room_id: this.props.member.roomId,
});
this.props.onFinished();
},
shouldComponentUpdate: function(nextProps, nextState) { shouldComponentUpdate: function(nextProps, nextState) {
if (this.state.hover !== nextState.hover) return true; if (this.state.hover !== nextState.hover) return true;
if (!this.props.member) { return false; } // e.g. 3pid members
if ( if (
this.member_last_modified_time === undefined || this.member_last_modified_time === undefined ||
this.member_last_modified_time < nextProps.member.getLastModifiedTime() this.member_last_modified_time < nextProps.member.getLastModifiedTime()
@ -70,13 +64,25 @@ module.exports = React.createClass({
}, },
onClick: function(e) { onClick: function(e) {
if (!this.props.member) { return; } // e.g. 3pid members
dis.dispatch({ dis.dispatch({
action: 'view_user', action: 'view_user',
member: this.props.member, member: this.props.member,
}); });
}, },
_getDisplayName: function() {
if (this.props.customDisplayName) {
return this.props.customDisplayName;
}
return this.props.member.name;
},
getPowerLabel: function() { getPowerLabel: function() {
if (!this.props.member) {
return this._getDisplayName();
}
var label = this.props.member.userId; var label = this.props.member.userId;
if (this.state.isTargetMod) { if (this.state.isTargetMod) {
label += " - Mod (" + this.props.member.powerLevelNorm + "%)"; label += " - Mod (" + this.props.member.powerLevelNorm + "%)";
@ -85,68 +91,74 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); var member = this.props.member;
var isMyUser = false;
this.member_last_modified_time = this.props.member.getLastModifiedTime(); var name = this._getDisplayName();
if (this.props.member.user) { var active = -1;
this.user_last_modified_time = this.props.member.user.getLastModifiedTime();
}
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
var power;
// if (this.props.member && this.props.member.powerLevelNorm > 0) {
// 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 presenceClass = "mx_MemberTile_offline"; var presenceClass = "mx_MemberTile_offline";
var mainClassName = "mx_MemberTile ";
if (this.props.member.user) { if (member) {
if (this.props.member.user.presence === "online") { if (member.user) {
presenceClass = "mx_MemberTile_online"; this.user_last_modified_time = member.user.getLastModifiedTime();
}
else if (this.props.member.user.presence === "unavailable") { // FIXME: make presence data update whenever User.presence changes...
presenceClass = "mx_MemberTile_unavailable"; active = (
(Date.now() - (member.user.lastPresenceTs - member.user.lastActiveAgo)) || -1
);
if (member.user.presence === "online") {
presenceClass = "mx_MemberTile_online";
}
else if (member.user.presence === "unavailable") {
presenceClass = "mx_MemberTile_unavailable";
}
} }
this.member_last_modified_time = member.getLastModifiedTime();
isMyUser = MatrixClientPeg.get().credentials.userId == member.userId;
// if (this.props.member && this.props.member.powerLevelNorm > 0) {
// 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 mainClassName = "mx_MemberTile ";
mainClassName += presenceClass; mainClassName += presenceClass;
if (this.state.hover) { if (this.state.hover) {
mainClassName += " mx_MemberTile_hover"; mainClassName += " mx_MemberTile_hover";
} }
var name = this.props.member.name;
// 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 nameEl; var nameEl;
if (this.state.hover) { if (this.state.hover) {
// FIXME: make presence data update whenever User.presence changes... var presenceState = (member && member.user) ? member.user.presence : null;
var active = this.props.member.user ? ((Date.now() - (this.props.member.user.lastPresenceTs - this.props.member.user.lastActiveAgo)) || -1) : -1; var PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
nameEl = ( nameEl = (
<div className="mx_MemberTile_details"> <div className="mx_MemberTile_details">
<img className="mx_MemberTile_chevron" src="img/member_chevron.png" width="8" height="12"/> <img className="mx_MemberTile_chevron" src="img/member_chevron.png" width="8" height="12"/>
<div className="mx_MemberTile_userId">{ name }</div> <div className="mx_MemberTile_userId">{ name }</div>
<PresenceLabel activeAgo={active} <PresenceLabel activeAgo={active}
presenceState={this.props.member.user ? this.props.member.user.presence : null} /> presenceState={presenceState} />
</div> </div>
); );
} }
else { else {
nameEl = nameEl = (
<div className="mx_MemberTile_name"> <div className="mx_MemberTile_name">
{ name } { name }
</div> </div>
);
} }
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
return ( return (
<div className={mainClassName} title={ this.getPowerLabel() } <div className={mainClassName} title={ this.getPowerLabel() }
onClick={ this.onClick } onMouseEnter={ this.mouseEnter } onClick={ this.onClick } onMouseEnter={ this.mouseEnter }
onMouseLeave={ this.mouseLeave }> onMouseLeave={ this.mouseLeave }>
<div className="mx_MemberTile_avatar"> <div className="mx_MemberTile_avatar">
<MemberAvatar member={this.props.member} width={36} height={36} /> <MemberAvatar member={this.props.member} width={36} height={36}
{ power } customDisplayName={this.props.customDisplayName} />
</div> </div>
{ nameEl } { nameEl }
</div> </div>