diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3100fb1d1..436136430 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -113,3 +113,8 @@ include the line in your commit or pull request comment:: can't be accepted. Git makes this trivial - just use the -s flag when you do ``git commit``, having first set ``user.name`` and ``user.email`` git configs (which you should have done anyway :) + +If you forgot to sign off your commits before making your pull request and are on git 2.17+ +you can mass signoff using rebase:: + + git rebase --signoff origin/develop diff --git a/spec/unit/room-member.spec.js b/spec/unit/room-member.spec.js index 298771128..77c2f7058 100644 --- a/spec/unit/room-member.spec.js +++ b/spec/unit/room-member.spec.js @@ -285,5 +285,52 @@ describe("RoomMember", function() { member.setMembershipEvent(joinEvent); // no-op expect(emitCount).toEqual(1); }); + + it("should set 'name' to user_id if it is just whitespace", function() { + const joinEvent = utils.mkMembership({ + event: true, + mship: "join", + user: userA, + room: roomId, + name: " \u200b ", + }); + + expect(member.name).toEqual(userA); // default = user_id + member.setMembershipEvent(joinEvent); + expect(member.name).toEqual(userA); // it should fallback because all whitespace + }); + + it("should disambiguate users on a fuzzy displayname match", function() { + const joinEvent = utils.mkMembership({ + event: true, + mship: "join", + user: userA, + room: roomId, + name: "Alíce\u200b", // note diacritic and zero width char + }); + + const roomState = { + getStateEvents: function(type) { + if (type !== "m.room.member") { + return []; + } + return [ + utils.mkMembership({ + event: true, mship: "join", room: roomId, + user: userC, name: "Alice", + }), + joinEvent, + ]; + }, + getUserIdsWithDisplayName: function(displayName) { + return [userA, userC]; + }, + }; + expect(member.name).toEqual(userA); // default = user_id + member.setMembershipEvent(joinEvent, roomState); + expect(member.name).toNotEqual("Alíce"); // it should disambig. + // user_id should be there somewhere + expect(member.name.indexOf(userA)).toNotEqual(-1); + }); }); }); diff --git a/src/models/room-member.js b/src/models/room-member.js index e7a4bf88c..ebc8df378 100644 --- a/src/models/room-member.js +++ b/src/models/room-member.js @@ -302,22 +302,14 @@ function calculateDisplayName(selfUserId, displayName, roomState) { return displayName; } - // First check if the displayname is something we consider truthy - // after stripping it of zero width characters and padding spaces - const strippedDisplayName = utils.removeHiddenChars(displayName); - if (!strippedDisplayName) { - return selfUserId; - } - // Next check if the name contains something that look like a mxid // If it does, it may be someone trying to impersonate someone else // Show full mxid in this case - // Also show mxid if there are other people with the same displayname - // ignoring any zero width chars (unicode 200B-200D) - // if their displayname is made up of just zero width chars, show full mxid + // Also show mxid if there are other people with the same or similar + // displayname, after hidden character removal. let disambiguate = /@.+:.+/.test(displayName); if (!disambiguate) { - const userIds = roomState.getUserIdsWithDisplayName(strippedDisplayName); + const userIds = roomState.getUserIdsWithDisplayName(displayName); disambiguate = userIds.some((u) => u !== selfUserId); } diff --git a/src/models/room-state.js b/src/models/room-state.js index 3dbd7ff88..95da5396b 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -75,6 +75,8 @@ function RoomState(roomId, oobMemberFlags = undefined) { // userId: RoomMember }; this._updateModifiedTime(); + + // stores fuzzy matches to a list of userIDs (applies utils.removeHiddenChars to keys) this._displayNameToUserIds = {}; this._userIdsToDisplayNames = {}; this._tokenToInvite = {}; // 3pid invite state_key to m.room.member invite @@ -529,12 +531,12 @@ RoomState.prototype.getLastModifiedTime = function() { }; /** - * Get user IDs with the specified display name. + * Get user IDs with the specified or similar display names. * @param {string} displayName The display name to get user IDs from. * @return {string[]} An array of user IDs or an empty array. */ RoomState.prototype.getUserIdsWithDisplayName = function(displayName) { - return this._displayNameToUserIds[displayName] || []; + return this._displayNameToUserIds[utils.removeHiddenChars(displayName)] || []; }; /**