diff --git a/src/app/(static)/grid/page.tsx b/src/app/(static)/grid/page.tsx
index e400c6ba..67e89f2f 100644
--- a/src/app/(static)/grid/page.tsx
+++ b/src/app/(static)/grid/page.tsx
@@ -1,6 +1,7 @@
import {
getPhotosCached,
getPhotosCountCached,
+ getPhotosDateRangeCached,
getUniqueCamerasCached,
getUniqueFilmSimulationsCached,
getUniqueTagsCached,
@@ -32,12 +33,14 @@ export default async function GridPage({ searchParams }: PaginationParams) {
const [
photos,
photosCount,
+ photosDateRange,
tags,
cameras,
simulations,
] = await Promise.all([
getPhotosCached({ limit }),
getPhotosCountCached(),
+ getPhotosDateRangeCached(),
getUniqueTagsCached(),
getUniqueCamerasCached(),
SHOW_FILM_SIMULATIONS ? getUniqueFilmSimulationsCached() : [],
@@ -52,7 +55,13 @@ export default async function GridPage({ searchParams }: PaginationParams) {
? }
contentSide={
}
sideHiddenOnMobile
/>
diff --git a/src/app/(static)/sets/page.tsx b/src/app/(static)/sets/page.tsx
index 9427e65d..50bea1a5 100644
--- a/src/app/(static)/sets/page.tsx
+++ b/src/app/(static)/sets/page.tsx
@@ -1,6 +1,7 @@
import {
getPhotosCached,
getPhotosCountCached,
+ getPhotosDateRangeCached,
getUniqueCamerasCached,
getUniqueFilmSimulationsCached,
getUniqueTagsCached,
@@ -23,11 +24,13 @@ export async function generateMetadata(): Promise {
export default async function SetsPage() {
const [
photosCount,
+ photosDateRange,
tags,
cameras,
simulations,
] = await Promise.all([
getPhotosCountCached(),
+ getPhotosDateRangeCached(),
getUniqueTagsCached(),
getUniqueCamerasCached(),
SHOW_FILM_SIMULATIONS ? getUniqueFilmSimulationsCached() : [],
@@ -46,6 +49,7 @@ export default async function SetsPage() {
cameras,
simulations,
photosCount,
+ photosDateRange,
}} />
}
diff --git a/src/cache/index.ts b/src/cache/index.ts
index 5dfd5922..0bdd40eb 100644
--- a/src/cache/index.ts
+++ b/src/cache/index.ts
@@ -20,6 +20,7 @@ import {
getUniqueFilmSimulations,
getPhotosFilmSimulationDateRange,
getPhotosFilmSimulationCount,
+ getPhotosDateRange,
} from '@/services/vercel-postgres';
import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo';
import { getBlobPhotoUrls, getBlobUploadUrls } from '@/services/blob';
@@ -118,6 +119,12 @@ export const getPhotosCached = (
[KEY_PHOTOS, ...getPhotosCacheKeys(...args)],
)(...args).then(parseCachedPhotosDates);
+export const getPhotosDateRangeCached =
+ unstable_cache(
+ getPhotosDateRange,
+ [KEY_PHOTOS, KEY_DATE_RANGE],
+ );
+
export const getPhotosCountCached =
unstable_cache(
getPhotosCount,
diff --git a/src/photo/PhotoGridSidebar.tsx b/src/photo/PhotoGridSidebar.tsx
index 47cb639f..0ee1fb0a 100644
--- a/src/photo/PhotoGridSidebar.tsx
+++ b/src/photo/PhotoGridSidebar.tsx
@@ -4,7 +4,7 @@ import HeaderList from '@/components/HeaderList';
import PhotoTag from '@/tag/PhotoTag';
import { FaTag } from 'react-icons/fa';
import { IoMdCamera } from 'react-icons/io';
-import { photoQuantityText } from '.';
+import { PhotoDateRange, dateRangeForPhotos, photoQuantityText } from '.';
import { Tags } from '@/tag';
import PhotoFilmSimulation from
'@/simulation/PhotoFilmSimulation';
@@ -17,12 +17,16 @@ export default function PhotoGridSidebar({
cameras,
simulations,
photosCount,
+ photosDateRange,
}: {
tags: Tags
cameras: Cameras
simulations: FilmSimulations
photosCount: number
+ photosDateRange?: PhotoDateRange
}) {
+ const { start, end } = dateRangeForPhotos(undefined, photosDateRange);
+
return (
<>
{tags.length > 0 && )}
/>}
{photosCount > 0 && }
>
);
diff --git a/src/photo/index.ts b/src/photo/index.ts
index dbf4e160..f2812538 100644
--- a/src/photo/index.ts
+++ b/src/photo/index.ts
@@ -194,7 +194,7 @@ const sortPhotosByDate = (
: a.takenAt.getTime() - b.takenAt.getTime());
export const dateRangeForPhotos = (
- photos: Photo[],
+ photos: Photo[] = [],
explicitDateRange?: PhotoDateRange,
) => {
const photosSorted = sortPhotosByDate(photos);
diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts
index 8c91b27b..80fd6f81 100644
--- a/src/services/vercel-postgres.ts
+++ b/src/services/vercel-postgres.ts
@@ -288,6 +288,12 @@ const sqlGetPhotosFilmSimulationCount = async (
hidden IS NOT TRUE
`.then(({ rows }) => parseInt(rows[0].count, 10));
+const sqlGetPhotosDateRange = async () => sql`
+ SELECT MIN(taken_at_naive) as start, MAX(taken_at_naive) as end
+ FROM photos
+ WHERE hidden IS NOT TRUE
+`.then(({ rows }) => rows[0] as PhotoDateRange);
+
const sqlGetPhotosTagDateRange = async (tag: string) => sql`
SELECT MIN(taken_at_naive) as start, MAX(taken_at_naive) as end
FROM photos
@@ -442,6 +448,8 @@ export const getPhoto = async (id: string): Promise => {
.then(({ rows }) => rows.map(parsePhotoFromDb))
.then(photos => photos.length > 0 ? photos[0] : undefined);
};
+export const getPhotosDateRange = () =>
+ safelyQueryPhotos(sqlGetPhotosDateRange);
export const getPhotosCount = () =>
safelyQueryPhotos(sqlGetPhotosCount);
export const getPhotosCountIncludingHidden = () =>