From 32c6260a3b0a26905bf563637979e6e089856819 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 18 Dec 2023 00:28:46 -0600 Subject: [PATCH 1/6] Rebuild photo query engine, preferring priority order --- README.md | 1 + src/cache/index.ts | 1 + src/services/vercel-postgres.ts | 190 +++++++++++-------------------- src/site/SiteChecklistClient.tsx | 11 ++ src/site/config.ts | 3 + 5 files changed, 80 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 64007c83..86a59c1f 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Installation - `NEXT_PUBLIC_PRO_MODE = 1` enables higher quality image storage - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data +- `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order - `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api` - `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo - `NEXT_PUBLIC_HIDE_FILM_SIMULATIONS = 1` prevents Fujifilm simulations showing up in `/grid` sidebar diff --git a/src/cache/index.ts b/src/cache/index.ts index 0bdd40eb..7673c577 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -48,6 +48,7 @@ const getPhotosCacheKeyForOption = ( // Primitive keys case 'sortBy': case 'limit': + case 'offset': case 'tag': case 'simulation': case 'includeHidden': { diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 80fd6f81..7ab19c6f 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -1,4 +1,4 @@ -import { sql } from '@vercel/postgres'; +import { db, sql } from '@vercel/postgres'; import { PhotoDb, PhotoDbInsert, @@ -11,6 +11,7 @@ import { Camera, Cameras, createCameraKey } from '@/camera'; import { parameterize } from '@/utility/string'; import { Tags } from '@/tag'; import { FilmSimulation, FilmSimulations } from '@/simulation'; +import { PRIORITY_ORDER_ENABLED } from '@/site/config'; const PHOTO_DEFAULT_LIMIT = 100; @@ -151,109 +152,6 @@ export const sqlRenamePhotoTagGlobally = (tag: string, updatedTag: string) => export const sqlDeletePhoto = (id: string) => sql`DELETE FROM photos WHERE id=${id}`; -const sqlGetPhotos = ( - limit = PHOTO_DEFAULT_LIMIT, - offset = 0, -) => - sql` - SELECT * FROM photos - WHERE hidden IS NOT TRUE - ORDER BY taken_at DESC - LIMIT ${limit} OFFSET ${offset} - `; - -const sqlGetPhotosIncludingHidden = ( - limit = PHOTO_DEFAULT_LIMIT, - offset = 0, -) => - sql` - SELECT * FROM photos - ORDER BY created_at DESC - LIMIT ${limit} OFFSET ${offset} - `; - -const sqlGetPhotosSortedByCreatedAt = ( - limit = PHOTO_DEFAULT_LIMIT, - offset = 0, -) => - sql` - SELECT * FROM photos - WHERE hidden IS NOT TRUE - ORDER BY created_at DESC - LIMIT ${limit} OFFSET ${offset} - `; - -const sqlGetPhotosSortedByPriority = ( - limit = PHOTO_DEFAULT_LIMIT, - offset = 0, -) => - sql` - SELECT * FROM photos - WHERE hidden IS NOT TRUE - ORDER BY priority_order ASC, taken_at DESC - LIMIT ${limit} OFFSET ${offset} - `; - -const sqlGetPhotosByTag = ( - limit = PHOTO_DEFAULT_LIMIT, - tag: string, -) => - sql` - SELECT * FROM photos - WHERE ${tag}=ANY(tags) - AND hidden IS NOT TRUE - ORDER BY taken_at DESC - LIMIT ${limit} - `; - -const sqlGetPhotosByCamera = async ( - limit = PHOTO_DEFAULT_LIMIT, - make: string, - model: string, -) => sql` - SELECT * FROM photos - WHERE - LOWER(make)=${parameterize(make)} AND - LOWER(REPLACE(model, ' ', '-'))=${parameterize(model)} - ORDER BY taken_at DESC - LIMIT ${limit} -`; - -const sqlGetPhotosBySimulation = async ( - limit = PHOTO_DEFAULT_LIMIT, - simulation: FilmSimulation, -) => sql` - SELECT * FROM photos - WHERE film_simulation=${simulation} - AND hidden IS NOT TRUE - ORDER BY taken_at DESC - LIMIT ${limit} -`; - -const sqlGetPhotosTakenAfterDateInclusive = ( - takenAt: Date, - limit?: number, -) => - sql` - SELECT * FROM photos - WHERE taken_at <= ${takenAt.toISOString()} - AND hidden IS NOT TRUE - ORDER BY taken_at DESC - LIMIT ${limit} - `; - -const sqlGetPhotosTakenBeforeDate = ( - takenAt: Date, - limit?: number, -) => - sql` - SELECT * FROM photos - WHERE taken_at > ${takenAt.toISOString()} - AND hidden IS NOT TRUE - ORDER BY taken_at ASC - LIMIT ${limit} - `; - const sqlGetPhoto = (id: string) => sql`SELECT * FROM photos WHERE id=${id} LIMIT 1`; @@ -367,6 +265,7 @@ const sqlGetUniqueFilmSimulations = async () => sql` export type GetPhotosOptions = { sortBy?: 'createdAt' | 'takenAt' | 'priority' limit?: number + offset?: number tag?: string camera?: Camera simulation?: FilmSimulation @@ -403,11 +302,11 @@ const safelyQueryPhotos = async (callback: () => Promise): Promise => { return result; }; -// PHOTOS export const getPhotos = async (options: GetPhotosOptions = {}) => { const { - sortBy = 'takenAt', - limit, + sortBy = PRIORITY_ORDER_ENABLED ? 'priority' : 'takenAt', + limit = PHOTO_DEFAULT_LIMIT, + offset = 0, tag, camera, simulation, @@ -416,30 +315,69 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { includeHidden, } = options; - let getPhotosSql = () => sqlGetPhotos(limit); + let sql = 'SELECT * FROM photos'; - if (includeHidden) { - getPhotosSql = () => sqlGetPhotosIncludingHidden(limit); - } else if (takenBefore) { - getPhotosSql = () => sqlGetPhotosTakenBeforeDate(takenBefore, limit); - } else if (takenAfterInclusive) { - // eslint-disable-next-line max-len - getPhotosSql = () => sqlGetPhotosTakenAfterDateInclusive(takenAfterInclusive, limit); - } else if (tag) { - getPhotosSql = () => sqlGetPhotosByTag(limit, tag); - } else if (camera) { - getPhotosSql = () => sqlGetPhotosByCamera(limit, camera.make, camera.model); - } else if (simulation) { - getPhotosSql = () => sqlGetPhotosBySimulation(limit, simulation); - } else if (sortBy === 'createdAt') { - getPhotosSql = () => sqlGetPhotosSortedByCreatedAt(limit); - } else if (sortBy === 'priority') { - getPhotosSql = () => sqlGetPhotosSortedByPriority(limit); + // WHERE + let wheres = [] as string[]; + let values = [] as (string | number)[]; + let valueNumber = 1; + if (!includeHidden) { + wheres.push('hidden IS NOT TRUE'); + } + if (takenBefore) { + wheres.push(`taken_at > $${valueNumber++}`); + values.push(takenBefore.toISOString()); + } + if (takenAfterInclusive) { + wheres.push(`taken_at <= $${valueNumber++}`); + values.push(takenAfterInclusive.toISOString()); + } + if (tag) { + wheres.push(`$${valueNumber++}=ANY(tags)`); + values.push(tag); + } + if (camera) { + wheres.push(`LOWER(make)=$${valueNumber++}`); + wheres.push(`LOWER(REPLACE(model, ' ', '-'))=$${valueNumber++}`); + values.push(parameterize(camera.make)); + values.push(parameterize(camera.model)); + } + if (simulation) { + wheres.push(`film_simulation=$${valueNumber++}`); + values.push(simulation); + } + if (wheres.length > 0) { + sql += ` WHERE ${wheres.join(' AND ')}`; } - return safelyQueryPhotos(getPhotosSql) + // ORDER BY + switch (sortBy) { + case 'createdAt': + sql += ' ORDER BY created_at DESC'; + break; + case 'takenAt': + sql += ' ORDER BY taken_at DESC'; + break; + case 'priority': + sql += ' ORDER BY priority_order ASC, taken_at DESC'; + break; + } + + // LIMIT + OFFSET + sql += ` LIMIT $${valueNumber++} OFFSET $${valueNumber++}`; + values.push(limit, offset); + + const client = await db.connect(); + + console.log({ + sql, + values, + }); + + return safelyQueryPhotos(() => client.query(sql, values)) .then(({ rows }) => rows.map(parsePhotoFromDb)); }; + export const getPhoto = async (id: string): Promise => { // Check for photo id forwarding // and convert short ids to uuids diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index 11c22341..9d571f37 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -33,6 +33,7 @@ export default function SiteChecklistClient({ showFilmSimulations, isProModeEnabled, isGeoPrivacyEnabled, + isPriorityOrderEnabled, isPublicApiEnabled, isOgTextBottomAligned, showRefreshButton, @@ -256,6 +257,16 @@ export default function SiteChecklistClient({ collection/display of location-based data {renderEnvVars(['NEXT_PUBLIC_GEO_PRIVACY'])} + + Set environment variable to {'"1"'} to prevent + priority order photo field affecting photo order + {renderEnvVars(['NEXT_PUBLIC_IGNORE_PRIORITY_ORDER'])} + Date: Mon, 18 Dec 2023 08:42:59 -0600 Subject: [PATCH 2/6] Fix priority-ordered photo detail thumbnails --- src/app/(static)/p/[photoId]/layout.tsx | 12 ++++++++++-- src/cache/index.ts | 4 +++- src/services/vercel-postgres.ts | 12 ++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/app/(static)/p/[photoId]/layout.tsx b/src/app/(static)/p/[photoId]/layout.tsx index 1664732b..24a7dea0 100644 --- a/src/app/(static)/p/[photoId]/layout.tsx +++ b/src/app/(static)/p/[photoId]/layout.tsx @@ -12,6 +12,7 @@ import { } from '@/site/paths'; import PhotoDetailPage from '@/photo/PhotoDetailPage'; import { getPhotoCached, getPhotosCached } from '@/cache'; +import { PRIORITY_ORDER_ENABLED } from '@/site/config'; interface PhotoProps { params: { photoId: string } @@ -60,9 +61,16 @@ export default async function PhotoPage({ photosBefore, photosAfter, ] = await Promise.all([ - getPhotosCached({ takenBefore: photo.takenAt, limit: 1 }), getPhotosCached({ - takenAfterInclusive: photo.takenAt, + ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== undefined) + ? { beforePriorityOrder: photo.priorityOrder } + : { takenBefore: photo.takenAt }, + limit: 1, + }), + getPhotosCached({ + ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== undefined) + ? { afterPriorityOrderInclusive: photo.priorityOrder } + : { takenAfterInclusive: photo.takenAt }, limit: GRID_THUMBNAILS_TO_SHOW_MAX + 1, }), ]); diff --git a/src/cache/index.ts b/src/cache/index.ts index 7673c577..fea2bf78 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -51,9 +51,11 @@ const getPhotosCacheKeyForOption = ( case 'offset': case 'tag': case 'simulation': + case 'beforePriorityOrder': + case 'afterPriorityOrderInclusive': case 'includeHidden': { const value = options[option]; - return value ? `${option}-${value}` : null; + return value !== undefined ? `${option}-${value}` : null; } // Date keys case 'takenBefore': diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 7ab19c6f..d912f270 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -271,6 +271,8 @@ export type GetPhotosOptions = { simulation?: FilmSimulation takenBefore?: Date takenAfterInclusive?: Date + beforePriorityOrder?: number + afterPriorityOrderInclusive?: number includeHidden?: boolean } @@ -312,6 +314,8 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { simulation, takenBefore, takenAfterInclusive, + beforePriorityOrder, + afterPriorityOrderInclusive, includeHidden, } = options; @@ -346,6 +350,14 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { wheres.push(`film_simulation=$${valueNumber++}`); values.push(simulation); } + if (beforePriorityOrder !== undefined) { + wheres.push(`id < $${valueNumber++}`); + values.push(beforePriorityOrder); + } + if (afterPriorityOrderInclusive !== undefined) { + wheres.push(`id >= $${valueNumber++}`); + values.push(afterPriorityOrderInclusive); + } if (wheres.length > 0) { sql += ` WHERE ${wheres.join(' AND ')}`; } From c2cb7ba494f8617825a59d9f18b5e92a35422030 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 18 Dec 2023 09:26:33 -0600 Subject: [PATCH 3/6] Remove sql logging --- src/services/vercel-postgres.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index d912f270..2f6f52e9 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -381,11 +381,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { const client = await db.connect(); - console.log({ - sql, - values, - }); - return safelyQueryPhotos(() => client.query(sql, values)) .then(({ rows }) => rows.map(parsePhotoFromDb)); }; From 996339e4acf7f5973775ff60e0d776d87145a3fe Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Mon, 18 Dec 2023 11:52:43 -0600 Subject: [PATCH 4/6] Adjust sql/priority handling --- src/app/(static)/p/[photoId]/layout.tsx | 4 +-- src/services/vercel-postgres.ts | 35 +++++++++++++------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/app/(static)/p/[photoId]/layout.tsx b/src/app/(static)/p/[photoId]/layout.tsx index 24a7dea0..eac8b33f 100644 --- a/src/app/(static)/p/[photoId]/layout.tsx +++ b/src/app/(static)/p/[photoId]/layout.tsx @@ -62,13 +62,13 @@ export default async function PhotoPage({ photosAfter, ] = await Promise.all([ getPhotosCached({ - ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== undefined) + ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== null) ? { beforePriorityOrder: photo.priorityOrder } : { takenBefore: photo.takenAt }, limit: 1, }), getPhotosCached({ - ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== undefined) + ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== null) ? { afterPriorityOrderInclusive: photo.priorityOrder } : { takenAfterInclusive: photo.takenAt }, limit: GRID_THUMBNAILS_TO_SHOW_MAX + 1, diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 2f6f52e9..2fb440e2 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -319,69 +319,70 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { includeHidden, } = options; - let sql = 'SELECT * FROM photos'; + let sql = ['SELECT * FROM photos']; + let values = [] as (string | number)[]; + let valueIndex = 1; // WHERE let wheres = [] as string[]; - let values = [] as (string | number)[]; - let valueNumber = 1; if (!includeHidden) { wheres.push('hidden IS NOT TRUE'); } if (takenBefore) { - wheres.push(`taken_at > $${valueNumber++}`); + wheres.push(`taken_at > $${valueIndex++}`); values.push(takenBefore.toISOString()); } if (takenAfterInclusive) { - wheres.push(`taken_at <= $${valueNumber++}`); + wheres.push(`taken_at <= $${valueIndex++}`); values.push(takenAfterInclusive.toISOString()); } if (tag) { - wheres.push(`$${valueNumber++}=ANY(tags)`); + wheres.push(`$${valueIndex++}=ANY(tags)`); values.push(tag); } if (camera) { - wheres.push(`LOWER(make)=$${valueNumber++}`); - wheres.push(`LOWER(REPLACE(model, ' ', '-'))=$${valueNumber++}`); + wheres.push(`LOWER(make)=$${valueIndex++}`); + wheres.push(`LOWER(REPLACE(model, ' ', '-'))=$${valueIndex++}`); values.push(parameterize(camera.make)); values.push(parameterize(camera.model)); } if (simulation) { - wheres.push(`film_simulation=$${valueNumber++}`); + wheres.push(`film_simulation=$${valueIndex++}`); values.push(simulation); } if (beforePriorityOrder !== undefined) { - wheres.push(`id < $${valueNumber++}`); + wheres.push(`priority_order < $${valueIndex++}`); values.push(beforePriorityOrder); } if (afterPriorityOrderInclusive !== undefined) { - wheres.push(`id >= $${valueNumber++}`); + // eslint-disable-next-line max-len + wheres.push(`priority_order >= $${valueIndex++} OR priority_order IS NULL`); values.push(afterPriorityOrderInclusive); } if (wheres.length > 0) { - sql += ` WHERE ${wheres.join(' AND ')}`; + sql.push(`WHERE ${wheres.join(' AND ')}`); } // ORDER BY switch (sortBy) { case 'createdAt': - sql += ' ORDER BY created_at DESC'; + sql.push('ORDER BY created_at DESC'); break; case 'takenAt': - sql += ' ORDER BY taken_at DESC'; + sql.push('ORDER BY taken_at DESC'); break; case 'priority': - sql += ' ORDER BY priority_order ASC, taken_at DESC'; + sql.push('ORDER BY priority_order ASC, taken_at DESC'); break; } // LIMIT + OFFSET - sql += ` LIMIT $${valueNumber++} OFFSET $${valueNumber++}`; + sql.push(`LIMIT $${valueIndex++} OFFSET $${valueIndex++}`); values.push(limit, offset); const client = await db.connect(); - return safelyQueryPhotos(() => client.query(sql, values)) + return safelyQueryPhotos(() => client.query(sql.join(' '), values)) .then(({ rows }) => rows.map(parsePhotoFromDb)); }; From fa998b6dc2a9203feb6f2a1083c7d685ac43e6c3 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 24 Dec 2023 15:21:58 -0500 Subject: [PATCH 5/6] Finalize photo near id query for photo thumbnails --- src/app/(static)/p/[photoId]/layout.tsx | 35 ++++++------------ src/cache/index.ts | 35 +++++++++--------- src/services/vercel-postgres.ts | 48 +++++++++++++++++-------- 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/src/app/(static)/p/[photoId]/layout.tsx b/src/app/(static)/p/[photoId]/layout.tsx index eac8b33f..06133c24 100644 --- a/src/app/(static)/p/[photoId]/layout.tsx +++ b/src/app/(static)/p/[photoId]/layout.tsx @@ -11,8 +11,7 @@ import { absolutePathForPhotoImage, } from '@/site/paths'; import PhotoDetailPage from '@/photo/PhotoDetailPage'; -import { getPhotoCached, getPhotosCached } from '@/cache'; -import { PRIORITY_ORDER_ENABLED } from '@/site/config'; +import { getPhotoCached, getPhotosNearIdCached } from '@/cache'; interface PhotoProps { params: { photoId: string } @@ -51,38 +50,24 @@ export async function generateMetadata({ export default async function PhotoPage({ params: { photoId }, children, -}: - PhotoProps & { children: React.ReactNode }) { - const photo = await getPhotoCached(photoId); +}: PhotoProps & { children: React.ReactNode }) { + const photos = await getPhotosNearIdCached( + photoId, + GRID_THUMBNAILS_TO_SHOW_MAX + 2, + ); + + const photo = photos.find(p => p.id === photoId); if (!photo) { redirect(PATH_ROOT); } - const [ - photosBefore, - photosAfter, - ] = await Promise.all([ - getPhotosCached({ - ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== null) - ? { beforePriorityOrder: photo.priorityOrder } - : { takenBefore: photo.takenAt }, - limit: 1, - }), - getPhotosCached({ - ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== null) - ? { afterPriorityOrderInclusive: photo.priorityOrder } - : { takenAfterInclusive: photo.takenAt }, - limit: GRID_THUMBNAILS_TO_SHOW_MAX + 1, - }), - ]); - - const photos = photosBefore.concat(photosAfter); + const index = photos.findIndex(p => p.id === photoId); return <> {children} ; } diff --git a/src/cache/index.ts b/src/cache/index.ts index fea2bf78..db0f2387 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -21,6 +21,7 @@ import { getPhotosFilmSimulationDateRange, getPhotosFilmSimulationCount, getPhotosDateRange, + getPhotosNearId, } from '@/services/vercel-postgres'; import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo'; import { getBlobPhotoUrls, getBlobUploadUrls } from '@/services/blob'; @@ -45,29 +46,20 @@ const getPhotosCacheKeyForOption = ( option: keyof GetPhotosOptions, ): string | null => { switch (option) { - // Primitive keys - case 'sortBy': - case 'limit': - case 'offset': - case 'tag': - case 'simulation': - case 'beforePriorityOrder': - case 'afterPriorityOrderInclusive': - case 'includeHidden': { - const value = options[option]; - return value !== undefined ? `${option}-${value}` : null; - } - // Date keys - case 'takenBefore': - case 'takenAfterInclusive': { - const value = options[option]; - return value ? `${option}-${value.toISOString()}` : null; - } // Complex keys case 'camera': { const value = options[option]; return value ? `${option}-${createCameraKey(value)}` : null; } + case 'takenBefore': + case 'takenAfterInclusive': { + const value = options[option]; + return value ? `${option}-${value.toISOString()}` : null; + } + // Primitive keys + default: + const value = options[option]; + return value !== undefined ? `${option}-${value}` : null; } }; @@ -122,6 +114,13 @@ export const getPhotosCached = ( [KEY_PHOTOS, ...getPhotosCacheKeys(...args)], )(...args).then(parseCachedPhotosDates); +export const getPhotosNearIdCached = ( + ...args: Parameters +) => unstable_cache( + getPhotosNearId, + [KEY_PHOTOS], +)(...args).then(parseCachedPhotosDates); + export const getPhotosDateRangeCached = unstable_cache( getPhotosDateRange, diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 2fb440e2..d3af54aa 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -271,8 +271,6 @@ export type GetPhotosOptions = { simulation?: FilmSimulation takenBefore?: Date takenAfterInclusive?: Date - beforePriorityOrder?: number - afterPriorityOrderInclusive?: number includeHidden?: boolean } @@ -314,8 +312,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { simulation, takenBefore, takenAfterInclusive, - beforePriorityOrder, - afterPriorityOrderInclusive, includeHidden, } = options; @@ -350,15 +346,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { wheres.push(`film_simulation=$${valueIndex++}`); values.push(simulation); } - if (beforePriorityOrder !== undefined) { - wheres.push(`priority_order < $${valueIndex++}`); - values.push(beforePriorityOrder); - } - if (afterPriorityOrderInclusive !== undefined) { - // eslint-disable-next-line max-len - wheres.push(`priority_order >= $${valueIndex++} OR priority_order IS NULL`); - values.push(afterPriorityOrderInclusive); - } if (wheres.length > 0) { sql.push(`WHERE ${wheres.join(' AND ')}`); } @@ -380,9 +367,40 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { sql.push(`LIMIT $${valueIndex++} OFFSET $${valueIndex++}`); values.push(limit, offset); - const client = await db.connect(); + return safelyQueryPhotos(async () => { + const client = await db.connect(); + return client.query(sql.join(' '), values); + }) + .then(({ rows }) => rows.map(parsePhotoFromDb)); +}; - return safelyQueryPhotos(() => client.query(sql.join(' '), values)) +export const getPhotosNearId = async ( + id: string, + limit: number, +) => { + const orderBy = PRIORITY_ORDER_ENABLED + ? 'ORDER BY priority_order ASC, taken_at DESC' + : 'ORDER BY taken_at DESC'; + + return safelyQueryPhotos(async () => { + const client = await db.connect(); + return client.query( + ` + WITH twi AS ( + SELECT *, row_number() + OVER (${orderBy}) as row_number + FROM photos + WHERE hidden IS NOT TRUE + ), + current AS (SELECT row_number FROM twi WHERE id = $1) + SELECT twi.* + FROM twi, current + WHERE twi.row_number >= current.row_number - 1 + LIMIT $2 + `, + [id, limit] + ); + }) .then(({ rows }) => rows.map(parsePhotoFromDb)); }; From 1ef6e8a29a188bd777585bcb72d47a29f37b1d05 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 24 Dec 2023 15:44:08 -0500 Subject: [PATCH 6/6] Fix photo detail thumbnail count --- src/app/(static)/p/[photoId]/layout.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app/(static)/p/[photoId]/layout.tsx b/src/app/(static)/p/[photoId]/layout.tsx index 06133c24..19a26fc9 100644 --- a/src/app/(static)/p/[photoId]/layout.tsx +++ b/src/app/(static)/p/[photoId]/layout.tsx @@ -59,15 +59,20 @@ export default async function PhotoPage({ const photo = photos.find(p => p.id === photoId); if (!photo) { redirect(PATH_ROOT); } - - const index = photos.findIndex(p => p.id === photoId); + + const isPhotoFirst = photos.findIndex(p => p.id === photoId) === 0; return <> {children} ; }