From 1c25ed89b01345da3af185bb1900dd7943c388aa Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 12 Apr 2017 15:05:39 +0100 Subject: [PATCH] Initial implementation of using new RM API As detailed here https://docs.google.com/document/d/1UWqdS-e1sdwkLDUY0wA4gZyIkRp-ekjsLZ8k6g_Zvso/edit, the RM state is no longer kept locally, but rather server-side. The client now uses it's locally-calculated RM to update the server and receives server updates via the per-room account data. The sending of the RR has been bundled in to reduce traffic when sending both. In effect, whenever a RR is sent the RM is sent with it but using the new API. This uses a js-sdk change which has set to be finalised and so might change. --- src/components/structures/TimelinePanel.js | 55 +++++++++++++++------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 8cd820c284..4fbca4d40a 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -102,9 +102,6 @@ var TimelinePanel = React.createClass({ }, statics: { - // a map from room id to read marker event ID - roomReadMarkerMap: {}, - // a map from room id to read marker event timestamp roomReadMarkerTsMap: {}, }, @@ -121,10 +118,15 @@ var TimelinePanel = React.createClass({ getInitialState: function() { // XXX: we could track RM per TimelineSet rather than per Room. // but for now we just do it per room for simplicity. + let initialReadMarker = null; if (this.props.manageReadMarkers) { - var initialReadMarker = - TimelinePanel.roomReadMarkerMap[this.props.timelineSet.room.roomId] - || this._getCurrentReadReceipt(); + const readmarker = this.props.timelineSet.room.getAccountData('m.read_marker'); + if (readmarker){ + initialReadMarker = readmarker.getContent().marker; + } else { + initialReadMarker = this._getCurrentReadReceipt(); + } + console.info('Read marker initially', initialReadMarker); } return { @@ -180,6 +182,7 @@ var TimelinePanel = React.createClass({ MatrixClientPeg.get().on("Room.redaction", this.onRoomRedaction); MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().on("Room.localEchoUpdated", this.onLocalEchoUpdated); + MatrixClientPeg.get().on("Room.accountData", this.onAccountData); this._initTimeline(this.props); }, @@ -466,6 +469,21 @@ var TimelinePanel = React.createClass({ this._reloadEvents(); }, + onAccountData: function(ev, room) { + if (this.unmounted) return; + + // ignore events for other rooms + if (room !== this.props.timelineSet.room) return; + + if (ev.getType() !== "m.read_marker") return; + + const markerEventId = ev.getContent().marker; + console.log('TimelinePanel: Read marker received from server', markerEventId); + + this.setState({ + readMarkerEventId: markerEventId, + }, this.props.onReadMarkerUpdated); + }, sendReadReceipt: function() { if (!this.refs.messagePanel) return; @@ -505,13 +523,23 @@ var TimelinePanel = React.createClass({ // we also remember the last read receipt we sent to avoid spamming the // same one at the server repeatedly - if (lastReadEventIndex > currentReadUpToEventIndex - && this.last_rr_sent_event_id != lastReadEvent.getId()) { + if ((lastReadEventIndex > currentReadUpToEventIndex && + this.last_rr_sent_event_id != lastReadEvent.getId()) || + this.last_rm_sent_event_id != this.state.readMarkerEventId) { + this.last_rr_sent_event_id = lastReadEvent.getId(); - MatrixClientPeg.get().sendReadReceipt(lastReadEvent).catch(() => { + this.last_rm_sent_event_id = this.state.readMarkerEventId; + + MatrixClientPeg.get().setRoomReadMarker( + this.props.timelineSet.room.roomId, + this.state.readMarkerEventId, + lastReadEvent + ).catch(() => { // it failed, so allow retries next time the user is active this.last_rr_sent_event_id = undefined; + this.last_rm_sent_event_id = undefined; }); + console.log('TimelinePanel: Read marker sent to the server ', this.state.readMarkerEventId, ); // do a quick-reset of our unreadNotificationCount to avoid having // to wait from the remote echo from the homeserver. @@ -956,16 +984,10 @@ var TimelinePanel = React.createClass({ _setReadMarker: function(eventId, eventTs, inhibitSetState) { var roomId = this.props.timelineSet.room.roomId; - if (TimelinePanel.roomReadMarkerMap[roomId] == eventId) { - // don't update the state (and cause a re-render) if there is - // no change to the RM. + if (eventId === this.state.readMarkerEventId) { return; } - // ideally we'd sync these via the server, but for now just stash them - // in a map. - TimelinePanel.roomReadMarkerMap[roomId] = eventId; - // in order to later figure out if the read marker is // above or below the visible timeline, we stash the timestamp. TimelinePanel.roomReadMarkerTsMap[roomId] = eventTs; @@ -974,6 +996,7 @@ var TimelinePanel = React.createClass({ return; } + // Do the local echo of the RM // run the render cycle before calling the callback, so that // getReadMarkerPosition() returns the right thing. this.setState({