/** Creates useful state for an infinite scroll subscription */ export function useColumnReverseInfiniteScroll( threshold: number, ends: { hasMoreBelow: boolean, hasMoreAbove: boolean } | null, loadMoreAbove: () => Promise, loadMoreBelow: () => Promise, setScrollRatio: Dispatch> ): [ updateCallable: (event: UIEvent) => void, loadAboveRetry: () => Promise, loadBelowRetry: () => Promise ] { const isMounted = useIsMountedRef(); const [ loadingAbove, setLoadingAbove ] = useState(false); const [ loadingBelow, setLoadingBelow ] = useState(false); const loadAbove = useCallback(async () => { if (loadingAbove) return; if (!ends || !ends.hasMoreAbove) return; setLoadingAbove(true); await loadMoreAbove(); if (!isMounted.current) return; setLoadingAbove(false); }, [ loadingAbove, loadMoreAbove, ends ]); const loadBelow = useCallback(async () => { if (loadingBelow) return; if (!ends || !ends.hasMoreBelow) return; setLoadingBelow(true); await loadMoreBelow(); if (!isMounted.current) return; setLoadingBelow(false); }, [ loadingBelow, loadMoreBelow, ends ]); const onScrollCallable = useCallback(async (event: UIEvent) => { const scrollTop = event.currentTarget.scrollTop; const scrollHeight = event.currentTarget.scrollHeight; const clientHeight = event.currentTarget.clientHeight; // WARNING // There's likely an inconsistency between browsers on this so have fun when you're working // on the cross-platform implementation of this // scrollTop apparantly is negative for column-reverse divs (this actually kindof makes sense if you flip your head upside down) // have to reverse this // I expect this was a change with some version of chromium. // MDN documentation issue: https://github.com/mdn/content/issues/10968 setScrollRatio(Math.abs(scrollTop / scrollHeight)); const distToTop = -(clientHeight - scrollHeight - scrollTop); // keep in mind scrollTop is negative >:] const distToBottom = -scrollTop; //LOG.debug(`scroll callable update. to top: ${distToTop}, to bottom: ${distToBottom}`) if (distToTop < threshold) { await loadAbove(); } if (distToBottom < threshold) { await loadBelow(); } }, [ setScrollRatio, loadAbove, loadBelow, threshold ]); return [ onScrollCallable, loadAbove, loadBelow ]; }