Generalize infinite scroll, add to camera views
This commit is contained in:
parent
6feb3346a4
commit
af7424315d
@ -1,14 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import InfinitePhotoScroll, {
|
||||
InfinitePhotoScrollExternalProps,
|
||||
} from '../photo/InfinitePhotoScroll';
|
||||
import InfinitePhotoScroll from '../photo/InfinitePhotoScroll';
|
||||
import AdminPhotosTable from './AdminPhotosTable';
|
||||
|
||||
export default function AdminPhotosTableInfinite({
|
||||
initialOffset,
|
||||
itemsPerPage,
|
||||
}: InfinitePhotoScrollExternalProps) {
|
||||
}: {
|
||||
initialOffset: number
|
||||
itemsPerPage: number
|
||||
}) {
|
||||
return (
|
||||
<InfinitePhotoScroll
|
||||
cacheKey="AdminPhotoTable"
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import {
|
||||
INFINITE_SCROLL_INITIAL_GRID,
|
||||
INFINITE_SCROLL_MULTIPLE_GRID,
|
||||
generateOgImageMetaForPhotos,
|
||||
} from '@/photo';
|
||||
import PhotoGrid from '@/photo/PhotoGrid';
|
||||
import PhotosEmptyState from '@/photo/PhotosEmptyState';
|
||||
import { MAX_PHOTOS_TO_SHOW_OG } from '@/image-response';
|
||||
import { Metadata } from 'next/types';
|
||||
@ -12,7 +9,7 @@ import PhotoGridSidebar from '@/photo/PhotoGridSidebar';
|
||||
import { getPhotoSidebarData } from '@/photo/data';
|
||||
import { getPhotos } from '@/photo/db';
|
||||
import { cache } from 'react';
|
||||
import PhotoGridInfinite from '@/photo/PhotoGridInfinite';
|
||||
import PhotoGridPage from '@/photo/PhotoGridPage';
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
@ -40,16 +37,10 @@ export default async function GridPage() {
|
||||
|
||||
return (
|
||||
photos.length > 0
|
||||
? <SiteGrid
|
||||
contentMain={<div className="space-y-0.5 sm:space-y-1">
|
||||
<PhotoGrid {...{ photos }} />
|
||||
{photosCount > photos.length &&
|
||||
<PhotoGridInfinite
|
||||
initialOffset={INFINITE_SCROLL_INITIAL_GRID}
|
||||
itemsPerPage={INFINITE_SCROLL_MULTIPLE_GRID}
|
||||
/>}
|
||||
</div>}
|
||||
contentSide={<div className="sticky top-4 space-y-4 mt-[-4px]">
|
||||
? <PhotoGridPage
|
||||
photos={photos}
|
||||
count={photosCount}
|
||||
sidebar={<div className="sticky top-4 space-y-4 mt-[-4px]">
|
||||
<PhotoGridSidebar {...{
|
||||
tags,
|
||||
cameras,
|
||||
@ -57,7 +48,6 @@ export default async function GridPage() {
|
||||
photosCount,
|
||||
}} />
|
||||
</div>}
|
||||
sideHiddenOnMobile
|
||||
/>
|
||||
: <PhotosEmptyState />
|
||||
);
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { Metadata } from 'next/types';
|
||||
import { CameraProps, getCameraFromParams } from '@/camera';
|
||||
import { generateMetaForCamera } from '@/camera/meta';
|
||||
import { GRID_THUMBNAILS_TO_SHOW_MAX } from '@/photo';
|
||||
import { PaginationParams } from '@/site/pagination';
|
||||
import {
|
||||
getPhotosCameraDataCached,
|
||||
getPhotosCameraDataCachedWithPagination,
|
||||
} from '@/camera/data';
|
||||
GRID_THUMBNAILS_TO_SHOW_MAX,
|
||||
INFINITE_SCROLL_INITIAL_GRID,
|
||||
} from '@/photo';
|
||||
import { getPhotosCameraDataCached } from '@/camera/data';
|
||||
import CameraOverview from '@/camera/CameraOverview';
|
||||
|
||||
export async function generateMetadata({
|
||||
@ -46,23 +45,18 @@ export async function generateMetadata({
|
||||
};
|
||||
}
|
||||
|
||||
export default async function CameraPage({
|
||||
params,
|
||||
searchParams,
|
||||
}: CameraProps & PaginationParams) {
|
||||
export default async function CameraPage({ params }: CameraProps) {
|
||||
const camera = getCameraFromParams(params);
|
||||
|
||||
const {
|
||||
const [
|
||||
photos,
|
||||
count,
|
||||
showMorePath,
|
||||
dateRange,
|
||||
} = await getPhotosCameraDataCachedWithPagination({
|
||||
{ count, dateRange },
|
||||
] = await getPhotosCameraDataCached({
|
||||
camera,
|
||||
searchParams,
|
||||
limit: INFINITE_SCROLL_INITIAL_GRID,
|
||||
});
|
||||
|
||||
return (
|
||||
<CameraOverview {...{ camera, photos, count, dateRange, showMorePath }} />
|
||||
<CameraOverview {...{ camera, photos, count, dateRange }} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,12 +6,11 @@ import {
|
||||
import CameraShareModal from '@/camera/CameraShareModal';
|
||||
import { generateMetaForCamera } from '@/camera/meta';
|
||||
import { Metadata } from 'next/types';
|
||||
import { GRID_THUMBNAILS_TO_SHOW_MAX } from '@/photo';
|
||||
import { PaginationParams } from '@/site/pagination';
|
||||
import {
|
||||
getPhotosCameraDataCached,
|
||||
getPhotosCameraDataCachedWithPagination,
|
||||
} from '@/camera/data';
|
||||
GRID_THUMBNAILS_TO_SHOW_MAX,
|
||||
INFINITE_SCROLL_INITIAL_GRID,
|
||||
} from '@/photo';
|
||||
import { getPhotosCameraDataCached } from '@/camera/data';
|
||||
import CameraOverview from '@/camera/CameraOverview';
|
||||
|
||||
export async function generateMetadata({
|
||||
@ -51,20 +50,15 @@ export async function generateMetadata({
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Share({
|
||||
params,
|
||||
searchParams,
|
||||
}: CameraProps & PaginationParams) {
|
||||
export default async function Share({ params }: CameraProps) {
|
||||
const cameraFromParams = getCameraFromParams(params);
|
||||
|
||||
const {
|
||||
const [
|
||||
photos,
|
||||
count,
|
||||
dateRange,
|
||||
showMorePath,
|
||||
} = await getPhotosCameraDataCachedWithPagination({
|
||||
{ count, dateRange },
|
||||
] = await getPhotosCameraDataCached({
|
||||
camera: cameraFromParams,
|
||||
searchParams,
|
||||
limit: INFINITE_SCROLL_INITIAL_GRID,
|
||||
});
|
||||
|
||||
const camera = cameraFromPhoto(photos[0], cameraFromParams);
|
||||
@ -72,7 +66,7 @@ export default async function Share({
|
||||
return <>
|
||||
<CameraShareModal {...{ camera, photos, count, dateRange }} />
|
||||
<CameraOverview
|
||||
{...{ camera, photos, count, dateRange, showMorePath }}
|
||||
{...{ camera, photos, count, dateRange }}
|
||||
animateOnFirstLoadOnly
|
||||
/>
|
||||
</>;
|
||||
|
||||
@ -1,42 +1,28 @@
|
||||
import { Photo, PhotoDateRange } from '@/photo';
|
||||
import { Camera } from '.';
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import AnimateItems from '@/components/AnimateItems';
|
||||
import CameraHeader from './CameraHeader';
|
||||
import PhotoGrid from '@/photo/PhotoGrid';
|
||||
import PhotoGridPage from '@/photo/PhotoGridPage';
|
||||
|
||||
export default function CameraOverview({
|
||||
camera,
|
||||
photos,
|
||||
count,
|
||||
dateRange,
|
||||
showMorePath,
|
||||
animateOnFirstLoadOnly,
|
||||
}: {
|
||||
camera: Camera,
|
||||
photos: Photo[],
|
||||
count: number,
|
||||
dateRange?: PhotoDateRange,
|
||||
showMorePath?: string,
|
||||
animateOnFirstLoadOnly?: boolean,
|
||||
}) {
|
||||
return (
|
||||
<SiteGrid
|
||||
contentMain={<div className="space-y-8 mt-4">
|
||||
<AnimateItems
|
||||
type="bottom"
|
||||
items={[
|
||||
<CameraHeader
|
||||
key="CameraHeader"
|
||||
{...{ camera, photos, count, dateRange }}
|
||||
/>,
|
||||
]}
|
||||
animateOnFirstLoadOnly
|
||||
/>
|
||||
<PhotoGrid
|
||||
{...{ photos, camera, showMorePath, animateOnFirstLoadOnly }}
|
||||
/>
|
||||
</div>}
|
||||
/>
|
||||
<PhotoGridPage {...{
|
||||
photos,
|
||||
count,
|
||||
camera,
|
||||
animateOnFirstLoadOnly,
|
||||
header: <CameraHeader {...{ camera, photos, count, dateRange }} />,
|
||||
}} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
import {
|
||||
PaginationSearchParams,
|
||||
getPaginationFromSearchParams,
|
||||
} from '@/site/pagination';
|
||||
import { Camera } from '.';
|
||||
import {
|
||||
getPhotosCached,
|
||||
getPhotosCameraMetaCached,
|
||||
} from '@/photo/cache';
|
||||
import { pathForCamera } from '@/site/paths';
|
||||
|
||||
export const getPhotosCameraDataCached = ({
|
||||
camera,
|
||||
@ -20,32 +15,3 @@ export const getPhotosCameraDataCached = ({
|
||||
getPhotosCached({ camera, limit }),
|
||||
getPhotosCameraMetaCached(camera),
|
||||
]);
|
||||
|
||||
export const getPhotosCameraDataCachedWithPagination = async ({
|
||||
camera,
|
||||
limit: limitProp,
|
||||
searchParams,
|
||||
}: {
|
||||
camera: Camera,
|
||||
limit?: number,
|
||||
searchParams?: PaginationSearchParams,
|
||||
}) => {
|
||||
const { offset, limit } = getPaginationFromSearchParams(searchParams);
|
||||
|
||||
const [photos, { count, dateRange }] =
|
||||
await getPhotosCameraDataCached({
|
||||
camera,
|
||||
limit: limitProp ?? limit,
|
||||
});
|
||||
|
||||
const showMorePath = count > photos.length
|
||||
? pathForCamera(camera, offset + 1)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
photos,
|
||||
count,
|
||||
dateRange,
|
||||
showMorePath,
|
||||
};
|
||||
};
|
||||
|
||||
@ -13,26 +13,26 @@ import { getPhotosCachedAction, getPhotosAction } from '@/photo/actions';
|
||||
import { Photo } from '.';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { useAppState } from '@/state/AppState';
|
||||
import { Camera } from '@/camera';
|
||||
|
||||
export type RevalidatePhoto = (
|
||||
photoId: string,
|
||||
revalidateRemainingPhotos?: boolean,
|
||||
) => Promise<any>;
|
||||
|
||||
export type InfinitePhotoScrollExternalProps = {
|
||||
initialOffset: number
|
||||
itemsPerPage: number
|
||||
}
|
||||
|
||||
export default function InfinitePhotoScroll({
|
||||
cacheKey,
|
||||
initialOffset,
|
||||
itemsPerPage,
|
||||
camera,
|
||||
wrapMoreButtonInGrid,
|
||||
useCachedPhotos = true,
|
||||
includeHiddenPhotos,
|
||||
children,
|
||||
}: InfinitePhotoScrollExternalProps & {
|
||||
}: {
|
||||
initialOffset: number
|
||||
itemsPerPage: number
|
||||
camera?: Camera
|
||||
cacheKey: string
|
||||
wrapMoreButtonInGrid?: boolean
|
||||
useCachedPhotos?: boolean
|
||||
@ -55,17 +55,25 @@ export default function InfinitePhotoScroll({
|
||||
|
||||
const fetcher = useCallback(([_key, size]: [string, number]) =>
|
||||
useCachedPhotos
|
||||
? getPhotosCachedAction(
|
||||
initialOffset + size * itemsPerPage,
|
||||
itemsPerPage,
|
||||
includeHiddenPhotos ? 'include' : 'exclude',
|
||||
)
|
||||
: getPhotosAction(
|
||||
initialOffset + size * itemsPerPage,
|
||||
itemsPerPage,
|
||||
includeHiddenPhotos ? 'include' : 'exclude',
|
||||
)
|
||||
, [useCachedPhotos, initialOffset, itemsPerPage, includeHiddenPhotos]);
|
||||
? getPhotosCachedAction({
|
||||
offset: initialOffset + size * itemsPerPage,
|
||||
limit: itemsPerPage,
|
||||
hidden: includeHiddenPhotos ? 'include' : 'exclude',
|
||||
camera,
|
||||
})
|
||||
: getPhotosAction({
|
||||
offset: initialOffset + size * itemsPerPage,
|
||||
limit: itemsPerPage,
|
||||
hidden: includeHiddenPhotos ? 'include' : 'exclude',
|
||||
camera,
|
||||
})
|
||||
, [
|
||||
useCachedPhotos,
|
||||
initialOffset,
|
||||
itemsPerPage,
|
||||
includeHiddenPhotos,
|
||||
camera,
|
||||
]);
|
||||
|
||||
const { data, isLoading, isValidating, error, mutate, setSize } =
|
||||
useSwrInfinite<Photo[]>(
|
||||
|
||||
@ -1,25 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import InfinitePhotoScroll, {
|
||||
InfinitePhotoScrollExternalProps,
|
||||
} from './InfinitePhotoScroll';
|
||||
import { Camera } from '@/camera';
|
||||
import { INFINITE_SCROLL_MULTIPLE_GRID } from '.';
|
||||
import InfinitePhotoScroll from './InfinitePhotoScroll';
|
||||
import PhotoGrid from './PhotoGrid';
|
||||
|
||||
export default function PhotoGridInfinite({
|
||||
initialOffset,
|
||||
itemsPerPage,
|
||||
}: InfinitePhotoScrollExternalProps) {
|
||||
camera,
|
||||
animateOnFirstLoadOnly,
|
||||
}: {
|
||||
initialOffset: number,
|
||||
camera?: Camera,
|
||||
animateOnFirstLoadOnly?: boolean,
|
||||
}) {
|
||||
return (
|
||||
<InfinitePhotoScroll
|
||||
cacheKey="Grid"
|
||||
initialOffset={initialOffset}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPage={INFINITE_SCROLL_MULTIPLE_GRID}
|
||||
camera={camera}
|
||||
>
|
||||
{({ photos, onLastPhotoVisible }) =>
|
||||
<PhotoGrid
|
||||
photos={photos}
|
||||
onLastPhotoVisible={onLastPhotoVisible}
|
||||
/>}
|
||||
<PhotoGrid {...{
|
||||
photos,
|
||||
camera,
|
||||
onLastPhotoVisible,
|
||||
animateOnFirstLoadOnly,
|
||||
}} />}
|
||||
</InfinitePhotoScroll>
|
||||
);
|
||||
}
|
||||
|
||||
49
src/photo/PhotoGridPage.tsx
Normal file
49
src/photo/PhotoGridPage.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import { Photo } from '.';
|
||||
import PhotoGrid from './PhotoGrid';
|
||||
import PhotoGridInfinite from './PhotoGridInfinite';
|
||||
import { Camera } from '@/camera';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import AnimateItems from '@/components/AnimateItems';
|
||||
|
||||
export default function PhotoGridPage({
|
||||
photos,
|
||||
count,
|
||||
camera,
|
||||
animateOnFirstLoadOnly,
|
||||
header,
|
||||
sidebar,
|
||||
}: {
|
||||
photos: Photo[]
|
||||
count: number
|
||||
camera?: Camera
|
||||
animateOnFirstLoadOnly?: boolean
|
||||
header?: JSX.Element
|
||||
sidebar?: JSX.Element
|
||||
}) {
|
||||
return (
|
||||
<SiteGrid
|
||||
contentMain={<div className={clsx(
|
||||
header && 'space-y-8 mt-4',
|
||||
)}>
|
||||
{header &&
|
||||
<AnimateItems
|
||||
type="bottom"
|
||||
items={[header]}
|
||||
animateOnFirstLoadOnly
|
||||
/>}
|
||||
<div className="space-y-0.5 sm:space-y-1">
|
||||
<PhotoGrid {...{ photos, camera, animateOnFirstLoadOnly }} />
|
||||
{count > photos.length &&
|
||||
<PhotoGridInfinite {...{
|
||||
initialOffset: photos.length,
|
||||
camera,
|
||||
animateOnFirstLoadOnly,
|
||||
}} />}
|
||||
</div>
|
||||
</div>}
|
||||
contentSide={sidebar}
|
||||
sideHiddenOnMobile
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,14 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import InfinitePhotoScroll, {
|
||||
InfinitePhotoScrollExternalProps,
|
||||
} from './InfinitePhotoScroll';
|
||||
import InfinitePhotoScroll from './InfinitePhotoScroll';
|
||||
import PhotosLarge from './PhotosLarge';
|
||||
|
||||
export default function PhotosLargeInfinite({
|
||||
initialOffset,
|
||||
itemsPerPage,
|
||||
}: InfinitePhotoScrollExternalProps) {
|
||||
}: {
|
||||
initialOffset: number
|
||||
itemsPerPage: number
|
||||
}) {
|
||||
return (
|
||||
<InfinitePhotoScroll
|
||||
cacheKey="PhotosLarge"
|
||||
|
||||
@ -211,23 +211,17 @@ export const getPhotosTagHiddenMetaCachedAction = async () =>
|
||||
|
||||
// Public/Private actions
|
||||
|
||||
export const getPhotosAction = async (
|
||||
offset: number,
|
||||
limit: number,
|
||||
hidden?: GetPhotosOptions['hidden'],
|
||||
) => (hidden === 'include' || hidden === 'only')
|
||||
? safelyRunAdminServerAction(() =>
|
||||
getPhotos({ offset, hidden, limit }))
|
||||
: getPhotos({ offset, hidden, limit });
|
||||
export const getPhotosAction = async (options: GetPhotosOptions) =>
|
||||
(options.hidden === 'include' || options.hidden === 'only')
|
||||
? safelyRunAdminServerAction(() =>
|
||||
getPhotos(options))
|
||||
: getPhotos(options);
|
||||
|
||||
export const getPhotosCachedAction = async (
|
||||
offset: number,
|
||||
limit: number,
|
||||
hidden?: GetPhotosOptions['hidden'],
|
||||
) => (hidden === 'include' || hidden === 'only')
|
||||
? safelyRunAdminServerAction(() =>
|
||||
getPhotosCachedCached({ offset, hidden, limit }))
|
||||
: getPhotosCachedCached({ offset, hidden, limit });
|
||||
export const getPhotosCachedAction = async (options: GetPhotosOptions) =>
|
||||
(options.hidden === 'include' || options.hidden === 'only')
|
||||
? safelyRunAdminServerAction(() =>
|
||||
getPhotosCachedCached(options))
|
||||
: getPhotosCachedCached(options);
|
||||
|
||||
// Public actions
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user