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"
|
className="LeftSearch custom-scroll"
|
||||||
items={foundMessages}
|
items={foundMessages}
|
||||||
onLoadMore={handleLoadMore}
|
onLoadMore={handleLoadMore}
|
||||||
|
// To prevent scroll jumps caused by delayed local results rendering
|
||||||
|
noScrollRestoreOnTop
|
||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{dateSearchQuery && (
|
{dateSearchQuery && (
|
||||||
|
|||||||
@ -314,6 +314,8 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
cacheBuster={cacheBuster}
|
cacheBuster={cacheBuster}
|
||||||
sensitiveArea={PROFILE_SENSITIVE_AREA}
|
sensitiveArea={PROFILE_SENSITIVE_AREA}
|
||||||
preloadBackwards={canRenderContents ? (resultType === 'members' ? MEMBERS_SLICE : SHARED_MEDIA_SLICE) : 0}
|
preloadBackwards={canRenderContents ? (resultType === 'members' ? MEMBERS_SLICE : SHARED_MEDIA_SLICE) : 0}
|
||||||
|
// To prevent scroll jumps caused by reordering member list
|
||||||
|
noScrollRestoreOnTop
|
||||||
noFastList
|
noFastList
|
||||||
onLoadMore={getMore}
|
onLoadMore={getMore}
|
||||||
onScroll={handleScroll}
|
onScroll={handleScroll}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ type OwnProps = {
|
|||||||
preloadBackwards?: number;
|
preloadBackwards?: number;
|
||||||
sensitiveArea?: number;
|
sensitiveArea?: number;
|
||||||
noScrollRestore?: boolean;
|
noScrollRestore?: boolean;
|
||||||
|
noScrollRestoreOnTop?: boolean;
|
||||||
noFastList?: boolean;
|
noFastList?: boolean;
|
||||||
cacheBuster?: any;
|
cacheBuster?: any;
|
||||||
children: any;
|
children: any;
|
||||||
@ -38,6 +39,7 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
sensitiveArea = DEFAULT_SENSITIVE_AREA,
|
sensitiveArea = DEFAULT_SENSITIVE_AREA,
|
||||||
// Used to turn off restoring scroll position (e.g. for frequently re-ordered chat or user lists)
|
// Used to turn off restoring scroll position (e.g. for frequently re-ordered chat or user lists)
|
||||||
noScrollRestore = false,
|
noScrollRestore = false,
|
||||||
|
noScrollRestoreOnTop = false,
|
||||||
noFastList,
|
noFastList,
|
||||||
// Used to re-query `listItemElements` if rendering is delayed by transition
|
// Used to re-query `listItemElements` if rendering is delayed by transition
|
||||||
cacheBuster,
|
cacheBuster,
|
||||||
@ -110,10 +112,14 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (noScrollRestoreOnTop && container.scrollTop === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
resetScroll(container, newScrollTop);
|
resetScroll(container, newScrollTop);
|
||||||
|
|
||||||
state.isScrollTopJustUpdated = true;
|
state.isScrollTopJustUpdated = true;
|
||||||
}, [noScrollRestore, itemSelector, items, cacheBuster]);
|
}, [items, itemSelector, noScrollRestore, noScrollRestoreOnTop, cacheBuster]);
|
||||||
|
|
||||||
const handleScroll = useCallback((e: UIEvent<HTMLDivElement>) => {
|
const handleScroll = useCallback((e: UIEvent<HTMLDivElement>) => {
|
||||||
if (loadMoreForwards && loadMoreBackwards) {
|
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