'use client'; import { preload } from 'swr'; import useSwrInfinite from 'swr/infinite'; import PhotosLarge from '@/photo/PhotosLarge'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import SiteGrid from '@/components/SiteGrid'; import Spinner from '@/components/Spinner'; import { getPhotosAction } from '@/photo/actions'; import { useAppState } from '@/state/AppState'; import { Photo } from '.'; import PhotoGrid from './PhotoGrid'; const KEY = 'PHOTOS'; export type RevalidatePhoto = ( photoId: string, revalidateRemainingPhotos?: boolean, ) => Promise; export default function InfinitePhotoScroll({ type = 'full-frame', initialOffset = 0, itemsPerPage = 12, prefetch = true, triggerOnView = true, }: { type?: 'full-frame' | 'grid' initialOffset?: number itemsPerPage?: number prefetch?: boolean triggerOnView?: boolean debug?: boolean }) { const { isUserSignedIn } = useAppState(); const buttonRef = useRef(null); const fetcher = useCallback(([_key, size]: [string, number]) => getPhotosAction( initialOffset + size * itemsPerPage, itemsPerPage, ) , [initialOffset, itemsPerPage]); const { data, isLoading, error, mutate, size, setSize } = useSwrInfinite( (size: number, prev: []) => prev && prev.length === 0 ? null : [KEY, size], fetcher, { revalidateOnFocus: isUserSignedIn, revalidateOnReconnect: isUserSignedIn, revalidateFirstPage: isUserSignedIn, }, ); const isFinished = useMemo(() => data && data[data.length - 1]?.length < itemsPerPage , [data, itemsPerPage]); useEffect(() => { if (prefetch) { preload([KEY, size ?? 0 + 1], fetcher); } }, [prefetch, size, fetcher]); const advance = useCallback(() => setSize(size => size + 1), [setSize]); useEffect(() => { // Only add observer if button is rendered if (buttonRef.current) { const observer = new IntersectionObserver(e => { if (triggerOnView && e[0].isIntersecting) { advance(); } }, { root: null, threshold: 0, }); observer.observe(buttonRef.current); return () => observer.disconnect(); } }, [triggerOnView, advance]); const photos = useMemo(() => (data ?? [])?.flat(), [data]); const revalidatePhoto: RevalidatePhoto = useCallback(( photoId: string, revalidateRemainingPhotos?: boolean, ) => mutate(data, { revalidate: (_data: Photo[], [_, size]:[string, number]) => { const i = (data ?? []).findIndex(photos => photos.some(photo => photo.id === photoId)); return revalidateRemainingPhotos ? size >= i : size === i; }, } as any), [data, mutate]); const renderMoreButton = () => ; return (
{type === 'full-frame' ? : } {!isFinished && type === 'full-frame' ? : renderMoreButton()}
); }