Build out final focal length views

This commit is contained in:
Sam Becker 2024-05-21 12:34:45 -05:00
parent 7cd5ccbe15
commit f7321bd831
13 changed files with 285 additions and 9 deletions

View File

@ -0,0 +1,86 @@
import {
RELATED_GRID_PHOTOS_TO_SHOW,
descriptionForPhoto,
titleForPhoto,
} from '@/photo';
import { Metadata } from 'next/types';
import { redirect } from 'next/navigation';
import {
PATH_ROOT,
absolutePathForPhoto,
absolutePathForPhotoImage,
} from '@/site/paths';
import PhotoDetailPage from '@/photo/PhotoDetailPage';
import { getPhotosNearIdCached } from '@/photo/cache';
import { ReactNode, cache } from 'react';
import { getPhotosMeta } from '@/photo/db/query';
import { getFocalLengthFromString } from '@/focal';
const getPhotosNearIdCachedCached = cache((photoId: string, focal: number) =>
getPhotosNearIdCached(
photoId,
{ focal, limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
));
interface PhotoFocalLengthProps {
params: { photoId: string, focal: string }
}
export async function generateMetadata({
params: { photoId, focal: focalString },
}: PhotoFocalLengthProps): Promise<Metadata> {
const focal = getFocalLengthFromString(focalString);
const { photo } = await getPhotosNearIdCachedCached(photoId, focal);
if (!photo) { return {}; }
const title = titleForPhoto(photo);
const description = descriptionForPhoto(photo);
const images = absolutePathForPhotoImage(photo);
const url = absolutePathForPhoto({ photo, focal });
return {
title,
description,
openGraph: {
title,
images,
description,
url,
},
twitter: {
title,
description,
images,
card: 'summary_large_image',
},
};
}
export default async function PhotoFocalLengthPage({
params: { photoId, focal: focalString },
children,
}: PhotoFocalLengthProps & { children: ReactNode }) {
const focal = getFocalLengthFromString(focalString);
const { photo, photos, photosGrid, indexNumber } =
await getPhotosNearIdCachedCached(photoId, focal);
if (!photo) { redirect(PATH_ROOT); }
const { count, dateRange } = await getPhotosMeta({ focal });
return <>
{children}
<PhotoDetailPage {...{
photo,
photos,
photosGrid,
focal,
indexNumber,
count,
dateRange,
}} />
</>;
}

View File

@ -0,0 +1,3 @@
export default function Page() {
return null;
}

View File

@ -0,0 +1,19 @@
import { getFocalLengthFromString } from '@/focal';
import { getPhotoCached } from '@/photo/cache';
import PhotoShareModal from '@/photo/PhotoShareModal';
import { PATH_ROOT } from '@/site/paths';
import { redirect } from 'next/navigation';
export default async function Share({
params: { photoId, focal: focalString },
}: {
params: { photoId: string, focal: string }
}) {
const focal = getFocalLengthFromString(focalString);
const photo = await getPhotoCached(photoId);
if (!photo) { return redirect(PATH_ROOT); }
return <PhotoShareModal {...{ photo, focal }} />;
}

View File

@ -1,6 +1,6 @@
import { generateMetaForFocalLength, getFocalLengthFromString } from '@/focal';
import FocalLengthOverview from '@/focal/FocalLengthOverview';
import { getPhotosFocalDataCached } from '@/focal/data';
import { getPhotosFocalLengthDataCached } from '@/focal/data';
import { INFINITE_SCROLL_GRID_PHOTO_INITIAL } from '@/photo';
import { PATH_ROOT } from '@/site/paths';
import type { Metadata } from 'next';
@ -8,7 +8,7 @@ import { redirect } from 'next/navigation';
import { cache } from 'react';
const getPhotosFocalDataCachedCached = cache((focal: number) =>
getPhotosFocalDataCached({
getPhotosFocalLengthDataCached({
focal,
limit: INFINITE_SCROLL_GRID_PHOTO_INITIAL,
}));

View File

@ -0,0 +1,70 @@
import { generateMetaForFocalLength, getFocalLengthFromString } from '@/focal';
import FocalLengthOverview from '@/focal/FocalLengthOverview';
import FocalLengthShareModal from '@/focal/FocalLengthShareModal';
import { getPhotosFocalLengthDataCached } from '@/focal/data';
import { INFINITE_SCROLL_GRID_PHOTO_INITIAL } from '@/photo';
import type { Metadata } from 'next';
import { cache } from 'react';
const getPhotosFocalLengthDataCachedCached = cache((focal: number) =>
getPhotosFocalLengthDataCached({
focal,
limit: INFINITE_SCROLL_GRID_PHOTO_INITIAL,
}));
interface FocalLengthProps {
params: { focal: string }
}
export async function generateMetadata({
params: { focal: focalString },
}: FocalLengthProps): Promise<Metadata> {
const focal = getFocalLengthFromString(focalString);
const [
photos,
{ count, dateRange },
] = await getPhotosFocalLengthDataCachedCached(focal);
const {
url,
title,
description,
images,
} = generateMetaForFocalLength(focal, photos, count, dateRange);
return {
title,
openGraph: {
title,
description,
images,
url,
},
twitter: {
images,
description,
card: 'summary_large_image',
},
description,
};
}
export default async function Share({
params: { focal: focalString },
}: FocalLengthProps) {
const focal = getFocalLengthFromString(focalString);
const [
photos,
{ count, dateRange },
] = await getPhotosFocalLengthDataCachedCached(focal);
return <>
<FocalLengthShareModal {...{ focal, photos, count, dateRange }} />
<FocalLengthOverview
{...{ focal, photos, count, dateRange }}
animateOnFirstLoadOnly
/>
</>;
}

View File

@ -1,6 +1,6 @@
import { Photo, PhotoDateRange } from '@/photo';
import { descriptionForFocalLengthPhotos } from '.';
import { pathForFocalLength } from '@/site/paths';
import { pathForFocalLengthShare } from '@/site/paths';
import PhotoSetHeader from '@/photo/PhotoSetHeader';
import PhotoFocalLength from './PhotoFocalLength';
@ -22,7 +22,6 @@ export default function FocalLengthHeader({
return (
<PhotoSetHeader
entity={<PhotoFocalLength focal={focal} contrast="high" />}
entityVerb="Tagged"
entityDescription={descriptionForFocalLengthPhotos(
photos,
undefined,
@ -30,7 +29,7 @@ export default function FocalLengthHeader({
)}
photos={photos}
selectedPhoto={selectedPhoto}
sharePath={pathForFocalLength(focal)}
sharePath={pathForFocalLengthShare(focal)}
indexNumber={indexNumber}
count={count}
dateRange={dateRange}

View File

@ -0,0 +1,50 @@
import { Photo, PhotoDateRange } from '@/photo';
import {
absolutePathForFocalLengthImage,
pathForFocalLength,
} from '@/site/paths';
import OGTile from '@/components/OGTile';
import { descriptionForFocalLengthPhotos, titleForFocalLength } from '.';
export type OGLoadingState = 'unloaded' | 'loading' | 'loaded' | 'failed';
export default function FocalLengthOGTile({
focal,
photos,
loadingState: loadingStateExternal,
riseOnHover,
onLoad,
onFail,
retryTime,
count,
dateRange,
}: {
focal: number
photos: Photo[]
loadingState?: OGLoadingState
onLoad?: () => void
onFail?: () => void
riseOnHover?: boolean
retryTime?: number
count?: number
dateRange?: PhotoDateRange
}) {
return (
<OGTile {...{
title: titleForFocalLength(focal, photos, count),
description: descriptionForFocalLengthPhotos(
photos,
true,
count,
dateRange,
),
path: pathForFocalLength(focal),
pathImageAbsolute: absolutePathForFocalLengthImage(focal),
loadingState: loadingStateExternal,
onLoad,
onFail,
riseOnHover,
retryTime,
}}/>
);
};

View File

@ -0,0 +1,26 @@
import { absolutePathForFocalLength, pathForFocalLength } from '@/site/paths';
import { Photo, PhotoDateRange } from '../photo';
import ShareModal from '@/components/ShareModal';
import FocalLengthOGTile from './FocalLengthOGTile';
export default function FocalLengthShareModal({
focal,
photos,
count,
dateRange,
}: {
focal: number
photos: Photo[]
count?: number
dateRange?: PhotoDateRange
}) {
return (
<ShareModal
title="Share Photos"
pathShare={absolutePathForFocalLength(focal)}
pathClose={pathForFocalLength(focal)}
>
<FocalLengthOGTile {...{ focal, photos, count, dateRange }} />
</ShareModal>
);
};

View File

@ -3,7 +3,7 @@ import {
getPhotosMetaCached,
} from '@/photo/cache';
export const getPhotosFocalDataCached = ({
export const getPhotosFocalLengthDataCached = ({
focal,
limit,
}: {

View File

@ -23,7 +23,7 @@ export const titleForFocalLength = (
photos: Photo[],
explicitCount?: number,
) => [
formatFocalLength(focal),
`${formatFocalLength(focal)} Focal Length`,
photoQuantityText(explicitCount ?? photos.length),
].join(' ');

View File

@ -12,6 +12,7 @@ import { FilmSimulation } from '@/simulation';
import FilmSimulationHeader from '@/simulation/FilmSimulationHeader';
import { TAG_HIDDEN } from '@/tag';
import HiddenHeader from '@/tag/HiddenHeader';
import FocalLengthHeader from '@/focal/FocalLengthHeader';
export default function PhotoDetailPage({
photo,
@ -20,6 +21,7 @@ export default function PhotoDetailPage({
tag,
camera,
simulation,
focal,
indexNumber,
count,
dateRange,
@ -30,6 +32,7 @@ export default function PhotoDetailPage({
tag?: string
camera?: Camera
simulation?: FilmSimulation
focal?: number
indexNumber?: number
count?: number
dateRange?: PhotoDateRange
@ -82,6 +85,19 @@ export default function PhotoDetailPage({
dateRange={dateRange}
/>}
/>}
{focal &&
<SiteGrid
className="mt-4 mb-8"
contentMain={
<FocalLengthHeader
focal={focal}
photos={photos}
selectedPhoto={photo}
indexNumber={indexNumber}
count={count}
dateRange={dateRange}
/>}
/>}
<AnimateItems
className="md:mb-8"
animateFromAppState
@ -109,6 +125,7 @@ export default function PhotoDetailPage({
tag={tag}
camera={camera}
simulation={simulation}
focal={focal}
animateOnFirstLoadOnly
/>}
contentSide={<AnimateItems
@ -130,6 +147,7 @@ export default function PhotoDetailPage({
tag,
camera,
simulation,
focal,
}} />
</div>,
]}

View File

@ -101,6 +101,7 @@ export default function PhotoLinks({
tag={tag}
camera={camera}
simulation={simulation}
focal={focal}
scroll={false}
prefetch
>
@ -112,6 +113,7 @@ export default function PhotoLinks({
tag={tag}
camera={camera}
simulation={simulation}
focal={focal}
scroll={false}
prefetch
>

View File

@ -125,11 +125,14 @@ export const pathForCameraShare = (camera: Camera) =>
export const pathForFilmSimulation = (simulation: FilmSimulation) =>
`${PREFIX_FILM_SIMULATION}/${simulation}`;
export const pathForFilmSimulationShare = (simulation: FilmSimulation) =>
`${pathForFilmSimulation(simulation)}/${SHARE}`;
export const pathForFocalLength = (focal: number) =>
`${PREFIX_FOCAL_LENGTH}/${focal}mm`;
export const pathForFilmSimulationShare = (simulation: FilmSimulation) =>
`${pathForFilmSimulation(simulation)}/${SHARE}`;
export const pathForFocalLengthShare = (focal: number) =>
`${pathForFocalLength(focal)}/${SHARE}`;;
export const absolutePathForPhoto = (params: PhotoPathParams) =>
`${BASE_URL}${pathForPhoto(params)}`;