From bd7553d1ea5a35462d72eb99f8d2a80a48439677 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Dec 2016 16:23:20 +0000 Subject: [PATCH 1/3] Add read receipt times to the hovertip of read markers Fixes #2709. Surprisingly, this data was never passed down to ReadReceiptMarker. --- src/components/views/avatars/MemberAvatar.js | 3 ++- src/components/views/rooms/EventTile.js | 15 +++++++++++++++ src/components/views/rooms/ReadReceiptMarker.js | 10 ++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/components/views/avatars/MemberAvatar.js b/src/components/views/avatars/MemberAvatar.js index 1f6736138d..c8a9abb4fe 100644 --- a/src/components/views/avatars/MemberAvatar.js +++ b/src/components/views/avatars/MemberAvatar.js @@ -33,6 +33,7 @@ module.exports = React.createClass({ onClick: React.PropTypes.func, // Whether the onClick of the avatar should be overriden to dispatch 'view_user' viewUserOnClick: React.PropTypes.bool, + title: React.PropTypes.string, }, getDefaultProps: function() { @@ -58,7 +59,7 @@ module.exports = React.createClass({ } return { name: props.member.name, - title: props.member.userId, + title: props.title || props.member.userId, imageUrl: Avatar.avatarUrlForMember(props.member, props.width, props.height, diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 8dd84f9364..9da3f7b265 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -290,6 +290,18 @@ module.exports = WithMatrixClient(React.createClass({ var left = 0; + var readReceiptData = Object.create(null); + var room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId()); + if (room) { + // [ {type/userId/data} ] + room.getReceiptsForEvent(this.props.mxEvent).forEach(function(r) { + if (r.type !== "m.read" || !r.data.ts) { + return; + } + readReceiptData[r.userId] = r.data; + }) + } + var receipts = this.props.readReceipts || []; for (var i = 0; i < receipts.length; ++i) { var member = receipts[i]; @@ -312,6 +324,8 @@ module.exports = WithMatrixClient(React.createClass({ //console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility); + var rData = readReceiptData[member.userId]; + // add to the start so the most recent is on the end (ie. ends up rightmost) avatars.unshift( ); diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index 91ba201683..ddf271d6f4 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -60,6 +60,9 @@ module.exports = React.createClass({ // callback for clicks on this RR onClick: React.PropTypes.func, + + // Timestamp when the receipt was read + timestamp: React.PropTypes.number, }, getDefaultProps: function() { @@ -162,6 +165,12 @@ module.exports = React.createClass({ visibility: this.props.hidden ? 'hidden' : 'visible', }; + var title; + if (this.props.timestamp) { + // "7:05:45 PM (@alice:matrix.org)" + title = new Date(this.props.timestamp).toLocaleTimeString() + " (" + this.props.member.userId + ")"; + } + return ( From 49010c3e93cf86d203e2f377111b127ac722227f Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 9 Dec 2016 11:24:10 +0000 Subject: [PATCH 2/3] Refactor how 'readReceipts' are passed into EventTiles Instead of passing a list of RoomMembers, pass a list of records with a `roomMember` prop and a `ts` prop so we can display the timestamp on hover. --- src/components/structures/MessagePanel.js | 34 +++++++++++++---------- src/components/views/rooms/EventTile.js | 28 ++++--------------- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 1e0db2321d..53bf560673 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -471,27 +471,33 @@ module.exports = React.createClass({ !== new Date(nextEventTs).toDateString()); }, - // get a list of the userids whose read receipts should - // be shown next to this event + // get a list of read receipts that should be shown next to this event + // Receipts are objects which have a 'roomMember' and 'ts'. _getReadReceiptsForEvent: function(event) { - var myUserId = MatrixClientPeg.get().credentials.userId; + const myUserId = MatrixClientPeg.get().credentials.userId; // get list of read receipts, sorted most recent first - var room = MatrixClientPeg.get().getRoom(event.getRoomId()); + const room = MatrixClientPeg.get().getRoom(event.getRoomId()); if (!room) { - // huh. return null; } + let receipts = []; + room.getReceiptsForEvent(event).forEach((r) => { + if (!r.userId || r.type !== "m.read" || r.userId === myUserId) { + return; // ignore non-read receipts and receipts from self. + } + let member = room.getMember(r.userId); + if (!member) { + return; // ignore unknown user IDs + } + receipts.push({ + roomMember: member, + ts: r.data ? r.data.ts : 0, + }); + }); - return room.getReceiptsForEvent(event).filter(function(r) { - return r.type === "m.read" && r.userId != myUserId; - }).sort(function(r1, r2) { - return r2.data.ts - r1.data.ts; - }).map(function(r) { - return room.getMember(r.userId); - }).filter(function(m) { - // check that the user is a known room member - return m; + return receipts.sort((r1, r2) => { + return r2.ts - r1.ts; }); }, diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 9da3f7b265..85020b564a 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -103,7 +103,7 @@ module.exports = WithMatrixClient(React.createClass({ /* callback called when dynamic content in events are loaded */ onWidgetLoad: React.PropTypes.func, - /* a list of Room Members whose read-receipts we should show */ + /* a list of read-receipts we should show. Each object has a 'roomMember' and 'ts'. */ readReceipts: React.PropTypes.arrayOf(React.PropTypes.object), /* opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -231,7 +231,7 @@ module.exports = WithMatrixClient(React.createClass({ return false; } for (var j = 0; j < rA.length; j++) { - if (rA[j].userId !== rB[j].userId) { + if (rA[j].roomMember.userId !== rB[j].roomMember.userId) { return false; } } @@ -287,31 +287,18 @@ module.exports = WithMatrixClient(React.createClass({ getReadAvatars: function() { var ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker'); var avatars = []; - var left = 0; - var readReceiptData = Object.create(null); - var room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId()); - if (room) { - // [ {type/userId/data} ] - room.getReceiptsForEvent(this.props.mxEvent).forEach(function(r) { - if (r.type !== "m.read" || !r.data.ts) { - return; - } - readReceiptData[r.userId] = r.data; - }) - } - var receipts = this.props.readReceipts || []; for (var i = 0; i < receipts.length; ++i) { - var member = receipts[i]; + var receipt = receipts[i]; var hidden = true; if ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) { hidden = false; } - var userId = member.userId; + var userId = receipt.roomMember.userId; var readReceiptInfo; if (this.props.readReceiptMap) { @@ -323,18 +310,15 @@ module.exports = WithMatrixClient(React.createClass({ } //console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility); - - var rData = readReceiptData[member.userId]; - // add to the start so the most recent is on the end (ie. ends up rightmost) avatars.unshift( -