Show raw lens content next to photos

This commit is contained in:
Sam Becker 2025-03-15 14:05:12 -05:00
parent bac0c51f75
commit bfe00c9565
7 changed files with 89 additions and 11 deletions

View File

@ -5,6 +5,7 @@ import { Camera } from '@/camera';
import { FilmSimulation } from '@/simulation'; import { FilmSimulation } from '@/simulation';
import { parameterize } from '@/utility/string'; import { parameterize } from '@/utility/string';
import { TAG_HIDDEN } from '@/tag'; import { TAG_HIDDEN } from '@/tag';
import { Lens } from '@/lens';
// Core paths // Core paths
export const PATH_ROOT = '/'; export const PATH_ROOT = '/';
@ -23,9 +24,10 @@ export const PATH_FEED_INFERRED = GRID_HOMEPAGE_ENABLED ? PATH_FEED : PATH
export const PREFIX_PHOTO = '/p'; export const PREFIX_PHOTO = '/p';
export const PREFIX_TAG = '/tag'; export const PREFIX_TAG = '/tag';
export const PREFIX_CAMERA = '/shot-on'; export const PREFIX_CAMERA = '/shot-on';
export const PREFIX_LENS = '/lens';
export const PREFIX_RECIPE = '/recipe';
export const PREFIX_FILM_SIMULATION = '/film'; export const PREFIX_FILM_SIMULATION = '/film';
export const PREFIX_FOCAL_LENGTH = '/focal'; export const PREFIX_FOCAL_LENGTH = '/focal';
export const PREFIX_RECIPE = '/recipe';
// Dynamic paths // Dynamic paths
const PATH_PHOTO_DYNAMIC = `${PREFIX_PHOTO}/[photoId]`; const PATH_PHOTO_DYNAMIC = `${PREFIX_PHOTO}/[photoId]`;
@ -113,6 +115,7 @@ export const pathForPhoto = ({
photo, photo,
tag, tag,
camera, camera,
lens,
simulation, simulation,
focal, focal,
recipe, recipe,
@ -123,6 +126,8 @@ export const pathForPhoto = ({
? `${pathForTag(tag)}/${getPhotoId(photo)}` ? `${pathForTag(tag)}/${getPhotoId(photo)}`
: camera : camera
? `${pathForCamera(camera)}/${getPhotoId(photo)}` ? `${pathForCamera(camera)}/${getPhotoId(photo)}`
: lens
? `${pathForLens(lens)}/${getPhotoId(photo)}`
: simulation : simulation
? `${pathForFilmSimulation(simulation)}/${getPhotoId(photo)}` ? `${pathForFilmSimulation(simulation)}/${getPhotoId(photo)}`
: recipe : recipe
@ -140,6 +145,9 @@ export const pathForCamera = ({ make, model }: Camera) =>
export const pathForFilmSimulation = (simulation: FilmSimulation) => export const pathForFilmSimulation = (simulation: FilmSimulation) =>
`${PREFIX_FILM_SIMULATION}/${simulation}`; `${PREFIX_FILM_SIMULATION}/${simulation}`;
export const pathForLens = ({ make, model }: Lens) =>
`${PREFIX_LENS}/${parameterize(make, true)}/${parameterize(model, true)}`;
export const pathForFocalLength = (focal: number) => export const pathForFocalLength = (focal: number) =>
`${PREFIX_FOCAL_LENGTH}/${focal}mm`; `${PREFIX_FOCAL_LENGTH}/${focal}mm`;

View File

@ -3,6 +3,8 @@ import { parameterize } from '@/utility/string';
const CAMERA_PLACEHOLDER: Camera = { make: 'Camera', model: 'Model' }; const CAMERA_PLACEHOLDER: Camera = { make: 'Camera', model: 'Model' };
const CAMERA_MAKE_APPLE = 'apple';
export type Camera = { export type Camera = {
make: string make: string
model: string model: string
@ -57,7 +59,7 @@ export const cameraFromPhoto = (
: fallback ?? CAMERA_PLACEHOLDER; : fallback ?? CAMERA_PLACEHOLDER;
const isCameraMakeApple = (make?: string) => const isCameraMakeApple = (make?: string) =>
make?.toLocaleLowerCase() === 'apple'; make?.toLocaleLowerCase() === CAMERA_MAKE_APPLE;
export const isCameraApple = ({ make }: Camera) => export const isCameraApple = ({ make }: Camera) =>
isCameraMakeApple(make); isCameraMakeApple(make);

34
src/lens/PhotoLens.tsx Normal file
View File

@ -0,0 +1,34 @@
import { pathForLens } from '@/app/paths';
import { Lens, formatLensText } from '.';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import { TbCone } from 'react-icons/tb';
export default function PhotoLens({
lens,
type,
badged,
contrast,
prefetch,
countOnHover,
className,
}: {
lens: Lens
hideAppleIcon?: boolean
countOnHover?: number
} & EntityLinkExternalProps) {
return (
<EntityLink
label={formatLensText(lens)}
href={pathForLens(lens)}
icon={<TbCone className="rotate-[270deg] translate-x-[-1px]" />}
type={type}
className={className}
badged={badged}
contrast={contrast}
prefetch={prefetch}
hoverEntity={countOnHover}
/>
);
}

View File

@ -3,6 +3,8 @@ import { parameterize } from '@/utility/string';
const LENS_PLACEHOLDER: Lens = { make: 'Lens', model: 'Model' }; const LENS_PLACEHOLDER: Lens = { make: 'Lens', model: 'Model' };
const LENS_MAKE_APPLE = 'apple';
export type Lens = { export type Lens = {
make: string make: string
model: string model: string
@ -45,3 +47,12 @@ export const lensFromPhoto = (
photo?.lensMake && photo?.lensModel photo?.lensMake && photo?.lensModel
? { make: photo.lensMake, model: photo.lensModel } ? { make: photo.lensMake, model: photo.lensModel }
: fallback ?? LENS_PLACEHOLDER; : fallback ?? LENS_PLACEHOLDER;
const isLensMakeApple = (make?: string) =>
make?.toLocaleLowerCase() === LENS_MAKE_APPLE;
export const isLensApple = ({ make }: Lens) =>
isLensMakeApple(make);
export const formatLensText = ({ make, model }: Lens, short = true) =>
short ? model : `${make} ${model}`;

View File

@ -6,6 +6,7 @@ import {
doesPhotoNeedBlurCompatibility, doesPhotoNeedBlurCompatibility,
shouldShowCameraDataForPhoto, shouldShowCameraDataForPhoto,
shouldShowExifDataForPhoto, shouldShowExifDataForPhoto,
shouldShowLensDataForPhoto,
titleForPhoto, titleForPhoto,
} from '.'; } from '.';
import SiteGrid from '@/components/SiteGrid'; import SiteGrid from '@/components/SiteGrid';
@ -45,6 +46,8 @@ import { AnimatePresence } from 'framer-motion';
import useRecipeOverlay from '../recipe/useRecipeOverlay'; import useRecipeOverlay from '../recipe/useRecipeOverlay';
import PhotoRecipeOverlay from '@/recipe/PhotoRecipeOverlay'; import PhotoRecipeOverlay from '@/recipe/PhotoRecipeOverlay';
import PhotoRecipe from '@/recipe/PhotoRecipe'; import PhotoRecipe from '@/recipe/PhotoRecipe';
import PhotoLens from '@/lens/PhotoLens';
import { lensFromPhoto } from '@/lens';
export default function PhotoLarge({ export default function PhotoLarge({
photo, photo,
@ -57,6 +60,7 @@ export default function PhotoLarge({
showTitle = true, showTitle = true,
showTitleAsH1, showTitleAsH1,
showCamera = true, showCamera = true,
showLens = true,
showSimulation = true, showSimulation = true,
showRecipe = true, showRecipe = true,
showZoomControls: showZoomControlsProp = true, showZoomControls: showZoomControlsProp = true,
@ -80,6 +84,7 @@ export default function PhotoLarge({
showTitle?: boolean showTitle?: boolean
showTitleAsH1?: boolean showTitleAsH1?: boolean
showCamera?: boolean showCamera?: boolean
showLens?: boolean
showSimulation?: boolean showSimulation?: boolean
showRecipe?: boolean showRecipe?: boolean
showZoomControls?: boolean showZoomControls?: boolean
@ -121,10 +126,11 @@ export default function PhotoLarge({
const tags = sortTags(photo.tags, primaryTag); const tags = sortTags(photo.tags, primaryTag);
const camera = cameraFromPhoto(photo); const camera = cameraFromPhoto(photo);
const lens = lensFromPhoto(photo);
const { recipeTitle: recipe } = photo; const { recipeTitle: recipe } = photo;
const showCameraContent = showCamera && shouldShowCameraDataForPhoto(photo); const showCameraContent = showCamera && shouldShowCameraDataForPhoto(photo);
const showLensContent = showLens && shouldShowLensDataForPhoto(photo);
const showRecipeContent = showRecipe && recipe; const showRecipeContent = showRecipe && recipe;
const showTagsContent = tags.length > 0; const showTagsContent = tags.length > 0;
const showExifContent = shouldShowExifDataForPhoto(photo); const showExifContent = shouldShowExifDataForPhoto(photo);
@ -284,6 +290,15 @@ export default function PhotoLarge({
contrast="medium" contrast="medium"
prefetch={prefetchRelatedLinks} prefetch={prefetchRelatedLinks}
/>} />}
{showLensContent &&
<>
<br />
<PhotoLens
lens={lens}
contrast="medium"
prefetch={prefetchRelatedLinks}
/>
</>}
{showRecipeContent && {showRecipeContent &&
<PhotoRecipe <PhotoRecipe
recipe={recipe} recipe={recipe}

View File

@ -298,6 +298,10 @@ const photoHasCameraData = (photo: Photo) =>
Boolean(photo.make) && Boolean(photo.make) &&
Boolean(photo.model); Boolean(photo.model);
const photoHasLensData = (photo: Photo) =>
Boolean(photo.lensMake) &&
Boolean(photo.lensModel);
const photoHasExifData = (photo: Photo) => const photoHasExifData = (photo: Photo) =>
Boolean(photo.focalLength) || Boolean(photo.focalLength) ||
Boolean(photo.focalLengthIn35MmFormat) || Boolean(photo.focalLengthIn35MmFormat) ||
@ -309,6 +313,9 @@ const photoHasExifData = (photo: Photo) =>
export const shouldShowCameraDataForPhoto = (photo: Photo) => export const shouldShowCameraDataForPhoto = (photo: Photo) =>
SHOW_EXIF_DATA && photoHasCameraData(photo); SHOW_EXIF_DATA && photoHasCameraData(photo);
export const shouldShowLensDataForPhoto = (photo: Photo) =>
SHOW_EXIF_DATA && photoHasLensData(photo);
export const shouldShowExifDataForPhoto = (photo: Photo) => export const shouldShowExifDataForPhoto = (photo: Photo) =>
SHOW_EXIF_DATA && photoHasExifData(photo); SHOW_EXIF_DATA && photoHasExifData(photo);

View File

@ -10,10 +10,10 @@ import { Recipes } from '@/recipe';
const CATEGORY_KEYS = [ const CATEGORY_KEYS = [
'tags', 'tags',
'cameras', 'cameras',
'lenses',
'recipes', 'recipes',
'films', 'films',
'focal-lengths', 'focal-lengths',
'lenses',
] as const; ] as const;
type CategoryKey = (typeof CATEGORY_KEYS)[number]; type CategoryKey = (typeof CATEGORY_KEYS)[number];
@ -23,6 +23,7 @@ type CategoryKeys = CategoryKey[];
export const DEFAULT_CATEGORY_KEYS: CategoryKeys = [ export const DEFAULT_CATEGORY_KEYS: CategoryKeys = [
'tags', 'tags',
'cameras', 'cameras',
'lenses',
'recipes', 'recipes',
'films', 'films',
]; ];