From 2ec32cac12bf6f9b1f796963cd25eb746439da16 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 15 Mar 2024 20:40:06 -0500 Subject: [PATCH 01/10] Add caption, description fields to Photo --- src/photo/form/index.ts | 2 ++ src/photo/index.ts | 2 ++ src/services/vercel-postgres.ts | 54 ++++++++++++++++++++++++--------- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts index c1404a4a..5979828b 100644 --- a/src/photo/form/index.ts +++ b/src/photo/form/index.ts @@ -54,6 +54,8 @@ const FORM_METADATA = ( tagOptions?: AnnotatedTag[] ): Record => ({ title: { label: 'title', capitalize: true }, + caption: { label: 'caption', capitalize: true }, + description: { label: 'description', capitalize: true, hide: true}, tags: { label: 'tags', tagOptions, diff --git a/src/photo/index.ts b/src/photo/index.ts index 05608c81..cb4a003a 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -47,6 +47,8 @@ export interface PhotoDbInsert extends PhotoExif { extension: string blurData?: string title?: string + caption?: string + description?: string tags?: string[] locationName?: string priorityOrder?: number diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 7cc7cc2b..00e41984 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -28,6 +28,8 @@ const sqlCreatePhotosTable = () => aspect_ratio REAL DEFAULT 1.5, blur_data TEXT, title VARCHAR(255), + caption TEXT, + description TEXT, tags VARCHAR(255)[], make VARCHAR(255), model VARCHAR(255), @@ -50,9 +52,18 @@ const sqlCreatePhotosTable = () => ) `; +// MIGRATION 01 +const MIGRATION_FIELDS_01 = ['caption', 'description']; +const sqlRunMigration01 = () => + sql` + ALTER TABLE photos + ADD COLUMN caption TEXT, + ADD COLUMN description TEXT + `; + // Must provide id as 8-character nanoid -export const sqlInsertPhoto = (photo: PhotoDbInsert) => { - return sql` +export const sqlInsertPhoto = (photo: PhotoDbInsert) => + safelyQueryPhotos(() => sql` INSERT INTO photos ( id, url, @@ -60,6 +71,8 @@ export const sqlInsertPhoto = (photo: PhotoDbInsert) => { aspect_ratio, blur_data, title, + caption, + description, tags, make, model, @@ -85,6 +98,8 @@ export const sqlInsertPhoto = (photo: PhotoDbInsert) => { ${photo.aspectRatio}, ${photo.blurData}, ${photo.title}, + ${photo.caption}, + ${photo.description}, ${convertArrayToPostgresString(photo.tags)}, ${photo.make}, ${photo.model}, @@ -103,17 +118,18 @@ export const sqlInsertPhoto = (photo: PhotoDbInsert) => { ${photo.takenAt}, ${photo.takenAtNaive} ) - `; -}; + `); export const sqlUpdatePhoto = (photo: PhotoDbInsert) => - sql` + safelyQueryPhotos(() => sql` UPDATE photos SET url=${photo.url}, extension=${photo.extension}, aspect_ratio=${photo.aspectRatio}, blur_data=${photo.blurData}, title=${photo.title}, + caption=${photo.caption}, + description=${photo.description}, tags=${convertArrayToPostgresString(photo.tags)}, make=${photo.make}, model=${photo.model}, @@ -133,27 +149,29 @@ export const sqlUpdatePhoto = (photo: PhotoDbInsert) => taken_at_naive=${photo.takenAtNaive}, updated_at=${(new Date()).toISOString()} WHERE id=${photo.id} - `; + `); export const sqlDeletePhotoTagGlobally = (tag: string) => - sql` + safelyQueryPhotos(() => sql` UPDATE photos SET tags=ARRAY_REMOVE(tags, ${tag}) WHERE ${tag}=ANY(tags) - `; + `); export const sqlRenamePhotoTagGlobally = (tag: string, updatedTag: string) => - sql` + safelyQueryPhotos(() => sql` UPDATE photos SET tags=ARRAY_REPLACE(tags, ${tag}, ${updatedTag}) WHERE ${tag}=ANY(tags) - `; + `); export const sqlDeletePhoto = (id: string) => - sql`DELETE FROM photos WHERE id=${id}`; + safelyQueryPhotos(() => sql`DELETE FROM photos WHERE id=${id}`); const sqlGetPhoto = (id: string) => - sql`SELECT * FROM photos WHERE id=${id} LIMIT 1`; + safelyQueryPhotos(() => + sql`SELECT * FROM photos WHERE id=${id} LIMIT 1` + ); const sqlGetPhotosCount = async () => sql` SELECT COUNT(*) FROM photos @@ -291,8 +309,16 @@ const safelyQueryPhotos = async (callback: () => Promise): Promise => { try { result = await callback(); } catch (e: any) { - if (/relation "photos" does not exist/i.test(e.message)) { - console.log('Creating table "photos" because it did not exist'); + if (MIGRATION_FIELDS_01.some(field => new RegExp( + `column "${field}" of relation "photos" does not exist`, + 'i', + ).test(e.message))) { + console.log('Running migration 01 ...'); + await sqlRunMigration01(); + result = await callback(); + } else if (/relation "photos" does not exist/i.test(e.message)) { + // If the table does not exist, create it + console.log('Creating photos table ...'); await sqlCreatePhotosTable(); result = await callback(); } else if (/endpoint is in transition/i.test(e.message)) { From fead3d01e5ad1c498de02b49679b515913fa5bc2 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 15 Mar 2024 21:14:20 -0500 Subject: [PATCH 02/10] Hide caption field for photos without titles --- src/photo/form/PhotoForm.tsx | 4 ++-- src/photo/form/index.ts | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index 8db158c1..6cf08010 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -170,13 +170,13 @@ export default function PhotoForm({ validate, capitalize, hideIfEmpty, - hideBasedOnCamera, + shouldHide, loadingMessage, type, }]) => ( (!hideIfEmpty || formData[key]) && - !hideBasedOnCamera?.(formData.make) + !shouldHide?.(formData) ) && boolean + shouldHide?: (formData: Partial) => boolean loadingMessage?: string type?: FieldSetType selectOptions?: { value: string, label: string }[] @@ -54,7 +54,11 @@ const FORM_METADATA = ( tagOptions?: AnnotatedTag[] ): Record => ({ title: { label: 'title', capitalize: true }, - caption: { label: 'caption', capitalize: true }, + caption: { + label: 'caption', + capitalize: true, + shouldHide: ({ title, caption }) => !title && !caption, + }, description: { label: 'description', capitalize: true, hide: true}, tags: { label: 'tags', @@ -80,7 +84,7 @@ const FORM_METADATA = ( label: 'fujifilm simulation', selectOptions: FILM_SIMULATION_FORM_INPUT_OPTIONS, selectOptionsDefaultLabel: 'Unknown', - hideBasedOnCamera: make => make !== MAKE_FUJIFILM, + shouldHide: ({ make }) => make !== MAKE_FUJIFILM, }, focalLength: { label: 'focal length' }, focalLengthIn35MmFormat: { label: 'focal length 35mm-equivalent' }, From 5e50d55989030e76e341e00727a45f8293962e59 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 16 Mar 2024 12:29:30 -0500 Subject: [PATCH 03/10] Only add columns if they don't exist --- src/services/vercel-postgres.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 00e41984..1562a980 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -57,8 +57,8 @@ const MIGRATION_FIELDS_01 = ['caption', 'description']; const sqlRunMigration01 = () => sql` ALTER TABLE photos - ADD COLUMN caption TEXT, - ADD COLUMN description TEXT + ADD COLUMN IF NOT EXISTS caption TEXT, + ADD COLUMN IF NOT EXISTS description TEXT `; // Must provide id as 8-character nanoid From fccfbe83f5c570374305131c272fa3b558544d6c Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 16 Mar 2024 13:03:05 -0500 Subject: [PATCH 04/10] Validate photo string lengths --- src/photo/form/PhotoForm.tsx | 8 ++++++++ src/photo/form/index.ts | 25 +++++++++++++++++++++---- src/services/vercel-postgres.ts | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index 6cf08010..6f4e4b76 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -168,6 +168,7 @@ export default function PhotoForm({ tagOptions, readOnly, validate, + validateStringMaxLength, capitalize, hideIfEmpty, shouldHide, @@ -189,6 +190,13 @@ export default function PhotoForm({ setFormData({ ...formData, [key]: value }); if (validate) { setFormErrors({ ...formErrors, [key]: validate(value) }); + } else if (validateStringMaxLength !== undefined) { + setFormErrors({ + ...formErrors, + [key]: value.length > validateStringMaxLength + ? `${validateStringMaxLength} characters or less` + : undefined, + }); } if (key === 'title') { onTitleChange?.(value.trim()); diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts index a1b9c7db..ec10e46a 100644 --- a/src/photo/form/index.ts +++ b/src/photo/form/index.ts @@ -39,6 +39,7 @@ type FormMeta = { virtual?: boolean readOnly?: boolean validate?: (value?: string) => string | undefined + validateStringMaxLength?: number capitalize?: boolean hide?: boolean hideIfEmpty?: boolean @@ -50,16 +51,29 @@ type FormMeta = { tagOptions?: AnnotatedTag[] }; +const STRING_MAX_LENGTH_SHORT = 255; +const STRING_MAX_LENGTH_LONG = 1000; + const FORM_METADATA = ( tagOptions?: AnnotatedTag[] ): Record => ({ - title: { label: 'title', capitalize: true }, + title: { + label: 'title', + capitalize: true, + validateStringMaxLength: STRING_MAX_LENGTH_SHORT, + }, caption: { label: 'caption', capitalize: true, + validateStringMaxLength: STRING_MAX_LENGTH_LONG, shouldHide: ({ title, caption }) => !title && !caption, }, - description: { label: 'description', capitalize: true, hide: true}, + description: { + label: 'description', + capitalize: true, + validateStringMaxLength: STRING_MAX_LENGTH_LONG, + hide: true, + }, tags: { label: 'tags', tagOptions, @@ -122,9 +136,12 @@ export const getFormErrors = ( export const isFormValid = (formData: Partial) => FORM_METADATA_ENTRIES().every( - ([key, { required, validate }]) => + ([key, { required, validate, validateStringMaxLength }]) => (!required || Boolean(formData[key])) && - (validate?.(formData[key]) === undefined) + (validate?.(formData[key]) === undefined) && + // eslint-disable-next-line max-len + (!validateStringMaxLength || (formData[key]?.length ?? 0) <= validateStringMaxLength) && + (key !== 'tags' || !doesTagsStringIncludeFavs(formData.tags ?? '')) ); // CREATE FORM DATA: FROM PHOTO diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 1562a980..4c234b05 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -52,7 +52,7 @@ const sqlCreatePhotosTable = () => ) `; -// MIGRATION 01 +// Migration 01 const MIGRATION_FIELDS_01 = ['caption', 'description']; const sqlRunMigration01 = () => sql` From b2e7b2902254952bd2764dbaef0b83c0c5637eda Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 16 Mar 2024 20:48:01 -0500 Subject: [PATCH 05/10] Add caption text to photo details --- src/components/EntityLink.tsx | 19 +++++++++++-- src/photo/PhotoGridSidebar.tsx | 3 ++ src/photo/PhotoLarge.tsx | 51 +++++++++++++++++----------------- src/tag/PhotoTag.tsx | 2 +- src/tag/PhotoTags.tsx | 8 ++++-- 5 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/components/EntityLink.tsx b/src/components/EntityLink.tsx index 64785490..f22ffee3 100644 --- a/src/components/EntityLink.tsx +++ b/src/components/EntityLink.tsx @@ -17,7 +17,7 @@ export default function EntityLink({ title, type = 'icon-first', badged, - contrast, + contrast = 'high', hoverEntity, }: { label: ReactNode @@ -36,6 +36,17 @@ export default function EntityLink({ ; + const classForContrast = () => { + switch (contrast) { + case 'low': + return 'text-dim'; + case 'high': + return 'text-main'; + default: + return 'text-medium'; + } + }; + return ( {type !== 'icon-only' && <> @@ -67,7 +78,9 @@ export default function EntityLink({ : )} />} @@ -60,6 +62,7 @@ export default function PhotoGridSidebar({ camera={camera} type="text-only" countOnHover={count} + contrast="low" hideAppleIcon badged />)} diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index ecfe79ac..f37ac43c 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -44,16 +44,6 @@ export default function PhotoLarge({ const tags = sortTags(photo.tags, primaryTag); const camera = cameraFromPhoto(photo); - - const renderMiniGrid = (children: JSX.Element, rightPadding = true) => -
*]:sm:flex-grow', - rightPadding && 'pr-2', - )}> - {children} -
; return ( - {renderMiniGrid(<> -
+ {/* Meta */} +
+
+ {photo.caption && <> +
+ {photo.caption} +
+ {tags.length > 0 &&
} + } {tags.length > 0 && - } + }
+
+ {/* EXIF: Camera + Film Simulation */} +
{showCamera && shouldShowCameraDataForPhoto(photo) &&
}
} - )} - {renderMiniGrid(<> +
+ {/* EXIF: Details */} +
{shouldShowExifDataForPhoto(photo) &&
  • @@ -132,13 +130,16 @@ export default function PhotoLarge({
  • {photo.isoFormatted}
  • {photo.exposureCompensationFormatted ?? '—'}
} +
+ {/* Date + Share */} +
{photo.takenAtNaiveFormatted}
@@ -153,7 +154,7 @@ export default function PhotoLarge({ shouldScroll={shouldScrollOnShare} />
- , false)} +
} /> ); diff --git a/src/tag/PhotoTag.tsx b/src/tag/PhotoTag.tsx index ecc4efaf..56fab6a6 100644 --- a/src/tag/PhotoTag.tsx +++ b/src/tag/PhotoTag.tsx @@ -19,7 +19,7 @@ export default function PhotoTag({ href={pathForTag(tag)} icon={} type={type} badged={badged} diff --git a/src/tag/PhotoTags.tsx b/src/tag/PhotoTags.tsx index 4804dc17..ef48121e 100644 --- a/src/tag/PhotoTags.tsx +++ b/src/tag/PhotoTags.tsx @@ -1,19 +1,21 @@ import PhotoTag from '@/tag/PhotoTag'; import { isTagFavs } from '.'; import FavsTag from './FavsTag'; +import { EntityLinkExternalProps } from '@/components/EntityLink'; export default function PhotoTags({ tags, + contrast, }: { tags: string[] -}) { +} & EntityLinkExternalProps) { return (
{tags.map(tag =>
{isTagFavs(tag) - ? - : } + ? + : }
)}
); From 4b4e169febdbec8595dcb6b1e3f68e65803208b8 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 16 Mar 2024 22:25:00 -0500 Subject: [PATCH 06/10] Refine new content positioning --- src/photo/PhotoLarge.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index f37ac43c..8e0d3275 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -63,6 +63,7 @@ export default function PhotoLarge({ 'sticky top-4 self-start -translate-y-1', 'grid grid-cols-2 sm:grid-cols-4 md:grid-cols-1', 'gap-y-4', + 'pb-4', )}> {/* Meta */}
@@ -134,12 +135,11 @@ export default function PhotoLarge({ {/* Date + Share */}
{photo.takenAtNaiveFormatted}
From aa52ca42619449c165647a8b02df7e164c9893b3 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 16 Mar 2024 23:02:24 -0500 Subject: [PATCH 07/10] Refine photo detail --- src/photo/PhotoLarge.tsx | 179 +++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 84 deletions(-) diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 8e0d3275..3e704dd5 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -16,7 +16,7 @@ import { cameraFromPhoto } from '@/camera'; import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; import { sortTags } from '@/tag'; import AdminPhotoMenu from '@/admin/AdminPhotoMenu'; -import { Suspense } from 'react'; +import { Suspense, useMemo } from 'react'; export default function PhotoLarge({ photo, @@ -45,6 +45,17 @@ export default function PhotoLarge({ const camera = cameraFromPhoto(photo); + const showCameraRow = showCamera && shouldShowCameraDataForPhoto(photo); + + const showExifRow = shouldShowExifDataForPhoto(photo); + + const rowCount = useMemo(() => { + let count = 1; + if (showCameraRow) { count++; } + if (showExifRow) { count++; } + return count; + }, [showCameraRow, showExifRow]); + return ( *:not(:last-child)]:pr-3', )}> {/* Meta */} -
-
-
-
- - {titleForPhoto(photo)} - -
- -
- -
-
+
+
+
+ + {titleForPhoto(photo)} +
- {photo.caption && <> -
- {photo.caption} + +
+
- {tags.length > 0 &&
} - } - {tags.length > 0 && - } +
+ {photo.caption && <> +
+ {photo.caption} +
+ {tags.length > 0 && +
} + } + {tags.length > 0 && + }
{/* EXIF: Camera + Film Simulation */} -
- {showCamera && shouldShowCameraDataForPhoto(photo) && -
- - {showSimulation && photo.filmSimulation && -
- -
} -
} -
- {/* EXIF: Details */} -
- {shouldShowExifDataForPhoto(photo) && -
    -
  • - {photo.focalLengthFormatted} - {photo.focalLengthIn35MmFormatFormatted && - <> - {' '} - - {photo.focalLengthIn35MmFormatFormatted} - - } -
  • -
  • {photo.fNumberFormatted}
  • -
  • {photo.exposureTimeFormatted}
  • -
  • {photo.isoFormatted}
  • -
  • {photo.exposureCompensationFormatted ?? '—'}
  • -
} -
- {/* Date + Share */} -
-
-
- {photo.takenAtNaiveFormatted} -
- + + {showSimulation && photo.filmSimulation && +
+ +
} +
} + {/* EXIF: Details */} + {showExifRow && +
    +
  • + {photo.focalLengthFormatted} + {photo.focalLengthIn35MmFormatFormatted && + <> + {' '} + + {photo.focalLengthIn35MmFormatFormatted} + + } +
  • +
  • {photo.fNumberFormatted}
  • +
  • {photo.exposureTimeFormatted}
  • +
  • {photo.isoFormatted}
  • +
  • {photo.exposureCompensationFormatted ?? '—'}
  • +
} + {/* Date + Share */} +
+
+ {photo.takenAtNaiveFormatted}
+
} /> From a6ba4f8257cc45d03e89d1d3d750f30898a7e54b Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 17 Mar 2024 19:39:37 -0500 Subject: [PATCH 08/10] Change db field: description to semantic_description --- src/photo/form/index.ts | 4 ++-- src/photo/index.ts | 2 +- src/services/vercel-postgres.ts | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts index ec10e46a..078e36c6 100644 --- a/src/photo/form/index.ts +++ b/src/photo/form/index.ts @@ -68,8 +68,8 @@ const FORM_METADATA = ( validateStringMaxLength: STRING_MAX_LENGTH_LONG, shouldHide: ({ title, caption }) => !title && !caption, }, - description: { - label: 'description', + semanticDescription: { + label: 'semantic description', capitalize: true, validateStringMaxLength: STRING_MAX_LENGTH_LONG, hide: true, diff --git a/src/photo/index.ts b/src/photo/index.ts index cb4a003a..5156e4b4 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -48,7 +48,7 @@ export interface PhotoDbInsert extends PhotoExif { blurData?: string title?: string caption?: string - description?: string + semanticDescription?: string tags?: string[] locationName?: string priorityOrder?: number diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 4c234b05..8516c62e 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -29,7 +29,7 @@ const sqlCreatePhotosTable = () => blur_data TEXT, title VARCHAR(255), caption TEXT, - description TEXT, + semantic_description TEXT, tags VARCHAR(255)[], make VARCHAR(255), model VARCHAR(255), @@ -53,12 +53,12 @@ const sqlCreatePhotosTable = () => `; // Migration 01 -const MIGRATION_FIELDS_01 = ['caption', 'description']; +const MIGRATION_FIELDS_01 = ['caption', 'semantic_description']; const sqlRunMigration01 = () => sql` ALTER TABLE photos ADD COLUMN IF NOT EXISTS caption TEXT, - ADD COLUMN IF NOT EXISTS description TEXT + ADD COLUMN IF NOT EXISTS semantic_description TEXT `; // Must provide id as 8-character nanoid @@ -72,7 +72,7 @@ export const sqlInsertPhoto = (photo: PhotoDbInsert) => blur_data, title, caption, - description, + semantic_description, tags, make, model, @@ -99,7 +99,7 @@ export const sqlInsertPhoto = (photo: PhotoDbInsert) => ${photo.blurData}, ${photo.title}, ${photo.caption}, - ${photo.description}, + ${photo.semanticDescription}, ${convertArrayToPostgresString(photo.tags)}, ${photo.make}, ${photo.model}, @@ -129,7 +129,7 @@ export const sqlUpdatePhoto = (photo: PhotoDbInsert) => blur_data=${photo.blurData}, title=${photo.title}, caption=${photo.caption}, - description=${photo.description}, + semantic_description=${photo.semanticDescription}, tags=${convertArrayToPostgresString(photo.tags)}, make=${photo.make}, model=${photo.model}, From 031a1498935ee5dd2e4c3a9d319025b24bba35c8 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 17 Mar 2024 22:44:26 -0500 Subject: [PATCH 09/10] Refine large photo layout --- src/camera/PhotoCamera.tsx | 13 +-- src/photo/PhotoLarge.tsx | 167 +++++++++++++++++-------------------- 2 files changed, 80 insertions(+), 100 deletions(-) diff --git a/src/camera/PhotoCamera.tsx b/src/camera/PhotoCamera.tsx index 15f58ede..1f9eb84c 100644 --- a/src/camera/PhotoCamera.tsx +++ b/src/camera/PhotoCamera.tsx @@ -3,7 +3,6 @@ import { pathForCamera } from '@/site/paths'; import { IoMdCamera } from 'react-icons/io'; import { Camera, formatCameraText } from '.'; import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; -import { clsx } from 'clsx/lite'; export default function PhotoCamera({ camera, @@ -27,18 +26,12 @@ export default function PhotoCamera({ icon={showAppleIcon ? : } type={showAppleIcon && isCameraApple ? 'icon-first' : type} badged={badged} diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 3e704dd5..3f999b2e 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -16,7 +16,7 @@ import { cameraFromPhoto } from '@/camera'; import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; import { sortTags } from '@/tag'; import AdminPhotoMenu from '@/admin/AdminPhotoMenu'; -import { Suspense, useMemo } from 'react'; +import { Suspense } from 'react'; export default function PhotoLarge({ photo, @@ -49,13 +49,6 @@ export default function PhotoLarge({ const showExifRow = shouldShowExifDataForPhoto(photo); - const rowCount = useMemo(() => { - let count = 1; - if (showCameraRow) { count++; } - if (showExifRow) { count++; } - return count; - }, [showCameraRow, showExifRow]); - return ( *:not(:last-child)]:pr-3', )}> {/* Meta */} -
-
-
- - {titleForPhoto(photo)} - -
- -
- +
+
+
+
+ + {titleForPhoto(photo)} +
- -
- {photo.caption && <> -
- {photo.caption} + +
+ +
+
- {tags.length > 0 && -
} - } - {tags.length > 0 && - } + {photo.caption && +
+ {photo.caption} +
} +
+ {(showCameraRow || tags.length > 0) && +
+ {showCameraRow && + } + {tags.length > 0 && + } +
}
- {/* EXIF: Camera + Film Simulation */} - {showCameraRow && -
- - {showSimulation && photo.filmSimulation && -
+ {/* EXIF Data */} +
+ {showExifRow && + <> +
    +
  • + {photo.focalLengthFormatted} + {photo.focalLengthIn35MmFormatFormatted && + <> + {' '} + + {photo.focalLengthIn35MmFormatFormatted} + + } +
  • +
  • {photo.fNumberFormatted}
  • +
  • {photo.exposureTimeFormatted}
  • +
  • {photo.isoFormatted}
  • +
  • {photo.exposureCompensationFormatted ?? '0ev'}
  • +
+ {showSimulation && photo.filmSimulation && -
} -
} - {/* EXIF: Details */} - {showExifRow && -
    -
  • - {photo.focalLengthFormatted} - {photo.focalLengthIn35MmFormatFormatted && - <> - {' '} - - {photo.focalLengthIn35MmFormatFormatted} - - } -
  • -
  • {photo.fNumberFormatted}
  • -
  • {photo.exposureTimeFormatted}
  • -
  • {photo.isoFormatted}
  • -
  • {photo.exposureCompensationFormatted ?? '—'}
  • -
} - {/* Date + Share */} -
+ />} + }
- {photo.takenAtNaiveFormatted} +
+ {photo.takenAtNaiveFormatted} +
+
-
} /> From f940798c4dd8233053ea4d0f3b40149b19b25964 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 17 Mar 2024 23:41:01 -0500 Subject: [PATCH 10/10] Finalize photo detail page with captions --- src/components/EntityLink.tsx | 2 +- src/photo/PhotoLarge.tsx | 62 +++++++++++++++++------------------ src/photo/index.ts | 16 ++++----- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/components/EntityLink.tsx b/src/components/EntityLink.tsx index f22ffee3..da271fd6 100644 --- a/src/components/EntityLink.tsx +++ b/src/components/EntityLink.tsx @@ -48,7 +48,7 @@ export default function EntityLink({ }; return ( - + 0; + const showExifContent = shouldShowExifDataForPhoto(photo); return ( } contentSide={
*:not(:last-child)]:pr-3', )}> {/* Meta */} -
-
-
-
- - {titleForPhoto(photo)} - -
- -
- -
-
+
+
+
+ + {titleForPhoto(photo)} +
+ +
+ +
+
+
+
{photo.caption &&
{photo.caption}
} + {(showCameraContent || showTagsContent) && +
+ {showCameraContent && + } + {showTagsContent && + } +
}
- {(showCameraRow || tags.length > 0) && -
- {showCameraRow && - } - {tags.length > 0 && - } -
}
{/* EXIF Data */}
- {showExifRow && + {showExifContent && <>
  • diff --git a/src/photo/index.ts b/src/photo/index.ts index 5156e4b4..89094cff 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -231,16 +231,16 @@ export const dateRangeForPhotos = ( }; const photoHasCameraData = (photo: Photo) => - photo.make && - photo.model; + Boolean(photo.make) && + Boolean(photo.model); const photoHasExifData = (photo: Photo) => - photo.focalLength || - photo.focalLengthIn35MmFormat || - photo.fNumberFormatted || - photo.isoFormatted || - photo.exposureTimeFormatted || - photo.exposureCompensationFormatted; + Boolean(photo.focalLength) || + Boolean(photo.focalLengthIn35MmFormat) || + Boolean(photo.fNumberFormatted) || + Boolean(photo.isoFormatted) || + Boolean(photo.exposureTimeFormatted) || + Boolean(photo.exposureCompensationFormatted); export const shouldShowCameraDataForPhoto = (photo: Photo) => SHOW_EXIF_DATA && photoHasCameraData(photo);