Finalize photo near id query for photo thumbnails
This commit is contained in:
parent
996339e4ac
commit
fa998b6dc2
@ -11,8 +11,7 @@ import {
|
|||||||
absolutePathForPhotoImage,
|
absolutePathForPhotoImage,
|
||||||
} from '@/site/paths';
|
} from '@/site/paths';
|
||||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||||
import { getPhotoCached, getPhotosCached } from '@/cache';
|
import { getPhotoCached, getPhotosNearIdCached } from '@/cache';
|
||||||
import { PRIORITY_ORDER_ENABLED } from '@/site/config';
|
|
||||||
|
|
||||||
interface PhotoProps {
|
interface PhotoProps {
|
||||||
params: { photoId: string }
|
params: { photoId: string }
|
||||||
@ -51,38 +50,24 @@ export async function generateMetadata({
|
|||||||
export default async function PhotoPage({
|
export default async function PhotoPage({
|
||||||
params: { photoId },
|
params: { photoId },
|
||||||
children,
|
children,
|
||||||
}:
|
}: PhotoProps & { children: React.ReactNode }) {
|
||||||
PhotoProps & { children: React.ReactNode }) {
|
const photos = await getPhotosNearIdCached(
|
||||||
const photo = await getPhotoCached(photoId);
|
photoId,
|
||||||
|
GRID_THUMBNAILS_TO_SHOW_MAX + 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
const photo = photos.find(p => p.id === photoId);
|
||||||
|
|
||||||
if (!photo) { redirect(PATH_ROOT); }
|
if (!photo) { redirect(PATH_ROOT); }
|
||||||
|
|
||||||
const [
|
const index = photos.findIndex(p => p.id === photoId);
|
||||||
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);
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
<PhotoDetailPage
|
<PhotoDetailPage
|
||||||
photo={photo}
|
photo={photo}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
photosGrid={photosAfter.slice(1)}
|
photosGrid={photos.slice(index === 0 ? 1 : 2)}
|
||||||
/>
|
/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/cache/index.ts
vendored
35
src/cache/index.ts
vendored
@ -21,6 +21,7 @@ import {
|
|||||||
getPhotosFilmSimulationDateRange,
|
getPhotosFilmSimulationDateRange,
|
||||||
getPhotosFilmSimulationCount,
|
getPhotosFilmSimulationCount,
|
||||||
getPhotosDateRange,
|
getPhotosDateRange,
|
||||||
|
getPhotosNearId,
|
||||||
} from '@/services/vercel-postgres';
|
} from '@/services/vercel-postgres';
|
||||||
import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo';
|
import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo';
|
||||||
import { getBlobPhotoUrls, getBlobUploadUrls } from '@/services/blob';
|
import { getBlobPhotoUrls, getBlobUploadUrls } from '@/services/blob';
|
||||||
@ -45,29 +46,20 @@ const getPhotosCacheKeyForOption = (
|
|||||||
option: keyof GetPhotosOptions,
|
option: keyof GetPhotosOptions,
|
||||||
): string | null => {
|
): string | null => {
|
||||||
switch (option) {
|
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
|
// Complex keys
|
||||||
case 'camera': {
|
case 'camera': {
|
||||||
const value = options[option];
|
const value = options[option];
|
||||||
return value ? `${option}-${createCameraKey(value)}` : null;
|
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)],
|
[KEY_PHOTOS, ...getPhotosCacheKeys(...args)],
|
||||||
)(...args).then(parseCachedPhotosDates);
|
)(...args).then(parseCachedPhotosDates);
|
||||||
|
|
||||||
|
export const getPhotosNearIdCached = (
|
||||||
|
...args: Parameters<typeof getPhotosNearId>
|
||||||
|
) => unstable_cache(
|
||||||
|
getPhotosNearId,
|
||||||
|
[KEY_PHOTOS],
|
||||||
|
)(...args).then(parseCachedPhotosDates);
|
||||||
|
|
||||||
export const getPhotosDateRangeCached =
|
export const getPhotosDateRangeCached =
|
||||||
unstable_cache(
|
unstable_cache(
|
||||||
getPhotosDateRange,
|
getPhotosDateRange,
|
||||||
|
|||||||
@ -271,8 +271,6 @@ export type GetPhotosOptions = {
|
|||||||
simulation?: FilmSimulation
|
simulation?: FilmSimulation
|
||||||
takenBefore?: Date
|
takenBefore?: Date
|
||||||
takenAfterInclusive?: Date
|
takenAfterInclusive?: Date
|
||||||
beforePriorityOrder?: number
|
|
||||||
afterPriorityOrderInclusive?: number
|
|
||||||
includeHidden?: boolean
|
includeHidden?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +312,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => {
|
|||||||
simulation,
|
simulation,
|
||||||
takenBefore,
|
takenBefore,
|
||||||
takenAfterInclusive,
|
takenAfterInclusive,
|
||||||
beforePriorityOrder,
|
|
||||||
afterPriorityOrderInclusive,
|
|
||||||
includeHidden,
|
includeHidden,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
@ -350,15 +346,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => {
|
|||||||
wheres.push(`film_simulation=$${valueIndex++}`);
|
wheres.push(`film_simulation=$${valueIndex++}`);
|
||||||
values.push(simulation);
|
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) {
|
if (wheres.length > 0) {
|
||||||
sql.push(`WHERE ${wheres.join(' AND ')}`);
|
sql.push(`WHERE ${wheres.join(' AND ')}`);
|
||||||
}
|
}
|
||||||
@ -380,9 +367,40 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => {
|
|||||||
sql.push(`LIMIT $${valueIndex++} OFFSET $${valueIndex++}`);
|
sql.push(`LIMIT $${valueIndex++} OFFSET $${valueIndex++}`);
|
||||||
values.push(limit, offset);
|
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));
|
.then(({ rows }) => rows.map(parsePhotoFromDb));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user