Finalize initial swr implementation
This commit is contained in:
parent
a08a2f8fb4
commit
4fa85316e8
@ -10,12 +10,15 @@ import { usePathname } from 'next/navigation';
|
||||
import { BiTrash } from 'react-icons/bi';
|
||||
import MoreMenu from '@/components/MoreMenu';
|
||||
import { useAppState } from '@/state/AppState';
|
||||
import { RevalidatePhotos } from '@/photo/InfinitePhotoScroll';
|
||||
|
||||
export default function AdminPhotoMenuClient({
|
||||
photo,
|
||||
revalidatePhoto,
|
||||
...props
|
||||
}: Omit<ComponentProps<typeof MoreMenu>, 'items'> & {
|
||||
photo: Photo
|
||||
revalidatePhoto?: RevalidatePhotos
|
||||
}) {
|
||||
const { isUserSignedIn } = useAppState();
|
||||
|
||||
@ -46,7 +49,7 @@ export default function AdminPhotoMenuClient({
|
||||
action: () => toggleFavoritePhotoAction(
|
||||
photo.id,
|
||||
shouldRedirectFav,
|
||||
),
|
||||
).then(() => revalidatePhoto?.()),
|
||||
}, {
|
||||
label: 'Delete',
|
||||
icon: <BiTrash
|
||||
@ -59,7 +62,9 @@ export default function AdminPhotoMenuClient({
|
||||
photo.id,
|
||||
photo.url,
|
||||
shouldRedirectDelete,
|
||||
);
|
||||
).then(() => {
|
||||
revalidatePhoto?.(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -8,6 +8,9 @@ import SiteGrid from '@/components/SiteGrid';
|
||||
import Spinner from '@/components/Spinner';
|
||||
import { getPhotosAction } from '@/photo/actions';
|
||||
import { useAppState } from '@/state/AppState';
|
||||
import { Photo } from '.';
|
||||
|
||||
export type RevalidatePhotos = (revalidateRemainingPhotos?: boolean) => void;
|
||||
|
||||
export default function InfinitePhotoScroll({
|
||||
key = 'PHOTOS',
|
||||
@ -15,7 +18,6 @@ export default function InfinitePhotoScroll({
|
||||
itemsPerPage = 12,
|
||||
prefetch = true,
|
||||
triggerOnView = true,
|
||||
debug = true,
|
||||
}: {
|
||||
key?: string
|
||||
initialOffset?: number
|
||||
@ -28,27 +30,25 @@ export default function InfinitePhotoScroll({
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const fetcher = useCallback((key: string) => {
|
||||
if (debug) { console.log('Fetching', key); }
|
||||
const offset = parseInt(key.split('-')[1]);
|
||||
return getPhotosAction(
|
||||
initialOffset + offset * itemsPerPage,
|
||||
const fetcher = useCallback(([_key, size]: [string, number]) =>
|
||||
getPhotosAction(
|
||||
initialOffset + size * itemsPerPage,
|
||||
itemsPerPage,
|
||||
key,
|
||||
);
|
||||
}, [initialOffset, itemsPerPage, debug]);
|
||||
)
|
||||
, [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 { data, isLoading, error, mutate, size, setSize } =
|
||||
useSwrInfinite<Photo[]>(
|
||||
(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
|
||||
@ -56,7 +56,7 @@ export default function InfinitePhotoScroll({
|
||||
|
||||
useEffect(() => {
|
||||
if (prefetch) {
|
||||
preload(`${key}-${size ?? 0 + 1}`, fetcher);
|
||||
preload([key, size ?? 0 + 1], fetcher);
|
||||
}
|
||||
}, [prefetch, key, size, fetcher]);
|
||||
|
||||
@ -81,7 +81,17 @@ export default function InfinitePhotoScroll({
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{data && <div className="space-y-1">
|
||||
{data.map((photos, i) => <PhotosLarge key={i} photos={photos} />)}
|
||||
{data.map((photos, i) =>
|
||||
<PhotosLarge
|
||||
key={i}
|
||||
photos={photos}
|
||||
revalidatePhotos={(revalidateRemainingPhotos?: boolean) => {
|
||||
mutate(data, {
|
||||
revalidate: (_data: any, [_, size]:[string, number]) =>
|
||||
revalidateRemainingPhotos ? size >= i : size === i,
|
||||
} as any);
|
||||
}}
|
||||
/>)}
|
||||
</div>}
|
||||
{!isFinished &&
|
||||
<SiteGrid
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Photo,
|
||||
altTextForPhoto,
|
||||
@ -19,6 +21,7 @@ import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid';
|
||||
import PhotoLink from './PhotoLink';
|
||||
import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config';
|
||||
import AdminPhotoMenuClient from '@/admin/AdminPhotoMenuClient';
|
||||
import { RevalidatePhotos } from './InfinitePhotoScroll';
|
||||
|
||||
export default function PhotoLarge({
|
||||
photo,
|
||||
@ -26,6 +29,7 @@ export default function PhotoLarge({
|
||||
priority,
|
||||
prefetch = SHOULD_PREFETCH_ALL_LINKS,
|
||||
prefetchRelatedLinks = SHOULD_PREFETCH_ALL_LINKS,
|
||||
revalidatePhoto,
|
||||
showCamera = true,
|
||||
showSimulation = true,
|
||||
shouldShareTag,
|
||||
@ -38,6 +42,7 @@ export default function PhotoLarge({
|
||||
priority?: boolean
|
||||
prefetch?: boolean
|
||||
prefetchRelatedLinks?: boolean
|
||||
revalidatePhoto?: RevalidatePhotos
|
||||
showCamera?: boolean
|
||||
showSimulation?: boolean
|
||||
shouldShareTag?: boolean
|
||||
@ -87,7 +92,10 @@ export default function PhotoLarge({
|
||||
prefetch={prefetch}
|
||||
/>
|
||||
<div className="absolute right-0 translate-y-[-4px] z-10">
|
||||
<AdminPhotoMenuClient photo={photo} />
|
||||
<AdminPhotoMenuClient {...{
|
||||
photo,
|
||||
revalidatePhoto,
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-baseline">
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import AnimateItems from '@/components/AnimateItems';
|
||||
import { Photo } from '.';
|
||||
import PhotoLarge from './PhotoLarge';
|
||||
import { RevalidatePhotos } from './InfinitePhotoScroll';
|
||||
|
||||
export default function PhotosLarge({
|
||||
photos,
|
||||
animate = true,
|
||||
prefetchFirstPhotoLinks,
|
||||
revalidatePhotos,
|
||||
}: {
|
||||
photos: Photo[]
|
||||
animate?: boolean
|
||||
prefetchFirstPhotoLinks?: boolean
|
||||
revalidatePhotos?: RevalidatePhotos
|
||||
}) {
|
||||
return (
|
||||
<AnimateItems
|
||||
@ -25,6 +28,7 @@ export default function PhotosLarge({
|
||||
photo={photo}
|
||||
priority={index <= 1}
|
||||
prefetchRelatedLinks={prefetchFirstPhotoLinks && index === 0}
|
||||
revalidatePhoto={revalidatePhotos}
|
||||
/>)}
|
||||
itemKeys={photos.map(photo => photo.id)}
|
||||
/>
|
||||
|
||||
@ -220,14 +220,5 @@ export async function getPhotoItemsAction(query: string) {
|
||||
: [];
|
||||
}
|
||||
|
||||
export const getPhotosAction = async (
|
||||
offset: number,
|
||||
limit: number,
|
||||
cacheKey: string,
|
||||
) =>
|
||||
getPhotosCachedCached({ offset, limit }).then(photos =>
|
||||
photos.map(photo => ({
|
||||
...photo,
|
||||
cacheKey,
|
||||
}))
|
||||
);
|
||||
export const getPhotosAction = async (offset: number, limit: number) =>
|
||||
getPhotosCachedCached({ offset, limit });
|
||||
|
||||
@ -83,7 +83,6 @@ export interface Photo extends PhotoDb {
|
||||
exposureTimeFormatted?: string
|
||||
exposureCompensationFormatted?: string
|
||||
takenAtNaiveFormatted: string
|
||||
cacheKey?: string
|
||||
}
|
||||
|
||||
export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user