Switch film chooser to tag input

This commit is contained in:
Sam Becker 2025-03-31 00:25:25 -05:00
parent 09d7a07722
commit 5f992788f2
7 changed files with 45 additions and 24 deletions

View File

@ -1,6 +1,7 @@
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { import {
getPhotoNoStore, getPhotoNoStore,
getUniqueFilmsCached,
getUniqueRecipesCached, getUniqueRecipesCached,
getUniqueTagsCached, getUniqueTagsCached,
} from '@/photo/cache'; } from '@/photo/cache';
@ -10,7 +11,6 @@ import {
AI_TEXT_GENERATION_ENABLED, AI_TEXT_GENERATION_ENABLED,
BLUR_ENABLED, BLUR_ENABLED,
IS_PREVIEW, IS_PREVIEW,
SHOW_RECIPES,
} from '@/app/config'; } from '@/app/config';
import { blurImageFromUrl, resizeImageFromUrl } from '@/photo/server'; import { blurImageFromUrl, resizeImageFromUrl } from '@/photo/server';
import { getNextImageUrlForManipulation } from '@/platforms/next-image'; import { getNextImageUrlForManipulation } from '@/platforms/next-image';
@ -26,11 +26,15 @@ export default async function PhotoEditPage({
if (!photo) { redirect(PATH_ADMIN); } if (!photo) { redirect(PATH_ADMIN); }
const uniqueTags = await getUniqueTagsCached(); const [
uniqueTags,
const uniqueRecipes = SHOW_RECIPES uniqueRecipes,
? await getUniqueRecipesCached() uniqueFilms,
: []; ] = await Promise.all([
getUniqueTagsCached(),
getUniqueRecipesCached(),
getUniqueFilmsCached(),
]);
const hasAiTextGeneration = AI_TEXT_GENERATION_ENABLED; const hasAiTextGeneration = AI_TEXT_GENERATION_ENABLED;
@ -52,6 +56,7 @@ export default async function PhotoEditPage({
photo, photo,
uniqueTags, uniqueTags,
uniqueRecipes, uniqueRecipes,
uniqueFilms,
hasAiTextGeneration, hasAiTextGeneration,
imageThumbnailBase64, imageThumbnailBase64,
blurData, blurData,

View File

@ -1,7 +1,11 @@
import { PATH_ADMIN } from '@/app/paths'; import { PATH_ADMIN } from '@/app/paths';
import { extractImageDataFromBlobPath } from '@/photo/server'; import { extractImageDataFromBlobPath } from '@/photo/server';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { getUniqueRecipesCached, getUniqueTagsCached } from '@/photo/cache'; import {
getUniqueFilmsCached,
getUniqueRecipesCached,
getUniqueTagsCached,
} from '@/photo/cache';
import UploadPageClient from '@/photo/UploadPageClient'; import UploadPageClient from '@/photo/UploadPageClient';
import { import {
AI_TEXT_AUTO_GENERATED_FIELDS, AI_TEXT_AUTO_GENERATED_FIELDS,
@ -45,10 +49,12 @@ export default async function UploadPage({ params }: Params) {
const [ const [
uniqueTags, uniqueTags,
uniqueRecipes, uniqueRecipes,
uniqueFilms,
recipeTitle, recipeTitle,
] = await Promise.all([ ] = await Promise.all([
getUniqueTagsCached(), getUniqueTagsCached(),
getUniqueRecipesCached(), getUniqueRecipesCached(),
getUniqueFilmsCached(),
formDataFromExif?.recipeData && formDataFromExif.film formDataFromExif?.recipeData && formDataFromExif.film
? getRecipeTitleForData( ? getRecipeTitleForData(
formDataFromExif.recipeData, formDataFromExif.recipeData,
@ -72,6 +78,7 @@ export default async function UploadPage({ params }: Params) {
formDataFromExif, formDataFromExif,
uniqueTags, uniqueTags,
uniqueRecipes, uniqueRecipes,
uniqueFilms,
hasAiTextGeneration, hasAiTextGeneration,
textFieldsToAutoGenerate, textFieldsToAutoGenerate,
imageThumbnailBase64, imageThumbnailBase64,

View File

@ -12,6 +12,8 @@ import {
FujifilmSimulation, FujifilmSimulation,
labelForFilm, labelForFilm,
} from '@/platforms/fujifilm/simulation'; } from '@/platforms/fujifilm/simulation';
import { formatCount } from '@/utility/string';
import { formatCountDescriptive } from '@/utility/string';
export type FilmSimulation = FujifilmSimulation; export type FilmSimulation = FujifilmSimulation;
@ -82,3 +84,11 @@ export const generateMetaForFilm = (
export const photoHasFilmData = (photo: Photo) => export const photoHasFilmData = (photo: Photo) =>
Boolean(photo.film); Boolean(photo.film);
export const convertFilmsForForm = (films: Films = []) =>
sortFilms(films)
.map(({ film, count }) => ({
value: film,
annotation: formatCount(count),
annotationAria: formatCountDescriptive(count),
}));

View File

@ -11,11 +11,13 @@ import usePhotoFormParent from './form/usePhotoFormParent';
import ExifCaptureButton from '@/admin/ExifCaptureButton'; import ExifCaptureButton from '@/admin/ExifCaptureButton';
import { useState } from 'react'; import { useState } from 'react';
import { Recipes } from '@/recipe'; import { Recipes } from '@/recipe';
import { Films } from '@/film';
export default function PhotoEditPageClient({ export default function PhotoEditPageClient({
photo, photo,
uniqueTags, uniqueTags,
uniqueRecipes, uniqueRecipes,
uniqueFilms,
hasAiTextGeneration, hasAiTextGeneration,
imageThumbnailBase64, imageThumbnailBase64,
blurData, blurData,
@ -23,6 +25,7 @@ export default function PhotoEditPageClient({
photo: Photo photo: Photo
uniqueTags: Tags uniqueTags: Tags
uniqueRecipes: Recipes uniqueRecipes: Recipes
uniqueFilms: Films
hasAiTextGeneration: boolean hasAiTextGeneration: boolean
imageThumbnailBase64: string imageThumbnailBase64: string
blurData: string blurData: string
@ -71,6 +74,7 @@ export default function PhotoEditPageClient({
updatedBlurData={blurData} updatedBlurData={blurData}
uniqueTags={uniqueTags} uniqueTags={uniqueTags}
uniqueRecipes={uniqueRecipes} uniqueRecipes={uniqueRecipes}
uniqueFilms={uniqueFilms}
aiContent={hasAiTextGeneration ? aiContent : undefined} aiContent={hasAiTextGeneration ? aiContent : undefined}
onTitleChange={setUpdatedTitle} onTitleChange={setUpdatedTitle}
onTextContentChange={setHasTextContent} onTextContentChange={setHasTextContent}

View File

@ -10,12 +10,14 @@ import AiButton from './ai/AiButton';
import { AiAutoGeneratedField } from './ai'; import { AiAutoGeneratedField } from './ai';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Recipes } from '@/recipe'; import { Recipes } from '@/recipe';
import { Films } from '@/film';
export default function UploadPageClient({ export default function UploadPageClient({
blobId, blobId,
formDataFromExif, formDataFromExif,
uniqueTags, uniqueTags,
uniqueRecipes, uniqueRecipes,
uniqueFilms,
hasAiTextGeneration, hasAiTextGeneration,
textFieldsToAutoGenerate, textFieldsToAutoGenerate,
imageThumbnailBase64, imageThumbnailBase64,
@ -25,6 +27,7 @@ export default function UploadPageClient({
formDataFromExif: Partial<PhotoFormData> formDataFromExif: Partial<PhotoFormData>
uniqueTags: Tags uniqueTags: Tags
uniqueRecipes: Recipes uniqueRecipes: Recipes
uniqueFilms: Films
hasAiTextGeneration?: boolean hasAiTextGeneration?: boolean
textFieldsToAutoGenerate?: AiAutoGeneratedField[], textFieldsToAutoGenerate?: AiAutoGeneratedField[],
imageThumbnailBase64?: string imageThumbnailBase64?: string
@ -65,6 +68,7 @@ export default function UploadPageClient({
initialPhotoForm={initialPhotoForm} initialPhotoForm={initialPhotoForm}
uniqueTags={uniqueTags} uniqueTags={uniqueTags}
uniqueRecipes={uniqueRecipes} uniqueRecipes={uniqueRecipes}
uniqueFilms={uniqueFilms}
aiContent={hasAiTextGeneration ? aiContent : undefined} aiContent={hasAiTextGeneration ? aiContent : undefined}
shouldStripGpsData={shouldStripGpsData} shouldStripGpsData={shouldStripGpsData}
onTitleChange={setUpdatedTitle} onTitleChange={setUpdatedTitle}

View File

@ -41,13 +41,9 @@ import ErrorNote from '@/components/ErrorNote';
import { convertRecipesForForm, Recipes } from '@/recipe'; import { convertRecipesForForm, Recipes } from '@/recipe';
import deepEqual from 'fast-deep-equal/es6/react'; import deepEqual from 'fast-deep-equal/es6/react';
import ApplyRecipeTitleGloballyCheckbox from './ApplyRecipesGloballyCheckbox'; import ApplyRecipeTitleGloballyCheckbox from './ApplyRecipesGloballyCheckbox';
import { FilmSimulation } from '@/film'; import { convertFilmsForForm, Films, FilmSimulation } from '@/film';
import IconFavs from '@/components/icons/IconFavs'; import IconFavs from '@/components/icons/IconFavs';
import IconHidden from '@/components/icons/IconHidden'; 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; const THUMBNAIL_SIZE = 300;
@ -58,6 +54,7 @@ export default function PhotoForm({
updatedBlurData, updatedBlurData,
uniqueTags, uniqueTags,
uniqueRecipes, uniqueRecipes,
uniqueFilms,
aiContent, aiContent,
shouldStripGpsData, shouldStripGpsData,
onTitleChange, onTitleChange,
@ -70,6 +67,7 @@ export default function PhotoForm({
updatedBlurData?: string updatedBlurData?: string
uniqueTags?: Tags uniqueTags?: Tags
uniqueRecipes?: Recipes uniqueRecipes?: Recipes
uniqueFilms?: Films
aiContent?: AiContent aiContent?: AiContent
shouldStripGpsData?: boolean shouldStripGpsData?: boolean
onTitleChange?: (updatedTitle: string) => void onTitleChange?: (updatedTitle: string) => void
@ -330,6 +328,7 @@ export default function PhotoForm({
{FORM_METADATA_ENTRIES( {FORM_METADATA_ENTRIES(
convertTagsForForm(uniqueTags), convertTagsForForm(uniqueTags),
convertRecipesForForm(uniqueRecipes), convertRecipesForForm(uniqueRecipes),
convertFilmsForForm(uniqueFilms),
aiContent !== undefined, aiContent !== undefined,
shouldStripGpsData, shouldStripGpsData,
) )
@ -412,18 +411,6 @@ export default function PhotoForm({
}; };
switch (key) { switch (key) {
case 'film':
return formData.make === MAKE_FUJIFILM
? <FieldSetWithStatus
key={key}
{...fieldProps}
selectOptions={FILM_SIMULATION_FORM_INPUT_OPTIONS}
selectOptionsDefaultLabel="Unknown"
/>
: <FieldSetWithStatus
key={key}
{...fieldProps}
/>;
case 'applyRecipeTitleGlobally': case 'applyRecipeTitleGlobally':
return <ApplyRecipeTitleGloballyCheckbox return <ApplyRecipeTitleGloballyCheckbox
key={key} key={key}

View File

@ -80,6 +80,7 @@ const STRING_MAX_LENGTH_LONG = 1000;
const FORM_METADATA = ( const FORM_METADATA = (
tagOptions?: AnnotatedTag[], tagOptions?: AnnotatedTag[],
recipeOptions?: AnnotatedTag[], recipeOptions?: AnnotatedTag[],
filmOptions?: AnnotatedTag[],
aiTextGeneration?: boolean, aiTextGeneration?: boolean,
shouldStripGpsData?: boolean, shouldStripGpsData?: boolean,
): Record<keyof PhotoFormData, FormMeta> => ({ ): Record<keyof PhotoFormData, FormMeta> => ({
@ -121,6 +122,9 @@ const FORM_METADATA = (
label: 'film', label: 'film',
note: 'Intended for Fujifilm simulations and analog scans', note: 'Intended for Fujifilm simulations and analog scans',
noteShort: 'Fujifilm simulations / analog scans', noteShort: 'Fujifilm simulations / analog scans',
tagOptions: filmOptions,
tagOptionsLimitValidationMessage: 'Photos can only have one film',
tagOptionsLimit: 1,
shouldNotOverwriteWithNullDataOnSync: true, shouldNotOverwriteWithNullDataOnSync: true,
}, },
recipeTitle: { recipeTitle: {