don't add to the bottom if there is nothing at the bottom
This commit is contained in:
parent
a7866df1ff
commit
7e703f84fd
@ -1,13 +1,13 @@
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useEffect } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction } from 'react';
|
||||
import { useColumnReverseInfiniteScroll } from '../require/react-helper';
|
||||
import Retry from './retry';
|
||||
|
||||
export interface InfiniteScrollProps {
|
||||
fetchRetryCallable: () => Promise<void>;
|
||||
fetchAboveCallable: () => Promise<{ hasMoreAbove: boolean, removedFromBottom: boolean }>;
|
||||
fetchBelowCallable: () => Promise<{ hasMoreBelow: boolean, removedFromTop: boolean }>;
|
||||
fetchAboveCallable: () => Promise<void>;
|
||||
fetchBelowCallable: () => Promise<void>;
|
||||
setScrollRatio: Dispatch<SetStateAction<number>>;
|
||||
fetchResult: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null;
|
||||
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null;
|
||||
fetchError: unknown | null;
|
||||
fetchAboveError: unknown | null;
|
||||
fetchBelowError: unknown | null;
|
||||
@ -24,7 +24,7 @@ const InfiniteScroll: FC<InfiniteScrollProps> = (props: InfiniteScrollProps) =>
|
||||
fetchAboveCallable,
|
||||
fetchBelowCallable,
|
||||
setScrollRatio,
|
||||
fetchResult,
|
||||
ends,
|
||||
fetchError,
|
||||
fetchAboveError,
|
||||
fetchBelowError,
|
||||
@ -36,22 +36,16 @@ const InfiniteScroll: FC<InfiniteScrollProps> = (props: InfiniteScrollProps) =>
|
||||
|
||||
const [
|
||||
updateScrollCallable,
|
||||
onLoadCallable,
|
||||
fetchAboveRetry,
|
||||
fetchBelowRetry
|
||||
] = useColumnReverseInfiniteScroll(
|
||||
600,
|
||||
ends,
|
||||
fetchAboveCallable,
|
||||
fetchBelowCallable,
|
||||
setScrollRatio
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetchResult) {
|
||||
onLoadCallable(fetchResult)
|
||||
}
|
||||
}, [ fetchResult, onLoadCallable ]);
|
||||
|
||||
return (
|
||||
<div className="infinite-scroll-scroll-base" onScroll={updateScrollCallable}>
|
||||
<div className="infinite-scroll-elements">
|
||||
|
@ -20,7 +20,7 @@ const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
fetchAboveCallable,
|
||||
fetchBelowCallable,
|
||||
setScrollRatio,
|
||||
fetchResult,
|
||||
ends,
|
||||
messagesResult,
|
||||
messagesFetchError,
|
||||
messagesFetchAboveError,
|
||||
@ -46,7 +46,7 @@ const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
fetchAboveCallable={fetchAboveCallable}
|
||||
fetchBelowCallable={fetchBelowCallable}
|
||||
setScrollRatio={setScrollRatio}
|
||||
fetchResult={fetchResult}
|
||||
ends={ends}
|
||||
fetchError={messagesFetchError}
|
||||
fetchAboveError={messagesFetchAboveError}
|
||||
fetchBelowError={messagesFetchBelowError}
|
||||
|
@ -344,10 +344,10 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
fetchBelowFunc: ((reference: T) => Promise<T[] | null>),
|
||||
): [
|
||||
fetchRetryCallable: () => Promise<void>,
|
||||
fetchAboveCallable: () => Promise<{ hasMoreAbove: boolean, removedFromBottom: boolean }>,
|
||||
fetchBelowCallable: () => Promise<{ hasMoreBelow: boolean, removedFromTop: boolean }>,
|
||||
fetchAboveCallable: () => Promise<void>,
|
||||
fetchBelowCallable: () => Promise<void>,
|
||||
setScrollRatio: Dispatch<SetStateAction<number>>,
|
||||
fetchResult: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null,
|
||||
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null,
|
||||
lastResult: SubscriptionResult<T[]> | null,
|
||||
fetchError: unknown | null,
|
||||
fetchAboveError: unknown | null,
|
||||
@ -370,7 +370,7 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
const [ fetchAboveError, setFetchAboveError ] = useState<unknown | null>(null);
|
||||
const [ fetchBelowError, setFetchBelowError ] = useState<unknown | null>(null);
|
||||
|
||||
const [ fetchResult, setFetchResult ] = useState<{ hasMoreAbove: boolean, hasMoreBelow: boolean } | null>(null);
|
||||
const [ ends, setEnds ] = useState<{ hasMoreAbove: boolean, hasMoreBelow: boolean } | null>(null);
|
||||
|
||||
// Percentage of scroll from top. i.e. 300px from top of 1000px scroll = 0.3 scroll ratio
|
||||
const [ scrollRatio, setScrollRatio ] = useState<number>(0.5);
|
||||
@ -394,17 +394,17 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
return elements.slice(fromTop, elements.length - fromBottom);
|
||||
}
|
||||
|
||||
const fetchAboveCallable = useCallback(async (): Promise<{ hasMoreAbove: boolean, removedFromBottom: boolean }> => {
|
||||
if (!isMounted.current) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
if (!lastResult || !lastResult.value || lastResult.value.length === 0) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
if (guild !== lastResult.guild) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
const fetchAboveCallable = useCallback(async (): Promise<void> => {
|
||||
if (!isMounted.current) return;
|
||||
if (!lastResult || !lastResult.value || lastResult.value.length === 0) return;
|
||||
if (guild !== lastResult.guild) return;
|
||||
|
||||
try {
|
||||
const reference = lastResult.value[0] as T;
|
||||
const aboveElements = await fetchAboveFunc(reference);
|
||||
const lastResultAfterAwait = getStateAfterAwait(setLastResult);
|
||||
if (!isMounted.current) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
if (!isMounted.current) return;
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return;
|
||||
setFetchAboveError(null);
|
||||
if (aboveElements) {
|
||||
const hasMoreAbove = aboveElements.length >= maxFetchElements;
|
||||
@ -418,30 +418,30 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
}
|
||||
return { value: newValue, guild: lastResult.guild };
|
||||
});
|
||||
return { hasMoreAbove, removedFromBottom };
|
||||
setEnds(prev => (prev && { hasMoreBelow: removedFromBottom || prev.hasMoreBelow, hasMoreAbove: prev.hasMoreAbove }));
|
||||
} else {
|
||||
return { hasMoreAbove: false, removedFromBottom: false };
|
||||
setEnds(prev => (prev && { hasMoreBelow: prev.hasMoreBelow, hasMoreAbove: false }));
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
LOG.error('error fetching above for subscription', e);
|
||||
const lastResultAfterAwait = getStateAfterAwait(setLastResult);
|
||||
if (!isMounted.current) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return { hasMoreAbove: false, removedFromBottom: false };
|
||||
if (!isMounted.current) return;
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return;
|
||||
setFetchAboveError(e);
|
||||
return { hasMoreAbove: true, removedFromBottom: false };
|
||||
setEnds(prev => (prev && { hasMoreBelow: prev.hasMoreBelow, hasMoreAbove: true }))
|
||||
}
|
||||
}, [ guild, lastResult, fetchAboveFunc, maxFetchElements ]);
|
||||
|
||||
const fetchBelowCallable = useCallback(async (): Promise<{ hasMoreBelow: boolean, removedFromTop: boolean }> => {
|
||||
if (!isMounted.current) return { hasMoreBelow: false, removedFromTop: false };
|
||||
if (!lastResult || !lastResult.value || lastResult.value.length === 0) return { hasMoreBelow: false, removedFromTop: false };
|
||||
if (guild !== lastResult.guild) return { hasMoreBelow: false, removedFromTop: false };
|
||||
const fetchBelowCallable = useCallback(async (): Promise<void> => {
|
||||
if (!isMounted.current) return;
|
||||
if (!lastResult || !lastResult.value || lastResult.value.length === 0) return;
|
||||
if (guild !== lastResult.guild) return;
|
||||
try {
|
||||
const reference = lastResult.value[lastResult.value.length - 1] as T;
|
||||
const belowElements = await fetchBelowFunc(reference);
|
||||
const lastResultAfterAwait = getStateAfterAwait(setLastResult);
|
||||
if (!isMounted.current) return { hasMoreBelow: false, removedFromTop: false };
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return { hasMoreBelow: false, removedFromTop: false };
|
||||
if (!isMounted.current) return;
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return;
|
||||
setFetchBelowError(null);
|
||||
if (belowElements) {
|
||||
const hasMoreBelow = belowElements.length >= maxFetchElements;
|
||||
@ -454,18 +454,18 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
removedFromTop = true;
|
||||
}
|
||||
return { value: newValue, guild: lastResult.guild };
|
||||
})
|
||||
return { hasMoreBelow, removedFromTop };
|
||||
});
|
||||
setEnds(prev => (prev && { hasMoreBelow, hasMoreAbove: removedFromTop || prev.hasMoreAbove })); // :)
|
||||
} else {
|
||||
return { hasMoreBelow: false, removedFromTop: false };
|
||||
setEnds(prev => (prev && { hasMoreBelow: false, hasMoreAbove: prev.hasMoreAbove }))
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
LOG.error('error fetching below for subscription', e);
|
||||
const lastResultAfterAwait = getStateAfterAwait(setLastResult);
|
||||
if (!isMounted.current) return { hasMoreBelow: false, removedFromTop: false };
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return { hasMoreBelow: false, removedFromTop: false };
|
||||
if (!isMounted.current) return;
|
||||
if (!lastResultAfterAwait || guild !== lastResultAfterAwait.guild) return;
|
||||
setFetchBelowError(e);
|
||||
return { hasMoreBelow: true, removedFromTop: false };
|
||||
setEnds(prev => (prev && { hasMoreBelow: true, hasMoreAbove: prev.hasMoreAbove }));
|
||||
}
|
||||
}, [ lastResult, fetchBelowFunc, maxFetchElements ]);
|
||||
|
||||
@ -476,7 +476,7 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
fetchValue = fetchValue.slice(Math.max(fetchValue.length - maxElements)).sort(sortFunc);
|
||||
}
|
||||
//LOG.debug('Got items: ', { fetchValueLength: fetchValue?.length ?? '<empty>' })
|
||||
setFetchResult({ hasMoreAbove, hasMoreBelow: false });
|
||||
setEnds({ hasMoreAbove, hasMoreBelow: false });
|
||||
setLastResult({ value: fetchValue, guild: fetchValueGuild });
|
||||
setFetchError(null);
|
||||
}, [ sortFunc, maxFetchElements, maxElements ]);
|
||||
@ -494,6 +494,7 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
const newElements = newEventArgsMap(...args);
|
||||
setLastResult((lastResult) => {
|
||||
if (lastResult === null) return null;
|
||||
if (ends?.hasMoreBelow) return lastResult; // Don't add to bottom if we are not at the bottom
|
||||
let newValue = (lastResult.value ?? []).concat(newElements).sort(sortFunc);
|
||||
if (newValue.length > maxElements) {
|
||||
// Remove in a way that tries to keep the scrollbar position consistent
|
||||
@ -566,7 +567,7 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
fetchAboveCallable,
|
||||
fetchBelowCallable,
|
||||
setScrollRatio,
|
||||
fetchResult,
|
||||
ends,
|
||||
lastResult,
|
||||
fetchError,
|
||||
fetchAboveError,
|
||||
|
@ -252,12 +252,12 @@ export function useAsyncSubmitButton<ResultType>(
|
||||
|
||||
export function useColumnReverseInfiniteScroll(
|
||||
threshold: number,
|
||||
loadMoreAbove: () => Promise<{ hasMoreAbove: boolean, removedFromBottom: boolean }>,
|
||||
loadMoreBelow: () => Promise<{ hasMoreBelow: boolean, removedFromTop: boolean }>,
|
||||
ends: { hasMoreBelow: boolean, hasMoreAbove: boolean } | null,
|
||||
loadMoreAbove: () => Promise<void>,
|
||||
loadMoreBelow: () => Promise<void>,
|
||||
setScrollRatio: Dispatch<SetStateAction<number>>
|
||||
): [
|
||||
updateCallable: (event: UIEvent<HTMLElement>) => void,
|
||||
onLoadCallable: (params: { hasMoreAbove: boolean, hasMoreBelow: boolean }) => void,
|
||||
loadAboveRetry: () => Promise<void>,
|
||||
loadBelowRetry: () => Promise<void>
|
||||
] {
|
||||
@ -266,28 +266,23 @@ export function useColumnReverseInfiniteScroll(
|
||||
const [ loadingAbove, setLoadingAbove ] = useState<boolean>(false);
|
||||
const [ loadingBelow, setLoadingBelow ] = useState<boolean>(false);
|
||||
|
||||
const [ hasMoreAbove, setHasMoreAbove ] = useState<boolean>(false);
|
||||
const [ hasMoreBelow, setHasMoreBelow ] = useState<boolean>(false);
|
||||
|
||||
const loadAbove = useCallback(async () => {
|
||||
if (loadingAbove || !hasMoreAbove) return;
|
||||
if (loadingAbove) return;
|
||||
if (!ends || !ends.hasMoreAbove) return;
|
||||
setLoadingAbove(true);
|
||||
const loadResult = await loadMoreAbove();
|
||||
await loadMoreAbove();
|
||||
if (!isMounted.current) return;
|
||||
setHasMoreAbove(loadResult.hasMoreAbove);
|
||||
setHasMoreBelow(oldHasMoreBelow => oldHasMoreBelow || loadResult.removedFromBottom);
|
||||
setLoadingAbove(false);
|
||||
}, [ loadingAbove, hasMoreAbove, loadMoreAbove ]);
|
||||
}, [ loadingAbove, loadMoreAbove, ends ]);
|
||||
|
||||
const loadBelow = useCallback(async () => {
|
||||
if (loadingBelow || !hasMoreBelow) return;
|
||||
if (loadingBelow) return;
|
||||
if (!ends || !ends.hasMoreBelow) return;
|
||||
setLoadingBelow(true);
|
||||
const loadResult = await loadMoreBelow();
|
||||
await loadMoreBelow();
|
||||
if (!isMounted.current) return;
|
||||
setHasMoreBelow(loadResult.hasMoreBelow);
|
||||
setHasMoreAbove(oldHasMoreAbove => oldHasMoreAbove || loadResult.removedFromTop);
|
||||
setLoadingBelow(false);
|
||||
}, [ loadingBelow, hasMoreBelow, loadMoreBelow ]);
|
||||
}, [ loadingBelow, loadMoreBelow, ends ]);
|
||||
|
||||
const onScrollCallable = useCallback(async (event: UIEvent<HTMLElement>) => {
|
||||
const scrollTop = event.currentTarget.scrollTop;
|
||||
@ -318,12 +313,7 @@ export function useColumnReverseInfiniteScroll(
|
||||
}
|
||||
}, [ setScrollRatio, loadAbove, loadBelow, threshold ]);
|
||||
|
||||
const onLoadCallable = useCallback((params: { hasMoreAbove: boolean, hasMoreBelow: boolean }) => {
|
||||
setHasMoreAbove(params.hasMoreAbove);
|
||||
setHasMoreBelow(params.hasMoreBelow);
|
||||
}, []);
|
||||
|
||||
return [ onScrollCallable, onLoadCallable, loadAbove, loadBelow ];
|
||||
return [ onScrollCallable, loadAbove, loadBelow ];
|
||||
}
|
||||
|
||||
// Makes sure to also allow you to fly-out a click starting inside of the ref'd element but was dragged outside
|
||||
@ -485,7 +475,7 @@ export function useContextClickContextMenu(
|
||||
return createContextMenu(alignment, relativeToPos, close);
|
||||
}, [ alignment, relativeToPos ]);
|
||||
|
||||
const [ contextMenu, toggle, close, open ] = useContextMenu(createContextMenuWithRelativeToPos, createContextMenuDeps);
|
||||
const [ contextMenu, _toggle, _close, open ] = useContextMenu(createContextMenuWithRelativeToPos, createContextMenuDeps);
|
||||
|
||||
const onContextMenu = useCallback((event: React.MouseEvent<HTMLElement>) => {
|
||||
setRelativeToPos({ x: event.clientX, y: event.clientY });
|
||||
|
Loading…
Reference in New Issue
Block a user