mirror of
https://github.com/element-hq/element-web
synced 2024-11-26 19:26:04 +03:00
Merge pull request #586 from matrix-org/kegan/read-receipt-show-time-on-hover
Add read receipt times to the hovertip of read markers
This commit is contained in:
commit
21e7b03e53
4 changed files with 60 additions and 22 deletions
|
@ -471,27 +471,33 @@ module.exports = React.createClass({
|
||||||
!== new Date(nextEventTs).toDateString());
|
!== new Date(nextEventTs).toDateString());
|
||||||
},
|
},
|
||||||
|
|
||||||
// get a list of the userids whose read receipts should
|
// get a list of read receipts that should be shown next to this event
|
||||||
// be shown next to this event
|
// Receipts are objects which have a 'roomMember' and 'ts'.
|
||||||
_getReadReceiptsForEvent: function(event) {
|
_getReadReceiptsForEvent: function(event) {
|
||||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
// get list of read receipts, sorted most recent first
|
// 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) {
|
if (!room) {
|
||||||
// huh.
|
|
||||||
return null;
|
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 receipts.sort((r1, r2) => {
|
||||||
return r.type === "m.read" && r.userId != myUserId;
|
return r2.ts - r1.ts;
|
||||||
}).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;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ module.exports = React.createClass({
|
||||||
onClick: React.PropTypes.func,
|
onClick: React.PropTypes.func,
|
||||||
// Whether the onClick of the avatar should be overriden to dispatch 'view_user'
|
// Whether the onClick of the avatar should be overriden to dispatch 'view_user'
|
||||||
viewUserOnClick: React.PropTypes.bool,
|
viewUserOnClick: React.PropTypes.bool,
|
||||||
|
title: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -58,7 +59,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: props.member.name,
|
name: props.member.name,
|
||||||
title: props.member.userId,
|
title: props.title || props.member.userId,
|
||||||
imageUrl: Avatar.avatarUrlForMember(props.member,
|
imageUrl: Avatar.avatarUrlForMember(props.member,
|
||||||
props.width,
|
props.width,
|
||||||
props.height,
|
props.height,
|
||||||
|
|
|
@ -103,7 +103,7 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
/* callback called when dynamic content in events are loaded */
|
/* callback called when dynamic content in events are loaded */
|
||||||
onWidgetLoad: React.PropTypes.func,
|
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),
|
readReceipts: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
|
|
||||||
/* opaque readreceipt info for each userId; used by ReadReceiptMarker
|
/* opaque readreceipt info for each userId; used by ReadReceiptMarker
|
||||||
|
@ -231,7 +231,7 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (var j = 0; j < rA.length; j++) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,19 +287,28 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
getReadAvatars: function() {
|
getReadAvatars: function() {
|
||||||
var ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker');
|
var ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker');
|
||||||
var avatars = [];
|
var avatars = [];
|
||||||
|
|
||||||
var left = 0;
|
var left = 0;
|
||||||
|
|
||||||
|
// It's possible that the receipt was sent several days AFTER the event.
|
||||||
|
// If it is, we want to display the complete date along with the HH:MM:SS,
|
||||||
|
// rather than just HH:MM:SS.
|
||||||
|
let dayAfterEvent = new Date(this.props.mxEvent.getTs());
|
||||||
|
dayAfterEvent.setDate(dayAfterEvent.getDate() + 1)
|
||||||
|
dayAfterEvent.setHours(0);
|
||||||
|
dayAfterEvent.setMinutes(0);
|
||||||
|
dayAfterEvent.setSeconds(0);
|
||||||
|
let dayAfterEventTime = dayAfterEvent.getTime();
|
||||||
|
|
||||||
var receipts = this.props.readReceipts || [];
|
var receipts = this.props.readReceipts || [];
|
||||||
for (var i = 0; i < receipts.length; ++i) {
|
for (var i = 0; i < receipts.length; ++i) {
|
||||||
var member = receipts[i];
|
var receipt = receipts[i];
|
||||||
|
|
||||||
var hidden = true;
|
var hidden = true;
|
||||||
if ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) {
|
if ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) {
|
||||||
hidden = false;
|
hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = member.userId;
|
var userId = receipt.roomMember.userId;
|
||||||
var readReceiptInfo;
|
var readReceiptInfo;
|
||||||
|
|
||||||
if (this.props.readReceiptMap) {
|
if (this.props.readReceiptMap) {
|
||||||
|
@ -311,15 +320,16 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility);
|
//console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility);
|
||||||
|
|
||||||
// add to the start so the most recent is on the end (ie. ends up rightmost)
|
// add to the start so the most recent is on the end (ie. ends up rightmost)
|
||||||
avatars.unshift(
|
avatars.unshift(
|
||||||
<ReadReceiptMarker key={userId} member={member}
|
<ReadReceiptMarker key={userId} member={receipt.roomMember}
|
||||||
leftOffset={left} hidden={hidden}
|
leftOffset={left} hidden={hidden}
|
||||||
readReceiptInfo={readReceiptInfo}
|
readReceiptInfo={readReceiptInfo}
|
||||||
checkUnmounting={this.props.checkUnmounting}
|
checkUnmounting={this.props.checkUnmounting}
|
||||||
suppressAnimation={this._suppressReadReceiptAnimation}
|
suppressAnimation={this._suppressReadReceiptAnimation}
|
||||||
onClick={this.toggleAllReadAvatars}
|
onClick={this.toggleAllReadAvatars}
|
||||||
|
timestamp={receipt.ts}
|
||||||
|
showFullTimestamp={receipt.ts >= dayAfterEventTime}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,12 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// callback for clicks on this RR
|
// callback for clicks on this RR
|
||||||
onClick: React.PropTypes.func,
|
onClick: React.PropTypes.func,
|
||||||
|
|
||||||
|
// Timestamp when the receipt was read
|
||||||
|
timestamp: React.PropTypes.number,
|
||||||
|
|
||||||
|
// True to show the full date/time rather than just the time
|
||||||
|
showFullTimestamp: React.PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -162,6 +168,20 @@ module.exports = React.createClass({
|
||||||
visibility: this.props.hidden ? 'hidden' : 'visible',
|
visibility: this.props.hidden ? 'hidden' : 'visible',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let title;
|
||||||
|
if (this.props.timestamp) {
|
||||||
|
let suffix = " (" + this.props.member.userId + ")";
|
||||||
|
let ts = new Date(this.props.timestamp);
|
||||||
|
if (this.props.showFullTimestamp) {
|
||||||
|
// "15/12/2016, 7:05:45 PM (@alice:matrix.org)"
|
||||||
|
title = ts.toLocaleString() + suffix;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// "7:05:45 PM (@alice:matrix.org)"
|
||||||
|
title = ts.toLocaleTimeString() + suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Velociraptor
|
<Velociraptor
|
||||||
startStyles={this.state.startStyles}
|
startStyles={this.state.startStyles}
|
||||||
|
@ -170,6 +190,7 @@ module.exports = React.createClass({
|
||||||
member={this.props.member}
|
member={this.props.member}
|
||||||
width={14} height={14} resizeMethod="crop"
|
width={14} height={14} resizeMethod="crop"
|
||||||
style={style}
|
style={style}
|
||||||
|
title={title}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
/>
|
/>
|
||||||
</Velociraptor>
|
</Velociraptor>
|
||||||
|
|
Loading…
Reference in a new issue