Get user pill profile remote data

Instead of relying on the local avatar/displayname of a user, request the data from the server and update the pill if it shows up.

This required a slight refactor which means we're not doing everything in `render` now. Also I noticed unknown rooms weren't being rendered _at all_! So now you get something that looks like a normal link but with the room alias/ID in it.
This commit is contained in:
Luke Barnard 2017-07-24 17:18:29 +01:00
parent eb0575d9c1
commit 61c16569cb

View File

@ -28,7 +28,7 @@ const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
// HttpUtils transformTags to relative links
const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room)\/(([\#\!\@\+]).*)$/;
export default React.createClass({
const Pill = React.createClass({
statics: {
isPillUrl: (url) => {
return !!REGEX_MATRIXTO.exec(url);
@ -36,6 +36,8 @@ export default React.createClass({
isMessagePillUrl: (url) => {
return !!REGEX_LOCAL_MATRIXTO.exec(url);
},
TYPE_USER_MENTION: 'TYPE_USER_MENTION',
TYPE_ROOM_MENTION: 'TYPE_ROOM_MENTION',
},
props: {
@ -47,10 +49,21 @@ export default React.createClass({
room: PropTypes.instanceOf(Room),
},
render: function() {
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
getInitialState() {
return {
// ID/alias of the room/user
resourceId: null,
// Type of pill
pillType: null,
// The member related to the user pill
member: null,
// The room related to the room pill
room: null,
};
},
componentWillMount() {
let regex = REGEX_MATRIXTO;
if (this.props.inMessage) {
regex = REGEX_LOCAL_MATRIXTO;
@ -60,46 +73,102 @@ export default React.createClass({
// resource and prefix will be undefined instead of throwing
const matrixToMatch = regex.exec(this.props.url) || [];
const resource = matrixToMatch[1]; // The room/user ID
const resourceId = matrixToMatch[1]; // The room/user ID
const prefix = matrixToMatch[2]; // The first character of prefix
// Default to the room/user ID
let linkText = resource;
const pillType = {
'@': Pill.TYPE_USER_MENTION,
'#': Pill.TYPE_ROOM_MENTION,
'!': Pill.TYPE_ROOM_MENTION,
}[prefix];
const isUserPill = prefix === '@';
const isRoomPill = prefix === '#' || prefix === '!';
let member;
let room;
switch (pillType) {
case Pill.TYPE_USER_MENTION: {
const localMember = this.props.room.getMember(resourceId);
member = localMember;
if (!localMember) {
member = new RoomMember(null, resourceId);
this.doProfileLookup(resourceId, member);
}
}
break;
case Pill.TYPE_ROOM_MENTION: {
const localRoom = resourceId[0] === '#' ?
MatrixClientPeg.get().getRooms().find((r) => {
return r.getAliases().includes(resourceId);
}) : MatrixClientPeg.get().getRoom(resourceId);
room = localRoom;
if (!localRoom) {
// TODO: This would require a new API to resolve a room alias to
// a room avatar and name.
// this.doRoomProfileLookup(resourceId, member);
}
}
break;
}
this.setState({resourceId, pillType, member, room});
},
doProfileLookup: function(userId, member) {
MatrixClientPeg.get().getProfileInfo(userId).done((resp) => {
member.name = resp.displayname;
member.rawDisplayName = resp.displayname;
member.events.member = {
getContent: () => {
return {avatar_url: resp.avatar_url};
},
};
this.setState({member});
}, (err) => {
console.error(
'Could not retrieve profile data for ' + userId + ':',
err,
);
});
},
render: function() {
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
const resource = this.state.resourceId;
let avatar = null;
let linkText = resource;
let pillClass;
let userId;
if (isUserPill) {
// If this user is not a member of this room, default to the empty member
// TODO: This could be improved by doing an async profile lookup
const member = this.props.room.getMember(resource) ||
new RoomMember(null, resource);
if (member) {
userId = member.userId;
linkText = member.name;
avatar = <MemberAvatar member={member} width={16} height={16}/>;
switch (this.state.pillType) {
case Pill.TYPE_USER_MENTION: {
// If this user is not a member of this room, default to the empty member
// TODO: This could be improved by doing an async profile lookup
const member = this.state.member;
if (member) {
userId = member.userId;
linkText = member.name;
avatar = <MemberAvatar member={member} width={16} height={16}/>;
pillClass = 'mx_UserPill';
}
}
} else if (isRoomPill) {
const room = prefix === '#' ?
MatrixClientPeg.get().getRooms().find((r) => {
return r.getAliases().includes(resource);
}) : MatrixClientPeg.get().getRoom(resource);
if (room) {
linkText = (room ? getDisplayAliasForRoom(room) : null) || resource;
avatar = <RoomAvatar room={room} width={16} height={16}/>;
break;
case Pill.TYPE_ROOM_MENTION: {
const room = this.state.room;
if (room) {
linkText = (room ? getDisplayAliasForRoom(room) : null) || resource;
avatar = <RoomAvatar room={room} width={16} height={16}/>;
pillClass = 'mx_RoomPill';
}
}
break;
}
const classes = classNames({
"mx_UserPill": isUserPill,
"mx_RoomPill": isRoomPill,
const classes = classNames(pillClass, {
"mx_UserPill_me": userId === MatrixClientPeg.get().credentials.userId,
});
if ((isUserPill || isRoomPill) && avatar) {
if (this.state.pillType) {
return this.props.inMessage ?
<a className={classes} href={this.props.url}>
{avatar}
@ -115,3 +184,5 @@ export default React.createClass({
}
},
});
export default Pill;