import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { immich } from '@/lib/immich'; import { getConfig, hasExifPanelContent } from '@/lib/config'; import { encodeAssetId } from '@/lib/tokens'; import { getServerDictionary } from '@/lib/i18n/server'; import { downloadsRemaining, findSessionByToken, isExpired, } from '@/lib/proofSessions'; import { AlbumDetailView } from '../[...path]/AlbumDetailView'; import { toPhotoItems } from '../[...path]/photoItems'; /** * Generate metadata for a proofing session. * * This is shared by both: * * /proof/ * /c/ */ export async function proofMetadata(token: string): Promise { const session = await findSessionByToken(token); const t = getServerDictionary(); return { title: !session ? t.error.notFoundTitle : isExpired(session) ? t.proofSession.expiredTitle : t.proofSession.greeting(session.clientName), robots: { index: false, follow: false, }, // Do not leak the proof URL through the Referer header. referrer: 'no-referrer', }; } /** * Render an existing Folio proofing session from its real token. * * Both /proof/ and our friendly /c/ route can call this, * which means there is only one copy of the actual gallery code. */ export async function renderProof(token: string) { const session = await findSessionByToken(token); if (!session) { notFound(); } const t = getServerDictionary(); if (isExpired(session)) { return (

{t.proofSession.expiredTitle}

{t.proofSession.expiredText}

); } const album = await immich.getProofingAlbum(session.albumId); if (!album) { notFound(); } const config = getConfig(); const images = toPhotoItems( album.assets, config.exif.onHover && config.exif.camera, config.exif.caption, ); /* * Only preserve selected photos that still exist in the Immich album. * * If a photo was selected by the client and is later removed from * the album, it should disappear from the saved selection too. */ const inAlbum = new Set(album.assets.map((asset) => asset.id)); const selected = session.selection .filter((id) => inAlbum.has(id)) .map(encodeAssetId); const validUntil = session.expiresOn ? new Date(`${session.expiresOn}T12:00:00`).toLocaleDateString( t.dateLocale, { year: 'numeric', month: 'long', day: 'numeric', }, ) : null; const gridStyle = { '--grid-columns': config.grid.columns, ...(config.gridGapExplicit ? { '--grid-gap': `${config.grid.gap}px`, } : {}), '--grid-aspect-ratio': config.grid.aspectRatio, } as React.CSSProperties; return (

{t.proofSession.greeting(session.clientName)}

{session.submittedAt ? t.proofSession.locked : t.proofSession.intro}

{validUntil && (

{t.proofSession.validUntil(validUntil)}

)} } /> ); }