Refactor infinite scroll component

This commit is contained in:
Sam Becker 2024-04-28 13:43:01 -05:00
parent 765f8367e5
commit eb59e58b1c
6 changed files with 101 additions and 35 deletions

View File

@ -10,9 +10,9 @@ import { MAX_PHOTOS_TO_SHOW_OG } from '@/image-response';
import { Metadata } from 'next/types';
import PhotoGridSidebar from '@/photo/PhotoGridSidebar';
import { getPhotoSidebarData } from '@/photo/data';
import InfinitePhotoScroll from '@/photo/InfinitePhotoScroll';
import { getPhotos } from '@/services/vercel-postgres';
import { cache } from 'react';
import InfinitePhotoScrollGrid from '@/photo/InfinitePhotoScrollGrid';
export const dynamic = 'force-static';
@ -41,8 +41,7 @@ export default async function GridPage() {
contentMain={<div className="space-y-0.5 sm:space-y-1">
<PhotoGrid {...{ photos }} />
{photosCount > photos.length &&
<InfinitePhotoScroll
type='grid'
<InfinitePhotoScrollGrid
initialOffset={INFINITE_SCROLL_INITIAL_GRID}
itemsPerPage={INFINITE_SCROLL_MULTIPLE_GRID}
/>}

View File

@ -6,10 +6,11 @@ import {
import PhotosEmptyState from '@/photo/PhotosEmptyState';
import { Metadata } from 'next/types';
import { MAX_PHOTOS_TO_SHOW_OG } from '@/image-response';
import InfinitePhotoScroll from '../photo/InfinitePhotoScroll';
import PhotosLarge from '@/photo/PhotosLarge';
import { cache } from 'react';
import { getPhotos, getPhotosCount } from '@/services/vercel-postgres';
import InfinitePhotoScrollPhotosLarge from
'@/photo/InfinitePhotoScrollPhotosLarge';
export const dynamic = 'force-static';
@ -43,8 +44,7 @@ export default async function HomePage() {
? <div className="space-y-1">
<PhotosLarge {...{ photos }} />
{photosCount > photos.length &&
<InfinitePhotoScroll
type="full-frame"
<InfinitePhotoScrollPhotosLarge
initialOffset={INFINITE_SCROLL_INITIAL_HOME}
itemsPerPage={INFINITE_SCROLL_MULTIPLE_HOME}
/>}

View File

@ -1,17 +1,16 @@
'use client';
import useSwrInfinite from 'swr/infinite';
import PhotosLarge from '@/photo/PhotosLarge';
import {
ReactNode,
useCallback,
useMemo,
useRef,
} from 'react';
import SiteGrid from '@/components/SiteGrid';
import Spinner from '@/components/Spinner';
import { getPhotosAction } from '@/photo/actions';
import { getPhotosCachedAction, getPhotosAction } from '@/photo/actions';
import { Photo } from '.';
import PhotoGrid from './PhotoGrid';
import { clsx } from 'clsx/lite';
import { useAppState } from '@/state/AppState';
@ -20,19 +19,31 @@ export type RevalidatePhoto = (
revalidateRemainingPhotos?: boolean,
) => Promise<any>;
export default function InfinitePhotoScroll({
type = 'full-frame',
initialOffset,
itemsPerPage,
}: {
type: 'full-frame' | 'grid'
export type InfinitePhotoScrollExternalProps = {
initialOffset: number
itemsPerPage: number
debug?: boolean
}
export default function InfinitePhotoScroll({
cacheKey,
initialOffset,
itemsPerPage,
wrapMoreButtonInGrid,
useCachedPhotos = true,
children,
}: InfinitePhotoScrollExternalProps & {
cacheKey: string
wrapMoreButtonInGrid: boolean
useCachedPhotos?: boolean
children: (props: {
photos: Photo[]
onLastPhotoVisible: () => void
revalidatePhoto?: RevalidatePhoto
}) => ReactNode
}) {
const { swrTimestamp, isUserSignedIn } = useAppState();
const key = `${swrTimestamp}-${type}`;
const key = `${swrTimestamp}-${cacheKey}`;
const keyGenerator = useCallback(
(size: number, prev: Photo[]) => prev && prev.length === 0
@ -40,12 +51,17 @@ export default function InfinitePhotoScroll({
: [key, size]
, [key]);
const fetcher = useCallback(([_key, size]: [string, number]) => {
return getPhotosAction(
initialOffset + size * itemsPerPage,
itemsPerPage,
);
}, [initialOffset, itemsPerPage]);
const fetcher = useCallback(([_key, size]: [string, number]) =>
useCachedPhotos
? getPhotosCachedAction(
initialOffset + size * itemsPerPage,
itemsPerPage,
)
: getPhotosAction(
initialOffset + size * itemsPerPage,
itemsPerPage,
)
, [useCachedPhotos, initialOffset, itemsPerPage]);
const { data, isLoading, isValidating, error, mutate, setSize } =
useSwrInfinite<Photo[]>(
@ -106,17 +122,12 @@ export default function InfinitePhotoScroll({
return (
<div className="space-y-4">
{type === 'full-frame'
? <PhotosLarge {...{
photos,
revalidatePhoto,
onLastPhotoVisible: advance,
}} />
: <PhotoGrid {...{
photos,
onLastPhotoVisible: advance,
}} />}
{!isFinished && (type === 'full-frame'
{children({
photos,
onLastPhotoVisible: advance,
revalidatePhoto,
})}
{!isFinished && (wrapMoreButtonInGrid
? <SiteGrid contentMain={renderMoreButton()} />
: renderMoreButton())}
</div>

View File

@ -0,0 +1,26 @@
'use client';
import InfinitePhotoScroll, {
InfinitePhotoScrollExternalProps,
} from './InfinitePhotoScroll';
import PhotoGrid from './PhotoGrid';
export default function InfinitePhotoScrollGrid({
initialOffset,
itemsPerPage,
}: InfinitePhotoScrollExternalProps) {
return (
<InfinitePhotoScroll
cacheKey="Grid"
initialOffset={initialOffset}
itemsPerPage={itemsPerPage}
wrapMoreButtonInGrid={false}
>
{({ photos, onLastPhotoVisible }) =>
<PhotoGrid {...{
photos,
onLastPhotoVisible,
}} />}
</InfinitePhotoScroll>
);
}

View File

@ -0,0 +1,27 @@
'use client';
import InfinitePhotoScroll, {
InfinitePhotoScrollExternalProps,
} from './InfinitePhotoScroll';
import PhotosLarge from './PhotosLarge';
export default function InfinitePhotoScrollPhotosLarge({
initialOffset,
itemsPerPage,
}: InfinitePhotoScrollExternalProps) {
return (
<InfinitePhotoScroll
cacheKey="PhotosLarge"
initialOffset={initialOffset}
itemsPerPage={itemsPerPage}
wrapMoreButtonInGrid
>
{({ photos, onLastPhotoVisible, revalidatePhoto }) =>
<PhotosLarge {...{
photos,
onLastPhotoVisible,
revalidatePhoto,
}} />}
</InfinitePhotoScroll>
);
}

View File

@ -195,9 +195,12 @@ export async function streamAiImageQueryAction(
streamOpenAiImageQuery(imageBase64, AI_IMAGE_QUERIES[query]));
}
export const getPhotosAction = async (offset: number, limit: number) =>
export const getPhotosCachedAction = async (offset: number, limit: number) =>
getPhotosCachedCached({ offset, limit });
export const getPhotosAction = async (offset: number, limit: number) =>
getPhotos({ offset, limit });
export const queryPhotosByTitleAction = async (query: string) =>
(await getPhotos({ query, limit: 10 }))
.filter(({ title }) => Boolean(title));