From 6be23effc75cdb06e3bae2d83fde43f922ee8488 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 26 Aug 2024 09:19:14 -0500 Subject: [PATCH 01/10] Create navigation-based photo header --- src/photo/PhotoDetailPage.tsx | 46 ++++++++++------------ src/photo/PhotoLarge.tsx | 17 ++++++-- src/photo/{PhotoLinks.tsx => PhotoNav.tsx} | 44 ++++++++++++++++++--- 3 files changed, 72 insertions(+), 35 deletions(-) rename src/photo/{PhotoLinks.tsx => PhotoNav.tsx} (71%) diff --git a/src/photo/PhotoDetailPage.tsx b/src/photo/PhotoDetailPage.tsx index ed70dec8..fb16f223 100644 --- a/src/photo/PhotoDetailPage.tsx +++ b/src/photo/PhotoDetailPage.tsx @@ -3,8 +3,7 @@ import { Photo, PhotoDateRange } from '.'; import PhotoLarge from './PhotoLarge'; import SiteGrid from '@/components/SiteGrid'; import PhotoGrid from './PhotoGrid'; -import { clsx } from 'clsx/lite'; -import PhotoLinks from './PhotoLinks'; +import PhotoNav from './PhotoNav'; import TagHeader from '@/tag/TagHeader'; import { Camera } from '@/camera'; import CameraHeader from '@/camera/CameraHeader'; @@ -102,6 +101,24 @@ export default function PhotoDetailPage({ dateRange={dateRange} />} />} + } + />, + ]} + /> } - contentSide={ - - , - ]} - />} /> ); diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index cda6ac5b..cba780a9 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -38,11 +38,13 @@ import { useAppState } from '@/state/AppState'; export default function PhotoLarge({ photo, + className, primaryTag, priority, prefetch = SHOULD_PREFETCH_ALL_LINKS, prefetchRelatedLinks = SHOULD_PREFETCH_ALL_LINKS, revalidatePhoto, + showTitle = true, showCamera = true, showSimulation = true, shouldShare = true, @@ -55,11 +57,13 @@ export default function PhotoLarge({ onVisible, }: { photo: Photo + className?: string primaryTag?: string priority?: boolean prefetch?: boolean prefetchRelatedLinks?: boolean revalidatePhoto?: RevalidatePhoto + showTitle?: boolean showCamera?: boolean showSimulation?: boolean shouldShare?: boolean @@ -85,10 +89,14 @@ export default function PhotoLarge({ const { arePhotosMatted, isUserSignedIn } = useAppState(); + const hasTitle = showTitle && ( + Boolean(photo.title) || + SHOW_PHOTO_TITLE_FALLBACK_TEXT + ); + const hasTitleContent = - photo.title || - SHOW_PHOTO_TITLE_FALLBACK_TEXT || - photo.caption; + hasTitle || + Boolean(photo.caption); const hasMetaContent = showCameraContent || @@ -102,6 +110,7 @@ export default function PhotoLarge({ return (
- {(photo.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) && + {hasTitle && +
- PREV + + + PREV + +
+ {(photo.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) && + } +
- NEXT + + NEXT + + - +
); }; From e0a83415b0172f65c5605794ea1f7ecb6d99413d Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 26 Aug 2024 10:03:21 -0500 Subject: [PATCH 02/10] Fix photo nav dark mode support --- src/photo/PhotoDetailPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/photo/PhotoDetailPage.tsx b/src/photo/PhotoDetailPage.tsx index fb16f223..630f20ce 100644 --- a/src/photo/PhotoDetailPage.tsx +++ b/src/photo/PhotoDetailPage.tsx @@ -110,7 +110,7 @@ export default function PhotoDetailPage({ contentMain={ Date: Sat, 31 Aug 2024 19:43:52 -0500 Subject: [PATCH 03/10] Combine photo nav + sets --- src/camera/CameraHeader.tsx | 5 +- src/focal/FocalLengthHeader.tsx | 5 +- src/photo/InfinitePhotoScroll.tsx | 9 +- src/photo/PhotoDetailPage.tsx | 142 +++++++----------- src/photo/PhotoGrid.tsx | 10 +- src/photo/PhotoGridContainer.tsx | 2 +- src/photo/PhotoHeader.tsx | 123 +++++++++++++++ src/photo/PhotoLink.tsx | 10 +- src/photo/PhotoMedium.tsx | 15 +- src/photo/{PhotoNav.tsx => PhotoPrevNext.tsx} | 110 ++++++-------- src/photo/PhotoSetHeader.tsx | 85 ----------- src/photo/PhotoShareModal.tsx | 10 +- src/photo/PhotoSmall.tsx | 15 +- src/photo/db/index.ts | 11 +- src/photo/index.ts | 10 ++ src/simulation/FilmSimulationHeader.tsx | 5 +- src/site/globals.css | 4 + src/site/paths.ts | 16 +- src/tag/HiddenHeader.tsx | 4 +- src/tag/TagHeader.tsx | 5 +- 20 files changed, 281 insertions(+), 315 deletions(-) create mode 100644 src/photo/PhotoHeader.tsx rename src/photo/{PhotoNav.tsx => PhotoPrevNext.tsx} (53%) delete mode 100644 src/photo/PhotoSetHeader.tsx diff --git a/src/camera/CameraHeader.tsx b/src/camera/CameraHeader.tsx index 82675100..69326614 100644 --- a/src/camera/CameraHeader.tsx +++ b/src/camera/CameraHeader.tsx @@ -1,6 +1,6 @@ import { Photo, PhotoDateRange } from '@/photo'; import { pathForCameraShare } from '@/site/paths'; -import PhotoSetHeader from '@/photo/PhotoSetHeader'; +import PhotoHeader from '@/photo/PhotoHeader'; import { Camera, cameraFromPhoto } from '.'; import PhotoCamera from './PhotoCamera'; import { descriptionForCameraPhotos } from './meta'; @@ -22,7 +22,8 @@ export default function CameraHeader({ }) { const camera = cameraFromPhoto(photos[0], cameraProp); return ( - } entityVerb="Photo" entityDescription={ diff --git a/src/focal/FocalLengthHeader.tsx b/src/focal/FocalLengthHeader.tsx index b40f459a..e30d68f3 100644 --- a/src/focal/FocalLengthHeader.tsx +++ b/src/focal/FocalLengthHeader.tsx @@ -1,7 +1,7 @@ import { Photo, PhotoDateRange } from '@/photo'; import { descriptionForFocalLengthPhotos } from '.'; import { pathForFocalLengthShare } from '@/site/paths'; -import PhotoSetHeader from '@/photo/PhotoSetHeader'; +import PhotoHeader from '@/photo/PhotoHeader'; import PhotoFocalLength from './PhotoFocalLength'; export default function FocalLengthHeader({ @@ -20,7 +20,8 @@ export default function FocalLengthHeader({ dateRange?: PhotoDateRange }) { return ( - } entityDescription={descriptionForFocalLengthPhotos( photos, diff --git a/src/photo/InfinitePhotoScroll.tsx b/src/photo/InfinitePhotoScroll.tsx index dfb85f37..b0739287 100644 --- a/src/photo/InfinitePhotoScroll.tsx +++ b/src/photo/InfinitePhotoScroll.tsx @@ -10,11 +10,9 @@ import { import SiteGrid from '@/components/SiteGrid'; import Spinner from '@/components/Spinner'; import { getPhotosCachedAction, getPhotosAction } from '@/photo/actions'; -import { Photo } from '.'; +import { Photo, PhotoSetAttributes } from '.'; import { clsx } from 'clsx/lite'; import { useAppState } from '@/state/AppState'; -import { Camera } from '@/camera'; -import { FilmSimulation } from '@/simulation'; import { GetPhotosOptions } from './db'; export type RevalidatePhoto = ( @@ -38,9 +36,6 @@ export default function InfinitePhotoScroll({ initialOffset: number itemsPerPage: number sortBy?: GetPhotosOptions['sortBy'] - tag?: string - camera?: Camera - simulation?: FilmSimulation cacheKey: string wrapMoreButtonInGrid?: boolean useCachedPhotos?: boolean @@ -50,7 +45,7 @@ export default function InfinitePhotoScroll({ onLastPhotoVisible: () => void revalidatePhoto?: RevalidatePhoto }) => ReactNode -}) { +} & PhotoSetAttributes) { const { swrTimestamp, isUserSignedIn } = useAppState(); const key = `${swrTimestamp}-${cacheKey}`; diff --git a/src/photo/PhotoDetailPage.tsx b/src/photo/PhotoDetailPage.tsx index 630f20ce..e86e5f5e 100644 --- a/src/photo/PhotoDetailPage.tsx +++ b/src/photo/PhotoDetailPage.tsx @@ -1,17 +1,15 @@ import AnimateItems from '@/components/AnimateItems'; -import { Photo, PhotoDateRange } from '.'; +import { Photo, PhotoDateRange, PhotoSetAttributes } from '.'; import PhotoLarge from './PhotoLarge'; import SiteGrid from '@/components/SiteGrid'; import PhotoGrid from './PhotoGrid'; -import PhotoNav from './PhotoNav'; import TagHeader from '@/tag/TagHeader'; -import { Camera } from '@/camera'; import CameraHeader from '@/camera/CameraHeader'; -import { FilmSimulation } from '@/simulation'; import FilmSimulationHeader from '@/simulation/FilmSimulationHeader'; import { TAG_HIDDEN } from '@/tag'; import HiddenHeader from '@/tag/HiddenHeader'; import FocalLengthHeader from '@/focal/FocalLengthHeader'; +import PhotoHeader from './PhotoHeader'; export default function PhotoDetailPage({ photo, @@ -30,94 +28,68 @@ export default function PhotoDetailPage({ photo: Photo photos: Photo[] photosGrid?: Photo[] - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number indexNumber?: number count?: number dateRange?: PhotoDateRange shouldShare?: boolean includeFavoriteInAdminMenu?: boolean -}) { +} & PhotoSetAttributes) { + let customHeader: JSX.Element | undefined; + + if (tag) { + customHeader = tag === TAG_HIDDEN + ? + : ; + } else if (camera) { + customHeader = ; + } else if (simulation) { + customHeader = ; + } else if (focal) { + customHeader = ; + } + return (
- {tag && - - : } + } - {camera && - } - />} - {simulation && - } - />} - {focal && - } - />} - } - />, - ]} /> void onAnimationComplete?: () => void -}) { +} & PhotoSetAttributes) { const { isUserSignedIn, selectedPhotoIds, diff --git a/src/photo/PhotoGridContainer.tsx b/src/photo/PhotoGridContainer.tsx index dd29fafd..0346c4b5 100644 --- a/src/photo/PhotoGridContainer.tsx +++ b/src/photo/PhotoGridContainer.tsx @@ -38,7 +38,7 @@ export default function PhotoGridContainer({ return ( {header && photo.id === selectedPhoto.id) + : undefined; + + const renderPrevNext = () => + ; + + const renderDateRange = () => + + {start === end + ? start + : <>{end}
– {start}} +
; + + return ( + + + {entity ?? ( + (selectedPhoto?.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) + ? + : <>X of X + )} + + + {entity && <> + {selectedPhotoIndex !== undefined + // eslint-disable-next-line max-len + ? `${entityVerb ? `${entityVerb} ` : ''}${indexNumber || (selectedPhotoIndex + 1)} of ${count ?? photos.length}` + : entityDescription} + {selectedPhotoIndex === undefined && sharePath && + } + } + +
+ {selectedPhoto + ? renderPrevNext() + : renderDateRange()} +
+ , + ]} + /> + ); +} diff --git a/src/photo/PhotoLink.tsx b/src/photo/PhotoLink.tsx index 34bcca38..088ffc1d 100644 --- a/src/photo/PhotoLink.tsx +++ b/src/photo/PhotoLink.tsx @@ -1,13 +1,11 @@ 'use client'; import { ReactNode } from 'react'; -import { Photo, titleForPhoto } from '@/photo'; +import { Photo, PhotoSetAttributes, titleForPhoto } from '@/photo'; import Link from 'next/link'; import { AnimationConfig } from '../components/AnimateItems'; import { useAppState } from '@/state/AppState'; import { pathForPhoto } from '@/site/paths'; -import { Camera } from '@/camera'; -import { FilmSimulation } from '@/simulation'; import { clsx } from 'clsx/lite'; export default function PhotoLink({ @@ -23,16 +21,12 @@ export default function PhotoLink({ children, }: { photo?: Photo - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number scroll?: boolean prefetch?: boolean nextPhotoAnimation?: AnimationConfig className?: string children?: ReactNode -}) { +} & PhotoSetAttributes) { const { setNextPhotoAnimation } = useAppState(); return ( diff --git a/src/photo/PhotoMedium.tsx b/src/photo/PhotoMedium.tsx index 72fdd0a4..df1bab48 100644 --- a/src/photo/PhotoMedium.tsx +++ b/src/photo/PhotoMedium.tsx @@ -1,12 +1,15 @@ 'use client'; -import { Photo, altTextForPhoto, doesPhotoNeedBlurCompatibility } from '.'; +import { + Photo, + PhotoSetAttributes, + altTextForPhoto, + doesPhotoNeedBlurCompatibility, +} from '.'; import ImageMedium from '@/components/image/ImageMedium'; import Link from 'next/link'; import { clsx } from 'clsx/lite'; import { pathForPhoto } from '@/site/paths'; -import { Camera } from '@/camera'; -import { FilmSimulation } from '@/simulation'; import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config'; import { useRef } from 'react'; import useOnVisible from '@/utility/useOnVisible'; @@ -24,16 +27,12 @@ export default function PhotoMedium({ onVisible, }: { photo: Photo - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number selected?: boolean priority?: boolean prefetch?: boolean className?: string onVisible?: () => void -}) { +} & PhotoSetAttributes) { const ref = useRef(null); useOnVisible(ref, onVisible); diff --git a/src/photo/PhotoNav.tsx b/src/photo/PhotoPrevNext.tsx similarity index 53% rename from src/photo/PhotoNav.tsx rename to src/photo/PhotoPrevNext.tsx index 25020df1..a8ef3b2d 100644 --- a/src/photo/PhotoNav.tsx +++ b/src/photo/PhotoPrevNext.tsx @@ -1,16 +1,17 @@ 'use client'; import { useEffect } from 'react'; -import { Photo, getNextPhoto, getPreviousPhoto } from '@/photo'; +import { + Photo, + PhotoSetAttributes, + getNextPhoto, + getPreviousPhoto, +} from '@/photo'; import PhotoLink from './PhotoLink'; import { useRouter } from 'next/navigation'; import { pathForPhoto } from '@/site/paths'; import { useAppState } from '@/state/AppState'; import { AnimationConfig } from '@/components/AnimateItems'; -import { Camera } from '@/camera'; -import { FilmSimulation } from '@/simulation'; -import { SHOW_PHOTO_TITLE_FALLBACK_TEXT } from '@/site/config'; -import { BiChevronLeft, BiChevronRight } from 'react-icons/bi'; import { clsx } from 'clsx/lite'; const LISTENER_KEYUP = 'keyup'; @@ -18,25 +19,19 @@ const LISTENER_KEYUP = 'keyup'; const ANIMATION_LEFT: AnimationConfig = { type: 'left', duration: 0.3 }; const ANIMATION_RIGHT: AnimationConfig = { type: 'right', duration: 0.3 }; -export default function PhotoNav({ +export default function PhotoPrevNext({ photo, - photos, + photos = [], className, tag, camera, simulation, focal, - prefetch, }: { - photo: Photo - photos: Photo[] + photo?: Photo + photos?: Photo[] className?: string - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number - prefetch?: boolean -}) { +} & PhotoSetAttributes) { const router = useRouter(); const { @@ -44,8 +39,8 @@ export default function PhotoNav({ shouldRespondToKeyboardCommands, } = useAppState(); - const previousPhoto = getPreviousPhoto(photo, photos); - const nextPhoto = getNextPhoto(photo, photos); + const previousPhoto = photo ? getPreviousPhoto(photo, photos) : undefined; + const nextPhoto = photo ? getNextPhoto(photo, photos) : undefined; useEffect(() => { if (shouldRespondToKeyboardCommands) { @@ -105,54 +100,39 @@ export default function PhotoNav({ 'flex items-center', className, )}> - - - - PREV - - -
- {(photo.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) && - } +
+ + + PREV + + + / + + + NEXT + +
- - - NEXT - - -
); }; diff --git a/src/photo/PhotoSetHeader.tsx b/src/photo/PhotoSetHeader.tsx deleted file mode 100644 index 87b4da2f..00000000 --- a/src/photo/PhotoSetHeader.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { clsx } from 'clsx/lite'; -import { Photo, PhotoDateRange, dateRangeForPhotos } from '.'; -import ShareButton from '@/components/ShareButton'; -import AnimateItems from '@/components/AnimateItems'; -import { ReactNode } from 'react'; -import { HIGH_DENSITY_GRID } from '@/site/config'; -import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid'; - -export default function PhotoSetHeader({ - entity, - entityVerb, - entityDescription, - photos, - selectedPhoto, - sharePath, - indexNumber, - count, - dateRange, -}: { - entity: ReactNode - entityVerb?: string - entityDescription: string - photos: Photo[] - selectedPhoto?: Photo - sharePath?: string - indexNumber?: number - count?: number - dateRange?: PhotoDateRange -}) { - const { start, end } = dateRangeForPhotos(photos, dateRange); - - const selectedPhotoIndex = selectedPhoto - ? photos.findIndex(photo => photo.id === selectedPhoto.id) - : undefined; - - return ( - - - {entity} - - - {selectedPhotoIndex !== undefined - // eslint-disable-next-line max-len - ? `${entityVerb ? `${entityVerb} ` : ''}${indexNumber || (selectedPhotoIndex + 1)} of ${count ?? photos.length}` - : entityDescription} - {selectedPhotoIndex === undefined && sharePath && - } - - - {start === end - ? start - : <>{end}
– {start}} -
- ]} - /> - ); -} diff --git a/src/photo/PhotoShareModal.tsx b/src/photo/PhotoShareModal.tsx index b9a6f14c..8f577454 100644 --- a/src/photo/PhotoShareModal.tsx +++ b/src/photo/PhotoShareModal.tsx @@ -1,17 +1,11 @@ import PhotoOGTile from '@/photo/PhotoOGTile'; import { absolutePathForPhoto, pathForPhoto } from '@/site/paths'; -import { Photo } from '.'; +import { Photo, PhotoSetAttributes } from '.'; import ShareModal from '@/components/ShareModal'; -import { Camera } from '@/camera'; -import { FilmSimulation } from '@/simulation'; export default function PhotoShareModal(props: { photo: Photo - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number -}) { +} & PhotoSetAttributes) { return ( void -}) { +} & PhotoSetAttributes) { const ref = useRef(null); useOnVisible(ref, onVisible); diff --git a/src/photo/db/index.ts b/src/photo/db/index.ts index 2bc13658..cf2b1ec7 100644 --- a/src/photo/db/index.ts +++ b/src/photo/db/index.ts @@ -1,8 +1,6 @@ -import { Camera } from '@/camera'; -import { Lens } from '@/lens'; -import { FilmSimulation } from '@/simulation'; import { PRIORITY_ORDER_ENABLED } from '@/site/config'; import { parameterize } from '@/utility/string'; +import { PhotoSetAttributes } from '..'; export const GENERATE_STATIC_PARAMS_LIMIT = 1000; export const PHOTO_DEFAULT_LIMIT = 100; @@ -12,16 +10,11 @@ export type GetPhotosOptions = { limit?: number offset?: number query?: string - tag?: string - camera?: Camera - lens?: Lens - simulation?: FilmSimulation - focal?: number takenBefore?: Date takenAfterInclusive?: Date updatedBefore?: Date hidden?: 'exclude' | 'include' | 'only' -}; +} & PhotoSetAttributes; export const areOptionsSensitive = (options: GetPhotosOptions) => options.hidden === 'include' || options.hidden === 'only'; diff --git a/src/photo/index.ts b/src/photo/index.ts index 646bd13e..bf3feab3 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -1,4 +1,6 @@ +import { Camera } from '@/camera'; import { formatFocalLength } from '@/focal'; +import { Lens } from '@/lens'; import { getNextImageUrlForRequest } from '@/services/next-image'; import { FilmSimulation } from '@/simulation'; import { HIGH_DENSITY_GRID, SHOW_EXIF_DATA } from '@/site/config'; @@ -99,6 +101,14 @@ export interface Photo extends PhotoDb { takenAtNaiveFormatted: string } +export interface PhotoSetAttributes { + tag?: string + camera?: Camera + simulation?: FilmSimulation + focal?: number + lens?: Lens // Unimplemented as a set +} + export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => { const photoDb = camelcaseKeys( photoDbRaw as unknown as Record diff --git a/src/simulation/FilmSimulationHeader.tsx b/src/simulation/FilmSimulationHeader.tsx index b0908572..a8c95402 100644 --- a/src/simulation/FilmSimulationHeader.tsx +++ b/src/simulation/FilmSimulationHeader.tsx @@ -1,7 +1,7 @@ import { Photo, PhotoDateRange } from '@/photo'; import { FilmSimulation, descriptionForFilmSimulationPhotos } from '.'; import { pathForFilmSimulationShare } from '@/site/paths'; -import PhotoSetHeader from '@/photo/PhotoSetHeader'; +import PhotoHeader from '@/photo/PhotoHeader'; import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; @@ -21,7 +21,8 @@ export default function FilmSimulationHeader({ dateRange?: PhotoDateRange }) { return ( - } entityVerb="Photo" entityDescription={descriptionForFilmSimulationPhotos( diff --git a/src/site/globals.css b/src/site/globals.css index c7eccf13..13d47311 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -142,6 +142,10 @@ @apply text-gray-400/80 dark:text-gray-400/50 } + .text-extra-extra-dim { + @apply + text-gray-200 dark:text-gray-800 + } .text-icon { @apply text-gray-800 dark:text-gray-200 diff --git a/src/site/paths.ts b/src/site/paths.ts index 8d15811d..e812bfa8 100644 --- a/src/site/paths.ts +++ b/src/site/paths.ts @@ -1,4 +1,4 @@ -import { Photo } from '@/photo'; +import { Photo, PhotoSetAttributes } from '@/photo'; import { BASE_URL, GRID_HOMEPAGE_ENABLED } from './config'; import { Camera } from '@/camera'; import { FilmSimulation } from '@/simulation'; @@ -75,13 +75,7 @@ export const PATHS_TO_CACHE = [ ...PATHS_ADMIN, ]; -interface PhotoPathParams { - photo: PhotoOrPhotoId - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number -} +type PhotoPathParams = { photo: PhotoOrPhotoId } & PhotoSetAttributes; // Absolute paths export const ABSOLUTE_PATH_FOR_HOME_IMAGE = `${BASE_URL}/home-image`; @@ -280,11 +274,7 @@ export const isPathProtected = (pathname?: string) => export const getPathComponents = (pathname = ''): { photoId?: string - tag?: string - camera?: Camera - simulation?: FilmSimulation - focal?: number -} => { +} & PhotoSetAttributes => { const photoIdFromPhoto = pathname.match( new RegExp(`^${PREFIX_PHOTO}/([^/]+)`))?.[1]; const photoIdFromTag = pathname.match( diff --git a/src/tag/HiddenHeader.tsx b/src/tag/HiddenHeader.tsx index feac25fb..244013be 100644 --- a/src/tag/HiddenHeader.tsx +++ b/src/tag/HiddenHeader.tsx @@ -1,5 +1,5 @@ import { Photo, photoQuantityText } from '@/photo'; -import PhotoSetHeader from '@/photo/PhotoSetHeader'; +import PhotoHeader from '@/photo/PhotoHeader'; import HiddenTag from './HiddenTag'; export default function HiddenHeader({ @@ -14,7 +14,7 @@ export default function HiddenHeader({ count: number }) { return ( - } entityDescription={photoQuantityText(count, false)} diff --git a/src/tag/TagHeader.tsx b/src/tag/TagHeader.tsx index 4a9e3165..6eff23bc 100644 --- a/src/tag/TagHeader.tsx +++ b/src/tag/TagHeader.tsx @@ -2,7 +2,7 @@ import { Photo, PhotoDateRange } from '@/photo'; import PhotoTag from './PhotoTag'; import { descriptionForTaggedPhotos, isTagFavs } from '.'; import { pathForTagShare } from '@/site/paths'; -import PhotoSetHeader from '@/photo/PhotoSetHeader'; +import PhotoHeader from '@/photo/PhotoHeader'; import FavsTag from './FavsTag'; export default function TagHeader({ @@ -21,7 +21,8 @@ export default function TagHeader({ dateRange?: PhotoDateRange }) { return ( - : } From 41ac60c1b991089c4961288a4d8dcbd4abb9def2 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 31 Aug 2024 22:23:35 -0500 Subject: [PATCH 04/10] Fix share button spacing --- src/photo/PhotoHeader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx index d9f8f54f..16544418 100644 --- a/src/photo/PhotoHeader.tsx +++ b/src/photo/PhotoHeader.tsx @@ -91,8 +91,8 @@ export default function PhotoHeader({ )} Date: Sat, 31 Aug 2024 23:30:21 -0500 Subject: [PATCH 05/10] Show prev/next chevrons on mobile --- src/camera/CameraHeader.tsx | 1 - src/photo/PhotoPrevNext.tsx | 11 +++++------ src/simulation/FilmSimulationHeader.tsx | 1 - 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/camera/CameraHeader.tsx b/src/camera/CameraHeader.tsx index 69326614..150d3a64 100644 --- a/src/camera/CameraHeader.tsx +++ b/src/camera/CameraHeader.tsx @@ -25,7 +25,6 @@ export default function CameraHeader({ } - entityVerb="Photo" entityDescription={ descriptionForCameraPhotos(photos, undefined, count, dateRange)} photos={photos} diff --git a/src/photo/PhotoPrevNext.tsx b/src/photo/PhotoPrevNext.tsx index a8ef3b2d..bf714327 100644 --- a/src/photo/PhotoPrevNext.tsx +++ b/src/photo/PhotoPrevNext.tsx @@ -13,6 +13,7 @@ import { pathForPhoto } from '@/site/paths'; import { useAppState } from '@/state/AppState'; import { AnimationConfig } from '@/components/AnimateItems'; import { clsx } from 'clsx/lite'; +import { FiChevronLeft, FiChevronRight } from 'react-icons/fi'; const LISTENER_KEYUP = 'keyup'; @@ -112,9 +113,8 @@ export default function PhotoPrevNext({ scroll={false} prefetch > - - PREV - + + PREV / - - NEXT - + + NEXT
diff --git a/src/simulation/FilmSimulationHeader.tsx b/src/simulation/FilmSimulationHeader.tsx index a8c95402..df07a4f3 100644 --- a/src/simulation/FilmSimulationHeader.tsx +++ b/src/simulation/FilmSimulationHeader.tsx @@ -24,7 +24,6 @@ export default function FilmSimulationHeader({ } - entityVerb="Photo" entityDescription={descriptionForFilmSimulationPhotos( photos, undefined, count, dateRange)} photos={photos} From deca3acacbf5de0859dcca23c65474dede131edb Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 1 Sep 2024 00:01:17 -0500 Subject: [PATCH 06/10] Refine spacing --- src/photo/PhotoDetailPage.tsx | 2 +- src/photo/PhotoGridContainer.tsx | 2 +- src/photo/PhotoLarge.tsx | 8 ++++++-- src/photo/PhotoPrevNext.tsx | 18 ++++++++++++------ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/photo/PhotoDetailPage.tsx b/src/photo/PhotoDetailPage.tsx index e86e5f5e..ecfd14d7 100644 --- a/src/photo/PhotoDetailPage.tsx +++ b/src/photo/PhotoDetailPage.tsx @@ -85,7 +85,7 @@ export default function PhotoDetailPage({ return (
{header &&
{photo.caption && -
+
{photo.caption}
} {(showCameraContent || showTagsContent) && @@ -233,7 +237,7 @@ export default function PhotoLarge({ photo={photo} className={clsx( 'text-medium', - // Prevent date collision with admin button + // Prevent collision with admin button !hasNonDateContent && isUserSignedIn && 'md:pr-7', )} /> diff --git a/src/photo/PhotoPrevNext.tsx b/src/photo/PhotoPrevNext.tsx index bf714327..37c276b0 100644 --- a/src/photo/PhotoPrevNext.tsx +++ b/src/photo/PhotoPrevNext.tsx @@ -101,10 +101,10 @@ export default function PhotoPrevNext({ 'flex items-center', className, )}> -
+
- + PREV - / + + / + - + NEXT
From dcc5df0a4041fd77b809953f651674c9c74c0414 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 1 Sep 2024 00:25:44 -0500 Subject: [PATCH 07/10] Refine photo header grid --- src/photo/PhotoHeader.tsx | 18 ++++++++++-------- src/photo/PhotoPrevNext.tsx | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx index 16544418..4a1780f6 100644 --- a/src/photo/PhotoHeader.tsx +++ b/src/photo/PhotoHeader.tsx @@ -72,14 +72,13 @@ export default function PhotoHeader({ items={[ {entity ?? ( (selectedPhoto?.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) @@ -91,12 +90,12 @@ export default function PhotoHeader({ )} {entity && <> {selectedPhotoIndex !== undefined @@ -111,7 +110,10 @@ export default function PhotoHeader({ />} } -
+
{selectedPhoto ? renderPrevNext() : renderDateRange()} diff --git a/src/photo/PhotoPrevNext.tsx b/src/photo/PhotoPrevNext.tsx index 37c276b0..1535b333 100644 --- a/src/photo/PhotoPrevNext.tsx +++ b/src/photo/PhotoPrevNext.tsx @@ -114,7 +114,7 @@ export default function PhotoPrevNext({ prefetch > PREV @@ -133,7 +133,7 @@ export default function PhotoPrevNext({ prefetch > NEXT From ae8da21ae597a9759eead795fb315ad61be5c856 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 1 Sep 2024 12:22:42 -0500 Subject: [PATCH 08/10] Fallback to dates in photo nav --- src/photo/PhotoHeader.tsx | 22 ++++++++++------------ src/photo/PhotoPrevNext.tsx | 4 ++-- src/utility/date.ts | 5 ++++- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx index 4a1780f6..3e13c662 100644 --- a/src/photo/PhotoHeader.tsx +++ b/src/photo/PhotoHeader.tsx @@ -8,13 +8,11 @@ import { import ShareButton from '@/components/ShareButton'; import AnimateItems from '@/components/AnimateItems'; import { ReactNode } from 'react'; -import { - HIGH_DENSITY_GRID, - SHOW_PHOTO_TITLE_FALLBACK_TEXT, -} from '@/site/config'; +import { HIGH_DENSITY_GRID } from '@/site/config'; import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid'; import PhotoPrevNext from './PhotoPrevNext'; import PhotoLink from './PhotoLink'; +import { formatDate } from '@/utility/date'; export default function PhotoHeader({ tag, @@ -80,14 +78,14 @@ export default function PhotoHeader({ - {entity ?? ( - (selectedPhoto?.title || SHOW_PHOTO_TITLE_FALLBACK_TEXT) - ? - : <>X of X - )} + {entity ?? (selectedPhoto + ? + {selectedPhoto.title || formatDate(selectedPhoto.takenAt, 'tiny')} + + : undefined)} PREV @@ -133,7 +133,7 @@ export default function PhotoPrevNext({ prefetch > NEXT diff --git a/src/utility/date.ts b/src/utility/date.ts index f758dd8f..fdbe6454 100644 --- a/src/utility/date.ts +++ b/src/utility/date.ts @@ -1,5 +1,6 @@ import { format, parseISO, parse } from 'date-fns'; +const DATE_STRING_FORMAT_TINY = 'dd MMM yy'; const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy'; const DATE_STRING_FORMAT_MEDIUM = 'dd MMM yy h:mma'; const DATE_STRING_FORMAT = 'dd MMM yyyy h:mma'; @@ -7,10 +8,12 @@ const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss'; type AmbiguousTimestamp = number | string; -type Length = 'short' | 'medium' | 'long'; +type Length = 'tiny' | 'short' | 'medium' | 'long'; export const formatDate = (date: Date, length: Length = 'long') => { switch (length) { + case 'tiny': + return format(date, DATE_STRING_FORMAT_TINY); case 'short': return format(date, DATE_STRING_FORMAT_SHORT); case 'medium': From 68a8568e9d3f59eef00f56ab6a94b42ec6d2b3f3 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 1 Sep 2024 13:26:47 -0500 Subject: [PATCH 09/10] Make header text responsive, refine grid breakpoints --- src/photo/PhotoHeader.tsx | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx index 3e13c662..5e5b16d2 100644 --- a/src/photo/PhotoHeader.tsx +++ b/src/photo/PhotoHeader.tsx @@ -13,6 +13,7 @@ import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid'; import PhotoPrevNext from './PhotoPrevNext'; import PhotoLink from './PhotoLink'; import { formatDate } from '@/utility/date'; +import ResponsiveText from '@/components/primitives/ResponsiveText'; export default function PhotoHeader({ tag, @@ -45,6 +46,10 @@ export default function PhotoHeader({ ? photos.findIndex(photo => photo.id === selectedPhoto.id) : undefined; + const paginationLabel = + (indexNumber || (selectedPhotoIndex ?? 0 + 1)) + ' of ' + + (count ?? photos.length); + const renderPrevNext = () => {entity ?? (selectedPhoto ? {entity && <> {selectedPhotoIndex !== undefined - // eslint-disable-next-line max-len - ? `${entityVerb ? `${entityVerb} ` : ''}${indexNumber || (selectedPhotoIndex + 1)} of ${count ?? photos.length}` + ? + {entityVerb || 'PHOTO'} {paginationLabel} + : entityDescription} {selectedPhotoIndex === undefined && sharePath && Date: Sun, 1 Sep 2024 21:59:19 -0500 Subject: [PATCH 10/10] Remove 'Untitled' fallback in key views --- README.md | 1 - src/photo/PhotoLarge.tsx | 12 ++++-------- src/site/SiteChecklistClient.tsx | 10 ---------- src/site/config.ts | 3 --- 4 files changed, 4 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index eeec7040..d9f56e91 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,6 @@ Application behavior can be changed by configuring the following environment var - `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information) -- `NEXT_PUBLIC_HIDE_TITLE_FALLBACK_TEXT = 1` prevents showing "Untitled" for photos without titles - `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order - `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api` - `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index 46b56f41..01fb21c3 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -25,10 +25,7 @@ import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation'; import { sortTags } from '@/tag'; import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid'; import PhotoLink from './PhotoLink'; -import { - SHOULD_PREFETCH_ALL_LINKS, - SHOW_PHOTO_TITLE_FALLBACK_TEXT, -} from '@/site/config'; +import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config'; import AdminPhotoMenuClient from '@/admin/AdminPhotoMenuClient'; import { RevalidatePhoto } from './InfinitePhotoScroll'; import { useRef } from 'react'; @@ -89,10 +86,9 @@ export default function PhotoLarge({ const { arePhotosMatted, isUserSignedIn } = useAppState(); - const hasTitle = showTitle && ( - Boolean(photo.title) || - SHOW_PHOTO_TITLE_FALLBACK_TEXT - ); + const hasTitle = + showTitle && + Boolean(photo.title); const hasTitleContent = hasTitle || diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index e1a82753..f1d4772a 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -59,7 +59,6 @@ export default function SiteChecklistClient({ arePhotosMatted, isBlurEnabled, isGeoPrivacyEnabled, - showPhotoTitleFallbackText, isPriorityOrderEnabled, isAiTextGenerationEnabled, aiTextAutoGeneratedFields, @@ -507,15 +506,6 @@ export default function SiteChecklistClient({ collection/display of location-based data: {renderEnvVars(['NEXT_PUBLIC_GEO_PRIVACY'])} - - Set environment variable to {'"1"'} to prevent - showing {'"Untitled"'} for photos without titles: - {renderEnvVars(['NEXT_PUBLIC_HIDE_TITLE_FALLBACK_TEXT'])} -