From 94fe9999db265589cc5dfeb0e7e7723e220c7abc Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 4 Apr 2017 11:55:53 +0100 Subject: [PATCH] Reimplement _saveScrollState The actual fix to https://github.com/vector-im/riot-web/issues/3175 is this change to `_saveScrollState`, which is to pick the trackedScrollToken based on which node is intersected by the bottom of the scroll panel. This is opposed to the previous logic that picked based on which node was the first from the bottom to be above the bottom of the viewport. In the case where the viewport bottom does not intersect any events, the topmost event is used. --- src/components/structures/ScrollPanel.js | 31 +++++++++++++++------- src/components/structures/TimelinePanel.js | 2 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 44176f73af..7460d6dac8 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -589,24 +589,35 @@ module.exports = React.createClass({ var itemlist = this.refs.itemlist; var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect(); var messages = itemlist.children; + let newScrollState = null; for (var i = messages.length-1; i >= 0; --i) { var node = messages[i]; if (!node.dataset.scrollToken) continue; var boundingRect = node.getBoundingClientRect(); - if (boundingRect.bottom < wrapperRect.bottom) { - this.scrollState = { - stuckAtBottom: false, - trackedScrollToken: node.dataset.scrollToken, - pixelOffset: wrapperRect.bottom - boundingRect.bottom, - }; - debuglog("ScrollPanel: saved scroll state", this.scrollState); - return; + newScrollState = { + stuckAtBottom: false, + trackedScrollToken: node.dataset.scrollToken, + pixelOffset: wrapperRect.bottom - boundingRect.bottom, + }; + // If the bottom of the panel intersects the ClientRect of node, use this node + // as the scrollToken. + // If this is false for the entire for-loop, we default to the last node + // (which is why newScrollState is set on every iteration). + if (boundingRect.top < wrapperRect.bottom && + wrapperRect.bottom < boundingRect.bottom) { + // Use this node as the scrollToken + break; } } - - debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport"); + // This is only false if there were no nodes with `node.dataset.scrollToken` set. + if (newScrollState) { + this.scrollState = newScrollState; + debuglog("ScrollPanel: saved scroll state", this.scrollState); + } else { + debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport"); + } }, _restoreSavedScrollState: function() { diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 0a7a2c90b7..ffeb5b9a1f 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -263,7 +263,7 @@ var TimelinePanel = React.createClass({ } ); - let count = backwards ? marker : this.state.events.length - marker; + let count = backwards ? marker + 1 : this.state.events.length - marker; if (count > 0) { debuglog("TimelinePanel: Unpaginating", count, "in direction", dir);