Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 880a484

Browse files
authored
Merge pull request #785 from matrix-org/luke/fix-scroll-past-big-event
Fix scroll token selection logic
2 parents 755ea96 + b0a04e6 commit 880a484

File tree

2 files changed

+39
-33
lines changed

2 files changed

+39
-33
lines changed

src/components/structures/ScrollPanel.js

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -333,33 +333,27 @@ module.exports = React.createClass({
333333
if (excessHeight <= 0) {
334334
return;
335335
}
336-
var itemlist = this.refs.itemlist;
337-
var tiles = itemlist.children;
336+
const tiles = this.refs.itemlist.children;
338337

339338
// The scroll token of the first/last tile to be unpaginated
340339
let markerScrollToken = null;
341340

342-
// Subtract clientHeights to simulate the events being unpaginated whilst counting
343-
// the events to be unpaginated.
344-
if (backwards) {
345-
// Iterate forwards from start of tiles, subtracting event tile height
346-
let i = 0;
347-
while (i < tiles.length && excessHeight > tiles[i].clientHeight) {
348-
excessHeight -= tiles[i].clientHeight;
349-
if (tiles[i].dataset.scrollToken) {
350-
markerScrollToken = tiles[i].dataset.scrollToken;
351-
}
352-
i++;
341+
// Subtract heights of tiles to simulate the tiles being unpaginated until the
342+
// excess height is less than the height of the next tile to subtract. This
343+
// prevents excessHeight becoming negative, which could lead to future
344+
// pagination.
345+
//
346+
// If backwards is true, we unpaginate (remove) tiles from the back (top).
347+
for (let i = 0; i < tiles.length; i++) {
348+
const tile = tiles[backwards ? tiles.length - 1 - i : i];
349+
// Subtract height of tile as if it were unpaginated
350+
excessHeight -= tile.clientHeight;
351+
// The tile may not have a scroll token, so guard it
352+
if (tile.dataset.scrollToken) {
353+
markerScrollToken = tile.dataset.scrollToken;
353354
}
354-
} else {
355-
// Iterate backwards from end of tiles, subtracting event tile height
356-
let i = tiles.length - 1;
357-
while (i > 0 && excessHeight > tiles[i].clientHeight) {
358-
excessHeight -= tiles[i].clientHeight;
359-
if (tiles[i].dataset.scrollToken) {
360-
markerScrollToken = tiles[i].dataset.scrollToken;
361-
}
362-
i--;
355+
if (tile.clientHeight > excessHeight) {
356+
break;
363357
}
364358
}
365359

@@ -589,24 +583,34 @@ module.exports = React.createClass({
589583
var itemlist = this.refs.itemlist;
590584
var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
591585
var messages = itemlist.children;
586+
let newScrollState = null;
592587

593588
for (var i = messages.length-1; i >= 0; --i) {
594589
var node = messages[i];
595590
if (!node.dataset.scrollToken) continue;
596591

597592
var boundingRect = node.getBoundingClientRect();
598-
if (boundingRect.bottom < wrapperRect.bottom) {
599-
this.scrollState = {
600-
stuckAtBottom: false,
601-
trackedScrollToken: node.dataset.scrollToken,
602-
pixelOffset: wrapperRect.bottom - boundingRect.bottom,
603-
};
604-
debuglog("ScrollPanel: saved scroll state", this.scrollState);
605-
return;
593+
newScrollState = {
594+
stuckAtBottom: false,
595+
trackedScrollToken: node.dataset.scrollToken,
596+
pixelOffset: wrapperRect.bottom - boundingRect.bottom,
597+
};
598+
// If the bottom of the panel intersects the ClientRect of node, use this node
599+
// as the scrollToken.
600+
// If this is false for the entire for-loop, we default to the last node
601+
// (which is why newScrollState is set on every iteration).
602+
if (boundingRect.top < wrapperRect.bottom) {
603+
// Use this node as the scrollToken
604+
break;
606605
}
607606
}
608-
609-
debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport");
607+
// This is only false if there were no nodes with `node.dataset.scrollToken` set.
608+
if (newScrollState) {
609+
this.scrollState = newScrollState;
610+
debuglog("ScrollPanel: saved scroll state", this.scrollState);
611+
} else {
612+
debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport");
613+
}
610614
},
611615

612616
_restoreSavedScrollState: function() {

src/components/structures/TimelinePanel.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,12 @@ var TimelinePanel = React.createClass({
251251
},
252252

253253
onMessageListUnfillRequest: function(backwards, scrollToken) {
254+
// If backwards, unpaginate from the back (i.e. the start of the timeline)
254255
let dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
255256
debuglog("TimelinePanel: unpaginating events in direction", dir);
256257

257-
// All tiles are inserted by MessagePanel to have a scrollToken === eventId
258+
// All tiles are inserted by MessagePanel to have a scrollToken === eventId, and
259+
// this particular event should be the first or last to be unpaginated.
258260
let eventId = scrollToken;
259261

260262
let marker = this.state.events.findIndex(

0 commit comments

Comments
 (0)