mirror of
https://github.com/danog/telegram-tt.git
synced 2025-01-22 05:11:55 +01:00
Infinite Scroll: Prevent scroll jumps in global search and member list
This commit is contained in:
parent
6ad6494be1
commit
81f7ab1639
@ -187,6 +187,8 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
className="LeftSearch custom-scroll"
|
||||
items={foundMessages}
|
||||
onLoadMore={handleLoadMore}
|
||||
// To prevent scroll jumps caused by delayed local results rendering
|
||||
noScrollRestoreOnTop
|
||||
noFastList
|
||||
>
|
||||
{dateSearchQuery && (
|
||||
|
@ -314,6 +314,8 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
cacheBuster={cacheBuster}
|
||||
sensitiveArea={PROFILE_SENSITIVE_AREA}
|
||||
preloadBackwards={canRenderContents ? (resultType === 'members' ? MEMBERS_SLICE : SHARED_MEDIA_SLICE) : 0}
|
||||
// To prevent scroll jumps caused by reordering member list
|
||||
noScrollRestoreOnTop
|
||||
noFastList
|
||||
onLoadMore={getMore}
|
||||
onScroll={handleScroll}
|
||||
|
@ -18,6 +18,7 @@ type OwnProps = {
|
||||
preloadBackwards?: number;
|
||||
sensitiveArea?: number;
|
||||
noScrollRestore?: boolean;
|
||||
noScrollRestoreOnTop?: boolean;
|
||||
noFastList?: boolean;
|
||||
cacheBuster?: any;
|
||||
children: any;
|
||||
@ -38,6 +39,7 @@ const InfiniteScroll: FC<OwnProps> = ({
|
||||
sensitiveArea = DEFAULT_SENSITIVE_AREA,
|
||||
// Used to turn off restoring scroll position (e.g. for frequently re-ordered chat or user lists)
|
||||
noScrollRestore = false,
|
||||
noScrollRestoreOnTop = false,
|
||||
noFastList,
|
||||
// Used to re-query `listItemElements` if rendering is delayed by transition
|
||||
cacheBuster,
|
||||
@ -110,10 +112,14 @@ const InfiniteScroll: FC<OwnProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (noScrollRestoreOnTop && container.scrollTop === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetScroll(container, newScrollTop);
|
||||
|
||||
state.isScrollTopJustUpdated = true;
|
||||
}, [noScrollRestore, itemSelector, items, cacheBuster]);
|
||||
}, [items, itemSelector, noScrollRestore, noScrollRestoreOnTop, cacheBuster]);
|
||||
|
||||
const handleScroll = useCallback((e: UIEvent<HTMLDivElement>) => {
|
||||
if (loadMoreForwards && loadMoreBackwards) {
|
||||
|
@ -1,81 +0,0 @@
|
||||
import { RefObject, UIEvent } from 'react';
|
||||
import { LoadMoreDirection } from '../../types';
|
||||
|
||||
import React, {
|
||||
FC, useCallback, useEffect, useMemo, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import { debounce } from '../../util/schedulers';
|
||||
|
||||
type OwnProps = {
|
||||
ref?: RefObject<HTMLDivElement>;
|
||||
className?: string;
|
||||
onLoadMore: ({ direction }: { direction: LoadMoreDirection }) => void;
|
||||
onScroll?: (e: UIEvent<HTMLDivElement>) => void;
|
||||
items: any[];
|
||||
sensitiveArea?: number;
|
||||
preloadBackwards?: number;
|
||||
children: any;
|
||||
};
|
||||
|
||||
const DEFAULT_SENSITIVE_AREA = 1200;
|
||||
const DEFAULT_PRELOAD_BACKWARDS = 20;
|
||||
|
||||
const SimpleInfiniteScroll: FC<OwnProps> = ({
|
||||
ref,
|
||||
className,
|
||||
onLoadMore,
|
||||
onScroll,
|
||||
items,
|
||||
sensitiveArea = DEFAULT_SENSITIVE_AREA,
|
||||
preloadBackwards = DEFAULT_PRELOAD_BACKWARDS,
|
||||
children,
|
||||
}: OwnProps) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
let containerRef = useRef<HTMLDivElement>(null);
|
||||
if (ref) {
|
||||
containerRef = ref;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const anchorTopRef = useRef<number>(null);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const onLoadMoreDebounced = useMemo(() => debounce(onLoadMore, 1000, true, false), [onLoadMore, items]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!items || items.length < preloadBackwards) {
|
||||
onLoadMoreDebounced({ direction: LoadMoreDirection.Backwards });
|
||||
}
|
||||
}, [items, onLoadMoreDebounced, preloadBackwards]);
|
||||
|
||||
const handleScroll = useCallback((e: UIEvent<HTMLDivElement>) => {
|
||||
if (onScroll) {
|
||||
onScroll(e);
|
||||
}
|
||||
const container = e.target as HTMLElement;
|
||||
const anchor = container.firstElementChild;
|
||||
if (!anchor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { scrollTop, scrollHeight, offsetHeight } = container;
|
||||
const newAnchorTop = anchor.getBoundingClientRect().top;
|
||||
const isNearBottom = scrollHeight - (scrollTop + offsetHeight) <= sensitiveArea;
|
||||
const isMovingDown = typeof anchorTopRef.current === 'number' && newAnchorTop < anchorTopRef.current;
|
||||
|
||||
if (isNearBottom && isMovingDown) {
|
||||
onLoadMoreDebounced({ direction: LoadMoreDirection.Backwards });
|
||||
}
|
||||
|
||||
anchorTopRef.current = newAnchorTop;
|
||||
}, [onLoadMoreDebounced, onScroll, sensitiveArea]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={className} onScroll={handleScroll}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SimpleInfiniteScroll;
|
Loading…
x
Reference in New Issue
Block a user