From 85b7be76c1a41153dab8785e73922fe5fd4d21f2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 Apr 2022 11:33:32 +0100 Subject: [PATCH 1/2] Remove data-scrollbar workaround --- res/css/structures/_RoomView.scss | 4 ---- src/components/structures/ScrollPanel.tsx | 17 ----------------- 2 files changed, 21 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index c73068896db..eba8ae8f6e8 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -70,10 +70,6 @@ limitations under the License. overflow-y: auto; flex: 1 1 0; overflow-anchor: none; - - &[data-scrollbar=false] { - overflow-y: hidden; - } } .mx_RoomView_messagePanelSearchSpinner { diff --git a/src/components/structures/ScrollPanel.tsx b/src/components/structures/ScrollPanel.tsx index 359c10509d4..a246a1a9119 100644 --- a/src/components/structures/ScrollPanel.tsx +++ b/src/components/structures/ScrollPanel.tsx @@ -22,7 +22,6 @@ import AutoHideScrollbar from "./AutoHideScrollbar"; import { getKeyBindingsManager } from "../../KeyBindingsManager"; import ResizeNotifier from "../../utils/ResizeNotifier"; import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts"; -import UIStore, { UI_EVENTS } from "../../stores/UIStore"; const DEBUG_SCROLL = false; @@ -209,8 +208,6 @@ export default class ScrollPanel extends React.Component { componentDidMount() { this.checkScroll(); - - UIStore.instance.on(UI_EVENTS.Resize, this.onUiResize); } componentDidUpdate() { @@ -233,8 +230,6 @@ export default class ScrollPanel extends React.Component { if (this.props.resizeNotifier) { this.props.resizeNotifier.removeListener("middlePanelResizedNoisy", this.onResize); } - - UIStore.instance.off(UI_EVENTS.Resize, this.onUiResize); } private onScroll = ev => { @@ -721,17 +716,6 @@ export default class ScrollPanel extends React.Component { } } - private onUiResize = () => { - this.setDataScrollbar(); - }; - - private setDataScrollbar(contentHeight = this.getMessagesHeight()) { - const sn = this.getScrollNode(); - const minHeight = sn.clientHeight; - const displayScrollbar = contentHeight > minHeight; - sn.dataset.scrollbar = displayScrollbar.toString(); - } - // need a better name that also indicates this will change scrollTop? Rebalance height? Reveal content? private async updateHeight(): Promise { // wait until user has stopped scrolling @@ -753,7 +737,6 @@ export default class ScrollPanel extends React.Component { const minHeight = sn.clientHeight; const height = Math.max(minHeight, contentHeight); this.pages = Math.ceil(height / PAGE_SIZE); - this.setDataScrollbar(contentHeight); this.bottomGrowth = 0; const newHeight = `${this.getListHeight()}px`; From 51b11550fe65b2d5085dc24996d9be4b2f5c7b5f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 22 Apr 2022 12:31:47 +0100 Subject: [PATCH 2/2] Add a special-case to ScrollPanel list height calculation for when the timeline is very barren --- src/components/structures/ScrollPanel.tsx | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/components/structures/ScrollPanel.tsx b/src/components/structures/ScrollPanel.tsx index a246a1a9119..598f2488e8b 100644 --- a/src/components/structures/ScrollPanel.tsx +++ b/src/components/structures/ScrollPanel.tsx @@ -28,12 +28,11 @@ const DEBUG_SCROLL = false; // The amount of extra scroll distance to allow prior to unfilling. // See getExcessHeight. const UNPAGINATION_PADDING = 6000; -// The number of milliseconds to debounce calls to onUnfillRequest, to prevent -// many scroll events causing many unfilling requests. +// The number of milliseconds to debounce calls to onUnfillRequest, +// to prevent many scroll events causing many unfilling requests. const UNFILL_REQUEST_DEBOUNCE_MS = 200; -// _updateHeight makes the height a ceiled multiple of this so we -// don't have to update the height too often. It also allows the user -// to scroll past the pagination spinner a bit so they don't feel blocked so +// updateHeight makes the height a ceiled multiple of this so we don't have to update the height too often. +// It also allows the user to scroll past the pagination spinner a bit so they don't feel blocked so // much while the content loads. const PAGE_SIZE = 400; @@ -192,16 +191,14 @@ export default class ScrollPanel extends React.Component { private preventShrinkingState: IPreventShrinkingState; private unfillDebouncer: number; private bottomGrowth: number; - private pages: number; + private minListHeight: number; private heightUpdateInProgress: boolean; private divScroll: HTMLDivElement; constructor(props, context) { super(props, context); - if (this.props.resizeNotifier) { - this.props.resizeNotifier.on("middlePanelResizedNoisy", this.onResize); - } + this.props.resizeNotifier?.on("middlePanelResizedNoisy", this.onResize); this.resetScrollState(); } @@ -227,9 +224,7 @@ export default class ScrollPanel extends React.Component { // (We could use isMounted(), but facebook have deprecated that.) this.unmounted = true; - if (this.props.resizeNotifier) { - this.props.resizeNotifier.removeListener("middlePanelResizedNoisy", this.onResize); - } + this.props.resizeNotifier?.removeListener("middlePanelResizedNoisy", this.onResize); } private onScroll = ev => { @@ -541,7 +536,7 @@ export default class ScrollPanel extends React.Component { stuckAtBottom: this.props.startAtBottom, }; this.bottomGrowth = 0; - this.pages = 0; + this.minListHeight = 0; this.scrollTimeout = new Timer(100); this.heightUpdateInProgress = false; }; @@ -734,9 +729,13 @@ export default class ScrollPanel extends React.Component { const sn = this.getScrollNode(); const itemlist = this.itemlist.current; const contentHeight = this.getMessagesHeight(); - const minHeight = sn.clientHeight; - const height = Math.max(minHeight, contentHeight); - this.pages = Math.ceil(height / PAGE_SIZE); + // Only round to the nearest page when we're basing the height off the content, not off the scrollNode height + // otherwise it'll cause too much overscroll which makes it possible to entirely scroll content off-screen. + if (contentHeight < sn.clientHeight - PAGE_SIZE) { + this.minListHeight = sn.clientHeight; + } else { + this.minListHeight = Math.ceil(contentHeight / PAGE_SIZE) * PAGE_SIZE; + } this.bottomGrowth = 0; const newHeight = `${this.getListHeight()}px`; @@ -805,7 +804,7 @@ export default class ScrollPanel extends React.Component { } private getListHeight(): number { - return this.bottomGrowth + (this.pages * PAGE_SIZE); + return this.bottomGrowth + this.minListHeight; } private getMessagesHeight(): number {