diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index 972e9cf7f1..66e2daddd5 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -173,15 +173,29 @@ module.exports = React.createClass({
// first figure out which is the last event in the list which we're
// actually going to show; this allows us to behave slightly
// differently for the last event in the list.
+ //
+ // we also need to figure out which is the last event we show which isn't
+ // a local echo, to manage the read-marker.
+ var lastShownEventIndex = -1;
+ var lastShownNonLocalEchoIndex = -1;
for (i = this.props.events.length-1; i >= 0; i--) {
var mxEv = this.props.events[i];
if (!EventTile.haveTileForEvent(mxEv)) {
continue;
}
+ if (lastShownEventIndex < 0) {
+ lastShownEventIndex = i;
+ }
+
+ if (mxEv.status) {
+ // this is a local echo
+ continue;
+ }
+
+ lastShownNonLocalEchoIndex = i;
break;
}
- var lastShownEventIndex = i;
var ret = [];
@@ -213,25 +227,34 @@ module.exports = React.createClass({
ret.push(
);
}
+ var isVisibleReadMarker = false;
+
if (eventId == this.props.readMarkerEventId) {
var visible = this.props.readMarkerVisible;
- // if the read marker comes at the end of the timeline, we don't want
- // to show it, but we still want to create the for it so that the
- // algorithms which depend on its position on the screen aren't confused.
- if (i >= lastShownEventIndex) {
+ // if the read marker comes at the end of the timeline (except
+ // for local echoes, which are excluded from RMs, because they
+ // don't have useful event ids), we don't want to show it, but
+ // we still want to create the for it so that the
+ // algorithms which depend on its position on the screen aren't
+ // confused.
+ if (i >= lastShownNonLocalEchoIndex) {
visible = false;
}
ret.push(this._getReadMarkerTile(visible));
readMarkerVisible = visible;
- } else if (eventId == this.currentReadMarkerEventId && !this.currentGhostEventId) {
+ isVisibleReadMarker = visible;
+ }
+
+ if (eventId == this.currentGhostEventId) {
+ // if we're showing an animation, continue to show it.
+ ret.push(this._getReadMarkerGhostTile());
+ } else if (!isVisibleReadMarker &&
+ eventId == this.currentReadMarkerEventId) {
// there is currently a read-up-to marker at this point, but no
// more. Show an animation of it disappearing.
ret.push(this._getReadMarkerGhostTile());
this.currentGhostEventId = eventId;
- } else if (eventId == this.currentGhostEventId) {
- // if we're showing an animation, continue to show it.
- ret.push(this._getReadMarkerGhostTile());
}
}
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index fe0a435b7e..7270a3ef4a 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -343,8 +343,6 @@ var TimelinePanel = React.createClass({
return;
}
- var currentIndex = this._indexForEventId(this.state.readMarkerEventId);
-
// move the RM to *after* the message at the bottom of the screen. This
// avoids a problem whereby we never advance the RM if there is a huge
// message which doesn't fit on the screen.
@@ -375,6 +373,38 @@ var TimelinePanel = React.createClass({
}
},
+
+ // advance the read marker past any events we sent ourselves.
+ _advanceReadMarkerPastMyEvents: function() {
+ // 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.
+ var events = this._timelineWindow.getEvents();
+
+ // first find where the current RM is
+ for (var i = 0; i < events.length; i++) {
+ if (events[i].getId() == this.state.readMarkerEventId)
+ break;
+ }
+ if (i >= events.length) {
+ return;
+ }
+
+ // now think about advancing it
+ var myUserId = MatrixClientPeg.get().credentials.userId;
+ for (; i < events.length; i++) {
+ var ev = events[i];
+ if (!ev.sender || ev.sender.userId != myUserId) {
+ break;
+ }
+ }
+ // i is now the first unread message which we didn't send ourselves.
+ i--;
+
+ var ev = events[i];
+ this._setReadMarker(ev.getId(), ev.getTs());
+ },
+
/* jump down to the bottom of this room, where new events are arriving
*/
jumpToLiveTimeline: function() {
@@ -527,6 +557,11 @@ var TimelinePanel = React.createClass({
var onLoaded = () => {
this._reloadEvents();
+ // If we switched away from the room while there were pending
+ // outgoing events, the read-marker will be before those events.
+ // We need to skip over any which have subsequently been sent.
+ this._advanceReadMarkerPastMyEvents();
+
this.setState({timelineLoading: false}, () => {
// initialise the scroll state of the message panel
if (!this.refs.messagePanel) {