Standardize film types/arguments
This commit is contained in:
parent
4441ab2b72
commit
0dfc12d06e
@ -1,11 +1,10 @@
|
||||
/* eslint-disable max-len */
|
||||
import {
|
||||
getEscapePath,
|
||||
getPathComponents,
|
||||
isPathCamera,
|
||||
isPathCameraPhoto,
|
||||
isPathFilmSimulation,
|
||||
isPathFilmSimulationPhoto,
|
||||
isPathFilm,
|
||||
isPathFilmPhoto,
|
||||
isPathFocalLength,
|
||||
isPathFocalLengthPhoto,
|
||||
isPathPhoto,
|
||||
@ -20,34 +19,34 @@ 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`;
|
||||
|
||||
const PATH_ROOT = '/';
|
||||
const PATH_GRID = '/grid';
|
||||
const PATH_FEED = '/feed';
|
||||
const PATH_ADMIN = '/admin/photos';
|
||||
const PATH_OG = '/og';
|
||||
const PATH_OG_ALL = `${PATH_OG}/all`;
|
||||
const PATH_OG_SAMPLE = `${PATH_OG}/sample`;
|
||||
const PATH_ROOT = '/';
|
||||
const PATH_GRID = '/grid';
|
||||
const PATH_FEED = '/feed';
|
||||
const PATH_ADMIN = '/admin/photos';
|
||||
const PATH_OG = '/og';
|
||||
const PATH_OG_ALL = `${PATH_OG}/all`;
|
||||
const PATH_OG_SAMPLE = `${PATH_OG}/sample`;
|
||||
|
||||
const PATH_PHOTO = `/p/${PHOTO_ID}`;
|
||||
const PATH_PHOTO = `/p/${PHOTO_ID}`;
|
||||
|
||||
const PATH_TAG = `/tag/${TAG}`;
|
||||
const PATH_TAG_PHOTO = `${PATH_TAG}/${PHOTO_ID}`;
|
||||
const PATH_TAG = `/tag/${TAG}`;
|
||||
const PATH_TAG_PHOTO = `${PATH_TAG}/${PHOTO_ID}`;
|
||||
|
||||
const PATH_TAG_HIDDEN = `/tag/${TAG_HIDDEN}`;
|
||||
const PATH_TAG_HIDDEN_PHOTO = `${PATH_TAG_HIDDEN}/${PHOTO_ID}`;
|
||||
const PATH_TAG_HIDDEN = `/tag/${TAG_HIDDEN}`;
|
||||
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_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}`;
|
||||
const PATH_FOCAL_LENGTH = `/focal/${FOCAL_LENGTH_STRING}`;
|
||||
const PATH_FOCAL_LENGTH_PHOTO = `${PATH_FOCAL_LENGTH}/${PHOTO_ID}`;
|
||||
|
||||
describe('Paths', () => {
|
||||
it('can be protected', () => {
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@ -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,
|
||||
]);
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>)}
|
||||
|
||||
@ -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 } =
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
}}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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,
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
}}
|
||||
|
||||
@ -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':
|
||||
|
||||
@ -53,7 +53,7 @@ export interface PhotoStats {
|
||||
lensesCount: number
|
||||
tagsCount: number
|
||||
recipesCount: number
|
||||
filmSimulationsCount: number
|
||||
filmsCount: number
|
||||
focalLengthsCount: number
|
||||
dateRange?: PhotoDateRange
|
||||
}
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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,
|
||||
]);
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -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 }),
|
||||
]);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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)`}}
|
||||
/>
|
||||
|
||||
@ -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 } =
|
||||
|
||||
@ -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
|
||||
/>}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
),
|
||||
);
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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++}`);
|
||||
|
||||
@ -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) =>
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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}
|
||||
/>;
|
||||
|
||||
@ -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() },
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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]"
|
||||
/>
|
||||
|
||||
@ -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,
|
||||
})
|
||||
|
||||
@ -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[],
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user