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));
};