updatedBlurData?: string
uniqueTags?: Tags
+ uniqueRecipes?: Recipes
aiContent?: AiContent
shouldStripGpsData?: boolean
onTitleChange?: (updatedTitle: string) => void
@@ -293,6 +296,7 @@ export default function PhotoForm({
{FORM_METADATA_ENTRIES(
convertTagsForForm(uniqueTags),
+ convertRecipesForForm(uniqueRecipes),
aiContent !== undefined,
)
.map(([key, {
@@ -302,6 +306,8 @@ export default function PhotoForm({
selectOptions,
selectOptionsDefaultLabel,
tagOptions,
+ tagOptionsLimit,
+ tagOptionsLimitValidationMessage,
readOnly,
validate,
validateStringMaxLength,
@@ -345,6 +351,9 @@ export default function PhotoForm({
selectOptions={selectOptions}
selectOptionsDefaultLabel={selectOptionsDefaultLabel}
tagOptions={tagOptions}
+ tagOptionsLimit={tagOptionsLimit}
+ // eslint-disable-next-line max-len
+ tagOptionsLimitValidationMessage={tagOptionsLimitValidationMessage}
required={required}
readOnly={readOnly}
spellCheck={spellCheck}
diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts
index 039bc6cc..5b5c2518 100644
--- a/src/photo/form/index.ts
+++ b/src/photo/form/index.ts
@@ -60,6 +60,8 @@ type FormMeta = {
selectOptions?: { value: string, label: string }[]
selectOptionsDefaultLabel?: string
tagOptions?: AnnotatedTag[]
+ tagOptionsLimit?: number
+ tagOptionsLimitValidationMessage?: string
shouldNotOverwriteWithNullDataOnSync?: boolean
};
@@ -68,6 +70,7 @@ const STRING_MAX_LENGTH_LONG = 1000;
const FORM_METADATA = (
tagOptions?: AnnotatedTag[],
+ recipeOptions?: AnnotatedTag[],
aiTextGeneration?: boolean,
): Record => ({
title: {
@@ -113,6 +116,9 @@ const FORM_METADATA = (
},
recipeTitle: {
label: 'recipe title',
+ tagOptions: recipeOptions,
+ tagOptionsLimit: 1,
+ tagOptionsLimitValidationMessage: 'Photos can only have one recipe',
spellCheck: false,
capitalize: false,
shouldHide: ({ make }) => make !== MAKE_FUJIFILM,
diff --git a/src/recipe/index.ts b/src/recipe/index.ts
index 78b7cf03..5db020e3 100644
--- a/src/recipe/index.ts
+++ b/src/recipe/index.ts
@@ -1,7 +1,11 @@
import { absolutePathForRecipe, absolutePathForRecipeImage } from '@/app/paths';
import { descriptionForPhotoSet, Photo, photoQuantityText } from '@/photo';
import { PhotoDateRange } from '@/photo';
-import { capitalizeWords } from '@/utility/string';
+import {
+ capitalizeWords,
+ formatCount,
+ formatCountDescriptive,
+} from '@/utility/string';
import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
import { FilmSimulation } from '@/simulation';
@@ -64,8 +68,13 @@ export const generateMetaForRecipe = (
export const photoHasRecipe = (photo?: Photo) =>
photo?.filmSimulation && photo?.recipeData;
-export const sortRecipesWithCount = (
- a: RecipeWithCount,
- b: RecipeWithCount,
-) =>
- a.recipe.localeCompare(b.recipe);
+export const sortRecipesWithCount = (recipes: Recipes = []) =>
+ recipes.sort((a, b) => a.recipe.localeCompare(b.recipe));
+
+export const convertRecipesForForm = (recipes: Recipes = []) =>
+ sortRecipesWithCount(recipes)
+ .map(({ recipe, count }) => ({
+ value: recipe,
+ annotation: formatCount(count),
+ annotationAria: formatCountDescriptive(count),
+ }));