From bd9cb5aae5c9311f071d8fc932a6e5478bf79bdb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 27 Aug 2020 09:51:00 +0200 Subject: [PATCH 1/3] add RoomMember.name which falls back to userId this will prevent the crash when left members have their displayname removed (another issue) --- src/matrix/room/members/Heroes.js | 8 ++++---- src/matrix/room/members/RoomMember.js | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/matrix/room/members/Heroes.js b/src/matrix/room/members/Heroes.js index 79902579..74f69db1 100644 --- a/src/matrix/room/members/Heroes.js +++ b/src/matrix/room/members/Heroes.js @@ -22,12 +22,12 @@ function calculateRoomName(sortedMembers, summary) { if (sortedMembers.length > 1) { const lastMember = sortedMembers[sortedMembers.length - 1]; const firstMembers = sortedMembers.slice(0, sortedMembers.length - 1); - return firstMembers.map(m => m.displayName).join(", ") + " and " + lastMember.displayName; + return firstMembers.map(m => m.name).join(", ") + " and " + lastMember.name; } else { - return sortedMembers[0].displayName; + return sortedMembers[0].name; } } else if (sortedMembers.length < countWithoutMe) { - return sortedMembers.map(m => m.displayName).join(", ") + ` and ${countWithoutMe} others`; + return sortedMembers.map(m => m.name).join(", ") + ` and ${countWithoutMe} others`; } else { // Empty Room return null; @@ -81,7 +81,7 @@ export class Heroes { for (const member of updatedHeroMembers) { this._members.set(member.userId, member); } - const sortedMembers = Array.from(this._members.values()).sort((a, b) => a.displayName.localeCompare(b.displayName)); + const sortedMembers = Array.from(this._members.values()).sort((a, b) => a.name.localeCompare(b.name)); this._roomName = calculateRoomName(sortedMembers, summary); } diff --git a/src/matrix/room/members/RoomMember.js b/src/matrix/room/members/RoomMember.js index e6303fe4..6157c42e 100644 --- a/src/matrix/room/members/RoomMember.js +++ b/src/matrix/room/members/RoomMember.js @@ -54,10 +54,23 @@ export class RoomMember { }); } + /** + * @return {String?} the display name, if any + */ get displayName() { return this._data.displayName; } + /** + * @return {String} the display name or userId + */ + get name() { + return this._data.displayName || this._data.userId; + } + + /** + * @return {String?} the avatar mxc url, if any + */ get avatarUrl() { return this._data.avatarUrl; } From 34ec96c1b86caa67334efacbfc23b49851e038c4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 27 Aug 2020 09:51:44 +0200 Subject: [PATCH 2/3] look for displayname/avatar in prev content as well as synapse doesn't set them on content for leave memberships this caused these props to be removed in storage --- src/matrix/room/members/RoomMember.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/matrix/room/members/RoomMember.js b/src/matrix/room/members/RoomMember.js index 6157c42e..02c3c292 100644 --- a/src/matrix/room/members/RoomMember.js +++ b/src/matrix/room/members/RoomMember.js @@ -27,21 +27,33 @@ export class RoomMember { if (typeof userId !== "string") { return; } - return this._fromMemberEventContent(roomId, userId, memberEvent.content); + const content = memberEvent.content; + const prevContent = memberEvent.unsigned?.prev_content; + const membership = content?.membership; + // fall back to prev_content for these as synapse doesn't (always?) + // put them on content for "leave" memberships + const displayName = content?.displayname || prevContent?.displayname; + const avatarUrl = content?.avatar_url || prevContent?.avatar_url; + return this._validateAndCreateMember(roomId, userId, membership, displayName, avatarUrl); } - + /** + * Creates a (historical) member from a member event that is the next member event + * after the point in time where we need a member for. This will use `prev_content`. + */ static fromReplacingMemberEvent(roomId, memberEvent) { const userId = memberEvent && memberEvent.state_key; if (typeof userId !== "string") { return; } - return this._fromMemberEventContent(roomId, userId, memberEvent.prev_content); + const content = memberEvent.unsigned?.prev_content + return this._validateAndCreateMember(roomId, userId, + content?.membership, + content?.displayname, + content?.avatar_url + ); } - static _fromMemberEventContent(roomId, userId, content) { - const membership = content?.membership; - const avatarUrl = content?.avatar_url; - const displayName = content?.displayname; + static _validateAndCreateMember(roomId, userId, membership, displayName, avatarUrl) { if (typeof membership !== "string") { return; } From 1fe496eeea4670fb901eae604f8cca9c4160784f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 27 Aug 2020 09:52:30 +0200 Subject: [PATCH 3/3] fix crash when state is not set (erroneously?) on gap response this seems to happen when the only event in the room is a m.room.create --- src/matrix/room/timeline/persistence/GapWriter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/matrix/room/timeline/persistence/GapWriter.js b/src/matrix/room/timeline/persistence/GapWriter.js index 834239f7..e9abded6 100644 --- a/src/matrix/room/timeline/persistence/GapWriter.js +++ b/src/matrix/room/timeline/persistence/GapWriter.js @@ -142,8 +142,9 @@ export class GapWriter { return RoomMember.fromReplacingMemberEvent(this._roomId, event)?.serialize(); } } - // assuming the member hasn't changed within the chunk, just take it from state if it's there - const stateMemberEvent = state.find(isOurUser); + // assuming the member hasn't changed within the chunk, just take it from state if it's there. + // Don't assume state is set though, as it can be empty at the top of the timeline in some circumstances + const stateMemberEvent = state?.find(isOurUser); if (stateMemberEvent) { return RoomMember.fromMemberEvent(this._roomId, stateMemberEvent)?.serialize(); }