Merge pull request #3184 from matrix-org/jryans/reactions-send-marks-unread

Track live events in timeline and use for read receipts and read markers
pull/21833/head
J. Ryan Stinnett 2019-07-05 17:55:59 +01:00 committed by GitHub
commit 017fc84862
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 40 additions and 25 deletions

View File

@ -2,6 +2,7 @@
Copyright 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -141,6 +142,7 @@ const TimelinePanel = React.createClass({
return {
events: [],
liveEvents: [],
timelineLoading: true, // track whether our room timeline is loading
// canBackPaginate == false may mean:
@ -322,9 +324,11 @@ const TimelinePanel = React.createClass({
// We can now paginate in the unpaginated direction
const canPaginateKey = (backwards) ? 'canBackPaginate' : 'canForwardPaginate';
const { events, liveEvents } = this._getEvents();
this.setState({
[canPaginateKey]: true,
events: this._getEvents(),
events,
liveEvents,
});
}
},
@ -356,10 +360,12 @@ const TimelinePanel = React.createClass({
debuglog("TimelinePanel: paginate complete backwards:"+backwards+"; success:"+r);
const { events, liveEvents } = this._getEvents();
const newState = {
[paginatingKey]: false,
[canPaginateKey]: r,
events: this._getEvents(),
events,
liveEvents,
};
// moving the window in this direction may mean that we can now
@ -453,15 +459,13 @@ const TimelinePanel = React.createClass({
this._timelineWindow.paginate(EventTimeline.FORWARDS, 1, false).done(() => {
if (this.unmounted) { return; }
const events = this._timelineWindow.getEvents();
const lastEv = events[events.length-1];
const { events, liveEvents } = this._getEvents();
const lastLiveEvent = liveEvents[liveEvents.length - 1];
// if we're at the end of the live timeline, append the pending events
if (this.props.timelineSet.room && !this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
events.push(...this.props.timelineSet.room.getPendingEvents());
}
const updatedState = {events: events};
const updatedState = {
events,
liveEvents,
};
let callRMUpdated;
if (this.props.manageReadMarkers) {
@ -478,13 +482,13 @@ const TimelinePanel = React.createClass({
callRMUpdated = false;
if (sender != myUserId && !UserActivity.sharedInstance().userActiveRecently()) {
updatedState.readMarkerVisible = true;
} else if (lastEv && this.getReadMarkerPosition() === 0) {
} else if (lastLiveEvent && this.getReadMarkerPosition() === 0) {
// we know we're stuckAtBottom, so we can advance the RM
// immediately, to save a later render cycle
this._setReadMarker(lastEv.getId(), lastEv.getTs(), true);
this._setReadMarker(lastLiveEvent.getId(), lastLiveEvent.getTs(), true);
updatedState.readMarkerVisible = false;
updatedState.readMarkerEventId = lastEv.getId();
updatedState.readMarkerEventId = lastLiveEvent.getId();
callRMUpdated = true;
}
}
@ -694,9 +698,12 @@ const TimelinePanel = React.createClass({
if (e.errcode === 'M_UNRECOGNIZED' && lastReadEvent) {
return MatrixClientPeg.get().sendReadReceipt(
lastReadEvent,
).catch(() => {
).catch((e) => {
console.error(e);
this.lastRRSentEventId = undefined;
});
} else {
console.error(e);
}
// it failed, so allow retries next time the user is active
this.lastRRSentEventId = undefined;
@ -762,9 +769,9 @@ const TimelinePanel = React.createClass({
_advanceReadMarkerPastMyEvents: function() {
if (!this.props.manageReadMarkers) return;
// we call _timelineWindow.getEvents() rather than using
// this.state.events, because react batches the update to the latter, so it
// may not have been updated yet.
// we call `_timelineWindow.getEvents()` rather than using
// `this.state.liveEvents`, because React batches the update to the
// latter, so it may not have been updated yet.
const events = this._timelineWindow.getEvents();
// first find where the current RM is
@ -1067,6 +1074,7 @@ const TimelinePanel = React.createClass({
} else {
this.setState({
events: [],
liveEvents: [],
canBackPaginate: false,
canForwardPaginate: false,
timelineLoading: true,
@ -1086,21 +1094,26 @@ const TimelinePanel = React.createClass({
// the results if so.
if (this.unmounted) return;
this.setState({
events: this._getEvents(),
});
this.setState(this._getEvents());
},
// get the list of events from the timeline window and the pending event list
_getEvents: function() {
const events = this._timelineWindow.getEvents();
// Hold onto the live events separately. The read receipt and read marker
// should use this list, so that they don't advance into pending events.
const liveEvents = [...events];
// if we're at the end of the live timeline, append the pending events
if (!this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
events.push(...this.props.timelineSet.getPendingEvents());
}
return events;
return {
events,
liveEvents,
};
},
_indexForEventId: function(evId) {
@ -1128,8 +1141,10 @@ const TimelinePanel = React.createClass({
const myUserId = MatrixClientPeg.get().credentials.userId;
let lastDisplayedIndex = null;
for (let i = this.state.events.length - 1; i >= 0; --i) {
const ev = this.state.events[i];
// Use `liveEvents` here because we don't want the read marker or read
// receipt to advance into pending events.
for (let i = this.state.liveEvents.length - 1; i >= 0; --i) {
const ev = this.state.liveEvents[i];
if (ignoreOwn && ev.sender && ev.sender.userId == myUserId) {
continue;
@ -1164,8 +1179,8 @@ const TimelinePanel = React.createClass({
// easier to reason about, so let's start there and optimise later if
// needed.
if (allowEventsWithoutTiles) {
for (let i = lastDisplayedIndex + 1; i < this.state.events.length; i++) {
const ev = this.state.events[i];
for (let i = lastDisplayedIndex + 1; i < this.state.liveEvents.length; i++) {
const ev = this.state.liveEvents[i];
if (EventTile.haveTileForEvent(ev)) {
break;
}