Ensure we always have receipts for users we've seen

This adds additional receipt storage to so that we can handle cases where the
receipts and events lists get out of sync. If we ever find a user who previously
had a receipt but momentarily no longer does, we recover their previous receipt
and go with that until we hear something new.

Part of https://github.com/vector-im/riot-web/issues/9745
This commit is contained in:
J. Ryan Stinnett 2019-06-03 13:28:31 +01:00
parent f8eb449dd7
commit 4cea2f0af1

View file

@ -115,6 +115,18 @@ module.exports = React.createClass({
// to manage its animations
this._readReceiptMap = {};
// Track read receipts by user ID. For each user ID we've ever shown a
// a read receipt for, we store an object:
// {
// lastShownEventId,
// receipt,
// }
// so that we can always keep receipts displayed by reverting back to
// the last shown event for that user ID when needed. This may feel like
// it duplicates the receipt storage in the room, but at this layer, we
// are tracking _shown_ event IDs, which the JS SDK knows nothing about.
this._readReceiptsByUserId = {};
// Remember the read marker ghost node so we can do the cleanup that
// Velocity requires
this._readMarkerGhostNode = null;
@ -604,6 +616,7 @@ module.exports = React.createClass({
// they are folded into the receipts of the last shown event.
_getReadReceiptsByShownEvent: function() {
const receiptsByEvent = {};
const receiptsByUserId = {};
let lastShownEventId;
for (const event of this.props.events) {
@ -613,11 +626,38 @@ module.exports = React.createClass({
if (!lastShownEventId) {
continue;
}
const existingReceipts = receiptsByEvent[lastShownEventId] || [];
const newReceipts = this._getReadReceiptsForEvent(event);
receiptsByEvent[lastShownEventId] = existingReceipts.concat(newReceipts);
// Record these receipts along with their last shown event ID for
// each associated user ID.
for (const receipt of newReceipts) {
receiptsByUserId[receipt.userId] = {
lastShownEventId,
receipt,
};
}
}
// It's possible in some cases (for example, when a read receipt
// advances before we have paginated in the new event that it's marking
// received) that we can temporarily not have a matching event for
// someone which had one in the last. By looking through our previous
// mapping of receipts by user ID, we can cover recover any receipts
// that would have been lost by using the same event ID from last time.
for (const userId in this._readReceiptsByUserId) {
if (receiptsByUserId[userId]) {
continue;
}
const { lastShownEventId, receipt } = this._readReceiptsByUserId[userId];
const existingReceipts = receiptsByEvent[lastShownEventId] || [];
receiptsByEvent[lastShownEventId] = existingReceipts.concat(receipt);
receiptsByUserId[userId] = { lastShownEventId, receipt };
}
this._readReceiptsByUserId = receiptsByUserId;
// After grouping receipts by shown events, do another pass to sort each
// receipt list.
for (const eventId in receiptsByEvent) {