Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Get user pill profile remote data and show unrecognised rooms as links #1246

Merged
merged 6 commits into from
Jul 25, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 105 additions & 31 deletions src/components/views/elements/Pill.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@ 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);
},
isMessagePillUrl: (url) => {
return !!REGEX_LOCAL_MATRIXTO.exec(url);
},
TYPE_USER_MENTION: 'TYPE_USER_MENTION',
TYPE_ROOM_MENTION: 'TYPE_ROOM_MENTION',
},

props: {
Expand All @@ -47,10 +49,22 @@ 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() {
this._unmounted = false;
let regex = REGEX_MATRIXTO;
if (this.props.inMessage) {
regex = REGEX_LOCAL_MATRIXTO;
Expand All @@ -60,46 +74,104 @@ 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];

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});
},

const isUserPill = prefix === '@';
const isRoomPill = prefix === '#' || prefix === '!';
componentWillUnmount() {
this._unmounted = true;
},

doProfileLookup: function(userId, member) {
MatrixClientPeg.get().getProfileInfo(userId).then((resp) => {
if (this._unmounted) {
return;
}
member.name = resp.displayname;
member.rawDisplayName = resp.displayname;
member.events.member = {
getContent: () => {
return {avatar_url: resp.avatar_url};
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ew for synthesising Member objects :(

Copy link
Contributor Author

@lukebarnard1 lukebarnard1 Jul 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree but sadly, this is the only way to update the member with a users's profile information. The best thing to do would probably be to fish out the important stuff from the membership event when it gets set instead of storing it with the member? Or do both, and set extra fields when setting the event but that also feels ming.

Said fields could then be clobbered, leaving the event out-of-date but that's strictly accurate.

FTR RoomMember only hangs on to its member event for the purposes of being able to return the correct avatar URL... But who knows what other layers do with member.events.member

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - problem is that we're using something that takes only a Member object, but sometimes we don't have one because we're just displaying a general user rather than a member of a room.

};
this.setState({member});
}).catch((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.rawDisplayName;
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
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} title={resource}>
{avatar}
Expand All @@ -115,3 +187,5 @@ export default React.createClass({
}
},
});

export default Pill;