diff --git a/app/admin/recipe/[photoId]/page.tsx b/app/admin/recipe/[photoId]/page.tsx
deleted file mode 100644
index f8129146..00000000
--- a/app/admin/recipe/[photoId]/page.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import SiteGrid from '@/components/SiteGrid';
-import { getPhoto, getPhotos } from '@/photo/db/query';
-import PhotoRecipeOverlay from '@/recipe/PhotoRecipeOverlay';
-
-export default async function AdminRecipePage({
- params,
-}: {
- params: Promise<{ photoId: string }>
-}) {
- const { photoId } = await params;
- const photo = await getPhoto(photoId);
- const photosHidden = await getPhotos({ hidden: 'only' });
- const { filmSimulation } = photo!;
- const { fujifilmRecipe } = photosHidden[0];
-
- return (
-
- :
- Can't find photo/recipe
-
}
- />
- );
-}
diff --git a/app/admin/recipe/page.tsx b/app/admin/recipe/page.tsx
deleted file mode 100644
index 67f01aa4..00000000
--- a/app/admin/recipe/page.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { getPhoto, getPhotos } from '@/photo/db/query';
-import PhotoRecipeOverlay from '@/recipe/PhotoRecipeOverlay';
-
-export default async function AdminRecipePage() {
- const [
- photos,
- photo1,
- photo2,
- photo3,
- photo4,
- photosHidden,
- ] = await Promise.all([
- getPhotos({ tag: 'favs' }),
- getPhoto('4zT6dgPr'),
- getPhoto('9MopluBn'),
- getPhoto('ifv8zq45'),
- getPhoto('2BO2YoW6'),
- getPhotos({ hidden: 'only', limit: 1 }),
- ]);
- const { fujifilmRecipe } = photosHidden[0];
- return (
-
- );
-}
diff --git a/app/film/[simulation]/page.tsx b/app/film/[simulation]/page.tsx
index a941adb7..ec6e74b6 100644
--- a/app/film/[simulation]/page.tsx
+++ b/app/film/[simulation]/page.tsx
@@ -7,6 +7,8 @@ import { getPhotosFilmSimulationDataCached } from '@/simulation/data';
import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/app/config';
import { Metadata } from 'next/types';
import { cache } from 'react';
+import { PATH_ROOT } from '@/app/paths';
+import { redirect } from 'next/navigation';
const getPhotosFilmSimulationDataCachedCached =
cache(getPhotosFilmSimulationDataCached);
@@ -38,6 +40,8 @@ export async function generateMetadata({
limit: INFINITE_SCROLL_GRID_INITIAL,
});
+ if (photos.length === 0) { return {}; }
+
const {
url,
title,
@@ -75,6 +79,8 @@ export default async function FilmSimulationPage({
limit: INFINITE_SCROLL_GRID_INITIAL,
});
+ if (photos.length === 0) { redirect(PATH_ROOT); }
+
return (
+ getPhotosNearIdCached(
+ photoId,
+ { recipe, limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
+ ));
+
+interface PhotoRecipeProps {
+ params: Promise<{ photoId: string, recipe: string }>
+}
+
+export async function generateMetadata({
+ params,
+}: PhotoRecipeProps): Promise {
+ const { photoId, recipe: recipeFromParams } = await params;
+
+ const recipe = decodeURIComponent(recipeFromParams);
+
+ const { photo } = await getPhotosNearIdCachedCached(photoId, recipe);
+
+ if (!photo) { return {}; }
+
+ const title = titleForPhoto(photo);
+ const description = descriptionForPhoto(photo);
+ const images = absolutePathForPhotoImage(photo);
+ const url = absolutePathForPhoto({ photo, recipe });
+
+ return {
+ title,
+ description,
+ openGraph: {
+ title,
+ images,
+ description,
+ url,
+ },
+ twitter: {
+ title,
+ description,
+ images,
+ card: 'summary_large_image',
+ },
+ };
+}
+
+export default async function PhotoTagPage({
+ params,
+}: PhotoRecipeProps) {
+ const { photoId, recipe: recipeFromParams } = await params;
+
+ const recipe = decodeURIComponent(recipeFromParams);
+
+ const { photo, photos, photosGrid, indexNumber } =
+ await getPhotosNearIdCachedCached(photoId, recipe);
+
+ if (!photo) { redirect(PATH_ROOT); }
+
+ const { count, dateRange } = await getPhotosMeta({ recipe });
+
+ return (
+
+ );
+}
diff --git a/app/recipe/[recipe]/image/route.tsx b/app/recipe/[recipe]/image/route.tsx
new file mode 100644
index 00000000..56e999e5
--- /dev/null
+++ b/app/recipe/[recipe]/image/route.tsx
@@ -0,0 +1,57 @@
+import { getPhotosCached } from '@/photo/cache';
+import {
+ IMAGE_OG_DIMENSION_SMALL,
+ MAX_PHOTOS_TO_SHOW_PER_TAG,
+} from '@/image-response';
+import { getIBMPlexMonoMedium } from '@/app/font';
+import { ImageResponse } from 'next/og';
+import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
+import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db';
+import { getUniqueRecipes } from '@/photo/db/query';
+import {
+ STATICALLY_OPTIMIZED_PHOTO_CATEGORY_OG_IMAGES,
+ IS_PRODUCTION,
+} from '@/app/config';
+import RecipeImageResponse from '@/image-response/RecipeImageResponse';
+
+export let generateStaticParams:
+ (() => Promise<{ recipe: string }[]>) | undefined = undefined;
+
+if (STATICALLY_OPTIMIZED_PHOTO_CATEGORY_OG_IMAGES && IS_PRODUCTION) {
+ generateStaticParams = async () => {
+ const recipes = await getUniqueRecipes();
+ return recipes
+ .slice(0, GENERATE_STATIC_PARAMS_LIMIT)
+ .map(({ recipe }) => ({ recipe }));
+ };
+}
+
+export async function GET(
+ _: Request,
+ context: { params: Promise<{ recipe: string }> },
+) {
+ const { recipe } = await context.params;
+
+ const [
+ photos,
+ { fontFamily, fonts },
+ headers,
+ ] = await Promise.all([
+ getPhotosCached({ limit: MAX_PHOTOS_TO_SHOW_PER_TAG, recipe }),
+ getIBMPlexMonoMedium(),
+ getImageResponseCacheControlHeaders(),
+ ]);
+
+ const { width, height } = IMAGE_OG_DIMENSION_SMALL;
+
+ return new ImageResponse(
+ ,
+ { width, height, fonts, headers },
+ );
+}
diff --git a/app/recipe/[recipe]/page.tsx b/app/recipe/[recipe]/page.tsx
index 57283086..830e4779 100644
--- a/app/recipe/[recipe]/page.tsx
+++ b/app/recipe/[recipe]/page.tsx
@@ -1,27 +1,24 @@
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
-import { getUniqueTags } from '@/photo/db/query';
+import { getUniqueRecipes } from '@/photo/db/query';
import { IS_PRODUCTION } from '@/app/config';
import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/app/config';
import { PATH_ROOT } from '@/app/paths';
-import { getPhotosTagDataCached } from '@/tag/data';
import type { Metadata } from 'next';
import { redirect } from 'next/navigation';
import { cache } from 'react';
-import { convertRecipeToTag, generateMetaForRecipe } from '@/recipe';
+import { generateMetaForRecipe } from '@/recipe';
import RecipeOverview from '@/recipe/RecipeOverview';
+import { getPhotosRecipeDataCached } from '@/recipe/data';
-const getPhotosTagDataCachedCached = cache((tag: string) =>
- getPhotosTagDataCached({ tag, limit: INFINITE_SCROLL_GRID_INITIAL}));
+const getPhotosRecipeDataCachedCached = cache(getPhotosRecipeDataCached);
export let generateStaticParams:
(() => Promise<{ recipe: string }[]>) | undefined = undefined;
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
generateStaticParams = async () => {
- const tags = await getUniqueTags();
- return tags
- .filter(({ tag }) => tag.startsWith('recipe'))
- .map(({ tag }) => ({ recipe: tag.replace(/^recipe-?/i, '')}));
+ const recipes = await getUniqueRecipes();
+ return recipes.map(({ recipe }) => ({ recipe }));
};
}
@@ -39,7 +36,10 @@ export async function generateMetadata({
const [
photos,
{ count, dateRange },
- ] = await getPhotosTagDataCachedCached(convertRecipeToTag(recipe));
+ ] = await getPhotosRecipeDataCachedCached({
+ recipe,
+ limit: INFINITE_SCROLL_GRID_INITIAL,
+ });
if (photos.length === 0) { return {}; }
@@ -77,7 +77,10 @@ export default async function RecipePage({
const [
photos,
{ count, dateRange },
- ] = await getPhotosTagDataCachedCached(convertRecipeToTag(recipe));
+ ] = await getPhotosRecipeDataCachedCached({
+ recipe,
+ limit: INFINITE_SCROLL_GRID_INITIAL,
+ });
if (photos.length === 0) { redirect(PATH_ROOT); }
diff --git a/src/app/paths.ts b/src/app/paths.ts
index 31b60343..f1bb086a 100644
--- a/src/app/paths.ts
+++ b/src/app/paths.ts
@@ -35,10 +35,6 @@ const PATH_FILM_SIMULATION_DYNAMIC = `${PREFIX_FILM_SIMULATION}/[simulation]`
const PATH_FOCAL_LENGTH_DYNAMIC = `${PREFIX_FOCAL_LENGTH}/[focal]`;
const PATH_RECIPE_DYNAMIC = `${PREFIX_RECIPE}/[recipe]`;
-// Search params
-export const SEARCH_PARAM_SHOW = 'show';
-export const SEARCH_PARAM_SHOW_RECIPE = 'recipe';
-
// Admin paths
export const PATH_ADMIN_PHOTOS = `${PATH_ADMIN}/photos`;
export const PATH_ADMIN_OUTDATED = `${PATH_ADMIN}/outdated`;
@@ -114,9 +110,8 @@ export const pathForPhoto = ({
simulation,
focal,
recipe,
- showRecipe,
-}: PhotoPathParams) => {
- const path = typeof photo !== 'string' && photo.hidden
+}: PhotoPathParams) =>
+ typeof photo !== 'string' && photo.hidden
? `${pathForTag(TAG_HIDDEN)}/${getPhotoId(photo)}`
: tag
? `${pathForTag(tag)}/${getPhotoId(photo)}`
@@ -129,10 +124,6 @@ export const pathForPhoto = ({
: recipe
? `${pathForRecipe(recipe)}/${getPhotoId(photo)}`
: `${PREFIX_PHOTO}/${getPhotoId(photo)}`;
- return showRecipe
- ? `${path}?${SEARCH_PARAM_SHOW}=${SEARCH_PARAM_SHOW_RECIPE}`
- : path;
-};
export const pathForTag = (tag: string) =>
`${PREFIX_TAG}/${tag}`;
diff --git a/src/image-response/RecipeImageResponse.tsx b/src/image-response/RecipeImageResponse.tsx
new file mode 100644
index 00000000..afd07fc6
--- /dev/null
+++ b/src/image-response/RecipeImageResponse.tsx
@@ -0,0 +1,51 @@
+import type { Photo } from '../photo';
+import ImageCaption from './components/ImageCaption';
+import ImagePhotoGrid from './components/ImagePhotoGrid';
+import ImageContainer from './components/ImageContainer';
+import type { NextImageSize } from '@/platforms/next-image';
+import { formatTag } from '@/tag';
+import { TbChecklist } from 'react-icons/tb';
+
+export default function RecipeImageResponse({
+ recipe,
+ photos,
+ width,
+ height,
+ fontFamily,
+}: {
+ recipe: string,
+ photos: Photo[]
+ width: NextImageSize
+ height: number
+ fontFamily: string
+}) {
+ return (
+
+
+ ,
+ }}>
+ {formatTag(recipe).toLocaleUpperCase()}
+
+
+ );
+}
diff --git a/src/photo/PhotoDetailPage.tsx b/src/photo/PhotoDetailPage.tsx
index 2a0ea013..619ffae8 100644
--- a/src/photo/PhotoDetailPage.tsx
+++ b/src/photo/PhotoDetailPage.tsx
@@ -11,6 +11,7 @@ import HiddenHeader from '@/tag/HiddenHeader';
import FocalLengthHeader from '@/focal/FocalLengthHeader';
import PhotoHeader from './PhotoHeader';
import { JSX } from 'react';
+import RecipeHeader from '@/recipe/RecipeHeader';
export default function PhotoDetailPage({
photo,
@@ -19,6 +20,7 @@ export default function PhotoDetailPage({
tag,
camera,
simulation,
+ recipe,
focal,
indexNumber,
count,
@@ -72,6 +74,14 @@ export default function PhotoDetailPage({
count={count}
dateRange={dateRange}
/>;
+ } else if (recipe) {
+ customHeader = ;
} else if (focal) {
customHeader = }
/>
,
]}
diff --git a/src/photo/PhotoGridSidebar.tsx b/src/photo/PhotoGridSidebar.tsx
index 5af0a37c..b8ca861e 100644
--- a/src/photo/PhotoGridSidebar.tsx
+++ b/src/photo/PhotoGridSidebar.tsx
@@ -21,8 +21,6 @@ import {
safelyParseFormattedHtml,
} from '@/utility/html';
import { clsx } from 'clsx/lite';
-import { convertTagToRecipe, isTagRecipe } from '@/recipe';
-import PhotoRecipe from '@/recipe/PhotoRecipe';
export default function PhotoGridSidebar({
tags,
@@ -53,47 +51,35 @@ export default function PhotoGridSidebar({
className="text-icon translate-y-[1px]"
/>}
items={tagsIncludingHidden.map(({ tag, count }) => {
- if (isTagRecipe(tag)) {
- return ;
- } else {
- switch (tag) {
- case TAG_FAVS:
- return ;
- case TAG_HIDDEN:
- return ;
- default:
- return ;
- }
+ case TAG_HIDDEN:
+ return ;
+ default:
+ return ;
}
})}
/>
diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx
index c06ae8a8..36662d10 100644
--- a/src/photo/PhotoHeader.tsx
+++ b/src/photo/PhotoHeader.tsx
@@ -22,6 +22,7 @@ export default function PhotoHeader({
camera,
simulation,
focal,
+ recipe,
photos,
selectedPhoto,
entity,
@@ -68,6 +69,7 @@ export default function PhotoHeader({
camera,
simulation,
focal,
+ recipe,
}} />;
const renderDateRange = () =>
diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx
index f5450b54..880f3b11 100644
--- a/src/photo/PhotoLarge.tsx
+++ b/src/photo/PhotoLarge.tsx
@@ -30,7 +30,7 @@ import {
} from '@/app/config';
import AdminPhotoMenuClient from '@/admin/AdminPhotoMenuClient';
import { RevalidatePhoto } from './InfinitePhotoScroll';
-import { useRef } from 'react';
+import { useMemo, useRef } from 'react';
import useVisible from '@/utility/useVisible';
import PhotoDate from './PhotoDate';
import { useAppState } from '@/state/AppState';
@@ -42,7 +42,8 @@ import { TbChecklist } from 'react-icons/tb';
import { IoCloseSharp } from 'react-icons/io5';
import { AnimatePresence } from 'framer-motion';
import useRecipeState from '../recipe/useRecipeState';
-import PhotoRecipeGrid from '@/recipe/PhotoRecipeGrid';
+import PhotoRecipeOverlay from '@/recipe/PhotoRecipeGrid';
+import PhotoRecipe from '@/recipe/PhotoRecipe';
export default function PhotoLarge({
photo,
@@ -56,12 +57,14 @@ export default function PhotoLarge({
showTitleAsH1,
showCamera = true,
showSimulation = true,
+ showRecipe = true,
showZoomControls: showZoomControlsProp = true,
shouldZoomOnFKeydown = true,
shouldShare = true,
shouldShareTag,
shouldShareCamera,
shouldShareSimulation,
+ shouldShareRecipe,
shouldShareFocalLength,
includeFavoriteInAdminMenu,
onVisible,
@@ -77,12 +80,14 @@ export default function PhotoLarge({
showTitleAsH1?: boolean
showCamera?: boolean
showSimulation?: boolean
+ showRecipe?: boolean
showZoomControls?: boolean
shouldZoomOnFKeydown?: boolean
shouldShare?: boolean
shouldShareTag?: boolean
shouldShareCamera?: boolean
shouldShareSimulation?: boolean
+ shouldShareRecipe?: boolean
shouldShareFocalLength?: boolean
includeFavoriteInAdminMenu?: boolean
onVisible?: () => void
@@ -101,21 +106,26 @@ export default function PhotoLarge({
const showZoomControls = showZoomControlsProp && areZoomControlsShown;
const refRecipe = useRef(null);
- const refRecipeTrigger = useRef(null);
+ const refRecipeTitle = useRef(null);
+ const refRecipeButton = useRef(null);
+ const refTriggers = useMemo(() => [refRecipeTitle, refRecipeButton], []);
const {
shouldShowRecipe,
toggleRecipe,
hideRecipe,
} = useRecipeState({
ref: refRecipe,
- refTrigger: refRecipeTrigger,
+ refTriggers,
});
const tags = sortTags(photo.tags, primaryTag);
const camera = cameraFromPhoto(photo);
+
+ const { recipeTitle: recipe } = photo;
const showCameraContent = showCamera && shouldShowCameraDataForPhoto(photo);
+ const showRecipeContent = showRecipe && recipe;
const showTagsContent = tags.length > 0;
const showExifContent = shouldShowExifDataForPhoto(photo);
@@ -132,6 +142,7 @@ export default function PhotoLarge({
const hasMetaContent =
showCameraContent ||
showTagsContent ||
+ showRecipeContent ||
showExifContent;
const hasNonDateContent =
@@ -185,11 +196,11 @@ export default function PhotoLarge({
)}>
{(shouldShowRecipe || shouldDebugRecipeOverlays) &&
- photo.fujifilmRecipe &&
+ photo.recipeData &&
photo.filmSimulation &&
-
{photo.caption}
}
- {(showCameraContent || showTagsContent) &&
+ {(showCameraContent || showRecipeContent || showTagsContent) &&
{showCameraContent &&
}
+ {showRecipeContent &&
+
}
{showTagsContent &&
{showExifContent &&
<>
@@ -310,7 +330,7 @@ export default function PhotoLarge({
{(
(showSimulation && photo.filmSimulation) ||
- (SHOW_RECIPES && photo.fujifilmRecipe)
+ (SHOW_RECIPES && showRecipe && photo.recipeData)
) &&
{showSimulation && photo.filmSimulation &&
@@ -318,9 +338,9 @@ export default function PhotoLarge({
simulation={photo.filmSimulation}
prefetch={prefetchRelatedLinks}
/>}
- {SHOW_RECIPES && photo.fujifilmRecipe &&
+ {SHOW_RECIPES && photo.recipeData &&
);
diff --git a/src/recipe/PhotoRecipeGrid.tsx b/src/recipe/PhotoRecipeGrid.tsx
index 5d4e677b..83a95125 100644
--- a/src/recipe/PhotoRecipeGrid.tsx
+++ b/src/recipe/PhotoRecipeGrid.tsx
@@ -10,7 +10,7 @@ import { RecipeProps } from '.';
const addSign = (value = 0) => value < 0 ? value : `+${value}`;
-export default function PhotoRecipeGrid({
+export default function PhotoRecipeOverlay({
ref,
recipe: {
dynamicRange,
diff --git a/src/recipe/PhotoRecipeOverlay.tsx b/src/recipe/PhotoRecipeOverlay.tsx
deleted file mode 100644
index ec02723a..00000000
--- a/src/recipe/PhotoRecipeOverlay.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-'use client';
-
-import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
-import clsx from 'clsx/lite';
-import ImageLarge from '@/components/image/ImageLarge';
-import PhotoRecipeGrid from './PhotoRecipeGrid';
-import { Photo } from '../photo';
-import { useEffect, useState } from 'react';
-export default function PhotoRecipeOverlay({
- photos,
- recipe,
- className,
-}: {
- photos: Photo[]
- recipe: FujifilmRecipe
- className?: string
-}) {
- const [photoIndex, setPhotoIndex] = useState(0);
-
- const photo = photos[photoIndex];
-
- useEffect(() => {
- const interval = setInterval(() => {
- setPhotoIndex((photoIndex + 1) % photos.length);
- }, 500);
- return () => clearInterval(interval);
- }, [photoIndex, photos]);
-
- return (
-
- );
-}
diff --git a/src/recipe/RecipeHeader.tsx b/src/recipe/RecipeHeader.tsx
index 0d4dd68b..a90356a8 100644
--- a/src/recipe/RecipeHeader.tsx
+++ b/src/recipe/RecipeHeader.tsx
@@ -1,10 +1,10 @@
'use client';
import { Photo, PhotoDateRange } from '@/photo';
-import { descriptionForTaggedPhotos } from '../tag';
import PhotoHeader from '@/photo/PhotoHeader';
import PhotoRecipe from './PhotoRecipe';
import { useAppState } from '@/state/AppState';
+import { descriptionForRecipePhotos } from '.';
export default function RecipeHeader({
recipe,
photos,
@@ -22,27 +22,27 @@ export default function RecipeHeader({
}) {
const { setRecipeModalProps } = useAppState();
- const photo = photos.find(({ filmSimulation, fujifilmRecipe }) =>
- fujifilmRecipe && filmSimulation);
+ const photo = photos.find(({ filmSimulation, recipeData }) =>
+ recipeData && filmSimulation);
return (
(
- photo?.fujifilmRecipe &&
+ photo?.recipeData &&
photo?.filmSimulation
) ? setRecipeModalProps?.({
simulation: photo.filmSimulation,
- recipe: photo.fujifilmRecipe,
+ recipe: photo.recipeData,
iso: photo.isoFormatted,
exposure: photo.exposureTimeFormatted,
})
: undefined}
/>}
- entityDescription={descriptionForTaggedPhotos(photos, undefined, count)}
+ entityDescription={descriptionForRecipePhotos(photos, undefined, count)}
photos={photos}
selectedPhoto={selectedPhoto}
indexNumber={indexNumber}
diff --git a/src/recipe/RecipeModal.tsx b/src/recipe/RecipeModal.tsx
index 5c6576a3..5883b66e 100644
--- a/src/recipe/RecipeModal.tsx
+++ b/src/recipe/RecipeModal.tsx
@@ -2,7 +2,7 @@
import Modal from '@/components/Modal';
import { useAppState } from '@/state/AppState';
-import PhotoRecipeGrid from './PhotoRecipeGrid';
+import PhotoRecipeOverlay from './PhotoRecipeGrid';
export default function ShareModals() {
const {
@@ -12,10 +12,11 @@ export default function ShareModals() {
if (recipeModalProps) {
return setRecipeModalProps?.(undefined)}
container={false}
>
- setRecipeModalProps?.(undefined),
}}/>
diff --git a/src/recipe/data.ts b/src/recipe/data.ts
new file mode 100644
index 00000000..4ffc302a
--- /dev/null
+++ b/src/recipe/data.ts
@@ -0,0 +1,16 @@
+import {
+ getPhotosCached,
+ getPhotosMetaCached,
+} from '@/photo/cache';
+
+export const getPhotosRecipeDataCached = ({
+ recipe,
+ limit,
+}: {
+ recipe: string,
+ limit?: number,
+}) =>
+ Promise.all([
+ getPhotosCached({ recipe, limit }),
+ getPhotosMetaCached({ recipe }),
+ ]);
diff --git a/src/recipe/index.ts b/src/recipe/index.ts
index 9499a3b5..19ce2ca4 100644
--- a/src/recipe/index.ts
+++ b/src/recipe/index.ts
@@ -1,13 +1,16 @@
import { absolutePathForRecipe, absolutePathForRecipeImage } from '@/app/paths';
import { descriptionForPhotoSet, Photo, photoQuantityText } from '@/photo';
import { PhotoDateRange } from '@/photo';
-import { Tags } from '../tag';
-import { parameterize } from '@/utility/string';
import { capitalizeWords } from '@/utility/string';
import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
import { FilmSimulation } from '@/simulation';
-const KEY_RECIPE = 'recipe';
+export type RecipeWithCount = {
+ recipe: string
+ count: number
+}
+
+export type Recipes = RecipeWithCount[]
export interface RecipeProps {
recipe: FujifilmRecipe
@@ -16,19 +19,6 @@ export interface RecipeProps {
exposure?: string
}
-export const isTagRecipe = (tag: string) =>
- (new RegExp(`^${KEY_RECIPE}-?`).test(tag));
-
-export const convertTagsToRecipes = (tags: Tags) =>
- tags.filter(({ tag }) => isTagRecipe(tag))
- .map(({ tag }) => convertTagToRecipe(tag));
-
-export const convertRecipeToTag = (recipe: string) =>
- `${KEY_RECIPE}-${parameterize(recipe)}`;
-
-export const convertTagToRecipe = (tag: string) =>
- tag.replace(new RegExp(`^${KEY_RECIPE}-?`), '');
-
export const formatRecipe = (recipe?: string) =>
capitalizeWords(recipe?.replaceAll('-', ' '));
diff --git a/src/recipe/useRecipeState.ts b/src/recipe/useRecipeState.ts
index 2420a9de..0ac10b25 100644
--- a/src/recipe/useRecipeState.ts
+++ b/src/recipe/useRecipeState.ts
@@ -1,20 +1,18 @@
import {
getPathComponents,
pathForPhoto,
- SEARCH_PARAM_SHOW_RECIPE,
} from '@/app/paths';
import { usePathname } from 'next/navigation';
-import { SEARCH_PARAM_SHOW } from '@/app/paths';
import { RefObject, useCallback, useEffect, useState } from 'react';
import { isElementEntirelyInViewport } from '@/utility/dom';
import useClickInsideOutside from '@/utility/useClickInsideOutside';
export default function useRecipeState({
ref,
- refTrigger,
+ refTriggers = [],
}: {
ref?: RefObject,
- refTrigger?: RefObject,
+ refTriggers?: RefObject[],
}) {
const pathname = usePathname();
@@ -23,13 +21,7 @@ export default function useRecipeState({
...pathComponents
} = getPathComponents(pathname);
- const searchParamShow = typeof document !== 'undefined'
- ? (new URLSearchParams(document.location.search)).get(SEARCH_PARAM_SHOW)
- : undefined;
-
- const showRecipeInitially = searchParamShow === SEARCH_PARAM_SHOW_RECIPE;
-
- const [shouldShowRecipe, setShouldShowRecipe] = useState(showRecipeInitially);
+ const [shouldShowRecipe, setShouldShowRecipe] = useState(false);
const setVisibility = useCallback((shouldShow: boolean) => {
if (shouldShow) {
@@ -69,7 +61,7 @@ export default function useRecipeState({
[setVisibility, shouldShowRecipe]);
useClickInsideOutside({
- htmlElements: [ref, refTrigger],
+ htmlElements: [ref, ...refTriggers],
onClickOutside: hideRecipe,
});
diff --git a/src/tag/PhotoTags.tsx b/src/tag/PhotoTags.tsx
index 9b0a3217..802f76ee 100644
--- a/src/tag/PhotoTags.tsx
+++ b/src/tag/PhotoTags.tsx
@@ -3,8 +3,6 @@ import { isTagFavs } from '.';
import FavsTag from './FavsTag';
import { EntityLinkExternalProps } from '@/components/primitives/EntityLink';
import { Fragment } from 'react';
-import { convertTagToRecipe, isTagRecipe } from '@/recipe';
-import PhotoRecipe from '@/recipe/PhotoRecipe';
export default function PhotoTags({
tags,
@@ -17,14 +15,9 @@ export default function PhotoTags({
{tags.map(tag =>
- {isTagRecipe(tag)
- ? console.log('clicked'),
- }} />
- : isTagFavs(tag)
- ?
- : }
+ {isTagFavs(tag)
+ ?
+ : }
)}
);