Standardize film types/arguments

This commit is contained in:
Sam Becker 2025-03-30 00:51:14 -05:00
parent 4441ab2b72
commit 0dfc12d06e
54 changed files with 350 additions and 470 deletions

View File

@ -1,11 +1,10 @@
/* eslint-disable max-len */
import {
getEscapePath,
getPathComponents,
isPathCamera,
isPathCameraPhoto,
isPathFilmSimulation,
isPathFilmSimulationPhoto,
isPathFilm,
isPathFilmPhoto,
isPathFocalLength,
isPathFocalLengthPhoto,
isPathPhoto,
@ -20,7 +19,7 @@ const TAG = 'tag-name';
const CAMERA_MAKE = 'fujifilm';
const CAMERA_MODEL = 'x-t1';
const CAMERA_OBJECT = { make: CAMERA_MAKE, model: CAMERA_MODEL };
const FILM_SIMULATION = 'acros';
const FILM = 'acros';
const FOCAL_LENGTH = 90;
const FOCAL_LENGTH_STRING = `${FOCAL_LENGTH}mm`;
@ -43,8 +42,8 @@ const PATH_TAG_HIDDEN_PHOTO = `${PATH_TAG_HIDDEN}/${PHOTO_ID}`;
const PATH_CAMERA = `/shot-on/${CAMERA_MAKE}/${CAMERA_MODEL}`;
const PATH_CAMERA_PHOTO = `${PATH_CAMERA}/${PHOTO_ID}`;
const PATH_FILM_SIMULATION = `/film/${FILM_SIMULATION}`;
const PATH_FILM_SIMULATION_PHOTO = `${PATH_FILM_SIMULATION}/${PHOTO_ID}`;
const PATH_FILM = `/film/${FILM}`;
const PATH_FILM_PHOTO = `${PATH_FILM}/${PHOTO_ID}`;
const PATH_FOCAL_LENGTH = `/focal/${FOCAL_LENGTH_STRING}`;
const PATH_FOCAL_LENGTH_PHOTO = `${PATH_FOCAL_LENGTH}/${PHOTO_ID}`;
@ -57,7 +56,7 @@ describe('Paths', () => {
expect(isPathProtected(PATH_TAG)).toBe(false);
expect(isPathProtected(PATH_TAG_PHOTO)).toBe(false);
expect(isPathProtected(PATH_CAMERA)).toBe(false);
expect(isPathProtected(PATH_FILM_SIMULATION)).toBe(false);
expect(isPathProtected(PATH_FILM)).toBe(false);
// Private
expect(isPathProtected(PATH_ADMIN)).toBe(true);
expect(isPathProtected(PATH_OG)).toBe(true);
@ -73,13 +72,13 @@ describe('Paths', () => {
expect(isPathTagPhoto(PATH_TAG_PHOTO)).toBe(true);
expect(isPathCamera(PATH_CAMERA)).toBe(true);
expect(isPathCameraPhoto(PATH_CAMERA_PHOTO)).toBe(true);
expect(isPathFilmSimulation(PATH_FILM_SIMULATION)).toBe(true);
expect(isPathFilmSimulationPhoto(PATH_FILM_SIMULATION_PHOTO)).toBe(true);
expect(isPathFilm(PATH_FILM)).toBe(true);
expect(isPathFilmPhoto(PATH_FILM_PHOTO)).toBe(true);
expect(isPathFocalLength(PATH_FOCAL_LENGTH)).toBe(true);
expect(isPathFocalLengthPhoto(PATH_FOCAL_LENGTH_PHOTO)).toBe(true);
// Negative
expect(isPathFocalLength(PATH_FILM_SIMULATION)).toBe(false);
expect(isPathFocalLengthPhoto(PATH_FILM_SIMULATION_PHOTO)).toBe(false);
expect(isPathFocalLength(PATH_FILM)).toBe(false);
expect(isPathFocalLengthPhoto(PATH_FILM_PHOTO)).toBe(false);
});
it('can be parsed', () => {
// Core
@ -103,13 +102,13 @@ describe('Paths', () => {
photoId: PHOTO_ID,
camera: CAMERA_OBJECT,
});
// Film Simulation
expect(getPathComponents(PATH_FILM_SIMULATION)).toEqual({
simulation: FILM_SIMULATION,
// Film
expect(getPathComponents(PATH_FILM)).toEqual({
film: FILM,
});
expect(getPathComponents(PATH_FILM_SIMULATION_PHOTO)).toEqual({
expect(getPathComponents(PATH_FILM_PHOTO)).toEqual({
photoId: PHOTO_ID,
simulation: FILM_SIMULATION,
film: FILM,
});
// Focal Length
expect(getPathComponents(PATH_FOCAL_LENGTH)).toEqual({
@ -134,9 +133,9 @@ describe('Paths', () => {
// Camera
expect(getEscapePath(PATH_CAMERA)).toEqual(PATH_ROOT);
expect(getEscapePath(PATH_CAMERA_PHOTO)).toEqual(PATH_CAMERA);
// Film Simulation
expect(getEscapePath(PATH_FILM_SIMULATION)).toEqual(PATH_ROOT);
expect(getEscapePath(PATH_FILM_SIMULATION_PHOTO)).toEqual(PATH_FILM_SIMULATION);
// Film
expect(getEscapePath(PATH_FILM)).toEqual(PATH_ROOT);
expect(getEscapePath(PATH_FILM_PHOTO)).toEqual(PATH_FILM);
// Focal Length
expect(getEscapePath(PATH_FOCAL_LENGTH)).toEqual(PATH_ROOT);
expect(getEscapePath(PATH_FOCAL_LENGTH_PHOTO)).toEqual(PATH_FOCAL_LENGTH);

View File

@ -129,7 +129,7 @@ export default function ComponentsPage() {
</div>
<div>
<EntityLink
icon={<PhotoFilmIcon simulation="astia" />}
icon={<PhotoFilmIcon film="astia" />}
label="Astia/Soft"
type="icon-last"
iconWide
@ -149,7 +149,7 @@ export default function ComponentsPage() {
</div>
<div>
<EntityLink
icon={<PhotoFilmIcon simulation="astia" />}
icon={<PhotoFilmIcon film="astia" />}
label="Astia/Soft"
type="icon-last"
iconWide
@ -184,7 +184,7 @@ export default function ComponentsPage() {
</div>
<div>
<EntityLink
icon={<PhotoFilmIcon simulation="astia" />}
icon={<PhotoFilmIcon film="astia" />}
label="Astia/Soft"
type="icon-last"
iconWide

View File

@ -32,7 +32,7 @@ export default async function RecipePageEdit({
const {
recipeData,
filmSimulation,
film,
} = getPhotoWithRecipeFromPhotos(photos) ?? {};
if (count === 0) { redirect(PATH_ADMIN); }
@ -42,11 +42,11 @@ export default async function RecipePageEdit({
backPath={PATH_ADMIN_RECIPES}
backLabel="Recipes"
breadcrumb={<AdminRecipeBadge {...{ recipe, count, hideBadge: true }} />}
accessory={recipeData && filmSimulation &&
accessory={recipeData && film &&
<AdminShowRecipeButton
title={recipe}
recipe={recipeData}
simulation={filmSimulation}
film={film}
/>
}
>

View File

@ -49,10 +49,10 @@ export default async function UploadPage({ params }: Params) {
] = await Promise.all([
getUniqueTagsCached(),
getUniqueRecipesCached(),
formDataFromExif?.recipeData && formDataFromExif.filmSimulation
formDataFromExif?.recipeData && formDataFromExif.film
? getRecipeTitleForData(
formDataFromExif.recipeData,
formDataFromExif.filmSimulation as FilmSimulation,
formDataFromExif.film as FilmSimulation,
)
: undefined,
]);

View File

@ -28,7 +28,7 @@ export default function FilmPage() {
Film Simulation:
</div>
<PhotoFilm
simulation={FILM_SIMULATION_FORM_INPUT_OPTIONS[index].value}
film={FILM_SIMULATION_FORM_INPUT_OPTIONS[index].value}
type="icon-first"
/>
<div className="mt-4 text-dim relative">

View File

@ -9,7 +9,7 @@ export default function FilmPage() {
{FILM_SIMULATION_FORM_INPUT_OPTIONS.map(({ value }) =>
<div key={value}>
<PhotoFilm
simulation={value}
film={value}
type="icon-first"
/>
</div>)}

View File

@ -20,20 +20,20 @@ import { cache } from 'react';
const getPhotosNearIdCachedCached = cache((
photoId: string,
simulation: FilmSimulation,
film: FilmSimulation,
) =>
getPhotosNearIdCached(
photoId,
{ film: simulation, limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
{ film, limit: RELATED_GRID_PHOTOS_TO_SHOW + 2 },
));
interface PhotoFilmSimulationProps {
interface PhotoFilmProps {
params: Promise<{ photoId: string, film: FilmSimulation }>
}
export async function generateMetadata({
params,
}: PhotoFilmSimulationProps): Promise<Metadata> {
}: PhotoFilmProps): Promise<Metadata> {
const { photoId, film } = await params;
const { photo } = await getPhotosNearIdCachedCached(photoId, film);
@ -65,7 +65,7 @@ export async function generateMetadata({
export default async function PhotoFilmPage({
params,
}: PhotoFilmSimulationProps) {
}: PhotoFilmProps) {
const { photoId, film } = await params;
const { photo, photos, photosGrid, indexNumber } =

View File

@ -9,14 +9,14 @@ import { FilmSimulation } from '@/film';
import { getIBMPlexMono } from '@/app/font';
import { ImageResponse } from 'next/og';
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
import { getUniqueFilmSimulations } from '@/photo/db/query';
import { getUniqueFilms } from '@/photo/db/query';
import { staticallyGenerateCategoryIfConfigured } from '@/app/static';
export const generateStaticParams = staticallyGenerateCategoryIfConfigured(
'films',
'image',
getUniqueFilmSimulations,
simulations => simulations.map(({ film: simulation }) => ({ simulation })),
getUniqueFilms,
films => films.map(({ film }) => ({ film })),
);
export async function GET(
@ -42,7 +42,7 @@ export async function GET(
return new ImageResponse(
<FilmImageResponse {...{
simulation: film,
film,
photos,
width,
height,

View File

@ -1,38 +1,38 @@
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
import { getUniqueFilmSimulations } from '@/photo/db/query';
import { FilmSimulation, generateMetaForFilmSimulation } from '@/film';
import { getUniqueFilms } from '@/photo/db/query';
import { FilmSimulation, generateMetaForFilm } from '@/film';
import FilmOverview from '@/film/FilmOverview';
import { getPhotosFilmSimulationDataCached } from '@/film/data';
import { getPhotosFilmDataCached } from '@/film/data';
import { Metadata } from 'next/types';
import { cache } from 'react';
import { PATH_ROOT } from '@/app/paths';
import { redirect } from 'next/navigation';
import { staticallyGenerateCategoryIfConfigured } from '@/app/static';
const getPhotosFilmSimulationDataCachedCached =
cache(getPhotosFilmSimulationDataCached);
const getPhotosFilmDataCachedCached =
cache(getPhotosFilmDataCached);
export const generateStaticParams = staticallyGenerateCategoryIfConfigured(
'films',
'page',
getUniqueFilmSimulations,
getUniqueFilms,
films => films.map(({ film }) => ({ film })),
);
interface FilmSimulationProps {
interface FilmProps {
params: Promise<{ film: FilmSimulation }>
}
export async function generateMetadata({
params,
}: FilmSimulationProps): Promise<Metadata> {
}: FilmProps): Promise<Metadata> {
const { film } = await params;
const [
photos,
{ count, dateRange },
] = await getPhotosFilmSimulationDataCachedCached({
simulation: film,
] = await getPhotosFilmDataCachedCached({
film,
limit: INFINITE_SCROLL_GRID_INITIAL,
});
@ -43,7 +43,7 @@ export async function generateMetadata({
title,
description,
images,
} = generateMetaForFilmSimulation(film, photos, count, dateRange);
} = generateMetaForFilm(film, photos, count, dateRange);
return {
title,
@ -64,14 +64,14 @@ export async function generateMetadata({
export default async function FilmPage({
params,
}: FilmSimulationProps) {
}: FilmProps) {
const { film } = await params;
const [
photos,
{ count, dateRange },
] = await getPhotosFilmSimulationDataCachedCached({
simulation: film,
] = await getPhotosFilmDataCachedCached({
film,
limit: INFINITE_SCROLL_GRID_INITIAL,
});
@ -79,7 +79,7 @@ export default async function FilmPage({
return (
<FilmOverview {...{
simulation: film,
film,
photos,
count,
dateRange,

View File

@ -29,7 +29,7 @@ export default async function GridPage() {
lenses,
tags,
recipes,
simulations,
films,
focalLengths,
] = await Promise.all([
getPhotosCached()
@ -49,7 +49,7 @@ export default async function GridPage() {
cameras,
lenses,
tags,
films: simulations,
films,
recipes,
focalLengths,
}}

View File

@ -9,7 +9,7 @@ import TagOGTile from '@/tag/TagOGTile';
const tag = 'cicadas';
const camera = { make: 'Fujifilm', model: 'X-T5' };
const cameraIcon = { make: 'Apple', model: 'iPhone 13 Pro' };
const simulation = 'acros';
const film = 'acros';
const focal = 90;
export default async function OGOverviewPage() {
@ -19,7 +19,7 @@ export default async function OGOverviewPage() {
photosTag,
photosFavs,
photosCamera,
photosSimulation,
photosFilm,
photosFocal,
] = await Promise.all([
getPhotosCached({ limit: 1 }).then(photos => photos[0])
@ -32,7 +32,7 @@ export default async function OGOverviewPage() {
.catch(() => []),
getPhotosCached({ limit: 1, camera })
.catch(() => []),
getPhotosCached({ limit: 1, film: simulation })
getPhotosCached({ limit: 1, film })
.catch(() => []),
getPhotosCached({ limit: 1, focal })
.catch(() => []),
@ -45,7 +45,7 @@ export default async function OGOverviewPage() {
<TagOGTile tag={tag} photos={photosTag} />
<TagOGTile tag={TAG_FAVS} photos={photosFavs} />
<CameraOGTile camera={camera} photos={photosCamera} />
<FilmOGTile simulation={simulation} photos={photosSimulation} />
<FilmOGTile film={film} photos={photosFilm} />
<FocalLengthOGTile focal={focal} photos={photosFocal} />
</div>
);

View File

@ -35,7 +35,7 @@ export default async function HomePage() {
lenses,
tags,
recipes,
simulations,
films,
focalLengths,
] = await Promise.all([
getPhotosCached()
@ -59,7 +59,7 @@ export default async function HomePage() {
lenses,
tags,
recipes,
films: simulations,
films,
focalLengths,
}}
/>

View File

@ -9,11 +9,11 @@ import { TbChecklist } from 'react-icons/tb';
export default function AdminShowRecipeButton({
title,
recipe,
simulation,
film,
}: {
title: string
recipe: FujifilmRecipe
simulation: FujifilmSimulation
film: FujifilmSimulation
}) {
const { setRecipeModalProps } = useAppState();
@ -26,7 +26,7 @@ export default function AdminShowRecipeButton({
onClick={() => setRecipeModalProps?.({
title,
recipe,
simulation,
film,
})}
>
Preview

View File

@ -1,7 +1,7 @@
import {
getPhotosMeta,
getUniqueCameras,
getUniqueFilmSimulations,
getUniqueFilms,
getUniqueFocalLengths,
getUniqueLenses,
getUniqueRecipes,
@ -34,7 +34,7 @@ export default async function AdminAppInsights() {
lenses,
tags,
recipes,
filmSimulations,
films,
focalLengths,
] = await Promise.all([
getPhotosMeta({ hidden: 'include' }),
@ -46,7 +46,7 @@ export default async function AdminAppInsights() {
getUniqueLenses(),
getUniqueTags(),
getUniqueRecipes(),
getUniqueFilmSimulations(),
getUniqueFilms(),
getUniqueFocalLengths(),
]);
@ -94,7 +94,7 @@ export default async function AdminAppInsights() {
lensesCount: lenses.length,
tagsCount: tags.length,
recipesCount: recipes.length,
filmSimulationsCount: filmSimulations.length,
filmsCount: films.length,
focalLengthsCount: focalLengths.length,
dateRange,
}}

View File

@ -96,7 +96,7 @@ export default function AdminAppInsightsClient({
lensesCount,
tagsCount,
recipesCount,
filmSimulationsCount,
filmsCount,
focalLengthsCount,
dateRange,
},
@ -487,11 +487,11 @@ export default function AdminAppInsightsClient({
/>
: null;
case 'films':
return filmSimulationsCount > 0
return filmsCount > 0
? <ScoreCardRow
key={category}
icon={<IconFilm size={15} />}
content={pluralize(filmSimulationsCount, 'film simulation')}
content={pluralize(filmsCount, 'film')}
/>
: null;
case 'focal-lengths':

View File

@ -53,7 +53,7 @@ export interface PhotoStats {
lensesCount: number
tagsCount: number
recipesCount: number
filmSimulationsCount: number
filmsCount: number
focalLengthsCount: number
dateRange?: PhotoDateRange
}

View File

@ -249,7 +249,7 @@ export const SHOW_TAGS =
CATEGORY_VISIBILITY.includes('tags');
export const SHOW_RECIPES =
CATEGORY_VISIBILITY.includes('recipes');
export const SHOW_FILM_SIMULATIONS =
export const SHOW_FILMS =
CATEGORY_VISIBILITY.includes('films');
export const SHOW_FOCAL_LENGTHS =
CATEGORY_VISIBILITY.includes('focal-lengths');

View File

@ -26,7 +26,7 @@ export const PREFIX_CAMERA = '/shot-on';
export const PREFIX_LENS = '/lens';
export const PREFIX_TAG = '/tag';
export const PREFIX_RECIPE = '/recipe';
export const PREFIX_FILM_SIMULATION = '/film';
export const PREFIX_FILM = '/film';
export const PREFIX_FOCAL_LENGTH = '/focal';
// Dynamic paths
@ -34,8 +34,7 @@ const PATH_PHOTO_DYNAMIC = `${PREFIX_PHOTO}/[photoId]`;
const PATH_CAMERA_DYNAMIC = `${PREFIX_CAMERA}/[make]/[model]`;
const PATH_LENS_DYNAMIC = `${PREFIX_LENS}/[make]/[model]`;
const PATH_TAG_DYNAMIC = `${PREFIX_TAG}/[tag]`;
// eslint-disable-next-line max-len
const PATH_FILM_SIMULATION_DYNAMIC = `${PREFIX_FILM_SIMULATION}/[simulation]`;
const PATH_FILM_DYNAMIC = `${PREFIX_FILM}/[film]`;
const PATH_FOCAL_LENGTH_DYNAMIC = `${PREFIX_FOCAL_LENGTH}/[focal]`;
const PATH_RECIPE_DYNAMIC = `${PREFIX_RECIPE}/[recipe]`;
@ -86,7 +85,7 @@ export const PATHS_TO_CACHE = [
PATH_CAMERA_DYNAMIC,
PATH_LENS_DYNAMIC,
PATH_TAG_DYNAMIC,
PATH_FILM_SIMULATION_DYNAMIC,
PATH_FILM_DYNAMIC,
PATH_FOCAL_LENGTH_DYNAMIC,
PATH_RECIPE_DYNAMIC,
...PATHS_ADMIN,
@ -121,7 +120,7 @@ export const pathForPhoto = ({
camera,
lens,
tag,
film: simulation,
film,
focal,
recipe,
}: PhotoPathParams) => {
@ -135,8 +134,8 @@ export const pathForPhoto = ({
prefix = pathForLens(lens);
} else if (tag) {
prefix = pathForTag(tag);
} else if (simulation) {
prefix = pathForFilmSimulation(simulation);
} else if (film) {
prefix = pathForFilm(film);
} else if (recipe) {
prefix = pathForRecipe(recipe);
} else if (focal) {
@ -152,8 +151,8 @@ export const pathForTag = (tag: string) =>
export const pathForCamera = ({ make, model }: Camera) =>
`${PREFIX_CAMERA}/${parameterize(make)}/${parameterize(model)}`;
export const pathForFilmSimulation = (simulation: FilmSimulation) =>
`${PREFIX_FILM_SIMULATION}/${simulation}`;
export const pathForFilm = (film: FilmSimulation) =>
`${PREFIX_FILM}/${film}`;
export const pathForLens = ({ make, model }: Lens) =>
make
@ -178,8 +177,8 @@ export const absolutePathForCamera= (camera: Camera) =>
export const absolutePathForLens= (lens: Lens) =>
`${BASE_URL}${pathForLens(lens)}`;
export const absolutePathForFilmSimulation = (simulation: FilmSimulation) =>
`${BASE_URL}${pathForFilmSimulation(simulation)}`;
export const absolutePathForFilm = (film: FilmSimulation) =>
`${BASE_URL}${pathForFilm(film)}`;
export const absolutePathForRecipe = (recipe: string) =>
`${BASE_URL}${pathForRecipe(recipe)}`;
@ -199,9 +198,8 @@ export const absolutePathForCameraImage= (camera: Camera) =>
export const absolutePathForLensImage= (lens: Lens) =>
`${absolutePathForLens(lens)}/image`;
export const absolutePathForFilmSimulationImage =
(simulation: FilmSimulation) =>
`${absolutePathForFilmSimulation(simulation)}/image`;
export const absolutePathForFilmImage = (film: FilmSimulation) =>
`${absolutePathForFilm(film)}/image`;
export const absolutePathForRecipeImage = (recipe: string) =>
`${absolutePathForRecipe(recipe)}/image`;
@ -230,13 +228,13 @@ export const isPathCamera = (pathname = '') =>
export const isPathCameraPhoto = (pathname = '') =>
new RegExp(`^${PREFIX_CAMERA}/[^/]+/[^/]+/[^/]+/?$`).test(pathname);
// film/[simulation]
export const isPathFilmSimulation = (pathname = '') =>
new RegExp(`^${PREFIX_FILM_SIMULATION}/[^/]+/?$`).test(pathname);
// film/[film]
export const isPathFilm = (pathname = '') =>
new RegExp(`^${PREFIX_FILM}/[^/]+/?$`).test(pathname);
// film/[simulation]/[photoId]
export const isPathFilmSimulationPhoto = (pathname = '') =>
new RegExp(`^${PREFIX_FILM_SIMULATION}/[^/]+/[^/]+/?$`).test(pathname);
// film/[film]/[photoId]
export const isPathFilmPhoto = (pathname = '') =>
new RegExp(`^${PREFIX_FILM}/[^/]+/[^/]+/?$`).test(pathname);
// focal/[focal]
export const isPathFocalLength = (pathname = '') =>
@ -299,8 +297,8 @@ export const getPathComponents = (pathname = ''): {
new RegExp(`^${PREFIX_TAG}/[^/]+/([^/]+)`))?.[1];
const photoIdFromCamera = pathname.match(
new RegExp(`^${PREFIX_CAMERA}/[^/]+/[^/]+/([^/]+)`))?.[1];
const photoIdFromFilmSimulation = pathname.match(
new RegExp(`^${PREFIX_FILM_SIMULATION}/[^/]+/([^/]+)`))?.[1];
const photoIdFromFilm = pathname.match(
new RegExp(`^${PREFIX_FILM}/[^/]+/([^/]+)`))?.[1];
const photoIdFromFocalLength = pathname.match(
new RegExp(`^${PREFIX_FOCAL_LENGTH}/[0-9]+mm/([^/]+)`))?.[1];
const tag = pathname.match(
@ -309,8 +307,8 @@ export const getPathComponents = (pathname = ''): {
new RegExp(`^${PREFIX_CAMERA}/([^/]+)`))?.[1];
const cameraModel = pathname.match(
new RegExp(`^${PREFIX_CAMERA}/[^/]+/([^/]+)`))?.[1];
const simulation = pathname.match(
new RegExp(`^${PREFIX_FILM_SIMULATION}/([^/]+)`))?.[1] as FilmSimulation;
const film = pathname.match(
new RegExp(`^${PREFIX_FILM}/([^/]+)`))?.[1] as FilmSimulation;
const focalString = pathname.match(
new RegExp(`^${PREFIX_FOCAL_LENGTH}/([0-9]+)mm`))?.[1];
@ -325,12 +323,12 @@ export const getPathComponents = (pathname = ''): {
photoIdFromPhoto ||
photoIdFromTag ||
photoIdFromCamera ||
photoIdFromFilmSimulation ||
photoIdFromFilm ||
photoIdFromFocalLength
),
tag,
camera,
film: simulation,
film,
focal,
};
};
@ -340,7 +338,7 @@ export const getEscapePath = (pathname?: string) => {
photoId,
tag,
camera,
film: simulation,
film,
focal,
} = getPathComponents(pathname);
@ -348,7 +346,7 @@ export const getEscapePath = (pathname?: string) => {
(photoId && isPathPhoto(pathname)) ||
(tag && isPathTag(pathname)) ||
(camera && isPathCamera(pathname)) ||
(simulation && isPathFilmSimulation(pathname)) ||
(film && isPathFilm(pathname)) ||
(focal && isPathFocalLength(pathname))
) {
return PATH_ROOT;
@ -356,8 +354,8 @@ export const getEscapePath = (pathname?: string) => {
return pathForTag(tag);
} else if (camera && isPathCameraPhoto(pathname)) {
return pathForCamera(camera);
} else if (simulation && isPathFilmSimulationPhoto(pathname)) {
return pathForFilmSimulation(simulation);
} else if (film && isPathFilmPhoto(pathname)) {
return pathForFilm(film);
} else if (focal && isPathFocalLengthPhoto(pathname)) {
return pathForFocalLength(focal);
}

View File

@ -1,13 +1,13 @@
import {
getUniqueCameras,
getUniqueFilmSimulations,
getUniqueFilms,
getUniqueFocalLengths,
getUniqueLenses,
getUniqueRecipes,
getUniqueTags,
} from '@/photo/db/query';
import {
SHOW_FILM_SIMULATIONS,
SHOW_FILMS,
SHOW_FOCAL_LENGTHS,
SHOW_LENSES,
SHOW_RECIPES,
@ -40,8 +40,8 @@ export const getDataForCategories = () => [
.then(sortCategoriesByCount)
.catch(() => [])
: [],
SHOW_FILM_SIMULATIONS
? getUniqueFilmSimulations()
SHOW_FILMS
? getUniqueFilms()
.then(sortCategoriesByCount)
.catch(() => [])
: [],
@ -58,7 +58,7 @@ export const getCountsForCategories = async () => {
lenses,
tags,
recipes,
filmSimulations,
films,
focalLengths,
] = await Promise.all(getDataForCategories());
@ -79,8 +79,8 @@ export const getCountsForCategories = async () => {
acc[recipe.recipe] = recipe.count;
return acc;
}, {} as Record<string, number>),
filmSimulations: filmSimulations.reduce((acc, filmSimulation) => {
acc[filmSimulation.film] = filmSimulation.count;
films: films.reduce((acc, film) => {
acc[film.film] = film.count;
return acc;
}, {} as Record<string, number>),
focalLengths: focalLengths.reduce((acc, focalLength) => {

View File

@ -1,7 +1,7 @@
import { Photo } from '../photo';
import { Camera, Cameras } from '@/camera';
import { PhotoDateRange } from '../photo';
import { FilmSimulation, FilmSimulations } from '@/film';
import { FilmSimulation, Films } from '@/film';
import { Lens, Lenses } from '@/lens';
import { Tags } from '@/tag';
import { FocalLengths } from '@/focal';
@ -48,7 +48,7 @@ export interface PhotoSetCategories {
lenses: Lenses
tags: Tags
recipes: Recipes
films: FilmSimulations
films: Films
focalLengths: FocalLengths
}

View File

@ -4,6 +4,7 @@ import { Camera } from '@/camera';
import { Lens } from '@/lens';
import { useAppState } from '@/state/AppState';
import { useCallback } from 'react';
import { FujifilmSimulation } from '@/platforms/fujifilm/simulation';
export default function useCategoryCounts() {
const { categoriesWithCounts } = useAppState();
@ -28,9 +29,9 @@ export default function useCategoryCounts() {
return recipeCounts[recipe];
}, [categoriesWithCounts]);
const getFilmSimulationCount = useCallback((simulation: string) => {
const filmSimulationCounts = categoriesWithCounts?.filmSimulations ?? {};
return filmSimulationCounts[simulation];
const getFilmCount = useCallback((film: FujifilmSimulation) => {
const filmCounts = categoriesWithCounts?.films ?? {};
return filmCounts[film];
}, [categoriesWithCounts]);
const getFocalLengthCount = useCallback((focalLength: number) => {
@ -43,7 +44,7 @@ export default function useCategoryCounts() {
getLensCount,
getTagCount,
getRecipeCount,
getFilmSimulationCount,
getFilmCount,
getFocalLengthCount,
};
}

View File

@ -10,7 +10,7 @@ export default function useCategoryCountsForPhoto(photo: Photo) {
getLensCount,
getTagCount,
getRecipeCount,
getFilmSimulationCount,
getFilmCount,
getFocalLengthCount,
} = useCategoryCounts();
@ -25,21 +25,20 @@ export default function useCategoryCountsForPhoto(photo: Photo) {
return acc;
}, {} as Record<string, number>),
recipeCount: photo.recipeTitle ? getRecipeCount(photo.recipeTitle) : 0,
simulationCount:
photo.filmSimulation ? getFilmSimulationCount(photo.filmSimulation) : 0,
filmCount: photo.film ? getFilmCount(photo.film) : 0,
focalCount: photo.focalLength ? getFocalLengthCount(photo.focalLength) : 0,
}), [
getCameraCount,
getLensCount,
getRecipeCount,
getFilmSimulationCount,
getFilmCount,
getFocalLengthCount,
getTagCount,
camera,
lens,
photo.tags,
photo.recipeTitle,
photo.filmSimulation,
photo.film,
photo.focalLength,
]);

View File

@ -25,7 +25,7 @@ import {
PATH_ROOT,
PATH_SIGN_IN,
pathForCamera,
pathForFilmSimulation,
pathForFilm,
pathForFocalLength,
pathForLens,
pathForPhoto,
@ -333,13 +333,13 @@ export default function CommandKClient({
})),
};
case 'films': return {
heading: 'Film Simulations',
heading: 'Films',
accessory: <IconFilm size={14} />,
items: films.map(({ film, count }) => ({
label: labelForFilm(film).medium,
annotation: formatCount(count),
annotationAria: formatCountDescriptive(count),
path: pathForFilmSimulation(film),
path: pathForFilm(film),
})),
};
case 'focal-lengths': return {

View File

@ -1,17 +1,17 @@
import { Photo, PhotoDateRange } from '@/photo';
import { FilmSimulation, descriptionForFilmSimulationPhotos } from '.';
import { FilmSimulation, descriptionForFilmPhotos } from '.';
import PhotoHeader from '@/photo/PhotoHeader';
import PhotoFilm from '@/film/PhotoFilm';
export default function FilmHeader({
simulation,
film,
photos,
selectedPhoto,
indexNumber,
count,
dateRange,
}: {
simulation: FilmSimulation
film: FilmSimulation
photos: Photo[]
selectedPhoto?: Photo
indexNumber?: number
@ -20,9 +20,9 @@ export default function FilmHeader({
}) {
return (
<PhotoHeader
film={simulation}
entity={<PhotoFilm {...{ simulation }} />}
entityDescription={descriptionForFilmSimulationPhotos(
film={film}
entity={<PhotoFilm {...{ film }} />}
entityDescription={descriptionForFilmPhotos(
photos, undefined, count, dateRange)}
photos={photos}
selectedPhoto={selectedPhoto}

View File

@ -1,19 +1,19 @@
import { Photo, PhotoDateRange } from '@/photo';
import {
absolutePathForFilmSimulationImage,
pathForFilmSimulation,
absolutePathForFilmImage,
pathForFilm,
} from '@/app/paths';
import OGTile from '@/components/OGTile';
import {
FilmSimulation,
descriptionForFilmSimulationPhotos,
titleForFilmSimulation,
descriptionForFilmPhotos,
titleForFilm,
} from '.';
export type OGLoadingState = 'unloaded' | 'loading' | 'loaded' | 'failed';
export default function FilmOGTile({
simulation,
film,
photos,
loadingState: loadingStateExternal,
riseOnHover,
@ -23,7 +23,7 @@ export default function FilmOGTile({
count,
dateRange,
}: {
simulation: FilmSimulation
film: FilmSimulation
photos: Photo[]
loadingState?: OGLoadingState
onLoad?: () => void
@ -35,11 +35,11 @@ export default function FilmOGTile({
}) {
return (
<OGTile {...{
title: titleForFilmSimulation(simulation, photos, count),
title: titleForFilm(film, photos, count),
description:
descriptionForFilmSimulationPhotos(photos, true, count, dateRange),
path: pathForFilmSimulation(simulation),
pathImageAbsolute: absolutePathForFilmSimulationImage(simulation),
descriptionForFilmPhotos(photos, true, count, dateRange),
path: pathForFilm(film),
pathImageAbsolute: absolutePathForFilmImage(film),
loadingState: loadingStateExternal,
onLoad,
onFail,

View File

@ -4,13 +4,13 @@ import { FilmSimulation } from '.';
import PhotoGridContainer from '@/photo/PhotoGridContainer';
export default function FilmOverview({
simulation,
film,
photos,
count,
dateRange,
animateOnFirstLoadOnly,
}: {
simulation: FilmSimulation,
film: FilmSimulation,
photos: Photo[],
count: number,
dateRange?: PhotoDateRange,
@ -18,12 +18,12 @@ export default function FilmOverview({
}) {
return (
<PhotoGridContainer {...{
cacheKey: `simulation-${simulation}`,
cacheKey: `film-${film}`,
photos,
count,
film: simulation,
film,
header: <FilmHeader {...{
simulation,
film,
photos,
count,
dateRange,

View File

@ -1,23 +1,23 @@
import { absolutePathForFilmSimulation } from '@/app/paths';
import { absolutePathForFilm } from '@/app/paths';
import { PhotoSetAttributes } from '../category';
import ShareModal from '@/share/ShareModal';
import FilmOGTile from './FilmOGTile';
import { FilmSimulation, shareTextForFilmSimulation } from '.';
import { FilmSimulation, shareTextForFilm } from '.';
export default function FilmShareModal({
simulation,
film,
photos,
count,
dateRange,
}: {
simulation: FilmSimulation
film: FilmSimulation
} & PhotoSetAttributes) {
return (
<ShareModal
pathShare={absolutePathForFilmSimulation(simulation)}
socialText={shareTextForFilmSimulation(simulation)}
pathShare={absolutePathForFilm(film)}
socialText={shareTextForFilm(film)}
>
<FilmOGTile {...{ simulation, photos, count, dateRange }} />
<FilmOGTile {...{ film, photos, count, dateRange }} />
</ShareModal>
);
};

View File

@ -1,6 +1,6 @@
import { labelForFilm } from '@/platforms/fujifilm/simulation';
import PhotoFilmIcon from './PhotoFilmIcon';
import { pathForFilmSimulation } from '@/app/paths';
import { pathForFilm } from '@/app/paths';
import { FilmSimulation } from '.';
import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
import EntityLink, {
@ -9,27 +9,27 @@ import EntityLink, {
import clsx from 'clsx/lite';
export default function PhotoFilm({
simulation,
film,
type = 'icon-last',
badged = true,
contrast = 'low',
countOnHover,
...props
}: {
simulation: FilmSimulation
film: FilmSimulation
countOnHover?: number
recipe?: FujifilmRecipe
} & EntityLinkExternalProps) {
const { small, medium, large } = labelForFilm(simulation);
const { small, medium, large } = labelForFilm(film);
return (
<EntityLink
{...props}
label={medium}
labelSmall={small}
href={pathForFilmSimulation(simulation)}
href={pathForFilm(film)}
icon={<PhotoFilmIcon
simulation={simulation}
film={film}
className={clsx(
contrast === 'frosted' && 'text-black',
type === 'icon-only'
@ -37,7 +37,7 @@ export default function PhotoFilm({
: 'translate-y-[-1px]',
)}
/>}
title={`Film Simulation: ${large}`}
title={`Film: ${large}`}
type={type}
badged={badged}
contrast={contrast}

View File

@ -7,12 +7,12 @@ const INTRINSIC_WIDTH = 28;
const INTRINSIC_HEIGHT = 16;
export default function PhotoFilmIcon({
simulation,
film,
height = INTRINSIC_HEIGHT,
className,
style,
}: {
simulation?: FilmSimulation
film?: FilmSimulation
height?: number
className?: string
style?: CSSProperties
@ -21,9 +21,9 @@ export default function PhotoFilmIcon({
<svg
className={className}
style={style}
aria-description={simulation
? labelForFilm(simulation).large
: 'Film Simulation'}
aria-description={film
? labelForFilm(film).large
: 'Film'}
width={INTRINSIC_WIDTH * height / INTRINSIC_HEIGHT}
height={height}
viewBox="0 0 28 16"
@ -33,7 +33,7 @@ export default function PhotoFilmIcon({
{(() => {
// Self-calling switch function and non-fragment groups
// necessary for ImageResponse compatibility
switch (simulation) {
switch (film) {
case 'monochrome': return <g>
<path fillRule="evenodd" clipRule="evenodd" d="M16.25 14H22.5C22.6381 14 22.75 13.8881 22.75 13.75V10.5202C22.75 10.4539 22.7763 10.3903 22.8232 10.3434L25.677 7.48989C25.7238 7.44301 25.7502 7.37942 25.7502 7.31311V4.25C25.7502 4.11193 25.6383 4 25.5002 4H16.25C16.1119 4 16 4.11194 16 4.25002L16.0002 6.49998C16.0002 6.63806 15.8882 6.75 15.7502 6.75H14.7502C14.6121 6.75 14.5002 6.86192 14.5002 6.99999L14.5 11C14.5 11.1381 14.6119 11.25 14.75 11.25H15.75C15.8881 11.25 16 11.3619 16 11.5V13.75C16 13.8881 16.1119 14 16.25 14ZM18.75 5H17V6.5H18.75V5ZM17 11.5H18.75V13H17V11.5ZM21.75 5H20V6.5H21.75V5ZM20 11.5H21.75V13H20V11.5ZM24.75 5H23V6.5H24.75V5Z" fill="currentColor"/>
<path fillRule="evenodd" clipRule="evenodd" d="M5.25 3.49999L2 3.49999C1.86193 3.49999 1.75 3.61192 1.75 3.74999V5.24998C1.75 5.38806 1.86193 5.49998 2 5.49998H3.00001C3.13808 5.49998 3.25001 5.61191 3.25001 5.74999L3.25 12.25C3.25 12.3881 3.13807 12.5 3 12.5H2C1.86193 12.5 1.75 12.6119 1.75 12.75V14.25C1.75 14.3881 1.86193 14.5 2 14.5H14.25C14.3881 14.5 14.5 14.3881 14.5 14.25V12.75C14.5 12.6119 14.3881 12.5 14.25 12.5H13.25C13.1119 12.5 13 12.3881 13 12.25L13 5.75C13 5.61193 13.112 5.5 13.25 5.5H14.25C14.3881 5.5 14.5 5.38807 14.5 5.25V3.74999C14.5 3.61192 14.3881 3.49999 14.25 3.49999L11 3.49999C10.8619 3.49998 10.75 3.38806 10.75 3.24999V1.74998C10.75 1.61191 10.6381 1.49998 10.5 1.49998H5.75C5.61193 1.49998 5.5 1.61191 5.5 1.74998V3.24999C5.5 3.38806 5.38807 3.49998 5.25 3.49999ZM9.03232 4.7985H5.38982V13H9.36132C9.65899 13 9.92532 12.9412 10.1603 12.8237C10.3953 12.6984 10.5951 12.53 10.7596 12.3185C10.9241 12.107 11.0494 11.8602 11.1356 11.5782C11.2217 11.2884 11.2648 10.9829 11.2648 10.6617C11.2648 10.3328 11.2139 10.0547 11.1121 9.8275C11.0102 9.5925 10.8771 9.4045 10.7126 9.2635C10.5481 9.11467 10.364 9.00892 10.1603 8.94625C9.96449 8.88358 9.77257 8.85225 9.58457 8.85225V8.66425C9.7569 8.65642 9.92532 8.62508 10.0898 8.57025C10.2543 8.50758 10.3992 8.41358 10.5246 8.28825C10.6577 8.15508 10.7635 7.97883 10.8418 7.7595C10.928 7.53233 10.9711 7.24642 10.9711 6.90175C10.9711 6.26725 10.8105 5.75808 10.4893 5.37425C10.176 4.99042 9.69032 4.7985 9.03232 4.7985ZM8.89132 11.5782H7.07007V9.55725H8.89132C9.07932 9.55725 9.22815 9.61208 9.33782 9.72175C9.45532 9.83142 9.51407 10.0155 9.51407 10.274V10.8615C9.51407 11.12 9.45532 11.3041 9.33782 11.4137C9.22815 11.5234 9.07932 11.5782 8.89132 11.5782ZM8.62107 8.17075H7.07007V6.22025H8.62107C8.8169 6.22025 8.96965 6.27508 9.07932 6.38475C9.18899 6.49442 9.24382 6.67458 9.24382 6.92525V7.46575C9.24382 7.71642 9.18899 7.89658 9.07932 8.00625C8.96965 8.11592 8.8169 8.17075 8.62107 8.17075Z" fill="currentColor"/>

View File

@ -4,14 +4,14 @@ import {
} from '@/photo/cache';
import { FilmSimulation } from '.';
export const getPhotosFilmSimulationDataCached = ({
simulation,
export const getPhotosFilmDataCached = ({
film,
limit,
}: {
simulation: FilmSimulation,
film: FilmSimulation,
limit?: number,
}) =>
Promise.all([
getPhotosCached({ film: simulation, limit }),
getPhotosMetaCached({ film: simulation }),
getPhotosCached({ film, limit }),
getPhotosMetaCached({ film }),
]);

View File

@ -5,8 +5,8 @@ import {
photoQuantityText,
} from '@/photo';
import {
absolutePathForFilmSimulation,
absolutePathForFilmSimulationImage,
absolutePathForFilm,
absolutePathForFilmImage,
} from '@/app/paths';
import {
FujifilmSimulation,
@ -15,27 +15,27 @@ import {
export type FilmSimulation = FujifilmSimulation;
export type FilmSimulationWithCount = {
export type FilmWithCount = {
film: FilmSimulation
count: number
}
export type FilmSimulations = FilmSimulationWithCount[]
export type Films = FilmWithCount[]
export const sortFilms = (
films: FilmSimulations,
films: Films,
) => films.sort(sortFilmsWithCount);
export const sortFilmsWithCount = (
a: FilmSimulationWithCount,
b: FilmSimulationWithCount,
a: FilmWithCount,
b: FilmWithCount,
) => {
const aLabel = labelForFilm(a.film).large;
const bLabel = labelForFilm(b.film).large;
return aLabel.localeCompare(bLabel);
};
export const titleForFilmSimulation = (
export const titleForFilm = (
film: FilmSimulation,
photos: Photo[],
explicitCount?: number,
@ -44,12 +44,12 @@ export const titleForFilmSimulation = (
photoQuantityText(explicitCount ?? photos.length),
].join(' ');
export const shareTextForFilmSimulation = (
export const shareTextForFilm = (
film: FilmSimulation,
) =>
`Photos shot on Fujifilm ${labelForFilm(film).large}`;
export const descriptionForFilmSimulationPhotos = (
export const descriptionForFilmPhotos = (
photos: Photo[],
dateBased?: boolean,
explicitCount?: number,
@ -63,22 +63,22 @@ export const descriptionForFilmSimulationPhotos = (
explicitDateRange,
);
export const generateMetaForFilmSimulation = (
export const generateMetaForFilm = (
film: FilmSimulation,
photos: Photo[],
explicitCount?: number,
explicitDateRange?: PhotoDateRange,
) => ({
url: absolutePathForFilmSimulation(film),
title: titleForFilmSimulation(film, photos, explicitCount),
description: descriptionForFilmSimulationPhotos(
url: absolutePathForFilm(film),
title: titleForFilm(film, photos, explicitCount),
description: descriptionForFilmPhotos(
photos,
true,
explicitCount,
explicitDateRange,
),
images: absolutePathForFilmSimulationImage(film),
images: absolutePathForFilmImage(film),
});
export const photoHasFilmSimulationData = (photo: Photo) =>
Boolean(photo.filmSimulation);
export const photoHasFilmData = (photo: Photo) =>
Boolean(photo.film);

View File

@ -11,13 +11,13 @@ import { FilmSimulation } from '@/film';
import { NextImageSize } from '@/platforms/next-image';
export default function FilmImageResponse({
simulation,
film,
photos,
width,
height,
fontFamily,
}: {
simulation: FilmSimulation,
film: FilmSimulation,
photos: Photo[]
width: NextImageSize
height: number
@ -37,11 +37,11 @@ export default function FilmImageResponse({
height,
fontFamily,
icon: <PhotoFilmIcon
simulation={simulation}
film={film}
height={height * .081}
style={{ transform: `translateY(${height * .001}px)`}}
/>,
title: labelForFilm(simulation).medium.toLocaleUpperCase(),
title: labelForFilm(film).medium.toLocaleUpperCase(),
}} />
</ImageContainer>
);

View File

@ -27,13 +27,13 @@ export default function RecipeImageResponse({
}) {
const {
recipeData,
filmSimulation,
film,
} = getPhotoWithRecipeFromPhotos(photos) ?? {};
let recipeLines = recipeData && filmSimulation
let recipeLines = recipeData && film
? generateRecipeText({
recipe: recipeData,
simulation: filmSimulation,
film,
}, true)
: [];
@ -109,10 +109,10 @@ export default function RecipeImageResponse({
flexGrow: 1,
}}>
{text}
{isStringFilmSimulation(text) && filmSimulation &&
{isStringFilmSimulation(text) && film &&
<div tw="flex">
<PhotoFilmIcon
simulation={filmSimulation}
film={film}
height={height * .06}
style={{ transform: `translateY(${-height * .001}px)`}}
/>

View File

@ -29,10 +29,12 @@ export default function InfinitePhotoScroll({
initialOffset,
itemsPerPage,
sortBy,
tag,
camera,
lens,
film: simulation,
tag,
recipe,
film,
focal,
wrapMoreButtonInGrid,
useCachedPhotos = true,
includeHiddenPhotos,
@ -73,7 +75,9 @@ export default function InfinitePhotoScroll({
camera,
lens,
tag,
film: simulation,
recipe,
film,
focal,
}, warmOnly)
, [
useCachedPhotos,
@ -84,7 +88,9 @@ export default function InfinitePhotoScroll({
camera,
lens,
tag,
simulation,
recipe,
film,
focal,
]);
const { data, isLoading, isValidating, error, mutate, size, setSize } =

View File

@ -22,7 +22,7 @@ export default function PhotoDetailPage({
tag,
camera,
lens,
film: simulation,
film,
recipe,
focal,
indexNumber,
@ -77,9 +77,9 @@ export default function PhotoDetailPage({
count={count}
dateRange={dateRange}
/>;
} else if (simulation) {
} else if (film) {
customHeader = <FilmHeader
simulation={simulation}
film={film}
photos={photos}
selectedPhoto={photo}
indexNumber={indexNumber}
@ -129,13 +129,13 @@ export default function PhotoDetailPage({
showTitleAsH1
showCamera={!camera}
showLens={!lens}
showSimulation={!simulation}
showFilm={!film}
showRecipe={!recipe}
shouldShare={shouldShare}
shouldShareCamera={camera !== undefined}
shouldShareLens={lens !== undefined}
shouldShareTag={tag !== undefined}
shouldShareSimulation={simulation !== undefined}
shouldShareFilm={film !== undefined}
shouldShareRecipe={recipe !== undefined}
shouldShareFocalLength={focal !== undefined}
includeFavoriteInAdminMenu={includeFavoriteInAdminMenu}
@ -149,7 +149,7 @@ export default function PhotoDetailPage({
selectedPhoto={photo}
tag={tag}
camera={camera}
film={simulation}
film={film}
focal={focal}
animateOnFirstLoadOnly
/>}

View File

@ -48,7 +48,7 @@ export default function PhotoGridSidebar({
cameras,
lenses,
tags,
films: simulations,
films,
recipes,
focalLengths,
} = categories;
@ -192,17 +192,17 @@ export default function PhotoGridSidebar({
/>
: null;
const filmsContent = simulations.length > 0
const filmsContent = films.length > 0
? <HeaderList
key="films"
title="Films"
icon={<IconFilm size={15} />}
maxItems={maxItemsPerCategory}
items={simulations
.map(({ film: simulation, count }) =>
items={films
.map(({ film, count }) =>
<PhotoFilm
key={simulation}
simulation={simulation}
key={film}
film={film}
countOnHover={count}
type="text-only"
prefetch={false}

View File

@ -6,7 +6,7 @@ import {
doesPhotoNeedBlurCompatibility,
shouldShowCameraDataForPhoto,
shouldShowExifDataForPhoto,
shouldShowFilmSimulationDataForPhoto,
shouldShowFilmDataForPhoto,
shouldShowLensDataForPhoto,
shouldShowRecipeDataForPhoto,
titleForPhoto,
@ -65,7 +65,7 @@ export default function PhotoLarge({
showTitleAsH1,
showCamera = true,
showLens = true,
showSimulation = true,
showFilm = true,
showRecipe = true,
showZoomControls: showZoomControlsProp = true,
shouldZoomOnFKeydown = true,
@ -73,7 +73,7 @@ export default function PhotoLarge({
shouldShareCamera,
shouldShareLens,
shouldShareTag,
shouldShareSimulation,
shouldShareFilm,
shouldShareRecipe,
shouldShareFocalLength,
includeFavoriteInAdminMenu,
@ -90,7 +90,7 @@ export default function PhotoLarge({
showTitleAsH1?: boolean
showCamera?: boolean
showLens?: boolean
showSimulation?: boolean
showFilm?: boolean
showRecipe?: boolean
showZoomControls?: boolean
shouldZoomOnFKeydown?: boolean
@ -98,7 +98,7 @@ export default function PhotoLarge({
shouldShareCamera?: boolean
shouldShareLens?: boolean
shouldShareTag?: boolean
shouldShareSimulation?: boolean
shouldShareFilm?: boolean
shouldShareRecipe?: boolean
shouldShareFocalLength?: boolean
includeFavoriteInAdminMenu?: boolean
@ -120,7 +120,7 @@ export default function PhotoLarge({
lensCount,
tagCounts,
recipeCount,
simulationCount,
filmCount,
} = useCategoryCountsForPhoto(photo);
const showZoomControls = showZoomControlsProp && areZoomControlsShown;
@ -150,8 +150,7 @@ export default function PhotoLarge({
const showTagsContent = tags.length > 0;
const showRecipeContent = showRecipe && shouldShowRecipeDataForPhoto(photo);
const showRecipeButton = shouldShowRecipeDataForPhoto(photo);
const showSimulationContent = showSimulation &&
shouldShowFilmSimulationDataForPhoto(photo);
const showFilmContent = showFilm && shouldShowFilmDataForPhoto(photo);
useVisible({ ref, onVisible });
@ -168,7 +167,7 @@ export default function PhotoLarge({
showLensContent ||
showTagsContent ||
showRecipeContent ||
showSimulationContent ||
showFilmContent ||
showExifContent;
const hasNonDateContent =
@ -226,12 +225,12 @@ export default function PhotoLarge({
<AnimatePresence>
{(shouldShowRecipeOverlay || shouldDebugRecipeOverlays) &&
photo.recipeData &&
photo.filmSimulation &&
photo.film &&
<PhotoRecipeOverlay
ref={refRecipe}
title={photo.recipeTitle}
recipe={photo.recipeData}
simulation={photo.filmSimulation}
film={photo.film}
iso={photo.isoFormatted}
exposure={photo.exposureCompensationFormatted}
onClose={hideRecipeOverlay}
@ -391,13 +390,13 @@ export default function PhotoLarge({
<li>{photo.isoFormatted}</li>
<li>{photo.exposureCompensationFormatted ?? '0ev'}</li>
</ul>
{(showRecipeButton || showSimulationContent) &&
{(showRecipeButton || showFilmContent) &&
<div className="flex items-center gap-2 *:w-auto">
{showSimulationContent && photo.filmSimulation &&
{showFilmContent && photo.film &&
<PhotoFilm
simulation={photo.filmSimulation}
film={photo.film}
prefetch={prefetchRelatedLinks}
countOnHover={simulationCount}
countOnHover={filmCount}
/>}
{showRecipeButton &&
<Tooltip content="Fujifilm Recipe">
@ -415,7 +414,7 @@ export default function PhotoLarge({
'px-[4px] py-[2.5px] my-[-3px]',
'translate-y-[2px]',
'hover:bg-dim active:bg-main',
!showSimulation && 'translate-x-[-2px]',
!showFilm && 'translate-x-[-2px]',
)}>
{shouldShowRecipeOverlay
? <IoCloseSharp size={15} />
@ -464,8 +463,8 @@ export default function PhotoLarge({
tag={shouldShareTag ? primaryTag : undefined}
camera={shouldShareCamera ? camera : undefined}
lens={shouldShareLens ? lens : undefined}
film={shouldShareSimulation
? photo.filmSimulation
film={shouldShareFilm
? photo.film
: undefined}
recipe={shouldShareRecipe
? recipeTitle

View File

@ -315,13 +315,13 @@ export const renamePhotoTagGloballyAction = async (formData: FormData) =>
export const getPhotosNeedingRecipeTitleCountAction = async (
recipeData: string,
simulation: FilmSimulation,
film: FilmSimulation,
photoIdToExclude?: string,
) =>
runAuthenticatedAdminServerAction(async () =>
await getPhotosNeedingRecipeTitleCount(
recipeData,
simulation,
film,
photoIdToExclude,
),
);

View File

@ -10,7 +10,7 @@ import {
getUniqueCameras,
getUniqueTags,
getUniqueTagsHidden,
getUniqueFilmSimulations,
getUniqueFilms,
getPhotosNearId,
getPhotosMostRecentUpdate,
getPhotosMeta,
@ -29,7 +29,7 @@ import {
PATH_GRID,
PATH_ROOT,
PREFIX_CAMERA,
PREFIX_FILM_SIMULATION,
PREFIX_FILM,
PREFIX_FOCAL_LENGTH,
PREFIX_LENS,
PREFIX_RECIPE,
@ -45,7 +45,7 @@ const KEY_PHOTO = 'photo';
const KEY_CAMERAS = 'cameras';
const KEY_LENSES = 'lenses';
const KEY_TAGS = 'tags';
const KEY_FILM_SIMULATIONS = 'film-simulations';
const KEY_FILMS = 'films';
const KEY_RECIPES = 'recipes';
const KEY_FOCAL_LENGTHS = 'focal-lengths';
// Type keys
@ -109,8 +109,8 @@ export const revalidateCamerasKey = () =>
export const revalidateLensesKey = () =>
revalidateTag(KEY_LENSES);
export const revalidateFilmSimulationsKey = () =>
revalidateTag(KEY_FILM_SIMULATIONS);
export const revalidateFilmsKey = () =>
revalidateTag(KEY_FILMS);
export const revalidateFocalLengthsKey = () =>
revalidateTag(KEY_FOCAL_LENGTHS);
@ -120,7 +120,7 @@ export const revalidateAllKeys = () => {
revalidateTagsKey();
revalidateCamerasKey();
revalidateLensesKey();
revalidateFilmSimulationsKey();
revalidateFilmsKey();
revalidateRecipesKey();
revalidateFocalLengthsKey();
};
@ -140,7 +140,7 @@ export const revalidatePhoto = (photoId: string) => {
revalidateTagsKey();
revalidateCamerasKey();
revalidateLensesKey();
revalidateFilmSimulationsKey();
revalidateFilmsKey();
revalidateRecipesKey();
revalidateFocalLengthsKey();
// Paths
@ -151,7 +151,7 @@ export const revalidatePhoto = (photoId: string) => {
revalidatePath(PREFIX_TAG, 'layout');
revalidatePath(PREFIX_CAMERA, 'layout');
revalidatePath(PREFIX_LENS, 'layout');
revalidatePath(PREFIX_FILM_SIMULATION, 'layout');
revalidatePath(PREFIX_FILM, 'layout');
revalidatePath(PREFIX_RECIPE, 'layout');
revalidatePath(PREFIX_FOCAL_LENGTH, 'layout');
revalidatePath(PATH_ADMIN, 'layout');
@ -231,10 +231,10 @@ export const getUniqueLensesCached =
[KEY_PHOTOS, KEY_LENSES],
);
export const getUniqueFilmSimulationsCached =
export const getUniqueFilmsCached =
unstable_cache(
getUniqueFilmSimulations,
[KEY_PHOTOS, KEY_FILM_SIMULATIONS],
getUniqueFilms,
[KEY_PHOTOS, KEY_FILMS],
);
export const getUniqueRecipesCached =

View File

@ -47,7 +47,7 @@ export const getWheresFromOptions = (
tag,
camera,
lens,
film: simulation,
film,
recipe,
focal,
} = options;
@ -108,9 +108,9 @@ export const getWheresFromOptions = (
wheres.push(`$${valuesIndex++}=ANY(tags)`);
wheresValues.push(tag);
}
if (simulation) {
wheres.push(`film_simulation=$${valuesIndex++}`);
wheresValues.push(simulation);
if (film) {
wheres.push(`film=$${valuesIndex++}`);
wheresValues.push(film);
}
if (recipe) {
wheres.push(`recipe_title=$${valuesIndex++}`);

View File

@ -50,6 +50,27 @@ export const MIGRATIONS: Migration[] = [{
ALTER TABLE photos
ADD COLUMN IF NOT EXISTS recipe_title VARCHAR(255)
`,
}, {
label: '05: Universal Film',
fields: ['film'],
run: () => sql`
DO $$
BEGIN
IF EXISTS(
SELECT 1
FROM information_schema.columns
WHERE table_name='photos'
AND column_name='film_simulation'
)
THEN
ALTER TABLE photos
RENAME COLUMN film_simulation TO film;
ELSE
ALTER TABLE photos
ADD COLUMN IF NOT EXISTS film VARCHAR(255);
END IF;
END $$;
`,
}];
export const migrationForError = (e: any) =>

View File

@ -13,7 +13,7 @@ import {
} from '@/photo';
import { Cameras, createCameraKey } from '@/camera';
import { Tags } from '@/tag';
import { FilmSimulation, FilmSimulations } from '@/film';
import { FilmSimulation, Films } from '@/film';
import { ADMIN_SQL_DEBUG_ENABLED } from '@/app/config';
import {
GetPhotosOptions,
@ -53,7 +53,7 @@ const createPhotosTable = () =>
location_name VARCHAR(255),
latitude DOUBLE PRECISION,
longitude DOUBLE PRECISION,
film_simulation VARCHAR(255),
film VARCHAR(255),
recipe_title VARCHAR(255),
recipe_data JSONB,
priority_order REAL,
@ -160,7 +160,7 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
location_name,
latitude,
longitude,
film_simulation,
film,
recipe_title,
recipe_data,
priority_order,
@ -191,7 +191,7 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
${photo.locationName},
${photo.latitude},
${photo.longitude},
${photo.filmSimulation},
${photo.film},
${photo.recipeTitle},
${photo.recipeData},
${photo.priorityOrder},
@ -225,7 +225,7 @@ export const updatePhoto = (photo: PhotoDbInsert) =>
location_name=${photo.locationName},
latitude=${photo.latitude},
longitude=${photo.longitude},
film_simulation=${photo.filmSimulation},
film=${photo.film},
recipe_title=${photo.recipeTitle},
recipe_data=${photo.recipeData},
priority_order=${photo.priorityOrder || null},
@ -367,14 +367,14 @@ export const getUniqueRecipes = async () =>
export const getRecipeTitleForData = async (
data: string | object,
simulation: FilmSimulation,
film: FilmSimulation,
) =>
// Includes legacy check on pre-stringified JSON
safelyQueryPhotos(() => sql`
SELECT recipe_title FROM photos
WHERE hidden IS NOT TRUE
AND recipe_data=${typeof data === 'string' ? data : JSON.stringify(data)}
AND film_simulation=${simulation}
AND film=${film}
LIMIT 1
`
.then(({ rows }) => rows[0]?.recipe_title as string | undefined)
@ -382,7 +382,7 @@ export const getRecipeTitleForData = async (
export const getPhotosNeedingRecipeTitleCount = async (
data: string,
simulation: FilmSimulation,
film: FilmSimulation,
photoIdToExclude?: string,
) =>
safelyQueryPhotos(() => sql`
@ -390,7 +390,7 @@ export const getPhotosNeedingRecipeTitleCount = async (
FROM photos
WHERE recipe_title IS NULL
AND recipe_data=${data}
AND film_simulation=${simulation}
AND film=${film}
AND id <> ${photoIdToExclude}
`.then(({ rows }) => parseInt(rows[0].count, 10))
, 'getPhotosNeedingRecipeTitleCount');
@ -398,29 +398,29 @@ export const getPhotosNeedingRecipeTitleCount = async (
export const updateAllMatchingRecipeTitles = (
title: string,
data: string,
simulation: FilmSimulation,
film: FilmSimulation,
) =>
safelyQueryPhotos(() => sql`
UPDATE photos
SET recipe_title=${title}
WHERE recipe_title IS NULL
AND recipe_data=${data}
AND film_simulation=${simulation}
AND film=${film}
`, 'updateAllMatchingRecipeTitles');
export const getUniqueFilmSimulations = async () =>
export const getUniqueFilms = async () =>
safelyQueryPhotos(() => sql`
SELECT DISTINCT film_simulation, COUNT(*)
SELECT DISTINCT film, COUNT(*)
FROM photos
WHERE hidden IS NOT TRUE AND film_simulation IS NOT NULL
GROUP BY film_simulation
ORDER BY film_simulation ASC
`.then(({ rows }): FilmSimulations => rows
.map(({ film_simulation, count }) => ({
film: film_simulation as FilmSimulation,
WHERE hidden IS NOT TRUE AND film IS NOT NULL
GROUP BY film
ORDER BY film ASC
`.then(({ rows }): Films => rows
.map(({ film, count }) => ({
film: film as FilmSimulation,
count: parseInt(count, 10),
})))
, 'getUniqueFilmSimulations');
, 'getUniqueFilms');
export const getUniqueFocalLengths = async () =>
safelyQueryPhotos(() => sql`

View File

@ -8,7 +8,7 @@ export default function ApplyRecipeTitleGloballyCheckbox({
recipeTitle,
hasRecipeTitleChanged,
recipeData,
simulation,
film,
onMatchResults,
...props
}: ComponentProps<typeof FieldSetWithStatus> & {
@ -16,7 +16,7 @@ export default function ApplyRecipeTitleGloballyCheckbox({
recipeTitle?: string
hasRecipeTitleChanged?: boolean
recipeData?: string
simulation?: FilmSimulation
film?: FilmSimulation
onMatchResults: (didFindMatchingPhotos: boolean) => void
}) {
const [matchingPhotosCount, setMatchingPhotosCount] = useState<number>();
@ -24,14 +24,14 @@ export default function ApplyRecipeTitleGloballyCheckbox({
const loading = matchingPhotosCount === undefined;
useEffect(() => {
if (recipeTitle && hasRecipeTitleChanged && recipeData && simulation) {
if (recipeTitle && hasRecipeTitleChanged && recipeData && film) {
setMatchingPhotosCount(undefined);
getPhotosNeedingRecipeTitleCountAction(recipeData, simulation, photoId)
getPhotosNeedingRecipeTitleCountAction(recipeData, film, photoId)
.then(setMatchingPhotosCount);
} else {
setMatchingPhotosCount(0);
}
}, [recipeTitle, hasRecipeTitleChanged, recipeData, simulation, photoId]);
}, [recipeTitle, hasRecipeTitleChanged, recipeData, film, photoId]);
useEffect(() => {
onMatchResults((matchingPhotosCount ?? 0) > 0);

View File

@ -414,7 +414,7 @@ export default function PhotoForm({
hasRecipeTitleChanged={
changedFormKeys.includes('recipeTitle')}
recipeData={formData.recipeData}
simulation={formData.filmSimulation as FilmSimulation}
film={formData.film as FilmSimulation}
onMatchResults={onMatchResults}
{...fieldProps}
/>;

View File

@ -119,8 +119,8 @@ const FORM_METADATA = (
aspectRatio: { label: 'aspect ratio', readOnly: true },
make: { label: 'camera make' },
model: { label: 'camera model' },
filmSimulation: {
label: 'fujifilm simulation',
film: {
label: 'film',
selectOptions: FILM_SIMULATION_FORM_INPUT_OPTIONS,
selectOptionsDefaultLabel: 'Unknown',
shouldHide: ({ make }) => make !== MAKE_FUJIFILM,
@ -274,7 +274,7 @@ export const convertPhotoToFormData = (photo: Photo): PhotoFormData => {
export const convertExifToFormData = (
data: ExifData,
filmSimulation?: FilmSimulation,
film?: FilmSimulation,
recipeData?: FujifilmRecipe,
): Omit<
Record<keyof PhotoExif, string | undefined>,
@ -298,7 +298,7 @@ export const convertExifToFormData = (
!GEO_PRIVACY_ENABLED ? data.tags?.GPSLatitude?.toString() : undefined,
longitude:
!GEO_PRIVACY_ENABLED ? data.tags?.GPSLongitude?.toString() : undefined,
filmSimulation,
film,
recipeData: JSON.stringify(recipeData),
...data.tags?.DateTimeOriginal && {
takenAt: convertTimestampWithOffsetToPostgresString(
@ -345,7 +345,7 @@ export const convertFormDataToPhotoDbInsert = (
return {
...(photoForm as PhotoFormData & {
filmSimulation?: FilmSimulation
film?: FilmSimulation
recipeData?: FujifilmRecipe
}),
...!photoForm.id && { id: generateNanoid() },

View File

@ -1,11 +1,11 @@
import { formatFocalLength } from '@/focal';
import { getNextImageUrlForRequest } from '@/platforms/next-image';
import { FilmSimulation, photoHasFilmSimulationData } from '@/film';
import { FilmSimulation, photoHasFilmData } from '@/film';
import {
HIGH_DENSITY_GRID,
IS_PREVIEW,
SHOW_EXIF_DATA,
SHOW_FILM_SIMULATIONS,
SHOW_FILMS,
SHOW_LENSES,
SHOW_RECIPES,
} from '@/app/config';
@ -65,7 +65,7 @@ export interface PhotoExif {
exposureCompensation?: number
latitude?: number
longitude?: number
filmSimulation?: FilmSimulation
film?: FilmSimulation
recipeData?: string
takenAt?: string
takenAtNaive?: string
@ -329,10 +329,10 @@ export const shouldShowRecipeDataForPhoto = (photo: Photo) =>
SHOW_RECIPES &&
photoHasRecipeData(photo);
export const shouldShowFilmSimulationDataForPhoto = (photo: Photo) =>
export const shouldShowFilmDataForPhoto = (photo: Photo) =>
SHOW_EXIF_DATA &&
SHOW_FILM_SIMULATIONS &&
photoHasFilmSimulationData(photo);
SHOW_FILMS &&
photoHasFilmData(photo);
export const shouldShowExifDataForPhoto = (photo: Photo) =>
SHOW_EXIF_DATA && photoHasExifData(photo);

View File

@ -58,7 +58,7 @@ export const extractImageDataFromBlobPath = async (
const extension = getExtensionFromStorageUrl(url);
let exifData: ExifData | undefined;
let filmSimulation: FilmSimulation | undefined;
let film: FilmSimulation | undefined;
let recipe: FujifilmRecipe | undefined;
let blurData: string | undefined;
let imageResizedBase64: string | undefined;
@ -89,7 +89,7 @@ export const extractImageDataFromBlobPath = async (
const exifDataBinary = parser.parse();
const makerNote = exifDataBinary.tags?.MakerNote;
if (Buffer.isBuffer(makerNote)) {
filmSimulation = getFujifilmSimulationFromMakerNote(makerNote);
film = getFujifilmSimulationFromMakerNote(makerNote);
recipe = getFujifilmRecipeFromMakerNote(makerNote);
}
}
@ -124,7 +124,7 @@ export const extractImageDataFromBlobPath = async (
url,
},
...generateBlurData && { blurData },
...convertExifToFormData(exifData, filmSimulation, recipe),
...convertExifToFormData(exifData, film, recipe),
},
},
imageResizedBase64,
@ -206,10 +206,10 @@ export const convertFormDataToPhotoDbInsertAndLookupRecipeTitle =
Promise<ReturnType<typeof convertFormDataToPhotoDbInsert>> => {
const photo = convertFormDataToPhotoDbInsert(...args);
if (photo.recipeData && !photo.recipeTitle && photo.filmSimulation) {
if (photo.recipeData && !photo.recipeTitle && photo.film) {
const recipeTitle = await getRecipeTitleForData(
photo.recipeData,
photo.filmSimulation,
photo.film,
);
if (recipeTitle) {
photo.recipeTitle = recipeTitle;
@ -229,12 +229,12 @@ export const propagateRecipeTitleIfNecessary = async (
formData.get('recipeTitle') &&
photo.recipeTitle &&
photo.recipeData &&
photo.filmSimulation
photo.film
) {
await updateAllMatchingRecipeTitles(
photo.recipeTitle,
photo.recipeData,
photo.filmSimulation,
photo.film,
);
}
};

View File

@ -222,8 +222,8 @@ const ALL_POSSIBLE_FILM_SIMULATION_LABELS = Object
large.toLocaleLowerCase(),
]);
export const isStringFilmSimulation = (simulation: string) =>
ALL_POSSIBLE_FILM_SIMULATION_LABELS.includes(simulation.toLocaleLowerCase());
export const isStringFilmSimulation = (film: string) =>
ALL_POSSIBLE_FILM_SIMULATION_LABELS.includes(film.toLocaleLowerCase());
export const labelForFilm = (film: FujifilmSimulation) =>
FILM_SIMULATION_LABELS[film];

View File

@ -1,143 +0,0 @@
import { FujifilmRecipe } from '@/platforms/fujifilm/recipe';
import { FilmSimulation } from '@/film';
import PhotoFilm from '@/film/PhotoFilm';
import clsx from 'clsx/lite';
import { ReactNode, RefObject } from 'react';
import { addSign, formatWhiteBalance } from '.';
export default function PhotoRecipeOGTile({
recipe,
simulation,
iso,
exposure,
}: {
ref?: RefObject<HTMLDivElement | null>
recipe: FujifilmRecipe
simulation: FilmSimulation
iso?: string
exposure?: string
}) {
const {
dynamicRange,
whiteBalance,
highISONoiseReduction,
noiseReductionBasic,
highlight,
shadow,
color,
sharpness,
clarity,
colorChromeEffect,
colorChromeFXBlue,
grainEffect,
bwAdjustment,
bwMagentaGreen,
} = recipe;
const whiteBalanceTypeFormatted = formatWhiteBalance(recipe);
const renderRow = (children: ReactNode, className?: string) =>
<div className={clsx(
'flex gap-2 *:w-full *:grow',
className,
)}>
{children}
</div>;
const renderDataSquare = (
value: ReactNode,
label?: string,
className?: string,
) => (
<div className={clsx(
'flex flex-col items-center justify-center gap-0.5 rounded-md min-w-0',
'rounded-md border',
'border-transparent',
'bg-neutral-100/60',
label && 'p-1',
className,
)}>
<div className="flex truncate max-w-full tracking-wide text-lg">
{typeof value === 'number' ? addSign(value) : value}
</div>
{label && <div className={clsx(
'text-[11px] leading-none tracking-wide font-medium text-black/50',
'uppercase',
)}>
{label}
</div>}
</div>
);
return (
<div
className={clsx(
'flex z-10',
'w-[37rem] p-10 aspect-video',
'text-[13.5px] text-black',
'bg-white/50',
'backdrop-blur-xl saturate-[300%]',
)}
>
<div className="flex flex-col gap-2 w-full">
{renderRow(<>
<div className={clsx(
'flex',
'text-lg leading-none text-black truncate',
)}>
KODAK PORTRA 500
</div>
<PhotoFilm
contrast="frosted"
simulation={simulation}
className="w-auto! grow-0!"
/>
</>, 'flex items-center gap-4')}
{renderRow(<>
{renderDataSquare(`DR${dynamicRange.development}`)}
{renderDataSquare(iso)}
{renderDataSquare(exposure ?? '0ev')}
</>)}
{renderRow(<>
{renderDataSquare(
whiteBalanceTypeFormatted.toUpperCase(),
`R${addSign(whiteBalance?.red)} / B${addSign(whiteBalance?.blue)}`,
)}
{renderDataSquare(
highISONoiseReduction ?? noiseReductionBasic ?? 'OFF',
'ISO NR',
)}
{renderDataSquare(highlight, 'Highlight')}
{renderDataSquare(shadow, 'Shadow')}
</>)}
{renderRow(<>
{renderDataSquare(color, 'Color')}
{renderDataSquare(sharpness, 'Sharpness')}
{renderDataSquare(clarity, 'Clarity')}
</>)}
{renderRow(<>
{renderDataSquare(
colorChromeEffect?.toLocaleUpperCase() ?? 'N/A',
'Color Chrome',
)}
{renderDataSquare(
colorChromeFXBlue?.toLocaleUpperCase() ?? 'N/A',
'FX Blue',
)}
</>)}
{renderRow(<>
{renderDataSquare(
grainEffect.roughness.toLocaleUpperCase(),
grainEffect.size === 'large'
? 'Large Grain'
: grainEffect.size === 'small'
? 'Small Grain'
: 'Grain',
)}
{renderDataSquare(bwAdjustment ?? 0, 'BW ADJ')}
{renderDataSquare(bwMagentaGreen ?? 0, 'BW M/G')}
</>)}
</div>
</div>
);
}

View File

@ -25,7 +25,7 @@ export default function PhotoRecipeOverlay({
ref,
title,
recipe,
simulation,
film,
onClose,
}: RecipeProps & {
ref?: RefObject<HTMLDivElement | null>
@ -122,7 +122,7 @@ export default function PhotoRecipeOverlay({
label={`${title
? `${formatRecipe(title).toLocaleUpperCase()} recipe`
: 'Recipe'}`}
text={generateRecipeText({ title, recipe, simulation }).join('\n')}
text={generateRecipeText({ title, recipe, film }).join('\n')}
iconSize={17}
className={clsx(
'translate-y-[0.5px]',
@ -147,10 +147,10 @@ export default function PhotoRecipeOverlay({
<div className="col-span-8">
{renderDataSquare(
<div className="flex items-center gap-1.5">
{labelForFilm(simulation).medium.toLocaleUpperCase()}
{labelForFilm(film).medium.toLocaleUpperCase()}
<PhotoFilm
contrast="frosted"
simulation={simulation}
film={film}
type="icon-only"
className="opacity-80 translate-y-[-0.5px]"
/>

View File

@ -32,11 +32,11 @@ export default function RecipeHeader({
contrast="high"
recipeOnClick={() => (
photo?.recipeData &&
photo?.filmSimulation
photo?.film
) ? setRecipeModalProps?.({
title: photo.recipeTitle,
recipe: photo.recipeData,
simulation: photo.filmSimulation,
film: photo.film,
iso: photo.isoFormatted,
exposure: photo.exposureTimeFormatted,
})

View File

@ -20,7 +20,7 @@ export type Recipes = RecipeWithCount[]
export interface RecipeProps {
title?: string
recipe: FujifilmRecipe
simulation: FilmSimulation
film: FilmSimulation
iso?: string
exposure?: string
}
@ -57,12 +57,12 @@ export const descriptionForRecipePhotos = (
export const generateRecipeText = ({
title,
recipe,
simulation,
film,
}: RecipeProps,
abbreviate?: boolean,
) => {
const lines = [
`${labelForFilm(simulation).small.toLocaleUpperCase()}`,
`${labelForFilm(film).small.toLocaleUpperCase()}`,
// eslint-disable-next-line max-len
`${formatWhiteBalance(recipe).toLocaleUpperCase()} ${formatWhiteBalanceColor(recipe)}`,
];
@ -133,7 +133,7 @@ export const generateMetaForRecipe = (
});
const photoHasRecipe = (photo?: Photo) =>
photo?.filmSimulation && photo?.recipeData;
photo?.film && photo?.recipeData;
export const getPhotoWithRecipeFromPhotos = (
photos: Photo[],

View File

@ -20,7 +20,7 @@ export default function ShareModals() {
camera,
lens,
tag,
film: simulation,
film,
recipe,
focal,
} = shareModalProps;
@ -30,7 +30,7 @@ export default function ShareModals() {
photo,
tag,
camera,
film: simulation,
film,
recipe,
focal,
}} />;
@ -42,8 +42,8 @@ export default function ShareModals() {
return <CameraShareModal {...{ camera, ...attributes }} />;
} else if (lens) {
return <LensShareModal {...{ lens, ...attributes }} />;
} else if (simulation) {
return <FilmShareModal {...{ simulation, ...attributes }} />;
} else if (film) {
return <FilmShareModal {...{ film, ...attributes }} />;
} else if (recipe) {
return <RecipeShareModal {...{ recipe, ...attributes }} />;
} else if (focal !== undefined) {

View File

@ -2,7 +2,7 @@ import { Photo } from '@/photo';
import { PhotoSetAttributes, PhotoSetCategory } from '@/category';
import {
absolutePathForCameraImage,
absolutePathForFilmSimulationImage,
absolutePathForFilmImage,
absolutePathForFocalLengthImage,
absolutePathForLensImage,
absolutePathForPhotoImage,
@ -21,7 +21,7 @@ export const getSharePathFromShareModalProps = ({
lens,
tag,
recipe,
film: simulation,
film,
focal,
}: ShareModalProps) => {
if (photo) {
@ -34,8 +34,8 @@ export const getSharePathFromShareModalProps = ({
return absolutePathForTagImage(tag);
} else if (recipe) {
return absolutePathForRecipeImage(recipe);
} else if (simulation) {
return absolutePathForFilmSimulationImage(simulation);
} else if (film) {
return absolutePathForFilmImage(film);
} else if (focal) {
return absolutePathForFocalLengthImage(focal);
}