Apply full photo set behavior to all sets
This commit is contained in:
parent
dcfc04c842
commit
bc87d2ec0f
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
RELATED_GRID_PHOTOS_TO_SHOW,
|
||||||
descriptionForPhoto,
|
descriptionForPhoto,
|
||||||
titleForPhoto,
|
titleForPhoto,
|
||||||
} from '@/photo';
|
} from '@/photo';
|
||||||
@ -10,12 +11,21 @@ import {
|
|||||||
absolutePathForPhotoImage,
|
absolutePathForPhotoImage,
|
||||||
} from '@/site/paths';
|
} from '@/site/paths';
|
||||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||||
import { getPhotoCached } from '@/photo/cache';
|
|
||||||
import { ReactNode, cache } from 'react';
|
import { ReactNode, cache } from 'react';
|
||||||
import { FilmSimulation } from '@/simulation';
|
import { FilmSimulation } from '@/simulation';
|
||||||
import { getPhotosFilmSimulationDataCached } from '@/simulation/data';
|
import {
|
||||||
|
getPhotosFilmSimulationMetaCached,
|
||||||
|
getPhotosNearIdCached,
|
||||||
|
} from '@/photo/cache';
|
||||||
|
|
||||||
const getPhotoCachedCached = cache(getPhotoCached);
|
const getPhotosNearIdCachedCached = cache((
|
||||||
|
photoId: string,
|
||||||
|
simulation: FilmSimulation,
|
||||||
|
) =>
|
||||||
|
getPhotosNearIdCached(
|
||||||
|
photoId,
|
||||||
|
{ simulation, limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
|
||||||
|
));
|
||||||
|
|
||||||
interface PhotoFilmSimulationProps {
|
interface PhotoFilmSimulationProps {
|
||||||
params: { photoId: string, simulation: FilmSimulation }
|
params: { photoId: string, simulation: FilmSimulation }
|
||||||
@ -24,7 +34,7 @@ interface PhotoFilmSimulationProps {
|
|||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params: { photoId, simulation },
|
params: { photoId, simulation },
|
||||||
}: PhotoFilmSimulationProps): Promise<Metadata> {
|
}: PhotoFilmSimulationProps): Promise<Metadata> {
|
||||||
const photo = await getPhotoCachedCached(photoId);
|
const { photo } = await getPhotosNearIdCachedCached(photoId, simulation);
|
||||||
|
|
||||||
if (!photo) { return {}; }
|
if (!photo) { return {}; }
|
||||||
|
|
||||||
@ -55,17 +65,24 @@ export default async function PhotoFilmSimulationPage({
|
|||||||
params: { photoId, simulation },
|
params: { photoId, simulation },
|
||||||
children,
|
children,
|
||||||
}: PhotoFilmSimulationProps & { children: ReactNode }) {
|
}: PhotoFilmSimulationProps & { children: ReactNode }) {
|
||||||
const photo = await getPhotoCachedCached(photoId);
|
const { photo, photos, photosGrid, indexNumber } =
|
||||||
|
await getPhotosNearIdCachedCached(photoId, simulation);
|
||||||
|
|
||||||
if (!photo) { redirect(PATH_ROOT); }
|
if (!photo) { redirect(PATH_ROOT); }
|
||||||
|
|
||||||
const [
|
const { count, dateRange } =
|
||||||
photos,
|
await getPhotosFilmSimulationMetaCached(simulation);
|
||||||
{ count, dateRange },
|
|
||||||
] = await getPhotosFilmSimulationDataCached({ simulation });
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
<PhotoDetailPage {...{ photo, photos, simulation, count, dateRange }} />
|
<PhotoDetailPage {...{
|
||||||
|
photo,
|
||||||
|
photos,
|
||||||
|
photosGrid,
|
||||||
|
simulation,
|
||||||
|
indexNumber,
|
||||||
|
count,
|
||||||
|
dateRange,
|
||||||
|
}} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
|||||||
import { getPhotosNearIdCached } from '@/photo/cache';
|
import { getPhotosNearIdCached } from '@/photo/cache';
|
||||||
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PAGES } from '@/site/config';
|
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PAGES } from '@/site/config';
|
||||||
import { GENERATE_STATIC_PARAMS_LIMIT, getPhotoIds } from '@/photo/db';
|
import { GENERATE_STATIC_PARAMS_LIMIT, getPhotoIds } from '@/photo/db';
|
||||||
import { cache } from 'react';
|
import { ReactNode, cache } from 'react';
|
||||||
|
|
||||||
const getPhotosNearIdCachedCached = cache((photoId: string, limit: number) =>
|
const getPhotosNearIdCachedCached = cache((photoId: string) =>
|
||||||
getPhotosNearIdCached(photoId, { limit }));
|
getPhotosNearIdCached(photoId, { limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 }));
|
||||||
|
|
||||||
export let generateStaticParams:
|
export let generateStaticParams:
|
||||||
(() => Promise<{ photoId: string }[]>) | undefined = undefined;
|
(() => Promise<{ photoId: string }[]>) | undefined = undefined;
|
||||||
@ -36,10 +36,7 @@ interface PhotoProps {
|
|||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params: { photoId },
|
params: { photoId },
|
||||||
}:PhotoProps): Promise<Metadata> {
|
}:PhotoProps): Promise<Metadata> {
|
||||||
const { photo } = await getPhotosNearIdCachedCached(
|
const { photo } = await getPhotosNearIdCachedCached(photoId);
|
||||||
photoId,
|
|
||||||
RELATED_GRID_PHOTOS_TO_SHOW + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!photo) { return {}; }
|
if (!photo) { return {}; }
|
||||||
|
|
||||||
@ -69,27 +66,14 @@ export async function generateMetadata({
|
|||||||
export default async function PhotoPage({
|
export default async function PhotoPage({
|
||||||
params: { photoId },
|
params: { photoId },
|
||||||
children,
|
children,
|
||||||
}: PhotoProps & { children: React.ReactNode }) {
|
}: PhotoProps & { children: ReactNode }) {
|
||||||
const { photos, photo } = await getPhotosNearIdCachedCached(
|
const { photo, photos, photosGrid } =
|
||||||
photoId,
|
await getPhotosNearIdCachedCached(photoId);
|
||||||
RELATED_GRID_PHOTOS_TO_SHOW + 2,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!photo) { redirect(PATH_ROOT); }
|
if (!photo) { redirect(PATH_ROOT); }
|
||||||
|
|
||||||
const isPhotoFirst = photos.findIndex(p => p.id === photoId) === 0;
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
<PhotoDetailPage
|
<PhotoDetailPage {...{ photo, photos, photosGrid }} />
|
||||||
photo={photo}
|
|
||||||
photos={photos}
|
|
||||||
photosGrid={photos.slice(
|
|
||||||
isPhotoFirst ? 1 : 2,
|
|
||||||
isPhotoFirst
|
|
||||||
? RELATED_GRID_PHOTOS_TO_SHOW + 1
|
|
||||||
: RELATED_GRID_PHOTOS_TO_SHOW + 2,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
INFINITE_SCROLL_GRID_PHOTO_INITIAL,
|
RELATED_GRID_PHOTOS_TO_SHOW,
|
||||||
descriptionForPhoto,
|
descriptionForPhoto,
|
||||||
titleForPhoto,
|
titleForPhoto,
|
||||||
} from '@/photo';
|
} from '@/photo';
|
||||||
@ -11,17 +11,33 @@ import {
|
|||||||
absolutePathForPhotoImage,
|
absolutePathForPhotoImage,
|
||||||
} from '@/site/paths';
|
} from '@/site/paths';
|
||||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||||
import { getPhotoCached } from '@/photo/cache';
|
import {
|
||||||
import { PhotoCameraProps, cameraFromPhoto } from '@/camera';
|
getPhotosCameraMetaCached,
|
||||||
import { getPhotosCameraDataCached } from '@/camera/data';
|
getPhotosNearIdCached,
|
||||||
|
} from '@/photo/cache';
|
||||||
|
import {
|
||||||
|
PhotoCameraProps,
|
||||||
|
cameraFromPhoto,
|
||||||
|
getCameraFromParams,
|
||||||
|
} from '@/camera';
|
||||||
import { ReactNode, cache } from 'react';
|
import { ReactNode, cache } from 'react';
|
||||||
|
|
||||||
const getPhotoCachedCached = cache(getPhotoCached);
|
const getPhotosNearIdCachedCached = cache((
|
||||||
|
photoId: string,
|
||||||
|
make: string,
|
||||||
|
model: string,
|
||||||
|
) =>
|
||||||
|
getPhotosNearIdCached(
|
||||||
|
photoId, {
|
||||||
|
camera: getCameraFromParams({ make, model }),
|
||||||
|
limit: RELATED_GRID_PHOTOS_TO_SHOW + 2,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params: { photoId, make, model },
|
params: { photoId, make, model },
|
||||||
}: PhotoCameraProps): Promise<Metadata> {
|
}: PhotoCameraProps): Promise<Metadata> {
|
||||||
const photo = await getPhotoCachedCached(photoId);
|
const { photo } = await getPhotosNearIdCachedCached(photoId, make, model);
|
||||||
|
|
||||||
if (!photo) { return {}; }
|
if (!photo) { return {}; }
|
||||||
|
|
||||||
@ -56,22 +72,25 @@ export default async function PhotoCameraPage({
|
|||||||
params: { photoId, make, model },
|
params: { photoId, make, model },
|
||||||
children,
|
children,
|
||||||
}: PhotoCameraProps & { children: ReactNode }) {
|
}: PhotoCameraProps & { children: ReactNode }) {
|
||||||
const photo = await getPhotoCachedCached(photoId);
|
const { photo, photos, photosGrid, indexNumber } =
|
||||||
|
await getPhotosNearIdCachedCached(photoId, make, model);
|
||||||
|
|
||||||
if (!photo) { redirect(PATH_ROOT); }
|
if (!photo) { redirect(PATH_ROOT); }
|
||||||
|
|
||||||
const [
|
const camera = cameraFromPhoto(photo, { make, model });
|
||||||
photos,
|
|
||||||
{ count, dateRange },
|
const { count, dateRange } = await getPhotosCameraMetaCached(camera);
|
||||||
camera,
|
|
||||||
] = await getPhotosCameraDataCached(
|
|
||||||
make,
|
|
||||||
model,
|
|
||||||
INFINITE_SCROLL_GRID_PHOTO_INITIAL,
|
|
||||||
);
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
<PhotoDetailPage {...{ photo, photos, camera, count, dateRange }} />
|
<PhotoDetailPage {...{
|
||||||
|
photo,
|
||||||
|
photos,
|
||||||
|
photosGrid,
|
||||||
|
camera,
|
||||||
|
indexNumber,
|
||||||
|
count,
|
||||||
|
dateRange,
|
||||||
|
}} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
RELATED_GRID_PHOTOS_TO_SHOW,
|
||||||
descriptionForPhoto,
|
descriptionForPhoto,
|
||||||
titleForPhoto,
|
titleForPhoto,
|
||||||
} from '@/photo';
|
} from '@/photo';
|
||||||
@ -10,11 +11,17 @@ import {
|
|||||||
absolutePathForPhotoImage,
|
absolutePathForPhotoImage,
|
||||||
} from '@/site/paths';
|
} from '@/site/paths';
|
||||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||||
import { getPhotoCached } from '@/photo/cache';
|
import {
|
||||||
import { getPhotosTagDataCached } from '@/tag/data';
|
getPhotosNearIdCached,
|
||||||
|
getPhotosTagMetaCached,
|
||||||
|
} from '@/photo/cache';
|
||||||
import { ReactNode, cache } from 'react';
|
import { ReactNode, cache } from 'react';
|
||||||
|
|
||||||
const getPhotoCachedCached = cache(getPhotoCached);
|
const getPhotosNearIdCachedCached = cache((photoId: string, tag: string) =>
|
||||||
|
getPhotosNearIdCached(
|
||||||
|
photoId,
|
||||||
|
{ tag, limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
|
||||||
|
));
|
||||||
|
|
||||||
interface PhotoTagProps {
|
interface PhotoTagProps {
|
||||||
params: { photoId: string, tag: string }
|
params: { photoId: string, tag: string }
|
||||||
@ -23,7 +30,7 @@ interface PhotoTagProps {
|
|||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params: { photoId, tag },
|
params: { photoId, tag },
|
||||||
}: PhotoTagProps): Promise<Metadata> {
|
}: PhotoTagProps): Promise<Metadata> {
|
||||||
const photo = await getPhotoCachedCached(photoId);
|
const { photo } = await getPhotosNearIdCachedCached(photoId, tag);
|
||||||
|
|
||||||
if (!photo) { return {}; }
|
if (!photo) { return {}; }
|
||||||
|
|
||||||
@ -54,17 +61,23 @@ export default async function PhotoTagPage({
|
|||||||
params: { photoId, tag },
|
params: { photoId, tag },
|
||||||
children,
|
children,
|
||||||
}: PhotoTagProps & { children: ReactNode }) {
|
}: PhotoTagProps & { children: ReactNode }) {
|
||||||
const photo = await getPhotoCachedCached(photoId);
|
const { photo, photos, photosGrid, indexNumber } =
|
||||||
|
await getPhotosNearIdCachedCached(photoId, tag);
|
||||||
|
|
||||||
if (!photo) { redirect(PATH_ROOT); }
|
if (!photo) { redirect(PATH_ROOT); }
|
||||||
|
|
||||||
const [
|
const { count, dateRange } = await getPhotosTagMetaCached(tag);
|
||||||
photos,
|
|
||||||
{ count, dateRange },
|
|
||||||
] = await getPhotosTagDataCached({ tag });
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
<PhotoDetailPage {...{ photo, photos, tag, count, dateRange }} />
|
<PhotoDetailPage {...{
|
||||||
|
photo,
|
||||||
|
photos,
|
||||||
|
photosGrid,
|
||||||
|
tag,
|
||||||
|
indexNumber,
|
||||||
|
count,
|
||||||
|
dateRange,
|
||||||
|
}} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,24 @@
|
|||||||
import { descriptionForPhoto, titleForPhoto } from '@/photo';
|
import {
|
||||||
|
RELATED_GRID_PHOTOS_TO_SHOW,
|
||||||
|
descriptionForPhoto,
|
||||||
|
titleForPhoto,
|
||||||
|
} from '@/photo';
|
||||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||||
import { getPhotoCached, getPhotosCached } from '@/photo/cache';
|
import {
|
||||||
|
getPhotosNearIdCached,
|
||||||
|
getPhotosTagHiddenMetaCached,
|
||||||
|
} from '@/photo/cache';
|
||||||
import { PATH_ROOT, absolutePathForPhoto } from '@/site/paths';
|
import { PATH_ROOT, absolutePathForPhoto } from '@/site/paths';
|
||||||
import { TAG_HIDDEN } from '@/tag';
|
import { TAG_HIDDEN } from '@/tag';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
import { cache } from 'react';
|
import { cache } from 'react';
|
||||||
|
|
||||||
const getPhotoCachedCached = cache(getPhotoCached);
|
const getPhotosNearIdCachedCached = cache((photoId: string) =>
|
||||||
|
getPhotosNearIdCached(
|
||||||
|
photoId,
|
||||||
|
{ hidden: 'only' , limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
|
||||||
|
));
|
||||||
|
|
||||||
interface PhotoTagProps {
|
interface PhotoTagProps {
|
||||||
params: { photoId: string }
|
params: { photoId: string }
|
||||||
@ -16,7 +27,7 @@ interface PhotoTagProps {
|
|||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params: { photoId },
|
params: { photoId },
|
||||||
}: PhotoTagProps): Promise<Metadata> {
|
}: PhotoTagProps): Promise<Metadata> {
|
||||||
const photo = await getPhotoCachedCached(photoId, true);
|
const { photo } = await getPhotosNearIdCachedCached(photoId);
|
||||||
|
|
||||||
if (!photo) { return {}; }
|
if (!photo) { return {}; }
|
||||||
|
|
||||||
@ -43,14 +54,22 @@ export async function generateMetadata({
|
|||||||
export default async function PhotoTagHiddenPage({
|
export default async function PhotoTagHiddenPage({
|
||||||
params: { photoId },
|
params: { photoId },
|
||||||
}: PhotoTagProps) {
|
}: PhotoTagProps) {
|
||||||
const photo = await getPhotoCachedCached(photoId, true);
|
const { photo, photos, photosGrid, indexNumber } =
|
||||||
|
await getPhotosNearIdCachedCached(photoId);
|
||||||
|
|
||||||
if (!photo) { redirect(PATH_ROOT); }
|
if (!photo) { redirect(PATH_ROOT); }
|
||||||
|
|
||||||
const photos = await getPhotosCached({ hidden: 'only' });
|
const { count, dateRange } = await getPhotosTagHiddenMetaCached();
|
||||||
const count = photos.length;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PhotoDetailPage {...{ photo, photos, count, tag: TAG_HIDDEN }} />
|
<PhotoDetailPage {...{
|
||||||
|
photo,
|
||||||
|
photos,
|
||||||
|
photosGrid,
|
||||||
|
indexNumber,
|
||||||
|
count,
|
||||||
|
dateRange,
|
||||||
|
tag: TAG_HIDDEN,
|
||||||
|
}} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export const {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const safelyRunAdminServerAction = async <T>(
|
export const runAuthenticatedAdminServerAction = async <T>(
|
||||||
callback: () => T,
|
callback: () => T,
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|||||||
@ -9,12 +9,14 @@ export default function CameraHeader({
|
|||||||
camera: cameraProp,
|
camera: cameraProp,
|
||||||
photos,
|
photos,
|
||||||
selectedPhoto,
|
selectedPhoto,
|
||||||
|
indexNumber,
|
||||||
count,
|
count,
|
||||||
dateRange,
|
dateRange,
|
||||||
}: {
|
}: {
|
||||||
camera: Camera
|
camera: Camera
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
selectedPhoto?: Photo
|
selectedPhoto?: Photo
|
||||||
|
indexNumber?: number
|
||||||
count?: number
|
count?: number
|
||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
@ -28,6 +30,7 @@ export default function CameraHeader({
|
|||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={selectedPhoto}
|
selectedPhoto={selectedPhoto}
|
||||||
sharePath={pathForCameraShare(camera)}
|
sharePath={pathForCameraShare(camera)}
|
||||||
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export default function PhotoDetailPage({
|
|||||||
tag,
|
tag,
|
||||||
camera,
|
camera,
|
||||||
simulation,
|
simulation,
|
||||||
|
indexNumber,
|
||||||
count,
|
count,
|
||||||
dateRange,
|
dateRange,
|
||||||
}: {
|
}: {
|
||||||
@ -29,6 +30,7 @@ export default function PhotoDetailPage({
|
|||||||
tag?: string
|
tag?: string
|
||||||
camera?: Camera
|
camera?: Camera
|
||||||
simulation?: FilmSimulation
|
simulation?: FilmSimulation
|
||||||
|
indexNumber?: number
|
||||||
count?: number
|
count?: number
|
||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
@ -41,6 +43,7 @@ export default function PhotoDetailPage({
|
|||||||
? <HiddenHeader
|
? <HiddenHeader
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
|
indexNumber={indexNumber}
|
||||||
count={count ?? 0}
|
count={count ?? 0}
|
||||||
/>
|
/>
|
||||||
: <TagHeader
|
: <TagHeader
|
||||||
@ -48,6 +51,8 @@ export default function PhotoDetailPage({
|
|||||||
tag={tag}
|
tag={tag}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
|
indexNumber={indexNumber}
|
||||||
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>}
|
||||||
/>}
|
/>}
|
||||||
@ -59,6 +64,7 @@ export default function PhotoDetailPage({
|
|||||||
camera={camera}
|
camera={camera}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>}
|
||||||
@ -71,6 +77,7 @@ export default function PhotoDetailPage({
|
|||||||
simulation={simulation}
|
simulation={simulation}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={photo}
|
selectedPhoto={photo}
|
||||||
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>}
|
/>}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export default function PhotoSetHeader({
|
|||||||
photos,
|
photos,
|
||||||
selectedPhoto,
|
selectedPhoto,
|
||||||
sharePath,
|
sharePath,
|
||||||
|
indexNumber,
|
||||||
count,
|
count,
|
||||||
dateRange,
|
dateRange,
|
||||||
}: {
|
}: {
|
||||||
@ -22,6 +23,7 @@ export default function PhotoSetHeader({
|
|||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
selectedPhoto?: Photo
|
selectedPhoto?: Photo
|
||||||
sharePath?: string
|
sharePath?: string
|
||||||
|
indexNumber?: number
|
||||||
count?: number
|
count?: number
|
||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
@ -59,7 +61,7 @@ export default function PhotoSetHeader({
|
|||||||
)}>
|
)}>
|
||||||
{selectedPhotoIndex !== undefined
|
{selectedPhotoIndex !== undefined
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
? `${entityVerb ? `${entityVerb} ` : ''}${selectedPhotoIndex + 1} of ${count ?? photos.length}`
|
? `${entityVerb ? `${entityVerb} ` : ''}${indexNumber || (selectedPhotoIndex + 1)} of ${count ?? photos.length}`
|
||||||
: entityDescription}
|
: entityDescription}
|
||||||
{selectedPhotoIndex === undefined && sharePath &&
|
{selectedPhotoIndex === undefined && sharePath &&
|
||||||
<ShareButton
|
<ShareButton
|
||||||
|
|||||||
@ -38,7 +38,7 @@ import {
|
|||||||
import { blurImageFromUrl, extractImageDataFromBlobPath } from './server';
|
import { blurImageFromUrl, extractImageDataFromBlobPath } from './server';
|
||||||
import { TAG_FAVS, isTagFavs } from '@/tag';
|
import { TAG_FAVS, isTagFavs } from '@/tag';
|
||||||
import { convertPhotoToPhotoDbInsert } from '.';
|
import { convertPhotoToPhotoDbInsert } from '.';
|
||||||
import { safelyRunAdminServerAction } from '@/auth';
|
import { runAuthenticatedAdminServerAction } from '@/auth';
|
||||||
import { AI_IMAGE_QUERIES, AiImageQuery } from './ai';
|
import { AI_IMAGE_QUERIES, AiImageQuery } from './ai';
|
||||||
import { streamOpenAiImageQuery } from '@/services/openai';
|
import { streamOpenAiImageQuery } from '@/services/openai';
|
||||||
import { BLUR_ENABLED } from '@/site/config';
|
import { BLUR_ENABLED } from '@/site/config';
|
||||||
@ -46,7 +46,7 @@ import { BLUR_ENABLED } from '@/site/config';
|
|||||||
// Private actions
|
// Private actions
|
||||||
|
|
||||||
export const createPhotoAction = async (formData: FormData) =>
|
export const createPhotoAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const photo = convertFormDataToPhotoDbInsert(formData, true);
|
const photo = convertFormDataToPhotoDbInsert(formData, true);
|
||||||
|
|
||||||
const updatedUrl = await convertUploadToPhoto(photo.url);
|
const updatedUrl = await convertUploadToPhoto(photo.url);
|
||||||
@ -60,7 +60,7 @@ export const createPhotoAction = async (formData: FormData) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const updatePhotoAction = async (formData: FormData) =>
|
export const updatePhotoAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const photo = convertFormDataToPhotoDbInsert(formData);
|
const photo = convertFormDataToPhotoDbInsert(formData);
|
||||||
|
|
||||||
let url: string | undefined;
|
let url: string | undefined;
|
||||||
@ -82,7 +82,7 @@ export const toggleFavoritePhotoAction = async (
|
|||||||
photoId: string,
|
photoId: string,
|
||||||
shouldRedirect?: boolean,
|
shouldRedirect?: boolean,
|
||||||
) =>
|
) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const photo = await getPhoto(photoId);
|
const photo = await getPhoto(photoId);
|
||||||
if (photo) {
|
if (photo) {
|
||||||
const { tags } = photo;
|
const { tags } = photo;
|
||||||
@ -102,7 +102,7 @@ export const deletePhotoAction = async (
|
|||||||
photoUrl: string,
|
photoUrl: string,
|
||||||
shouldRedirect?: boolean,
|
shouldRedirect?: boolean,
|
||||||
) =>
|
) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl));
|
await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl));
|
||||||
revalidateAllKeysAndPaths();
|
revalidateAllKeysAndPaths();
|
||||||
if (shouldRedirect) {
|
if (shouldRedirect) {
|
||||||
@ -111,7 +111,7 @@ export const deletePhotoAction = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const deletePhotoFormAction = async (formData: FormData) =>
|
export const deletePhotoFormAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(() =>
|
runAuthenticatedAdminServerAction(() =>
|
||||||
deletePhotoAction(
|
deletePhotoAction(
|
||||||
formData.get('id') as string,
|
formData.get('id') as string,
|
||||||
formData.get('url') as string,
|
formData.get('url') as string,
|
||||||
@ -119,7 +119,7 @@ export const deletePhotoFormAction = async (formData: FormData) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const deletePhotoTagGloballyAction = async (formData: FormData) =>
|
export const deletePhotoTagGloballyAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const tag = formData.get('tag') as string;
|
const tag = formData.get('tag') as string;
|
||||||
|
|
||||||
await sqlDeletePhotoTagGlobally(tag);
|
await sqlDeletePhotoTagGlobally(tag);
|
||||||
@ -129,7 +129,7 @@ export const deletePhotoTagGloballyAction = async (formData: FormData) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const renamePhotoTagGloballyAction = async (formData: FormData) =>
|
export const renamePhotoTagGloballyAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const tag = formData.get('tag') as string;
|
const tag = formData.get('tag') as string;
|
||||||
const updatedTag = formData.get('updatedTag') as string;
|
const updatedTag = formData.get('updatedTag') as string;
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ export const renamePhotoTagGloballyAction = async (formData: FormData) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const deleteBlobPhotoAction = async (formData: FormData) =>
|
export const deleteBlobPhotoAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
await deleteStorageUrl(formData.get('url') as string);
|
await deleteStorageUrl(formData.get('url') as string);
|
||||||
|
|
||||||
revalidateAdminPaths();
|
revalidateAdminPaths();
|
||||||
@ -157,7 +157,7 @@ export const deleteBlobPhotoAction = async (formData: FormData) =>
|
|||||||
export const getExifDataAction = async (
|
export const getExifDataAction = async (
|
||||||
photoFormPrevious: Partial<PhotoFormData>,
|
photoFormPrevious: Partial<PhotoFormData>,
|
||||||
): Promise<Partial<PhotoFormData>> =>
|
): Promise<Partial<PhotoFormData>> =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const { url } = photoFormPrevious;
|
const { url } = photoFormPrevious;
|
||||||
if (url) {
|
if (url) {
|
||||||
const { photoFormExif } = await extractImageDataFromBlobPath(url);
|
const { photoFormExif } = await extractImageDataFromBlobPath(url);
|
||||||
@ -171,7 +171,7 @@ export const getExifDataAction = async (
|
|||||||
// Accessed from admin photo table
|
// Accessed from admin photo table
|
||||||
// will update blur data
|
// will update blur data
|
||||||
export const syncPhotoExifDataAction = async (formData: FormData) =>
|
export const syncPhotoExifDataAction = async (formData: FormData) =>
|
||||||
safelyRunAdminServerAction(async () => {
|
runAuthenticatedAdminServerAction(async () => {
|
||||||
const photoId = formData.get('id') as string;
|
const photoId = formData.get('id') as string;
|
||||||
if (photoId) {
|
if (photoId) {
|
||||||
const photo = await getPhoto(photoId);
|
const photo = await getPhoto(photoId);
|
||||||
@ -193,32 +193,32 @@ export const syncPhotoExifDataAction = async (formData: FormData) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const syncCacheAction = async () =>
|
export const syncCacheAction = async () =>
|
||||||
safelyRunAdminServerAction(revalidateAllKeysAndPaths);
|
runAuthenticatedAdminServerAction(revalidateAllKeysAndPaths);
|
||||||
|
|
||||||
export const streamAiImageQueryAction = async (
|
export const streamAiImageQueryAction = async (
|
||||||
imageBase64: string,
|
imageBase64: string,
|
||||||
query: AiImageQuery,
|
query: AiImageQuery,
|
||||||
) =>
|
) =>
|
||||||
safelyRunAdminServerAction(() =>
|
runAuthenticatedAdminServerAction(() =>
|
||||||
streamOpenAiImageQuery(imageBase64, AI_IMAGE_QUERIES[query]));
|
streamOpenAiImageQuery(imageBase64, AI_IMAGE_QUERIES[query]));
|
||||||
|
|
||||||
export const getImageBlurAction = async (url: string) =>
|
export const getImageBlurAction = async (url: string) =>
|
||||||
safelyRunAdminServerAction(() => blurImageFromUrl(url));
|
runAuthenticatedAdminServerAction(() => blurImageFromUrl(url));
|
||||||
|
|
||||||
export const getPhotosTagHiddenMetaCachedAction = async () =>
|
export const getPhotosTagHiddenMetaCachedAction = async () =>
|
||||||
safelyRunAdminServerAction(getPhotosTagHiddenMetaCached);
|
runAuthenticatedAdminServerAction(getPhotosTagHiddenMetaCached);
|
||||||
|
|
||||||
// Public/Private actions
|
// Public/Private actions
|
||||||
|
|
||||||
export const getPhotosAction = async (options: GetPhotosOptions) =>
|
export const getPhotosAction = async (options: GetPhotosOptions) =>
|
||||||
(options.hidden === 'include' || options.hidden === 'only')
|
(options.hidden === 'include' || options.hidden === 'only')
|
||||||
? safelyRunAdminServerAction(() =>
|
? runAuthenticatedAdminServerAction(() =>
|
||||||
getPhotos(options))
|
getPhotos(options))
|
||||||
: getPhotos(options);
|
: getPhotos(options);
|
||||||
|
|
||||||
export const getPhotosCachedAction = async (options: GetPhotosOptions) =>
|
export const getPhotosCachedAction = async (options: GetPhotosOptions) =>
|
||||||
(options.hidden === 'include' || options.hidden === 'only')
|
(options.hidden === 'include' || options.hidden === 'only')
|
||||||
? safelyRunAdminServerAction(() =>
|
? runAuthenticatedAdminServerAction(() =>
|
||||||
getPhotosCached (options))
|
getPhotosCached (options))
|
||||||
: getPhotosCached(options);
|
: getPhotosCached(options);
|
||||||
|
|
||||||
|
|||||||
@ -141,11 +141,23 @@ export const getPhotosNearIdCached = (
|
|||||||
...args: Parameters<typeof getPhotosNearId>
|
...args: Parameters<typeof getPhotosNearId>
|
||||||
) => unstable_cache(
|
) => unstable_cache(
|
||||||
getPhotosNearId,
|
getPhotosNearId,
|
||||||
[KEY_PHOTOS],
|
[KEY_PHOTOS, ...getPhotosCacheKeys(args[1])],
|
||||||
)(...args).then(({ photos, photo }) => ({
|
)(...args).then(({ photos, indexNumber }) => {
|
||||||
photos: parseCachedPhotosDates(photos),
|
const [photoId, { limit }] = args;
|
||||||
photo: photo ? parseCachedPhotoDates(photo) : undefined,
|
const photo = photos.find(({ id }) => id === photoId);
|
||||||
}));
|
const isPhotoFirst = photos.findIndex(p => p.id === photoId) === 0;
|
||||||
|
return {
|
||||||
|
photo: photo ? parseCachedPhotoDates(photo) : undefined,
|
||||||
|
photos: parseCachedPhotosDates(photos),
|
||||||
|
...limit && {
|
||||||
|
photosGrid: photos.slice(
|
||||||
|
isPhotoFirst ? 1 : 2,
|
||||||
|
isPhotoFirst ? limit - 1 : limit,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
indexNumber,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
export const getPhotosDateRangeCached =
|
export const getPhotosDateRangeCached =
|
||||||
unstable_cache(
|
unstable_cache(
|
||||||
|
|||||||
@ -488,10 +488,11 @@ export const getPhotosNearId = async (
|
|||||||
);
|
);
|
||||||
}, `getPhotosNearId: ${photoId}`)
|
}, `getPhotosNearId: ${photoId}`)
|
||||||
.then(({ rows }) => {
|
.then(({ rows }) => {
|
||||||
const photos = rows.map(parsePhotoFromDb);
|
const photo = rows.find(({ id }) => id === photoId);
|
||||||
|
const indexNumber = photo ? parseInt(photo.row_number) : undefined;
|
||||||
return {
|
return {
|
||||||
photos,
|
photos: rows.map(parsePhotoFromDb),
|
||||||
photo: photos.find(photo => photo.id === photoId),
|
indexNumber,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { createOpenAI } from '@ai-sdk/openai';
|
|||||||
import { kv } from '@vercel/kv';
|
import { kv } from '@vercel/kv';
|
||||||
import { Ratelimit } from '@upstash/ratelimit';
|
import { Ratelimit } from '@upstash/ratelimit';
|
||||||
import { AI_TEXT_GENERATION_ENABLED, HAS_VERCEL_KV } from '@/site/config';
|
import { AI_TEXT_GENERATION_ENABLED, HAS_VERCEL_KV } from '@/site/config';
|
||||||
import { safelyRunAdminServerAction } from '@/auth';
|
import { runAuthenticatedAdminServerAction } from '@/auth';
|
||||||
import { removeBase64Prefix } from '@/utility/image';
|
import { removeBase64Prefix } from '@/utility/image';
|
||||||
|
|
||||||
const RATE_LIMIT_IDENTIFIER = 'openai-image-query';
|
const RATE_LIMIT_IDENTIFIER = 'openai-image-query';
|
||||||
@ -28,7 +28,7 @@ export const streamOpenAiImageQuery = async (
|
|||||||
imageBase64: string,
|
imageBase64: string,
|
||||||
query: string,
|
query: string,
|
||||||
) => {
|
) => {
|
||||||
return safelyRunAdminServerAction(async () => {
|
return runAuthenticatedAdminServerAction(async () => {
|
||||||
if (ratelimit) {
|
if (ratelimit) {
|
||||||
let success = false;
|
let success = false;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -9,12 +9,14 @@ export default function FilmSimulationHeader({
|
|||||||
simulation,
|
simulation,
|
||||||
photos,
|
photos,
|
||||||
selectedPhoto,
|
selectedPhoto,
|
||||||
|
indexNumber,
|
||||||
count,
|
count,
|
||||||
dateRange,
|
dateRange,
|
||||||
}: {
|
}: {
|
||||||
simulation: FilmSimulation
|
simulation: FilmSimulation
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
selectedPhoto?: Photo
|
selectedPhoto?: Photo
|
||||||
|
indexNumber?: number
|
||||||
count?: number
|
count?: number
|
||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
@ -27,6 +29,7 @@ export default function FilmSimulationHeader({
|
|||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={selectedPhoto}
|
selectedPhoto={selectedPhoto}
|
||||||
sharePath={pathForFilmSimulationShare(simulation)}
|
sharePath={pathForFilmSimulationShare(simulation)}
|
||||||
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -5,10 +5,12 @@ import HiddenTag from './HiddenTag';
|
|||||||
export default function HiddenHeader({
|
export default function HiddenHeader({
|
||||||
photos,
|
photos,
|
||||||
selectedPhoto,
|
selectedPhoto,
|
||||||
|
indexNumber,
|
||||||
count,
|
count,
|
||||||
}: {
|
}: {
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
selectedPhoto?: Photo
|
selectedPhoto?: Photo
|
||||||
|
indexNumber?: number
|
||||||
count: number
|
count: number
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@ -18,6 +20,8 @@ export default function HiddenHeader({
|
|||||||
entityDescription={photoQuantityText(count, false)}
|
entityDescription={photoQuantityText(count, false)}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={selectedPhoto}
|
selectedPhoto={selectedPhoto}
|
||||||
|
indexNumber={indexNumber}
|
||||||
|
count={count}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,12 +9,14 @@ export default function TagHeader({
|
|||||||
tag,
|
tag,
|
||||||
photos,
|
photos,
|
||||||
selectedPhoto,
|
selectedPhoto,
|
||||||
|
indexNumber,
|
||||||
count,
|
count,
|
||||||
dateRange,
|
dateRange,
|
||||||
}: {
|
}: {
|
||||||
tag: string
|
tag: string
|
||||||
photos: Photo[]
|
photos: Photo[]
|
||||||
selectedPhoto?: Photo
|
selectedPhoto?: Photo
|
||||||
|
indexNumber?: number
|
||||||
count?: number
|
count?: number
|
||||||
dateRange?: PhotoDateRange
|
dateRange?: PhotoDateRange
|
||||||
}) {
|
}) {
|
||||||
@ -28,6 +30,7 @@ export default function TagHeader({
|
|||||||
photos={photos}
|
photos={photos}
|
||||||
selectedPhoto={selectedPhoto}
|
selectedPhoto={selectedPhoto}
|
||||||
sharePath={pathForTagShare(tag)}
|
sharePath={pathForTagShare(tag)}
|
||||||
|
indexNumber={indexNumber}
|
||||||
count={count}
|
count={count}
|
||||||
dateRange={dateRange}
|
dateRange={dateRange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user