Account for lenses without makes
This commit is contained in:
parent
7e207ed95b
commit
500b9b4561
@ -16,24 +16,29 @@ import {
|
||||
getPhotosNearIdCached,
|
||||
} from '@/photo/cache';
|
||||
import { cache } from 'react';
|
||||
import { getLensFromParams, lensFromPhoto, PhotoLensProps } from '@/lens';
|
||||
import {
|
||||
formatLensParams,
|
||||
getLensPhotoFromParams,
|
||||
lensFromPhoto,
|
||||
LensPhotoProps,
|
||||
} from '@/lens';
|
||||
|
||||
const getPhotosNearIdCachedCached = cache((
|
||||
photoId: string,
|
||||
make: string,
|
||||
make: string | undefined,
|
||||
model: string,
|
||||
) =>
|
||||
getPhotosNearIdCached(
|
||||
photoId, {
|
||||
lens: getLensFromParams({ make, model }),
|
||||
lens: formatLensParams({ make, model }),
|
||||
limit: RELATED_GRID_PHOTOS_TO_SHOW + 2,
|
||||
},
|
||||
));
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: PhotoLensProps): Promise<Metadata> {
|
||||
const { photoId, make, model } = await params;
|
||||
}: LensPhotoProps): Promise<Metadata> {
|
||||
const { photoId, make, model } = await getLensPhotoFromParams(params);
|
||||
|
||||
const { photo } = await getPhotosNearIdCachedCached(photoId, make, model);
|
||||
|
||||
@ -67,8 +72,8 @@ export async function generateMetadata({
|
||||
|
||||
export default async function PhotoLensPage({
|
||||
params,
|
||||
}: PhotoLensProps) {
|
||||
const { photoId, make, model } = await params;
|
||||
}: LensPhotoProps) {
|
||||
const { photoId, make, model } = await getLensPhotoFromParams(params);
|
||||
|
||||
const { photo, photos, photosGrid, indexNumber } =
|
||||
await getPhotosNearIdCachedCached(photoId, make, model);
|
||||
|
||||
@ -31,7 +31,7 @@ export async function GET(
|
||||
_: Request,
|
||||
context: LensProps,
|
||||
) {
|
||||
const lens = getLensFromParams(await context.params);
|
||||
const lens = await getLensFromParams(context.params);
|
||||
|
||||
const [
|
||||
photos,
|
||||
|
||||
@ -7,10 +7,10 @@ import { getUniqueLenses } from '@/photo/db/query';
|
||||
import { generateMetaForLens } from '@/lens/meta';
|
||||
import { getPhotosLensDataCached } from '@/lens/data';
|
||||
import LensOverview from '@/lens/LensOverview';
|
||||
import { LensProps } from '@/lens';
|
||||
import { getLensFromParams, Lens, LensProps } from '@/lens';
|
||||
|
||||
const getPhotosLensDataCachedCached = cache((
|
||||
make: string,
|
||||
make: string | undefined,
|
||||
model: string,
|
||||
) => getPhotosLensDataCached(
|
||||
make,
|
||||
@ -19,7 +19,7 @@ const getPhotosLensDataCachedCached = cache((
|
||||
));
|
||||
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ make: string, model: string }[]>) | undefined = undefined;
|
||||
(() => Promise<Lens[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
@ -31,7 +31,7 @@ if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: LensProps): Promise<Metadata> {
|
||||
const { make, model } = await params;
|
||||
const { make, model } = await getLensFromParams(params);
|
||||
|
||||
const [
|
||||
photos,
|
||||
@ -66,7 +66,7 @@ export async function generateMetadata({
|
||||
export default async function LensPage({
|
||||
params,
|
||||
}: LensProps) {
|
||||
const { make, model } = await params;
|
||||
const { make, model } = await getLensFromParams(params);
|
||||
|
||||
const [
|
||||
photos,
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
import {
|
||||
PhotoCameraProps,
|
||||
cameraFromPhoto,
|
||||
getCameraFromParams,
|
||||
formatCameraParams,
|
||||
} from '@/camera';
|
||||
import { cache } from 'react';
|
||||
|
||||
@ -29,7 +29,7 @@ const getPhotosNearIdCachedCached = cache((
|
||||
) =>
|
||||
getPhotosNearIdCached(
|
||||
photoId, {
|
||||
camera: getCameraFromParams({ make, model }),
|
||||
camera: formatCameraParams({ make, model }),
|
||||
limit: RELATED_GRID_PHOTOS_TO_SHOW + 2,
|
||||
},
|
||||
));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getPhotosCached } from '@/photo/cache';
|
||||
import { Camera, CameraProps, getCameraFromParams } from '@/camera';
|
||||
import { Camera, CameraProps, formatCameraParams } from '@/camera';
|
||||
import {
|
||||
IMAGE_OG_DIMENSION_SMALL,
|
||||
MAX_PHOTOS_TO_SHOW_PER_CATEGORY,
|
||||
@ -31,7 +31,7 @@ export async function GET(
|
||||
_: Request,
|
||||
context: CameraProps,
|
||||
) {
|
||||
const camera = getCameraFromParams(await context.params);
|
||||
const camera = formatCameraParams(await context.params);
|
||||
|
||||
const [
|
||||
photos,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Metadata } from 'next/types';
|
||||
import { CameraProps } from '@/camera';
|
||||
import { Camera, CameraProps } from '@/camera';
|
||||
import { generateMetaForCamera } from '@/camera/meta';
|
||||
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
|
||||
import { getPhotosCameraDataCached } from '@/camera/data';
|
||||
@ -19,7 +19,7 @@ const getPhotosCameraDataCachedCached = cache((
|
||||
));
|
||||
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ make: string, model: string }[]>) | undefined = undefined;
|
||||
(() => Promise<Camera[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
|
||||
@ -22,17 +22,18 @@ export const PATH_FEED_INFERRED = GRID_HOMEPAGE_ENABLED ? PATH_FEED : PATH
|
||||
|
||||
// Path prefixes
|
||||
export const PREFIX_PHOTO = '/p';
|
||||
export const PREFIX_TAG = '/tag';
|
||||
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_FOCAL_LENGTH = '/focal';
|
||||
|
||||
// Dynamic paths
|
||||
const PATH_PHOTO_DYNAMIC = `${PREFIX_PHOTO}/[photoId]`;
|
||||
const PATH_TAG_DYNAMIC = `${PREFIX_TAG}/[tag]`;
|
||||
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_FOCAL_LENGTH_DYNAMIC = `${PREFIX_FOCAL_LENGTH}/[focal]`;
|
||||
@ -61,6 +62,9 @@ export const PATH_API_PRESIGNED_URL = `${PATH_API_STORAGE}/presigned-url`;
|
||||
// Modifiers
|
||||
const EDIT = 'edit';
|
||||
|
||||
// Special characters
|
||||
export const MISSING_FIELD = '-';
|
||||
|
||||
export const PATHS_ADMIN = [
|
||||
PATH_ADMIN,
|
||||
PATH_ADMIN_PHOTOS,
|
||||
@ -79,8 +83,9 @@ export const PATHS_TO_CACHE = [
|
||||
PATH_FEED,
|
||||
PATH_OG,
|
||||
PATH_PHOTO_DYNAMIC,
|
||||
PATH_TAG_DYNAMIC,
|
||||
PATH_CAMERA_DYNAMIC,
|
||||
PATH_LENS_DYNAMIC,
|
||||
PATH_TAG_DYNAMIC,
|
||||
PATH_FILM_SIMULATION_DYNAMIC,
|
||||
PATH_FOCAL_LENGTH_DYNAMIC,
|
||||
PATH_RECIPE_DYNAMIC,
|
||||
@ -119,22 +124,27 @@ export const pathForPhoto = ({
|
||||
simulation,
|
||||
focal,
|
||||
recipe,
|
||||
}: PhotoPathParams) =>
|
||||
typeof photo !== 'string' && photo.hidden
|
||||
? `${pathForTag(TAG_HIDDEN)}/${getPhotoId(photo)}`
|
||||
: tag
|
||||
? `${pathForTag(tag)}/${getPhotoId(photo)}`
|
||||
: camera
|
||||
? `${pathForCamera(camera)}/${getPhotoId(photo)}`
|
||||
: lens
|
||||
? `${pathForLens(lens)}/${getPhotoId(photo)}`
|
||||
: simulation
|
||||
? `${pathForFilmSimulation(simulation)}/${getPhotoId(photo)}`
|
||||
: recipe
|
||||
? `${pathForRecipe(recipe)}/${getPhotoId(photo)}`
|
||||
: focal
|
||||
? `${pathForFocalLength(focal)}/${getPhotoId(photo)}`
|
||||
: `${PREFIX_PHOTO}/${getPhotoId(photo)}`;
|
||||
}: PhotoPathParams) => {
|
||||
let prefix = PREFIX_PHOTO;
|
||||
|
||||
if (typeof photo !== 'string' && photo.hidden) {
|
||||
prefix = pathForTag(TAG_HIDDEN);
|
||||
} else if (camera) {
|
||||
prefix = pathForCamera(camera);
|
||||
} else if (lens) {
|
||||
prefix = pathForLens(lens);
|
||||
} else if (tag) {
|
||||
prefix = pathForTag(tag);
|
||||
} else if (simulation) {
|
||||
prefix = pathForFilmSimulation(simulation);
|
||||
} else if (recipe) {
|
||||
prefix = pathForRecipe(recipe);
|
||||
} else if (focal) {
|
||||
prefix = pathForFocalLength(focal);
|
||||
}
|
||||
|
||||
return `${prefix}/${getPhotoId(photo)}`;
|
||||
};
|
||||
|
||||
export const pathForTag = (tag: string) =>
|
||||
`${PREFIX_TAG}/${tag}`;
|
||||
@ -146,7 +156,9 @@ export const pathForFilmSimulation = (simulation: FilmSimulation) =>
|
||||
`${PREFIX_FILM_SIMULATION}/${simulation}`;
|
||||
|
||||
export const pathForLens = ({ make, model }: Lens) =>
|
||||
`${PREFIX_LENS}/${parameterize(make)}/${parameterize(model)}`;
|
||||
make
|
||||
? `${PREFIX_LENS}/${parameterize(make)}/${parameterize(model)}`
|
||||
: `${PREFIX_LENS}/${MISSING_FIELD}/${parameterize(model)}`;
|
||||
|
||||
export const pathForFocalLength = (focal: number) =>
|
||||
`${PREFIX_FOCAL_LENGTH}/${focal}mm`;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { cameraFromPhoto, getCameraFromParams } from '.';
|
||||
import { cameraFromPhoto, formatCameraParams } from '.';
|
||||
import {
|
||||
getPhotosCached,
|
||||
getPhotosMetaCached,
|
||||
@ -9,7 +9,7 @@ export const getPhotosCameraDataCached = async (
|
||||
model: string,
|
||||
limit: number,
|
||||
) => {
|
||||
const camera = getCameraFromParams({ make, model });
|
||||
const camera = formatCameraParams({ make, model });
|
||||
return Promise.all([
|
||||
getPhotosCached({ camera, limit }),
|
||||
getPhotosMetaCached({ camera }),
|
||||
|
||||
@ -29,7 +29,7 @@ export type Cameras = CameraWithCount[];
|
||||
export const createCameraKey = ({ make, model }: Partial<Camera>) =>
|
||||
parameterize(`${make ?? 'ANY'}-${model ?? 'ANY'}`);
|
||||
|
||||
export const getCameraFromParams = ({
|
||||
export const formatCameraParams = ({
|
||||
make,
|
||||
model,
|
||||
}: {
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { getLensFromParams, lensFromPhoto } from '.';
|
||||
import { formatLensParams, lensFromPhoto } from '.';
|
||||
import {
|
||||
getPhotosCached,
|
||||
getPhotosMetaCached,
|
||||
} from '@/photo/cache';
|
||||
|
||||
export const getPhotosLensDataCached = async (
|
||||
make: string,
|
||||
make: string | undefined,
|
||||
model: string,
|
||||
limit: number,
|
||||
) => {
|
||||
const lens = getLensFromParams({ make, model });
|
||||
const lens = formatLensParams({ make, model });
|
||||
return Promise.all([
|
||||
getPhotosCached({ lens, limit }),
|
||||
getPhotosMetaCached({ lens }),
|
||||
|
||||
@ -1,20 +1,23 @@
|
||||
import { Photo } from '@/photo';
|
||||
import { parameterize } from '@/utility/string';
|
||||
import { formatAppleLensText, isLensMakeApple } from '../platforms/apple';
|
||||
import { MISSING_FIELD } from '@/app/paths';
|
||||
|
||||
const LENS_PLACEHOLDER: Lens = { make: 'Lens', model: 'Model' };
|
||||
|
||||
export type Lens = {
|
||||
make: string
|
||||
make?: string
|
||||
model: string
|
||||
};
|
||||
|
||||
type LensWithPhotoId = Lens & { photoId: string };
|
||||
|
||||
export interface LensProps {
|
||||
params: Promise<Lens>
|
||||
}
|
||||
|
||||
export interface PhotoLensProps {
|
||||
params: Promise<Lens & { photoId: string }>
|
||||
export interface LensPhotoProps {
|
||||
params: Promise<LensWithPhotoId>
|
||||
}
|
||||
|
||||
export type LensWithCount = {
|
||||
@ -25,18 +28,36 @@ export type LensWithCount = {
|
||||
|
||||
export type Lenses = LensWithCount[];
|
||||
|
||||
export const getLensFromParams = async (
|
||||
params: Promise<Lens>,
|
||||
): Promise<Lens> => {
|
||||
const { make, model } = await params;
|
||||
return make === MISSING_FIELD
|
||||
? { model }
|
||||
: { make, model };
|
||||
};
|
||||
|
||||
export const getLensPhotoFromParams = async (
|
||||
params: Promise<LensWithPhotoId>,
|
||||
): Promise<LensWithPhotoId> => {
|
||||
const { make, model, photoId } = await params;
|
||||
return make === MISSING_FIELD
|
||||
? { model, photoId }
|
||||
: { make, model, photoId };
|
||||
};
|
||||
|
||||
// Support keys for make-only and model-only lens queries
|
||||
export const createLensKey = ({ make, model }: Partial<Lens>) =>
|
||||
parameterize(`${make ?? 'ANY'}-${model ?? 'ANY'}`);
|
||||
|
||||
export const getLensFromParams = ({
|
||||
export const formatLensParams = ({
|
||||
make,
|
||||
model,
|
||||
}: {
|
||||
make: string,
|
||||
make?: string,
|
||||
model: string,
|
||||
}): Lens => ({
|
||||
make: parameterize(make),
|
||||
make: make ? parameterize(make) : undefined,
|
||||
model: parameterize(model),
|
||||
});
|
||||
|
||||
@ -53,7 +74,7 @@ export const lensFromPhoto = (
|
||||
photo: Photo | undefined,
|
||||
fallback?: Lens,
|
||||
): Lens =>
|
||||
photo?.lensMake && photo?.lensModel
|
||||
photo?.lensModel
|
||||
? { make: photo.lensMake, model: photo.lensModel }
|
||||
: fallback ?? LENS_PLACEHOLDER;
|
||||
|
||||
@ -66,7 +87,7 @@ export const formatLensText = (
|
||||
= 'medium',
|
||||
) => {
|
||||
// Capture simple make without modifiers like 'Corporation' or 'Company'
|
||||
const makeSimple = make.match(/^(\S+)/)?.[1];
|
||||
const makeSimple = make?.match(/^(\S+)/)?.[1];
|
||||
const doesModelStartWithMake = (
|
||||
makeSimple &&
|
||||
modelRaw.toLocaleLowerCase().startsWith(makeSimple.toLocaleLowerCase())
|
||||
@ -78,7 +99,7 @@ export const formatLensText = (
|
||||
|
||||
switch (length) {
|
||||
case 'long':
|
||||
return `${make} ${model}`;
|
||||
return make ? `${make} ${model}` : model;
|
||||
case 'medium':
|
||||
return doesModelStartWithMake
|
||||
? model.replace(makeSimple, '').trim()
|
||||
|
||||
@ -14,12 +14,6 @@ import { GRID_GAP_CLASSNAME } from '@/components';
|
||||
export default function PhotoGrid({
|
||||
photos,
|
||||
selectedPhoto,
|
||||
tag,
|
||||
camera,
|
||||
lens,
|
||||
simulation,
|
||||
focal,
|
||||
recipe,
|
||||
photoPriority,
|
||||
fast,
|
||||
animate = true,
|
||||
@ -31,6 +25,7 @@ export default function PhotoGrid({
|
||||
canSelect,
|
||||
onLastPhotoVisible,
|
||||
onAnimationComplete,
|
||||
...categories
|
||||
}: {
|
||||
photos: Photo[]
|
||||
selectedPhoto?: Photo
|
||||
@ -95,12 +90,7 @@ export default function PhotoGrid({
|
||||
)}
|
||||
{...{
|
||||
photo,
|
||||
tag,
|
||||
camera,
|
||||
lens,
|
||||
simulation,
|
||||
focal,
|
||||
recipe,
|
||||
...categories,
|
||||
selected: photo.id === selectedPhoto?.id,
|
||||
priority: photoPriority,
|
||||
onVisible: index === photos.length - 1
|
||||
|
||||
@ -12,16 +12,11 @@ export default function PhotoGridContainer({
|
||||
cacheKey,
|
||||
photos,
|
||||
count,
|
||||
tag,
|
||||
camera,
|
||||
lens,
|
||||
simulation,
|
||||
focal,
|
||||
recipe,
|
||||
animateOnFirstLoadOnly,
|
||||
header,
|
||||
sidebar,
|
||||
canSelect,
|
||||
...categories
|
||||
}: {
|
||||
cacheKey: string
|
||||
count: number
|
||||
@ -50,12 +45,7 @@ export default function PhotoGridContainer({
|
||||
<div className={GRID_SPACE_CLASSNAME}>
|
||||
<PhotoGrid {...{
|
||||
photos,
|
||||
tag,
|
||||
camera,
|
||||
lens,
|
||||
simulation,
|
||||
focal,
|
||||
recipe,
|
||||
...categories,
|
||||
animateOnFirstLoadOnly,
|
||||
onAnimationComplete,
|
||||
canSelect,
|
||||
@ -64,13 +54,8 @@ export default function PhotoGridContainer({
|
||||
<PhotoGridInfinite {...{
|
||||
cacheKey,
|
||||
initialOffset: photos.length,
|
||||
...categories,
|
||||
canStart: shouldAnimateDynamicItems,
|
||||
tag,
|
||||
camera,
|
||||
lens,
|
||||
simulation,
|
||||
focal,
|
||||
recipe,
|
||||
animateOnFirstLoadOnly,
|
||||
canSelect,
|
||||
}} />}
|
||||
|
||||
@ -9,12 +9,9 @@ export default function PhotoGridInfinite({
|
||||
cacheKey,
|
||||
initialOffset,
|
||||
canStart,
|
||||
tag,
|
||||
camera,
|
||||
simulation,
|
||||
focal,
|
||||
animateOnFirstLoadOnly,
|
||||
canSelect,
|
||||
...categories
|
||||
}: {
|
||||
cacheKey: string
|
||||
initialOffset: number
|
||||
@ -24,18 +21,13 @@ export default function PhotoGridInfinite({
|
||||
cacheKey={cacheKey}
|
||||
initialOffset={initialOffset}
|
||||
itemsPerPage={INFINITE_SCROLL_GRID_MULTIPLE}
|
||||
tag={tag}
|
||||
camera={camera}
|
||||
simulation={simulation}
|
||||
{...categories}
|
||||
>
|
||||
{({ photos, onLastPhotoVisible }) =>
|
||||
<PhotoGrid {...{
|
||||
photos,
|
||||
...categories,
|
||||
canStart,
|
||||
tag,
|
||||
camera,
|
||||
simulation,
|
||||
focal,
|
||||
onLastPhotoVisible,
|
||||
animateOnFirstLoadOnly,
|
||||
canSelect,
|
||||
|
||||
@ -100,6 +100,8 @@ export const getWheresFromOptions = (
|
||||
}
|
||||
if (lens?.model) {
|
||||
wheres.push(`${parameterizeForDb('lens_model')}=$${valuesIndex++}`);
|
||||
// Ensure unique queries for lenses missing makes
|
||||
if (!lens.make) { wheres.push('lens_make IS NULL'); }
|
||||
wheresValues.push(parameterize(lens.model));
|
||||
}
|
||||
if (tag) {
|
||||
|
||||
@ -340,7 +340,6 @@ export const getUniqueLenses = async () =>
|
||||
lens_make, lens_model, COUNT(*)
|
||||
FROM photos
|
||||
WHERE hidden IS NOT TRUE
|
||||
AND trim(lens_make) <> ''
|
||||
AND trim(lens_model) <> ''
|
||||
GROUP BY lens_make, lens_model
|
||||
ORDER BY lens ASC
|
||||
|
||||
@ -301,7 +301,6 @@ const photoHasCameraData = (photo: Photo) =>
|
||||
Boolean(photo.model);
|
||||
|
||||
const photoHasLensData = (photo: Photo) =>
|
||||
Boolean(photo.lensMake) &&
|
||||
Boolean(photo.lensModel);
|
||||
|
||||
const photoHasRecipeData = (photo: Photo) =>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user