diff --git a/apps/iiif/.iiifrc-base.yml b/apps/iiif/.iiifrc-base.yml index dbe28ada..0d683ee2 100644 --- a/apps/iiif/.iiifrc-base.yml +++ b/apps/iiif/.iiifrc-base.yml @@ -9,13 +9,18 @@ run: - extract-slug-source - delft-extract-labels - extract-part-of-collection - - extract-thumbnail + - extract-thumbnail-local - manifest-sqlite - metadata-analysis - site-collections - delft-image-source - typesense-manifests +config: + extract-thumbnail-local: + width: 1080 + height: 1080 + stores: collective-access: type: iiif-json diff --git a/apps/iiif/scripts/delft-label-extract.js b/apps/iiif/scripts/delft-label-extract.js index ba6eb4ad..d12d958d 100644 --- a/apps/iiif/scripts/delft-label-extract.js +++ b/apps/iiif/scripts/delft-label-extract.js @@ -13,10 +13,13 @@ extract( if (!resource) return false; const label = resource.label; + const summary = resource.summary; return { meta: { label: getValue(label), + intlLabel: label, + intlSummary: summary }, }; } diff --git a/apps/iiif/scripts/extract-thumbnail-local.js b/apps/iiif/scripts/extract-thumbnail-local.js new file mode 100644 index 00000000..160fd626 --- /dev/null +++ b/apps/iiif/scripts/extract-thumbnail-local.js @@ -0,0 +1,56 @@ +import { createThumbnailHelper } from "@iiif/helpers"; +import { extract } from "iiif-hss"; + +extract({ + id: "extract-thumbnail-local", + name: "Extract Thumbnail", + types: ["Manifest"], + invalidate: async (resource, api, config) => { + const cache = await api.caches.value; + return !cache.extractThumbnail && cache.extractThumbnail !== false; + }}, + async (resource, api, config) => { + const vault = resource.vault; + const helper = createThumbnailHelper(vault); + const thumbnail = await helper.getBestThumbnailAtSize( + api.resource, + config.width + ? { + width: config.width, + height: config.height || config.width, + } + : { + width: 256, + height: 256, + }, + config.dereference || false + ); + + if (thumbnail?.best) { + return { + meta: { thumbnail: thumbnail.best }, + caches: { extractThumbnail: true }, + }; + } + else { + const thumbnail = await helper.getBestThumbnailAtSize( + api.resource, { + width: 256, + height: 256, + }, + config.dereference || false + ); + + if (thumbnail?.best) { + return { + meta: { thumbnail: thumbnail.best }, + caches: { extractThumbnail: true }, + }; + } + } + + return { + caches: { extractThumbnail: false }, + }; + }, +) \ No newline at end of file diff --git a/apps/static-site/content/pages/en/collections-listing.mdx b/apps/static-site/content/pages/en/collections-listing.mdx new file mode 100644 index 00000000..3d43a6c7 --- /dev/null +++ b/apps/static-site/content/pages/en/collections-listing.mdx @@ -0,0 +1,4 @@ +--- +title: Collections +path: "/collections" +--- diff --git a/apps/static-site/content/pages/en/exhibitions-listing.mdx b/apps/static-site/content/pages/en/exhibitions-listing.mdx new file mode 100644 index 00000000..1c991189 --- /dev/null +++ b/apps/static-site/content/pages/en/exhibitions-listing.mdx @@ -0,0 +1,4 @@ +--- +title: Exhibitions +path: "/exhibitions" +--- diff --git a/apps/static-site/content/pages/en/home.mdx b/apps/static-site/content/pages/en/home.mdx new file mode 100644 index 00000000..8f5ca1a9 --- /dev/null +++ b/apps/static-site/content/pages/en/home.mdx @@ -0,0 +1,8 @@ +--- +title: Academic Heritage, History and Art +description: Explore the history of Delft University of Technology and the Special Collections of TU Delft Library +image: "https://dlc.services/thumbs/7/13/00a1a6c3-25cb-4873-b570-2a77205852c9/full/683,1024/0/default.jpg" +imageWidth: 683 +imageHeight: 1024 +path: "/" +--- diff --git a/apps/static-site/content/pages/en/publications-listing.mdx b/apps/static-site/content/pages/en/publications-listing.mdx new file mode 100644 index 00000000..64971fd7 --- /dev/null +++ b/apps/static-site/content/pages/en/publications-listing.mdx @@ -0,0 +1,4 @@ +--- +title: Publications +path: "/publications" +--- diff --git a/apps/static-site/content/pages/en/search.mdx b/apps/static-site/content/pages/en/search.mdx new file mode 100644 index 00000000..33e347be --- /dev/null +++ b/apps/static-site/content/pages/en/search.mdx @@ -0,0 +1,4 @@ +--- +title: Search +path: "/search" +--- diff --git a/apps/static-site/content/pages/nl/collections-listing.mdx b/apps/static-site/content/pages/nl/collections-listing.mdx new file mode 100644 index 00000000..e557a973 --- /dev/null +++ b/apps/static-site/content/pages/nl/collections-listing.mdx @@ -0,0 +1,4 @@ +--- +title: Collecties +path: "/collections" +--- diff --git a/apps/static-site/content/pages/nl/exhibitions-listing.mdx b/apps/static-site/content/pages/nl/exhibitions-listing.mdx new file mode 100644 index 00000000..c3ffe909 --- /dev/null +++ b/apps/static-site/content/pages/nl/exhibitions-listing.mdx @@ -0,0 +1,4 @@ +--- +title: Tentoonstellingen +path: "/exhibitions" +--- diff --git a/apps/static-site/content/pages/nl/home.mdx b/apps/static-site/content/pages/nl/home.mdx new file mode 100644 index 00000000..13600a0b --- /dev/null +++ b/apps/static-site/content/pages/nl/home.mdx @@ -0,0 +1,8 @@ +--- +title: Academisch Erfgoed, Geschiedenis en Kunst +description: Verken de geschiedenis van de TU Delft en de bijzondere collecties van de TU Delft Library +image: "https://dlc.services/thumbs/7/13/00a1a6c3-25cb-4873-b570-2a77205852c9/full/683,1024/0/default.jpg" +path: "/" +imageWidth: 683 +imageHeight: 1024 +--- diff --git a/apps/static-site/content/pages/nl/publications-listing.mdx b/apps/static-site/content/pages/nl/publications-listing.mdx new file mode 100644 index 00000000..4b1d318a --- /dev/null +++ b/apps/static-site/content/pages/nl/publications-listing.mdx @@ -0,0 +1,4 @@ +--- +title: Publicaties +path: "/publications" +--- diff --git a/apps/static-site/content/pages/nl/search.mdx b/apps/static-site/content/pages/nl/search.mdx new file mode 100644 index 00000000..63dd1a43 --- /dev/null +++ b/apps/static-site/content/pages/nl/search.mdx @@ -0,0 +1,4 @@ +--- +title: Zoek +path: "/search" +--- diff --git a/apps/static-site/contentlayer.config.ts b/apps/static-site/contentlayer.config.ts index 8b2c2bad..cb4ac649 100644 --- a/apps/static-site/contentlayer.config.ts +++ b/apps/static-site/contentlayer.config.ts @@ -11,12 +11,15 @@ const Pages = defineDocumentType(() => ({ title: { type: "string", required: true }, path: { type: "string", required: true }, description: { type: "string", required: false }, + image: { type: "string", required: false }, + imageWidth: { type: "number", required: false }, + imageHeight: { type: "number", required: false }, }, computedFields: { lang: { type: "string", - resolve: (publication) => { - return publication._id.split("/")[1]; + resolve: (page) => { + return page._id.split("/")[1]; }, }, }, @@ -28,9 +31,12 @@ const Publication = defineDocumentType(() => ({ contentType: "mdx", fields: { title: { type: "string", required: true }, + description: { type: "string", required: false }, date: { type: "string", required: false }, author: { type: "string", required: false }, image: { type: "string", required: false }, + imageWidth: { type: "number", required: false }, + imageHeight: { type: "number", required: false }, hero: { type: "string", required: false }, toc: { type: "boolean", default: false }, depth: { type: "number", default: 3 }, diff --git a/apps/static-site/public/metadata/default.jpg b/apps/static-site/public/metadata/default.jpg new file mode 100644 index 00000000..e239f28b Binary files /dev/null and b/apps/static-site/public/metadata/default.jpg differ diff --git a/apps/static-site/src/app/[locale]/about/page.tsx b/apps/static-site/src/app/[locale]/about/page.tsx index 0fe4f077..0d4eae46 100644 --- a/apps/static-site/src/app/[locale]/about/page.tsx +++ b/apps/static-site/src/app/[locale]/about/page.tsx @@ -1,24 +1,43 @@ -import { allPages } from ".contentlayer/generated"; import { Slot } from "@/blocks/slot"; import { SlotContext } from "@/blocks/slot-context"; import { Page } from "@/components/Page"; import { Illustration } from "@/components/blocks/Illustration"; import { useMDXComponent } from "next-contentlayer/hooks"; +import { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { getBasicMetadata, makeTitle, getMdx, getDefaultMetaMdx } from "@/helpers/metadata"; -export default async function AboutPage({ params }: { params: { locale: string } }) { - const aboutPages = allPages.filter((page) => page.path === "/about"); - const aboutPage = aboutPages.find((page) => page.lang === params.locale) || aboutPages[0]; - - if (!aboutPage) throw new Error(`No about page found for locale ${params.locale}`); +export async function generateMetadata({ params }: { params: { locale: string } }): Promise { + const t = await getTranslations(); + const path = "/about"; + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const page = getMdx({ params: { pageName: "About", path: path, locale: params.locale } }); + const title = makeTitle([page.title, defaultMeta.title]); + const description = page.description ?? defaultMeta.description; + return getBasicMetadata({ + locale: params.locale, + siteName: defaultMeta.title, + title: title, + description: description, + image: { + url: page.image ?? defaultMeta.image, + width: page.image ? page.imageWidth : defaultMeta.imageWidth, + height: page.image ? page.imageHeight : defaultMeta.imageHeight, + }, + path: path, + }); +} - const MDXContent = useMDXComponent(aboutPage.body.code); +export default async function AboutPage({ params }: { params: { locale: string } }) { + const page = getMdx({ params: { pageName: "About", path: "/about", locale: params.locale } }); + const MDXContent = useMDXComponent(page.body.code); const CustomSlot = (inner: any) => { return ; }; return ( -

{aboutPage.title}

+

{page.title}

diff --git a/apps/static-site/src/app/[locale]/collections/[collection]/page.tsx b/apps/static-site/src/app/[locale]/collections/[collection]/page.tsx index 3634c302..4c2d2fd0 100644 --- a/apps/static-site/src/app/[locale]/collections/[collection]/page.tsx +++ b/apps/static-site/src/app/[locale]/collections/[collection]/page.tsx @@ -1,8 +1,48 @@ import { CollectionPage } from "@/components/pages/CollectionPage"; import { loadCollection } from "@/iiif"; import { Page } from "@/components/Page"; -import { unstable_setRequestLocale } from "next-intl/server"; +import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; // import siteMap from "@repo/iiif/build/meta/sitemap.json"; +import { Metadata } from "next"; +import { getValue } from "@iiif/helpers"; +import { baseURL, makeTitle, getDefaultMetaMdx } from "@/helpers/metadata"; + +export async function generateMetadata({ + params, +}: { + params: { collection: string; locale: string }; +}): Promise { + const t = await getTranslations(); + const slug = `collections/${params.collection}`; + const { collection } = await loadCollection(slug); + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const collTitle = getValue(collection.label, { language: params.locale, fallbackLanguages: ["nl", "en"] }); + const description = + getValue(collection.summary, { language: params.locale, fallbackLanguages: ["nl", "en"] }) ?? + defaultMeta.description; + const title = makeTitle([collTitle, defaultMeta.title]); + const url = `/collections/${params.collection}`; + // this page currently uses the default meta image as a 'collection image' is not available. + return { + metadataBase: new URL(baseURL), + description: description, + title: title, + openGraph: { + locale: params.locale, + siteName: defaultMeta.title, + title: title, + type: "website", + url: url, + images: [ + { + url: defaultMeta.image ?? "", + width: defaultMeta.imageWidth, + height: defaultMeta.imageHeight, + }, + ], + }, + }; +} export default async function Collection({ params }: { params: { collection: string; locale: string } }) { unstable_setRequestLocale(params.locale); diff --git a/apps/static-site/src/app/[locale]/collections/page.tsx b/apps/static-site/src/app/[locale]/collections/page.tsx index ea0d0f62..1144a55b 100644 --- a/apps/static-site/src/app/[locale]/collections/page.tsx +++ b/apps/static-site/src/app/[locale]/collections/page.tsx @@ -2,6 +2,29 @@ import { Page } from "@/components/Page"; import { Slot } from "@/blocks/slot"; import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; import { CollectionListing } from "@/components/pages/CollectionListing"; +import { getBasicMetadata, makeTitle, getMdx, getDefaultMetaMdx } from "@/helpers/metadata"; +import { Metadata } from "next"; + +export async function generateMetadata({ params }: { params: { locale: string } }): Promise { + const t = await getTranslations(); + const path = "/collections"; + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const page = getMdx({ params: { pageName: "Collections", path: path, locale: params.locale } }); + const title = makeTitle([page.title, defaultMeta.title]); + const description = page.description ?? defaultMeta.description; + return getBasicMetadata({ + locale: params.locale, + siteName: defaultMeta.title, + title: title, + description: description, + image: { + url: page.image ?? defaultMeta.image, + width: page.image ? page.imageWidth : defaultMeta.imageWidth, + height: page.image ? page.imageHeight : defaultMeta.imageHeight, + }, + path: path, + }); +} export default async function Collections(props: { params: { locale: string } }) { unstable_setRequestLocale(props.params.locale); @@ -9,7 +32,6 @@ export default async function Collections(props: { params: { locale: string } }) // List of collections. return ( - {/* */} diff --git a/apps/static-site/src/app/[locale]/exhibitions/[exhibition]/page.tsx b/apps/static-site/src/app/[locale]/exhibitions/[exhibition]/page.tsx index fa6ad408..5e03e72f 100644 --- a/apps/static-site/src/app/[locale]/exhibitions/[exhibition]/page.tsx +++ b/apps/static-site/src/app/[locale]/exhibitions/[exhibition]/page.tsx @@ -1,27 +1,66 @@ import { Page } from "@/components/Page"; import { ExhibitionPage } from "@/components/pages/ExhibitionPage"; -import { unstable_setRequestLocale } from "next-intl/server"; +import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; import { ManifestLoader } from "@/app/provider"; import imageServiceLinks from "@repo/iiif/build/meta/image-service-links.json"; import allExhibitions from "@repo/iiif/build/collections/exhibitions/collection.json"; import { SlotContext } from "@/blocks/slot-context"; -import { loadManifest } from "@/iiif"; +import type { Metadata } from "next"; +import { loadManifest, loadManifestMeta } from "@/iiif"; +import { getValue } from "@iiif/helpers"; +import { baseURL, makeTitle, getDefaultMetaMdx } from "@/helpers/metadata"; -export const generateStaticParams = async () => { - const exhibitions = []; - for (const item of allExhibitions.items) { - const slug = item["hss:slug"].replace("manifests/", ""); - exhibitions.push({ - exhibition: slug, - lang: "en", - }); - exhibitions.push({ - exhibition: slug, - lang: "nl", - }); - } - return exhibitions; -}; +export async function generateMetadata({ + params, +}: { + params: { exhibition: string; locale: string }; +}): Promise { + const t = await getTranslations(); + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const manifestSlug = `manifests/${params.exhibition}`; + const meta = await loadManifestMeta(manifestSlug); + const exTitle = getValue(meta.intlLabel, { language: params.locale, fallbackLanguages: ["nl", "en"] }); + const description = + getValue(meta.intlSummary, { language: params.locale, fallbackLanguages: ["nl", "en"] }) ?? defaultMeta.description; + const title = makeTitle([exTitle, defaultMeta.title]); + const url = `/exhibitions/${params.exhibition}`; + return { + metadataBase: new URL(baseURL), + description: description, + title: title, + openGraph: { + locale: params.locale, + siteName: defaultMeta.title, + title: title, + type: "website", + url: url, + images: [ + { + url: meta.thumbnail.id ?? defaultMeta.image ?? "", + width: meta.thumbnail ? meta.thumbnail.width : defaultMeta.imageWidth, + height: meta.thumbnail ? meta.thumbnail.height : defaultMeta.imageHeight, + }, + ], + }, + }; +} + +// no static rendering for now... +// export const generateStaticParams = async () => { +// const exhibitions = []; +// for (const item of allExhibitions.items) { +// const slug = item["hss:slug"].replace("manifests/", ""); +// exhibitions.push({ +// exhibition: slug, +// lang: "en", +// }); +// exhibitions.push({ +// exhibition: slug, +// lang: "nl", +// }); +// } +// return exhibitions; +// }; export default async function Exhibition({ params }: { params: { exhibition: string; locale: string } }) { unstable_setRequestLocale(params.locale); diff --git a/apps/static-site/src/app/[locale]/exhibitions/page.tsx b/apps/static-site/src/app/[locale]/exhibitions/page.tsx index 71326b54..4833fd1d 100644 --- a/apps/static-site/src/app/[locale]/exhibitions/page.tsx +++ b/apps/static-site/src/app/[locale]/exhibitions/page.tsx @@ -3,6 +3,30 @@ import { unstable_setRequestLocale } from "next-intl/server"; import { ExhibitionListing } from "@/components/pages/ExhibitionListing"; import { Slot } from "@/blocks/slot"; import exhibitions from "@repo/iiif/build/collections/exhibitions/collection.json"; +import { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { getBasicMetadata, makeTitle, getMdx, getDefaultMetaMdx } from "@/helpers/metadata"; + +export async function generateMetadata({ params }: { params: { locale: string } }): Promise { + const t = await getTranslations(); + const path = "/exhibitions"; + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const page = getMdx({ params: { pageName: "Exhibitions", path: path, locale: params.locale } }); + const title = makeTitle([page.title, defaultMeta.title]); + const description = page.description ?? defaultMeta.description; + return getBasicMetadata({ + locale: params.locale, + siteName: defaultMeta.title, + title: title, + description: description, + image: { + url: page.image ?? defaultMeta.image, + width: page.image ? page.imageWidth : defaultMeta.imageWidth, + height: page.image ? page.imageHeight : defaultMeta.imageHeight, + }, + path: path, + }); +} export default function ExhibitionsPage({ params }: { params: { locale: string } }) { unstable_setRequestLocale(params.locale); diff --git a/apps/static-site/src/app/[locale]/layout.tsx b/apps/static-site/src/app/[locale]/layout.tsx index a5b0450a..33582382 100644 --- a/apps/static-site/src/app/[locale]/layout.tsx +++ b/apps/static-site/src/app/[locale]/layout.tsx @@ -3,16 +3,32 @@ import type { Metadata } from "next"; import BlockEditor from "../../blocks/block-editor"; import { Provider } from "../provider"; import { ReactNode } from "react"; -import { unstable_setRequestLocale } from "next-intl/server"; +import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; import { GlobalHeader } from "@/components/GlobalHeader"; import localFont from "next/font/local"; import { SlotContext } from "@/blocks/slot-context"; import { GlobalFooter } from "@/components/GlobalFooter"; +import { getBasicMetadata, getMdx } from "@/helpers/metadata"; +import { $ } from "bun"; -export const metadata: Metadata = { - title: "TU Delft Academic Heritage", - description: "TU Delft Library's Special Collections portal" -}; +export async function generateMetadata({ params }: { params: { locale: string } }): Promise { + const t = await getTranslations(); + const path = "/"; + const page = getMdx({ params: { pageName: "Home", path: path, locale: params.locale } }); + const description = page.description; + return getBasicMetadata({ + locale: params.locale, + siteName: page.title, + title: page.title, + description: description, + image: { + url: page.image, + width: page.imageWidth, + height: page.imageHeight, + }, + path: path, + }); +} if (process.env.NODE_ENV !== "production") { // @ts-expect-error typescript can't resolve CSS diff --git a/apps/static-site/src/app/[locale]/objects/[manifest]/page.tsx b/apps/static-site/src/app/[locale]/objects/[manifest]/page.tsx index 42dd5c78..74270637 100644 --- a/apps/static-site/src/app/[locale]/objects/[manifest]/page.tsx +++ b/apps/static-site/src/app/[locale]/objects/[manifest]/page.tsx @@ -4,10 +4,55 @@ import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; import { ManifestPage } from "@/components/pages/ManifestPage"; import { ManifestLoader } from "@/app/provider"; import related from "@repo/iiif/build/meta/related-objects.json"; +import type { Metadata } from "next"; +import { getValue } from "@iiif/helpers"; +import { baseURL, makeTitle, getDefaultMetaMdx } from "@/helpers/metadata"; -export default async function ManifestP({ params }: { params: { locale: string; manifest: string } }) { +export async function generateMetadata({ + params, +}: { + params: { manifest: string; locale: string }; +}): Promise { + const t = await getTranslations(); + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const manifestSlug = `manifests/${params.manifest}`; + const meta = await loadManifestMeta(manifestSlug); + const objTitle = getValue(meta.intlLabel, { language: params.locale, fallbackLanguages: ["nl", "en"] }); + const description = + getValue(meta.intlSummary, { language: params.locale, fallbackLanguages: ["nl", "en"] }) ?? defaultMeta.description; + const title = makeTitle([objTitle, defaultMeta.title]); + const url = `/objects/${params.manifest}`; + return { + metadataBase: new URL(baseURL), + description: description, + title: title, + openGraph: { + locale: params.locale, + siteName: defaultMeta.title, + title: title, + type: "website", + url: url, + images: [ + { + url: meta.thumbnail.id ?? defaultMeta.image ?? "", + width: meta.thumbnail ? meta.thumbnail.width : defaultMeta.imageWidth, + height: meta.thumbnail ? meta.thumbnail.height : defaultMeta.imageHeight, + }, + ], + }, + }; +} + +export default async function ManifestP({ + params, + searchParams, +}: { + params: { locale: string; manifest: string }; + searchParams: { id: string }; +}) { unstable_setRequestLocale(params.locale); const t = await getTranslations(); + const idNum = searchParams?.id ? parseInt(searchParams.id) : 0; const manifestSlug = `manifests/${params.manifest}`; const { manifest, meta } = await loadManifest(manifestSlug); @@ -44,6 +89,7 @@ export default async function ManifestP({ params }: { params: { locale: string; manifest={manifest} meta={meta} related={relatedSnippets} + initialCanvasIndex={Number.isNaN(idNum) ? 0 : idNum} /> diff --git a/apps/static-site/src/app/[locale]/publications/[publication]/page.tsx b/apps/static-site/src/app/[locale]/publications/[publication]/page.tsx index cd0bec8b..791f5751 100644 --- a/apps/static-site/src/app/[locale]/publications/[publication]/page.tsx +++ b/apps/static-site/src/app/[locale]/publications/[publication]/page.tsx @@ -1,7 +1,56 @@ import { allPublications } from "contentlayer/generated"; import { Page } from "@/components/Page"; import { PublicationPage } from "@/components/pages/PublicationPage"; -import { unstable_setRequestLocale } from "next-intl/server"; +import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; +import type { Metadata } from "next"; +import { getValue } from "@iiif/helpers"; +import { baseURL, makeTitle, getDefaultMetaMdx } from "@/helpers/metadata"; + +export async function generateMetadata({ + params, +}: { + params: { publication: string; locale: string }; +}): Promise { + const t = await getTranslations(); + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const getValueParams = { language: params.locale, fallbackLanguages: ["nl", "en"] }; + const publicationInLanguage = allPublications.find( + (post) => post.id === params.publication && post.lang === params.locale + ); + const publication = publicationInLanguage || allPublications.find((post) => post.id === params.publication); + const pubTitle = publication && getValue(publication.title, getValueParams); + const title = makeTitle([pubTitle, defaultMeta.title]); + const description = (publication && getValue(publication.description, getValueParams)) ?? defaultMeta.description; + const author = { + name: (publication && getValue(publication.author, getValueParams)) || "", + }; + const pubDateStr = publication && getValue(publication.date, getValueParams); + const pubDate = pubDateStr && new Date(pubDateStr).toISOString(); + const publicationsURL = `/publications`; + + return { + metadataBase: new URL(baseURL), + authors: author, + title: title, + description: description, + openGraph: { + authors: [author.name], + locale: params.locale, + publishedTime: pubDate, + siteName: defaultMeta.title, + title: title, + type: "article", + url: publication ? `${publicationsURL}/${publication.id}` : publicationsURL, + images: [ + { + url: publication?.image ?? defaultMeta.image ?? "", + width: publication?.image ? publication?.imageWidth : defaultMeta.imageWidth, + height: publication?.image ? publication?.imageHeight : defaultMeta.imageHeight, + }, + ], + }, + }; +} export default async function Publication({ params }: { params: { publication: string; locale: string } }) { unstable_setRequestLocale(params.locale); diff --git a/apps/static-site/src/app/[locale]/publications/page.tsx b/apps/static-site/src/app/[locale]/publications/page.tsx index 2a1f220d..9f2b7698 100644 --- a/apps/static-site/src/app/[locale]/publications/page.tsx +++ b/apps/static-site/src/app/[locale]/publications/page.tsx @@ -2,6 +2,29 @@ import { allPublications } from "contentlayer/generated"; import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; import { PublicationListPage } from "@/components/pages/PublicationListPage"; import { Page } from "@/components/Page"; +import { Metadata } from "next"; +import { getBasicMetadata, makeTitle, getMdx, getDefaultMetaMdx } from "@/helpers/metadata"; + +export async function generateMetadata({ params }: { params: { locale: string } }): Promise { + const t = await getTranslations(); + const path = "/publications"; + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const page = getMdx({ params: { pageName: "Publications", path: path, locale: params.locale } }); + const title = makeTitle([page.title, defaultMeta.title]); + const description = page.description ?? defaultMeta.description; + return getBasicMetadata({ + locale: params.locale, + siteName: defaultMeta.title, + title: title, + description: description, + image: { + url: page.image ?? defaultMeta.image, + width: page.image ? page.imageWidth : defaultMeta.imageWidth, + height: page.image ? page.imageHeight : defaultMeta.imageHeight, + }, + path: path, + }); +} export default async function PublicationsList({ params }: { params: { locale: string } }) { unstable_setRequestLocale(params.locale); diff --git a/apps/static-site/src/app/[locale]/search/page.tsx b/apps/static-site/src/app/[locale]/search/page.tsx index ab64eb61..8600d48c 100644 --- a/apps/static-site/src/app/[locale]/search/page.tsx +++ b/apps/static-site/src/app/[locale]/search/page.tsx @@ -1,13 +1,37 @@ import { SearchPage } from "@/components/pages/SearchPage"; import { Page } from "@/components/Page"; -import { unstable_setRequestLocale } from "next-intl/server"; +import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; +import { Metadata } from "next"; +import { getBasicMetadata, makeTitle, getMdx, getDefaultMetaMdx } from "@/helpers/metadata"; + +export async function generateMetadata({ params }: { params: { locale: string } }): Promise { + const t = await getTranslations(); + const path = "/search"; + const defaultMeta = getDefaultMetaMdx({ params: { locale: params.locale } }); + const page = getMdx({ params: { pageName: "Search", path: path, locale: params.locale } }); + const title = makeTitle([page.title, defaultMeta.title]); + const description = page.description ?? defaultMeta.description; + const image = page.image; + return getBasicMetadata({ + locale: params.locale, + siteName: defaultMeta.title, + title: title, + description: description, + image: { + url: page.image ?? defaultMeta.image, + width: page.image ? page.imageWidth : defaultMeta.imageWidth, + height: page.image ? page.imageHeight : defaultMeta.imageHeight, + }, + path: path, + }); +} export default async function Search({ params }: { params: { locale: string } }) { unstable_setRequestLocale(params.locale); - + const page = getMdx({ params: { pageName: "Search", path: "/search", locale: params.locale } }); return ( - + ); } diff --git a/apps/static-site/src/components/pages/CollectionPage.tsx b/apps/static-site/src/components/pages/CollectionPage.tsx index 815b0285..91692d95 100644 --- a/apps/static-site/src/components/pages/CollectionPage.tsx +++ b/apps/static-site/src/components/pages/CollectionPage.tsx @@ -31,82 +31,80 @@ function getSlugFromId(id: string) { export async function CollectionPage(props: CollectionPageProps) { const t = await getTranslations(); - console.log(props.collection); - return ( -
-
-
-
{t("Collection")}
-
-

- {props.collection.label} -

-
- -
+
+
+
+
{t("Collection")}
+
+

+ {props.collection.label} +

+
+
-
-
-
- {props.collection.metadata || props.collection.summary ? ( - - ) : null}
- -
- +
+
+ {props.collection.metadata || props.collection.summary ? ( + -
+ ) : null}
-
-
-
- {props.collection.items.map((manifest) => { - // @todo fix the bug in hss. - const slug = getObjectSlug(manifest["hss:slug"] || getSlugFromId(manifest.id)); - let thumbnail = (manifest.thumbnail || [])[0]?.id; - if (thumbnail) { - thumbnail.replace("/200,/", "/400,/"); - } - return ( -
-
-
- - - -
-

- - {manifest.label} - -

+
+ +
+
+
+
+
+ {props.collection.items.map((manifest) => { + // @todo fix the bug in hss. + const slug = getObjectSlug(manifest["hss:slug"] || getSlugFromId(manifest.id)); + + let thumbnail = (manifest.thumbnail || [])[0]?.id; + if (thumbnail) { + thumbnail.replace("/200,/", "/400,/"); + } + return ( +
+
+
+ + +
+

+ + {manifest.label} + +

- ); - })} -
+
+ ); + })}
+
); } diff --git a/apps/static-site/src/components/pages/ManifestPage.tsx b/apps/static-site/src/components/pages/ManifestPage.tsx index 4aae984c..529149e4 100644 --- a/apps/static-site/src/components/pages/ManifestPage.tsx +++ b/apps/static-site/src/components/pages/ManifestPage.tsx @@ -34,16 +34,24 @@ interface ManifestPageProps { relatedObjects: string; partOfCollections: string; }; + + initialCanvasIndex: number; } const runtimeOptions = { maxOverZoom: 2 }; -export function ManifestPage({ related, manifest, meta, content }: ManifestPageProps) { - const { currentSequenceIndex } = useSimpleViewer(); +export function ManifestPage({ related, manifest, meta, content, initialCanvasIndex }: ManifestPageProps) { + const context = useSimpleViewer(); + const { currentSequenceIndex } = context; + const previousSeqIndex = useRef(currentSequenceIndex); const atlas = useRef(); - useEffect(() => { + if (currentSequenceIndex == previousSeqIndex.current) { + context.setCurrentCanvasIndex(initialCanvasIndex); + } else { + context.setCurrentCanvasIndex(currentSequenceIndex); + } if (atlas.current) { setTimeout(() => atlas.current?.runtime.world.goHome(true), 5); } diff --git a/apps/static-site/src/components/pages/PublicationPage.tsx b/apps/static-site/src/components/pages/PublicationPage.tsx index faf96c84..ecb3a369 100644 --- a/apps/static-site/src/components/pages/PublicationPage.tsx +++ b/apps/static-site/src/components/pages/PublicationPage.tsx @@ -13,7 +13,7 @@ export interface PublicationPageProps { interface PublicationHeading { id: string; heading: string; - level: number + level: number; } export async function PublicationPage(props: PublicationPageProps) { @@ -28,7 +28,7 @@ export async function PublicationPage(props: PublicationPageProps) { const t = await getTranslations(); - const depth = props.publication.depth > 4 ? 4 : props.publication.depth < 1 ? 1 : props.publication.depth + const depth = props.publication.depth > 4 ? 4 : props.publication.depth < 1 ? 1 : props.publication.depth; return (
@@ -50,21 +50,23 @@ export async function PublicationPage(props: PublicationPageProps) {

{t("Table of contents")}

    - {props.publication.headings.filter((heading: PublicationHeading) => heading.level <= depth).map((heading: PublicationHeading) => { - const leftMargins: { - [index: number]: string - } = { - 1: "ml-0", - 2: "ml-4", - 3: "ml-8", - 4: "ml-12" - } - return ( -
  • - {heading.heading} -
  • - ); - })} + {props.publication.headings + .filter((heading: PublicationHeading) => heading.level <= depth) + .map((heading: PublicationHeading) => { + const leftMargins: { + [index: number]: string; + } = { + 1: "ml-0", + 2: "ml-4", + 3: "ml-8", + 4: "ml-12", + }; + return ( +
  • + {heading.heading} +
  • + ); + })}
) : null} diff --git a/apps/static-site/src/components/pages/SearchPage.tsx b/apps/static-site/src/components/pages/SearchPage.tsx index 75b02acd..81048e59 100644 --- a/apps/static-site/src/components/pages/SearchPage.tsx +++ b/apps/static-site/src/components/pages/SearchPage.tsx @@ -6,10 +6,10 @@ import { SearchTabs } from "../search/SearchTabs"; import { FacetList } from "../search/FacetList"; import { SearchWrapper, getFacets } from "../search/SearchWrapper"; -export function SearchPage() { +export function SearchPage({ title }: { title: string }) { return (
-

Search

+

{title}

page.path === params.path); + const page = pages.find((p) => p.lang === params.locale) ?? pages[0]; + if (!page) throw new Error(`No ${params.pageName} page found for locale ${params.locale}`); + return page; +} + +export function getDefaultMetaMdx({ params }: { params: { locale: string } }) { + return getMdx({ + params: { + pageName: "Home", + path: "/", + locale: params.locale, + }, + }); +} diff --git a/apps/static-site/src/iiif.ts b/apps/static-site/src/iiif.ts index a8c20853..de4e28ae 100644 --- a/apps/static-site/src/iiif.ts +++ b/apps/static-site/src/iiif.ts @@ -22,6 +22,15 @@ export async function loadCollection(slug: string) { return ret; } +export async function loadCollectionMeta(slug: string) { + const resp = await fetch(`${IIIF_URL}${slug}/meta.json`); + + if (resp.ok) { + const json = await resp.json(); + return json; + } +} + export async function loadManifest(slug: string) { const manifestReq = fetch(`${IIIF_URL}${slug}/manifest.json`); const metaReq = fetch(`${IIIF_URL}${slug}/meta.json`); diff --git a/apps/static-site/translations/en.json b/apps/static-site/translations/en.json index 6af95638..a40ef7b4 100644 --- a/apps/static-site/translations/en.json +++ b/apps/static-site/translations/en.json @@ -9,7 +9,9 @@ "Collections": "Collections", "View source on Github": "View source on Github", "Drag and Drop IIIF Resource": "Drag and Drop IIIF Resource", + "Search": "Search", "Search collection": "Search collection...", + "All items": "All items", "No results found": "No results found", "Search results": "Search results", "Exhibitions": "Exhibitions", @@ -22,5 +24,7 @@ "Contact and accessibility": "Contact and accessibility", "Article": "Article", "Table of contents": "Table of contents", - "All items": "All items" + "Summary": "Summary", + "defaultTitle": "Academic Heritage, History and Art", + "defaultDesc": "Explore the history of Delft University of Technology and the Special Collections of TU Delft Library" } diff --git a/apps/static-site/translations/nl.json b/apps/static-site/translations/nl.json index 873e798a..4b7ad9d0 100644 --- a/apps/static-site/translations/nl.json +++ b/apps/static-site/translations/nl.json @@ -3,13 +3,14 @@ "Publications": "Publicaties", "Academic Heritage": "Academisch Erfgoed", "About": "Over", - "Curated collections": "Curated collecties", + "Curated collections": "Gecureerde collecties", "All Collections": "Alle collecties", "Collection": "Collectie", "Collections": "Collecties", - "View source on Github": "Bekijk bron op Github", - "Drag and Drop IIIF Resource": "Sleep en laat los IIIF Resource", - "Search collection": "Zoek collectie...", + "View source on Github": "Bekijk broncode op Github", + "Drag and Drop IIIF Resource": "Drag and Drop IIIF Resource", + "Search": "Zoek", + "Search collection": "Doorzoek collectie...", "All items": "Alle items", "No results found": "Geen resultaten gevonden", "Search results": "Zoekresultaten", @@ -22,5 +23,8 @@ "Delft University of Technology": "Technische Universiteit Delft", "Contact and accessibility": "Contact en bereikbaarheid", "Article": "Artikel", - "Table of contents": "Inhoudsopgave" + "Table of contents": "Inhoudsopgave", + "Summary": "Summier", + "defaultTitle": "Academisch Erfgoed, Geschiedenis en Kunst", + "defaultDesc": "Verken de geschiedenis van de TU Delft en de bijzondere collecties van de TU Delft Library" }