Merge pull request #805 from matrix-org/luke/feature-read-marker
Sync RM across instances of Riotpull/21833/head
commit
ae8d04b35d
|
@ -388,6 +388,8 @@ module.exports = React.createClass({
|
|||
isVisibleReadMarker = visible;
|
||||
}
|
||||
|
||||
// XXX: there should be no need for a ghost tile - we should just use a
|
||||
// a dispatch (user_activity_end) to start the RM animation.
|
||||
if (eventId == this.currentGhostEventId) {
|
||||
// if we're showing an animation, continue to show it.
|
||||
ret.push(this._getReadMarkerGhostTile());
|
||||
|
|
|
@ -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,14 @@ 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.fully_read');
|
||||
if (readmarker){
|
||||
initialReadMarker = readmarker.getContent().event_id;
|
||||
} else {
|
||||
initialReadMarker = this._getCurrentReadReceipt();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -173,6 +174,7 @@ var TimelinePanel = React.createClass({
|
|||
debuglog("TimelinePanel: mounting");
|
||||
|
||||
this.last_rr_sent_event_id = undefined;
|
||||
this.last_rm_sent_event_id = undefined;
|
||||
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -247,6 +250,7 @@ var TimelinePanel = React.createClass({
|
|||
client.removeListener("Room.redaction", this.onRoomRedaction);
|
||||
client.removeListener("Room.receipt", this.onRoomReceipt);
|
||||
client.removeListener("Room.localEchoUpdated", this.onLocalEchoUpdated);
|
||||
client.removeListener("Room.accountData", this.onAccountData);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -414,6 +418,7 @@ var TimelinePanel = React.createClass({
|
|||
} else if(lastEv && 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);
|
||||
updatedState.readMarkerVisible = false;
|
||||
updatedState.readMarkerEventId = lastEv.getId();
|
||||
|
@ -466,6 +471,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.fully_read") return;
|
||||
|
||||
// XXX: roomReadMarkerTsMap not updated here so it is now inconsistent. Replace
|
||||
// this mechanism of determining where the RM is relative to the view-port with
|
||||
// one supported by the server (the client needs more than an event ID).
|
||||
this.setState({
|
||||
readMarkerEventId: ev.getContent().event_id,
|
||||
}, this.props.onReadMarkerUpdated);
|
||||
},
|
||||
|
||||
sendReadReceipt: function() {
|
||||
if (!this.refs.messagePanel) return;
|
||||
|
@ -505,12 +525,29 @@ 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().setRoomReadMarkers(
|
||||
this.props.timelineSet.room.roomId,
|
||||
this.state.readMarkerEventId,
|
||||
lastReadEvent
|
||||
).catch((e) => {
|
||||
// /read_markers API is not implemented on this HS, fallback to just RR
|
||||
if (e.errcode === 'M_UNRECOGNIZED') {
|
||||
return MatrixClientPeg.get().sendReadReceipt(
|
||||
lastReadEvent
|
||||
).catch(() => {
|
||||
this.last_rr_sent_event_id = undefined;
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
});
|
||||
|
||||
// do a quick-reset of our unreadNotificationCount to avoid having
|
||||
|
@ -707,7 +744,7 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// the messagePanel doesn't know where the read marker is.
|
||||
// if we know the timestamp of the read marker, make a guess based on that.
|
||||
var rmTs = TimelinePanel.roomReadMarkerTsMap[this.props.timelineSet.roomId];
|
||||
const rmTs = TimelinePanel.roomReadMarkerTsMap[this.props.timelineSet.room.roomId];
|
||||
if (rmTs && this.state.events.length > 0) {
|
||||
if (rmTs < this.state.events[0].getTs()) {
|
||||
return -1;
|
||||
|
@ -957,16 +994,12 @@ 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.
|
||||
// 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;
|
||||
|
@ -975,6 +1008,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({
|
||||
|
@ -1022,7 +1056,6 @@ var TimelinePanel = React.createClass({
|
|||
// events when viewing historical messages, we get stuck in a loop
|
||||
// of paginating our way through the entire history of the room.
|
||||
var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
||||
|
||||
return (
|
||||
<MessagePanel ref="messagePanel"
|
||||
hidden={ this.props.hidden }
|
||||
|
|
Loading…
Reference in New Issue