diff --git a/src/components/Chat.vue b/src/components/Chat.vue index f06302d..3563d7c 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -149,7 +149,7 @@ @@ -166,8 +166,9 @@ - - - +
@@ -264,7 +269,12 @@ - + { + self.events = self.timelineWindow.getEvents(); if (success) { - self.events = self.timelineWindow.getEvents(); return _getMoreIfNeeded.call(self); } else { return Promise.reject("Failed to paginate"); @@ -587,6 +599,7 @@ export default { this.onRoomJoined(null); } else { // Error. Done loading. + this.events = this.timelineWindow.getEvents(); this.initialLoadDone = true; } }) @@ -700,6 +713,9 @@ export default { case "m.room.avatar": return RoomAvatarChanged; + + case "m.room.history_visibility": + return RoomHistoryVisibility; } return DebugEvent; }, @@ -1039,7 +1055,7 @@ export default { /** Stop Read Receipt timer */ stopRRTimer() { if (this.rrTimer) { - clearInterval(this.rrTimer); + clearTimeout(this.rrTimer); this.rrTimer = null; } }, @@ -1049,32 +1065,48 @@ export default { */ restartRRTimer() { this.stopRRTimer(); - this.rrTimer = setInterval(this.rrTimerElapsed, READ_RECEIPT_TIMEOUT); + this.rrTimer = setTimeout(this.rrTimerElapsed, READ_RECEIPT_TIMEOUT); }, rrTimerElapsed() { - const container = this.$refs.chatContainer; - const el = util.getLastVisibleElement(container); - if (el) { - const eventId = el.getAttribute("eventId"); - if (eventId && this.room) { - const event = this.room.findEventById(eventId); - if (event && (!this.lastRR || event.getTs() > this.lastRR.getTs())) { - // Disable timer while we are sending - clearInterval(this.rrTimer); - this.rrTimer = null; + this.rrTimer = null; - // Send read receipt + const container = this.$refs.chatContainer; + const elFirst = util.getFirstVisibleElement(container); + const elLast = util.getLastVisibleElement(container); + if (elFirst && elLast) { + const eventIdFirst = elFirst.getAttribute("eventId"); + const eventIdLast = elLast.getAttribute("eventId"); + if (eventIdLast && this.room) { + var event = this.room.findEventById(eventIdLast); + const index = this.events.indexOf(event); + + // Walk backwards through visible events to the first one that is incoming + // + var lastTimestamp = 0; + if (this.lastRR) { + lastTimestamp = this.lastRR.getTs(); + } + + for (var i = index; i >= 0; i--) { + event = this.events[i]; + if (event == this.lastRR || event.getTs() <= lastTimestamp) { + // Already sent this or too old... + break; + } + // Is it an incoming event? + if (event.getSender() !== this.$matrix.currentUserId) { + // Send read receipt this.$matrix.matrixClient .sendReadReceipt(event) .then(() => { this.$matrix.matrixClient.setRoomReadMarkers( this.room.roomId, - eventId + event.getId() ); }) .then(() => { - console.log("RR sent for event: " + eventId); + console.log("RR sent for event: " + event.getId()); this.lastRR = event; }) .catch((err) => { @@ -1083,9 +1115,17 @@ export default { .finally(() => { this.restartRRTimer(); }); + return; // Bail out here + } + + // Stop iterating at first visible + if (event.getId() == eventIdFirst) { + break; + } } } } + this.restartRRTimer(); }, showDayMarkerBeforeEvent(event) { diff --git a/src/components/messages/RoomHistoryVisibility.vue b/src/components/messages/RoomHistoryVisibility.vue new file mode 100644 index 0000000..b3cfc26 --- /dev/null +++ b/src/components/messages/RoomHistoryVisibility.vue @@ -0,0 +1,34 @@ + + + + + \ No newline at end of file diff --git a/src/plugins/utils.js b/src/plugins/utils.js index 5439ba4..65e9f61 100644 --- a/src/plugins/utils.js +++ b/src/plugins/utils.js @@ -348,9 +348,17 @@ class Util { return null; } + getFirstVisibleElement(parentNode) { + const y = parentNode.scrollTop; + return this.getElementAtY(parentNode, y); + } + getLastVisibleElement(parentNode) { const y = parentNode.scrollTop + parentNode.clientHeight; + return this.getElementAtY(parentNode, y); + } + getElementAtY(parentNode, y) { let start = 0; let end = parentNode.children.length - 1;