Merge pull request #164 from sambecker/static-categories
Offer static optimization for photo categories
This commit is contained in:
commit
0bb0647608
@ -104,9 +104,10 @@ Application behavior can be changed by configuring the following environment var
|
||||
#### Site behavior
|
||||
- `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage
|
||||
- `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured)
|
||||
- `NEXT_PUBLIC_PRO_MODE = 1` enables higher quality image storage (results in increased storage usage)
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES = 1` enables static optimization for pages, i.e., renders pages at build time (results in increased project usage)—⚠️ _Experimental_
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (results in increased project usage)—⚠️ _Experimental_
|
||||
- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` do not optimize photo uploads before storing (⚠️ results in increased storage usage)
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time (⚠️ results in increased project usage)
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time (⚠️ results in increased project usage)
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time (⚠️ results in increased project usage)
|
||||
- `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios)
|
||||
- `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage)
|
||||
- `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information)
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
import PhotoUpload from '@/photo/PhotoUpload';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import { AI_TEXT_GENERATION_ENABLED, PRO_MODE_ENABLED } from '@/site/config';
|
||||
import {
|
||||
AI_TEXT_GENERATION_ENABLED,
|
||||
PRESERVE_ORIGINAL_UPLOADS,
|
||||
} from '@/site/config';
|
||||
import AdminPhotosTable from '@/admin/AdminPhotosTable';
|
||||
import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite';
|
||||
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
|
||||
@ -43,7 +46,7 @@ export default function AdminPhotosClient({
|
||||
<div className="flex">
|
||||
<div className="grow min-w-0">
|
||||
<PhotoUpload
|
||||
shouldResize={!PRO_MODE_ENABLED}
|
||||
shouldResize={!PRESERVE_ORIGINAL_UPLOADS}
|
||||
isUploading={isUploading}
|
||||
setIsUploading={setIsUploading}
|
||||
onLastUpload={onLastPhotoUpload}
|
||||
|
||||
@ -1,13 +1,26 @@
|
||||
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
|
||||
import { getUniqueFilmSimulations } from '@/photo/db/query';
|
||||
import { FilmSimulation, generateMetaForFilmSimulation } from '@/simulation';
|
||||
import FilmSimulationOverview from '@/simulation/FilmSimulationOverview';
|
||||
import { IS_PRODUCTION } from '@/site/config';
|
||||
import { getPhotosFilmSimulationDataCached } from '@/simulation/data';
|
||||
import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config';
|
||||
import { Metadata } from 'next/types';
|
||||
import { cache } from 'react';
|
||||
|
||||
const getPhotosFilmSimulationDataCachedCached =
|
||||
cache(getPhotosFilmSimulationDataCached);
|
||||
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ simulation: FilmSimulation }[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
const simulations = await getUniqueFilmSimulations();
|
||||
return simulations.map(({ simulation }) => ({ simulation }));
|
||||
};
|
||||
}
|
||||
|
||||
interface FilmSimulationProps {
|
||||
params: Promise<{ simulation: FilmSimulation }>
|
||||
}
|
||||
|
||||
@ -2,6 +2,9 @@ import { generateMetaForFocalLength, getFocalLengthFromString } from '@/focal';
|
||||
import FocalLengthOverview from '@/focal/FocalLengthOverview';
|
||||
import { getPhotosFocalLengthDataCached } from '@/focal/data';
|
||||
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
|
||||
import { IS_PRODUCTION } from '@/site/config';
|
||||
import { getUniqueFocalLengths } from '@/photo/db/query';
|
||||
import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config';
|
||||
import { PATH_ROOT } from '@/site/paths';
|
||||
import type { Metadata } from 'next';
|
||||
import { redirect } from 'next/navigation';
|
||||
@ -13,6 +16,16 @@ const getPhotosFocalDataCachedCached = cache((focal: number) =>
|
||||
limit: INFINITE_SCROLL_GRID_INITIAL,
|
||||
}));
|
||||
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ focal: string }[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
const focalLengths = await getUniqueFocalLengths();
|
||||
return focalLengths.map(({ focal }) => ({ focal: focal.toString() }));
|
||||
};
|
||||
}
|
||||
|
||||
interface FocalLengthProps {
|
||||
params: Promise<{ focal: string }>
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@ import PhotoImageResponse from '@/image-response/PhotoImageResponse';
|
||||
import { getIBMPlexMonoMedium } from '@/site/font';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
|
||||
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_OG_IMAGES } from '@/site/config';
|
||||
import {
|
||||
IS_PRODUCTION,
|
||||
STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES,
|
||||
} from '@/site/config';
|
||||
import { getPhotoIds } from '@/photo/db/query';
|
||||
import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db';
|
||||
import { isNextImageReadyBasedOnPhotos } from '@/photo';
|
||||
@ -12,7 +15,7 @@ import { isNextImageReadyBasedOnPhotos } from '@/photo';
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ photoId: string }[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_OG_IMAGES && IS_PRODUCTION) {
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
const photos = await getPhotoIds({ limit: GENERATE_STATIC_PARAMS_LIMIT });
|
||||
return photos.map(photoId => ({ photoId }));
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
} from '@/site/paths';
|
||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||
import { getPhotosNearIdCached } from '@/photo/cache';
|
||||
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PAGES } from '@/site/config';
|
||||
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PHOTOS } from '@/site/config';
|
||||
import { getPhotoIds } from '@/photo/db/query';
|
||||
import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db';
|
||||
import { cache } from 'react';
|
||||
@ -25,7 +25,7 @@ const getPhotosNearIdCachedCached = cache((photoId: string) =>
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ photoId: string }[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PAGES && IS_PRODUCTION) {
|
||||
if (STATICALLY_OPTIMIZED_PHOTOS && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
const photos = await getPhotoIds({ limit: GENERATE_STATIC_PARAMS_LIMIT });
|
||||
return photos.map(photoId => ({ photoId }));
|
||||
|
||||
@ -5,6 +5,9 @@ import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
|
||||
import { getPhotosCameraDataCached } from '@/camera/data';
|
||||
import CameraOverview from '@/camera/CameraOverview';
|
||||
import { cache } from 'react';
|
||||
import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config';
|
||||
import { IS_PRODUCTION } from '@/site/config';
|
||||
import { getUniqueCameras } from '@/photo/db/query';
|
||||
|
||||
const getPhotosCameraDataCachedCached = cache((
|
||||
make: string,
|
||||
@ -15,6 +18,16 @@ const getPhotosCameraDataCachedCached = cache((
|
||||
INFINITE_SCROLL_GRID_INITIAL,
|
||||
));
|
||||
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ make: string, model: string }[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
const cameras = await getUniqueCameras();
|
||||
return cameras.map(({ camera: { make, model } }) => ({ make, model }));
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: CameraProps): Promise<Metadata> {
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
|
||||
import { getUniqueTags } from '@/photo/db/query';
|
||||
import { IS_PRODUCTION } from '@/site/config';
|
||||
import { STATICALLY_OPTIMIZED_PHOTO_CATEGORIES } from '@/site/config';
|
||||
import { PATH_ROOT } from '@/site/paths';
|
||||
import { generateMetaForTag } from '@/tag';
|
||||
import TagOverview from '@/tag/TagOverview';
|
||||
@ -10,6 +13,16 @@ import { cache } from 'react';
|
||||
const getPhotosTagDataCachedCached = cache((tag: string) =>
|
||||
getPhotosTagDataCached({ tag, limit: INFINITE_SCROLL_GRID_INITIAL}));
|
||||
|
||||
export let generateStaticParams:
|
||||
(() => Promise<{ tag: string }[]>) | undefined = undefined;
|
||||
|
||||
if (STATICALLY_OPTIMIZED_PHOTO_CATEGORIES && IS_PRODUCTION) {
|
||||
generateStaticParams = async () => {
|
||||
const tags = await getUniqueTags();
|
||||
return tags.map(({ tag }) => ({ tag }));
|
||||
};
|
||||
}
|
||||
|
||||
interface TagProps {
|
||||
params: Promise<{ tag: string }>
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import { ExifData, ExifParserFactory } from 'ts-exif-parser';
|
||||
import { PhotoFormData } from './form';
|
||||
import { FilmSimulation } from '@/simulation';
|
||||
import sharp, { Sharp } from 'sharp';
|
||||
import { GEO_PRIVACY_ENABLED, PRO_MODE_ENABLED } from '@/site/config';
|
||||
import { GEO_PRIVACY_ENABLED, PRESERVE_ORIGINAL_UPLOADS } from '@/site/config';
|
||||
|
||||
const IMAGE_WIDTH_RESIZE = 200;
|
||||
const IMAGE_WIDTH_BLUR = 200;
|
||||
@ -169,5 +169,5 @@ export const removeGpsData = async (image: ArrayBuffer) =>
|
||||
GPSHPositioningError: GPS_NULL_STRING,
|
||||
},
|
||||
})
|
||||
.toFormat('jpeg', { quality: PRO_MODE_ENABLED ? 95 : 80 })
|
||||
.toFormat('jpeg', { quality: PRESERVE_ORIGINAL_UPLOADS ? 95 : 80 })
|
||||
.toBuffer();
|
||||
|
||||
@ -51,11 +51,12 @@ export default function SiteChecklistClient({
|
||||
showFilmSimulations,
|
||||
showExifInfo,
|
||||
defaultTheme,
|
||||
isProModeEnabled,
|
||||
areOriginalUploadsPreserved,
|
||||
isGridHomepageEnabled,
|
||||
isStaticallyOptimized,
|
||||
arePagesStaticallyOptimized,
|
||||
areOGImagesStaticallyOptimized,
|
||||
arePhotosStaticallyOptimized,
|
||||
arePhotoOGImagesStaticallyOptimized,
|
||||
arePhotoCategoriesStaticallyOptimized,
|
||||
arePhotosMatted,
|
||||
isBlurEnabled,
|
||||
isGeoPrivacyEnabled,
|
||||
@ -168,6 +169,16 @@ export default function SiteChecklistClient({
|
||||
{label}
|
||||
</span>
|
||||
</div>;
|
||||
|
||||
const renderSubStatusWithEnvVar = (
|
||||
type: ComponentProps<typeof StatusIcon>['type'],
|
||||
variable: string,
|
||||
) =>
|
||||
renderSubStatus(
|
||||
type,
|
||||
renderEnvVars([variable]),
|
||||
'translate-y-[5px]',
|
||||
);
|
||||
|
||||
const renderError = ({
|
||||
connection,
|
||||
@ -452,13 +463,13 @@ export default function SiteChecklistClient({
|
||||
{renderEnvVars(['NEXT_PUBLIC_DEFAULT_THEME'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Pro mode"
|
||||
status={isProModeEnabled}
|
||||
title="Preserve original uploads"
|
||||
status={areOriginalUploadsPreserved}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to enable
|
||||
higher quality image storage:
|
||||
{renderEnvVars(['NEXT_PUBLIC_PRO_MODE'])}
|
||||
Set environment variable to {'"1"'} to prevent
|
||||
image uploads being optimized before storing:
|
||||
{renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Static optimization"
|
||||
@ -468,15 +479,17 @@ export default function SiteChecklistClient({
|
||||
>
|
||||
Set environment variable to {'"1"'} to enable static optimization,
|
||||
i.e., rendering pages and images at build time:
|
||||
{renderSubStatus(
|
||||
arePagesStaticallyOptimized ? 'checked' : 'optional',
|
||||
renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES']),
|
||||
'translate-y-[3.5px]',
|
||||
{renderSubStatusWithEnvVar(
|
||||
arePhotosStaticallyOptimized ? 'checked' : 'optional',
|
||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS',
|
||||
)}
|
||||
{renderSubStatus(
|
||||
areOGImagesStaticallyOptimized ? 'checked' : 'optional',
|
||||
renderEnvVars(['NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES']),
|
||||
'translate-y-[3.5px]',
|
||||
{renderSubStatusWithEnvVar(
|
||||
arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional',
|
||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES',
|
||||
)}
|
||||
{renderSubStatusWithEnvVar(
|
||||
arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional',
|
||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES',
|
||||
)}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
|
||||
@ -124,20 +124,28 @@ export const CURRENT_STORAGE: StorageType =
|
||||
|
||||
// SETTINGS
|
||||
|
||||
export const GRID_HOMEPAGE_ENABLED =
|
||||
process.env.NEXT_PUBLIC_GRID_HOMEPAGE === '1';
|
||||
export const DEFAULT_THEME =
|
||||
process.env.NEXT_PUBLIC_DEFAULT_THEME === 'dark'
|
||||
? 'dark'
|
||||
: process.env.NEXT_PUBLIC_DEFAULT_THEME === 'light'
|
||||
? 'light'
|
||||
: 'system';
|
||||
export const PRO_MODE_ENABLED =
|
||||
export const PRESERVE_ORIGINAL_UPLOADS =
|
||||
process.env.NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS === '1' ||
|
||||
// Legacy environment variable name
|
||||
process.env.NEXT_PUBLIC_PRO_MODE === '1';
|
||||
export const GRID_HOMEPAGE_ENABLED =
|
||||
process.env.NEXT_PUBLIC_GRID_HOMEPAGE === '1';
|
||||
export const STATICALLY_OPTIMIZED_PAGES =
|
||||
export const STATICALLY_OPTIMIZED_PHOTOS =
|
||||
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS === '1' ||
|
||||
// Legacy environment variable name
|
||||
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES === '1';
|
||||
export const STATICALLY_OPTIMIZED_OG_IMAGES =
|
||||
export const STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES =
|
||||
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES === '1' ||
|
||||
// Legacy environment variable name
|
||||
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES === '1';
|
||||
export const STATICALLY_OPTIMIZED_PHOTO_CATEGORIES =
|
||||
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES === '1';
|
||||
export const MATTE_PHOTOS =
|
||||
process.env.NEXT_PUBLIC_MATTE_PHOTOS === '1';
|
||||
export const BLUR_ENABLED =
|
||||
@ -208,15 +216,17 @@ export const CONFIG_CHECKLIST_STATUS = {
|
||||
showSocial: SHOW_SOCIAL,
|
||||
showFilmSimulations: SHOW_FILM_SIMULATIONS,
|
||||
showExifInfo: SHOW_EXIF_DATA,
|
||||
defaultTheme: DEFAULT_THEME,
|
||||
isProModeEnabled: PRO_MODE_ENABLED,
|
||||
isGridHomepageEnabled: GRID_HOMEPAGE_ENABLED,
|
||||
defaultTheme: DEFAULT_THEME,
|
||||
areOriginalUploadsPreserved: PRESERVE_ORIGINAL_UPLOADS,
|
||||
isStaticallyOptimized: (
|
||||
STATICALLY_OPTIMIZED_PAGES ||
|
||||
STATICALLY_OPTIMIZED_OG_IMAGES
|
||||
STATICALLY_OPTIMIZED_PHOTOS ||
|
||||
STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES ||
|
||||
STATICALLY_OPTIMIZED_PHOTO_CATEGORIES
|
||||
),
|
||||
arePagesStaticallyOptimized: STATICALLY_OPTIMIZED_PAGES,
|
||||
areOGImagesStaticallyOptimized: STATICALLY_OPTIMIZED_OG_IMAGES,
|
||||
arePhotosStaticallyOptimized: STATICALLY_OPTIMIZED_PHOTOS,
|
||||
arePhotoOGImagesStaticallyOptimized: STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES,
|
||||
arePhotoCategoriesStaticallyOptimized: STATICALLY_OPTIMIZED_PHOTO_CATEGORIES,
|
||||
arePhotosMatted: MATTE_PHOTOS,
|
||||
isBlurEnabled: BLUR_ENABLED,
|
||||
isGeoPrivacyEnabled: GEO_PRIVACY_ENABLED,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user