From 5f992788f2bbf5a030f9e58e15e10ce0ed3cdd21 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 31 Mar 2025 00:25:25 -0500 Subject: [PATCH] Switch film chooser to tag input --- app/admin/photos/[photoId]/edit/page.tsx | 17 +++++++++++------ app/admin/uploads/[uploadPath]/page.tsx | 9 ++++++++- src/film/index.ts | 10 ++++++++++ src/photo/PhotoEditPageClient.tsx | 4 ++++ src/photo/UploadPageClient.tsx | 4 ++++ src/photo/form/PhotoForm.tsx | 21 ++++----------------- src/photo/form/index.ts | 4 ++++ 7 files changed, 45 insertions(+), 24 deletions(-) diff --git a/app/admin/photos/[photoId]/edit/page.tsx b/app/admin/photos/[photoId]/edit/page.tsx index e81de2c7..1cf889f0 100644 --- a/app/admin/photos/[photoId]/edit/page.tsx +++ b/app/admin/photos/[photoId]/edit/page.tsx @@ -1,6 +1,7 @@ import { redirect } from 'next/navigation'; import { getPhotoNoStore, + getUniqueFilmsCached, getUniqueRecipesCached, getUniqueTagsCached, } from '@/photo/cache'; @@ -10,7 +11,6 @@ import { AI_TEXT_GENERATION_ENABLED, BLUR_ENABLED, IS_PREVIEW, - SHOW_RECIPES, } from '@/app/config'; import { blurImageFromUrl, resizeImageFromUrl } from '@/photo/server'; import { getNextImageUrlForManipulation } from '@/platforms/next-image'; @@ -26,11 +26,15 @@ export default async function PhotoEditPage({ if (!photo) { redirect(PATH_ADMIN); } - const uniqueTags = await getUniqueTagsCached(); - - const uniqueRecipes = SHOW_RECIPES - ? await getUniqueRecipesCached() - : []; + const [ + uniqueTags, + uniqueRecipes, + uniqueFilms, + ] = await Promise.all([ + getUniqueTagsCached(), + getUniqueRecipesCached(), + getUniqueFilmsCached(), + ]); const hasAiTextGeneration = AI_TEXT_GENERATION_ENABLED; @@ -52,6 +56,7 @@ export default async function PhotoEditPage({ photo, uniqueTags, uniqueRecipes, + uniqueFilms, hasAiTextGeneration, imageThumbnailBase64, blurData, diff --git a/app/admin/uploads/[uploadPath]/page.tsx b/app/admin/uploads/[uploadPath]/page.tsx index d55b9912..055be14e 100644 --- a/app/admin/uploads/[uploadPath]/page.tsx +++ b/app/admin/uploads/[uploadPath]/page.tsx @@ -1,7 +1,11 @@ import { PATH_ADMIN } from '@/app/paths'; import { extractImageDataFromBlobPath } from '@/photo/server'; import { redirect } from 'next/navigation'; -import { getUniqueRecipesCached, getUniqueTagsCached } from '@/photo/cache'; +import { + getUniqueFilmsCached, + getUniqueRecipesCached, + getUniqueTagsCached, +} from '@/photo/cache'; import UploadPageClient from '@/photo/UploadPageClient'; import { AI_TEXT_AUTO_GENERATED_FIELDS, @@ -45,10 +49,12 @@ export default async function UploadPage({ params }: Params) { const [ uniqueTags, uniqueRecipes, + uniqueFilms, recipeTitle, ] = await Promise.all([ getUniqueTagsCached(), getUniqueRecipesCached(), + getUniqueFilmsCached(), formDataFromExif?.recipeData && formDataFromExif.film ? getRecipeTitleForData( formDataFromExif.recipeData, @@ -72,6 +78,7 @@ export default async function UploadPage({ params }: Params) { formDataFromExif, uniqueTags, uniqueRecipes, + uniqueFilms, hasAiTextGeneration, textFieldsToAutoGenerate, imageThumbnailBase64, diff --git a/src/film/index.ts b/src/film/index.ts index b360e70f..23882656 100644 --- a/src/film/index.ts +++ b/src/film/index.ts @@ -12,6 +12,8 @@ import { FujifilmSimulation, labelForFilm, } from '@/platforms/fujifilm/simulation'; +import { formatCount } from '@/utility/string'; +import { formatCountDescriptive } from '@/utility/string'; export type FilmSimulation = FujifilmSimulation; @@ -82,3 +84,11 @@ export const generateMetaForFilm = ( export const photoHasFilmData = (photo: Photo) => Boolean(photo.film); + +export const convertFilmsForForm = (films: Films = []) => + sortFilms(films) + .map(({ film, count }) => ({ + value: film, + annotation: formatCount(count), + annotationAria: formatCountDescriptive(count), + })); diff --git a/src/photo/PhotoEditPageClient.tsx b/src/photo/PhotoEditPageClient.tsx index 79d32201..bfc3fe84 100644 --- a/src/photo/PhotoEditPageClient.tsx +++ b/src/photo/PhotoEditPageClient.tsx @@ -11,11 +11,13 @@ import usePhotoFormParent from './form/usePhotoFormParent'; import ExifCaptureButton from '@/admin/ExifCaptureButton'; import { useState } from 'react'; import { Recipes } from '@/recipe'; +import { Films } from '@/film'; export default function PhotoEditPageClient({ photo, uniqueTags, uniqueRecipes, + uniqueFilms, hasAiTextGeneration, imageThumbnailBase64, blurData, @@ -23,6 +25,7 @@ export default function PhotoEditPageClient({ photo: Photo uniqueTags: Tags uniqueRecipes: Recipes + uniqueFilms: Films hasAiTextGeneration: boolean imageThumbnailBase64: string blurData: string @@ -71,6 +74,7 @@ export default function PhotoEditPageClient({ updatedBlurData={blurData} uniqueTags={uniqueTags} uniqueRecipes={uniqueRecipes} + uniqueFilms={uniqueFilms} aiContent={hasAiTextGeneration ? aiContent : undefined} onTitleChange={setUpdatedTitle} onTextContentChange={setHasTextContent} diff --git a/src/photo/UploadPageClient.tsx b/src/photo/UploadPageClient.tsx index 37a1129d..5eebd787 100644 --- a/src/photo/UploadPageClient.tsx +++ b/src/photo/UploadPageClient.tsx @@ -10,12 +10,14 @@ import AiButton from './ai/AiButton'; import { AiAutoGeneratedField } from './ai'; import { useMemo } from 'react'; import { Recipes } from '@/recipe'; +import { Films } from '@/film'; export default function UploadPageClient({ blobId, formDataFromExif, uniqueTags, uniqueRecipes, + uniqueFilms, hasAiTextGeneration, textFieldsToAutoGenerate, imageThumbnailBase64, @@ -25,6 +27,7 @@ export default function UploadPageClient({ formDataFromExif: Partial uniqueTags: Tags uniqueRecipes: Recipes + uniqueFilms: Films hasAiTextGeneration?: boolean textFieldsToAutoGenerate?: AiAutoGeneratedField[], imageThumbnailBase64?: string @@ -65,6 +68,7 @@ export default function UploadPageClient({ initialPhotoForm={initialPhotoForm} uniqueTags={uniqueTags} uniqueRecipes={uniqueRecipes} + uniqueFilms={uniqueFilms} aiContent={hasAiTextGeneration ? aiContent : undefined} shouldStripGpsData={shouldStripGpsData} onTitleChange={setUpdatedTitle} diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index 09448321..c0fdef5a 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -41,13 +41,9 @@ import ErrorNote from '@/components/ErrorNote'; import { convertRecipesForForm, Recipes } from '@/recipe'; import deepEqual from 'fast-deep-equal/es6/react'; import ApplyRecipeTitleGloballyCheckbox from './ApplyRecipesGloballyCheckbox'; -import { FilmSimulation } from '@/film'; +import { convertFilmsForForm, Films, FilmSimulation } from '@/film'; import IconFavs from '@/components/icons/IconFavs'; import IconHidden from '@/components/icons/IconHidden'; -import { MAKE_FUJIFILM } from '@/platforms/fujifilm'; -import { - FILM_SIMULATION_FORM_INPUT_OPTIONS, -} from '@/platforms/fujifilm/simulation'; const THUMBNAIL_SIZE = 300; @@ -58,6 +54,7 @@ export default function PhotoForm({ updatedBlurData, uniqueTags, uniqueRecipes, + uniqueFilms, aiContent, shouldStripGpsData, onTitleChange, @@ -70,6 +67,7 @@ export default function PhotoForm({ updatedBlurData?: string uniqueTags?: Tags uniqueRecipes?: Recipes + uniqueFilms?: Films aiContent?: AiContent shouldStripGpsData?: boolean onTitleChange?: (updatedTitle: string) => void @@ -330,6 +328,7 @@ export default function PhotoForm({ {FORM_METADATA_ENTRIES( convertTagsForForm(uniqueTags), convertRecipesForForm(uniqueRecipes), + convertFilmsForForm(uniqueFilms), aiContent !== undefined, shouldStripGpsData, ) @@ -412,18 +411,6 @@ export default function PhotoForm({ }; switch (key) { - case 'film': - return formData.make === MAKE_FUJIFILM - ? - : ; case 'applyRecipeTitleGlobally': return => ({ @@ -121,6 +122,9 @@ const FORM_METADATA = ( label: 'film', note: 'Intended for Fujifilm simulations and analog scans', noteShort: 'Fujifilm simulations / analog scans', + tagOptions: filmOptions, + tagOptionsLimitValidationMessage: 'Photos can only have one film', + tagOptionsLimit: 1, shouldNotOverwriteWithNullDataOnSync: true, }, recipeTitle: {