Another go at fixing the jumpy scroll

The most recent problem was that we were setting _lastSetScroll whenever we
wrote to scrollTop (and ignoring the next scroll event which matched that
offset), but if there was no change to scrollTop, we wouldn't actually get a
scroll event, so would ignore some future scroll event instead.

Make sure that we only set _lastSetScroll if there's a change to scrollTop.

(Fixes https://github.com/vector-im/vector-web/issues/1162, more)
This commit is contained in:
Richard van der Hoff 2016-03-22 19:33:02 +00:00
parent a82c0580d2
commit 4fd49976ae

View file

@ -321,12 +321,7 @@ module.exports = React.createClass({
// saved is to do the scroll, then save the updated state. (Calculating // saved is to do the scroll, then save the updated state. (Calculating
// it ourselves is hard, and we can't rely on an onScroll callback // it ourselves is hard, and we can't rely on an onScroll callback
// happening, since there may be no user-visible change here). // happening, since there may be no user-visible change here).
var scrollNode = this._getScrollNode(); this._setScrollTop(Number.MAX_VALUE);
scrollNode.scrollTop = scrollNode.scrollHeight;
debuglog("Scrolled to bottom; offset now", scrollNode.scrollTop);
this._lastSetScroll = scrollNode.scrollTop;
this._saveScrollState(); this._saveScrollState();
}, },
@ -390,9 +385,9 @@ module.exports = React.createClass({
var boundingRect = node.getBoundingClientRect(); var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom; var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
if(scrollDelta != 0) { if(scrollDelta != 0) {
scrollNode.scrollTop += scrollDelta; this._setScrollTop(scrollNode.scrollTop + scrollDelta);
// see the comments in onMessageListScroll regarding recentEventScroll // see the comments in onScroll regarding recentEventScroll
this.recentEventScroll = scrollNode.scrollTop; this.recentEventScroll = scrollNode.scrollTop;
} }
@ -437,15 +432,34 @@ module.exports = React.createClass({
var scrollNode = this._getScrollNode(); var scrollNode = this._getScrollNode();
if (scrollState.stuckAtBottom) { if (scrollState.stuckAtBottom) {
scrollNode.scrollTop = scrollNode.scrollHeight; this._setScrollTop(Number.MAX_VALUE);
debuglog("Scrolled to bottom; offset now", scrollNode.scrollTop);
} else if (scrollState.trackedScrollToken) { } else if (scrollState.trackedScrollToken) {
this._scrollToToken(scrollState.trackedScrollToken, this._scrollToToken(scrollState.trackedScrollToken,
scrollState.pixelOffset); scrollState.pixelOffset);
} }
this._lastSetScroll = scrollNode.scrollTop;
}, },
_setScrollTop: function(scrollTop) {
var scrollNode = this._getScrollNode();
var prevScroll = scrollNode.scrollTop;
scrollNode.scrollTop = scrollTop;
// If this change generates a scroll event, we should not update the saved
// scroll state on it. See the comments in onScroll.
//
// If we *don't* expect a scroll event, we need to leave _lastSetScroll
// alone, otherwise we'll end up ignoring a future scroll event which is
// nothing to do with this change.
if (scrollNode.scrollTop != prevScroll) {
this._lastSetScroll = scrollNode.scrollTop;
}
debuglog("Set scrollTop:", scrollNode.scrollTop,
"requested:", scrollTop,
"_lastSetScroll:", this._lastSetScroll);
},
/* get the DOM node which has the scrollTop property we care about for our /* get the DOM node which has the scrollTop property we care about for our
* message panel. * message panel.
@ -457,17 +471,7 @@ module.exports = React.createClass({
throw new Error("ScrollPanel._getScrollNode called when unmounted"); throw new Error("ScrollPanel._getScrollNode called when unmounted");
} }
var panel = ReactDOM.findDOMNode(this.refs.geminiPanel); return this.refs.geminiPanel.scrollbar.getViewElement();
// If the gemini scrollbar is doing its thing, this will be a div within
// the message panel (ie, the gemini container); otherwise it will be the
// message panel itself.
if (panel.classList.contains('gm-prevented')) {
return panel;
} else {
return panel.children[2]; // XXX: Fragile!
}
}, },
render: function() { render: function() {