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

Commit eba815a

Browse files
committed
Debounce read marker update on scroll
Reverts #6751 in favour of debouncing the updates to read markers, because it seems allowing the scroll to be 1px away from the bottom was important for some browsers and meant they never got to the bottom. We can fix the problem instead by debouncing the update to read markers, because the scroll state gets reset back to the bottom when componentDidUpdate() runs which happens after the read marker code does a setState(). This should probably be debounced anyway since it doesn't need to be run that frequently. Fixes element-hq/element-web#18961 Type: bugfix
1 parent 25cd66d commit eba815a

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

src/components/structures/ScrollPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export default class ScrollPanel extends React.Component<IProps> {
276276
// for scrollTop happen on certain browsers/platforms
277277
// when scrolled all the way down. E.g. Chrome 72 on debian.
278278
// so check difference < 1;
279-
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) < 1;
279+
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
280280
};
281281

282282
// returns the vertical height in the given direction that can be removed from

src/components/structures/TimelinePanel.tsx

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,14 @@ import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
4747
import Spinner from "../views/elements/Spinner";
4848
import EditorStateTransfer from '../../utils/EditorStateTransfer';
4949
import ErrorDialog from '../views/dialogs/ErrorDialog';
50+
import { debounce } from 'lodash';
5051

5152
const PAGINATE_SIZE = 20;
5253
const INITIAL_SIZE = 20;
5354
const READ_RECEIPT_INTERVAL_MS = 500;
5455

56+
const READ_MARKER_DEBOUNCE_MS = 100;
57+
5558
const DEBUG = false;
5659

5760
let debuglog = function(...s: any[]) {};
@@ -475,22 +478,35 @@ class TimelinePanel extends React.Component<IProps, IState> {
475478
}
476479

477480
if (this.props.manageReadMarkers) {
478-
const rmPosition = this.getReadMarkerPosition();
479-
// we hide the read marker when it first comes onto the screen, but if
480-
// it goes back off the top of the screen (presumably because the user
481-
// clicks on the 'jump to bottom' button), we need to re-enable it.
482-
if (rmPosition < 0) {
483-
this.setState({ readMarkerVisible: true });
484-
}
485-
486-
// if read marker position goes between 0 and -1/1,
487-
// (and user is active), switch timeout
488-
const timeout = this.readMarkerTimeout(rmPosition);
489-
// NO-OP when timeout already has set to the given value
490-
this.readMarkerActivityTimer.changeTimeout(timeout);
481+
this.doManageReadMarkers();
491482
}
492483
};
493484

485+
/*
486+
* Debounced function to manage read markers because we don't need to
487+
* do this on every tiny scroll update. It also sets state which causes
488+
* a component update, which can in turn reset the scroll position, so
489+
* it's important we allow the browser to scroll a bit before running this
490+
* (hence trailing edge only and debounce rather than throttle because
491+
* we really only need to update this once the user has finished scrolling,
492+
* not periodically while they scroll).
493+
*/
494+
private doManageReadMarkers = debounce(() => {
495+
const rmPosition = this.getReadMarkerPosition();
496+
// we hide the read marker when it first comes onto the screen, but if
497+
// it goes back off the top of the screen (presumably because the user
498+
// clicks on the 'jump to bottom' button), we need to re-enable it.
499+
if (rmPosition < 0) {
500+
this.setState({ readMarkerVisible: true });
501+
}
502+
503+
// if read marker position goes between 0 and -1/1,
504+
// (and user is active), switch timeout
505+
const timeout = this.readMarkerTimeout(rmPosition);
506+
// NO-OP when timeout already has set to the given value
507+
this.readMarkerActivityTimer.changeTimeout(timeout);
508+
}, READ_MARKER_DEBOUNCE_MS, { leading: false, trailing: true });
509+
494510
private onAction = (payload: ActionPayload): void => {
495511
switch (payload.action) {
496512
case "ignore_state_changed":

0 commit comments

Comments
 (0)