Skip to content

Commit

Permalink
Merge pull request #62 from oxen-io/sanity_metadata
Browse files Browse the repository at this point in the history
Sanity metadata
  • Loading branch information
Aerilym authored Oct 14, 2024
2 parents cc42243 + 2d8ebc1 commit 0a986da
Show file tree
Hide file tree
Showing 23 changed files with 470 additions and 10 deletions.
5 changes: 5 additions & 0 deletions apps/foundation/app/(Sanity)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { ReactNode } from 'react';
import '@session/ui/styles';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'STF | Sanity Studio',
};

export default function SanityLayout({ children }: { children: ReactNode }) {
return (
Expand Down
64 changes: 63 additions & 1 deletion apps/foundation/app/(Site)/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import { getLandingPageSlug } from '@/lib/sanity/sanity-server';
import PortableText from '@/components/PortableText';
import logger from '@/lib/logger';
import { NEXTJS_EXPLICIT_IGNORED_ROUTES, NEXTJS_IGNORED_PATTERNS } from '@/lib/constants';
import type { Metadata, ResolvingMetadata } from 'next';
import { generateSanityMetadata } from '@session/sanity-cms/lib/metadata';
import { getFileBySlug } from '@session/sanity-cms/queries/getFile';
import FileDownload from '@session/sanity-cms/components/SanityFileDownload';
import { getTranslations } from 'next-intl/server';

/**
* Force static rendering and cache the data of a layout or page by forcing `cookies()`, `headers()`
Expand All @@ -19,6 +24,39 @@ export const dynamic = 'force-static';
*/
export const dynamicParams = true;

export async function generateMetadata(
{ params }: PageProps,
parent: ResolvingMetadata
): Promise<Metadata> {
const slug = params.slug;
if (!slug) {
logger.warn(`No slug provided for metadata generation`);
return {};
}

if (
NEXTJS_EXPLICIT_IGNORED_ROUTES.includes(slug) ||
NEXTJS_IGNORED_PATTERNS.some((pattern) => slug.includes(pattern))
) {
return {};
}

logger.info(`Generating metadata for slug ${slug}`);

const page = await getPageBySlug({ client, slug });

if (!page) {
logger.warn(`No page found for slug ${slug}`);
return {};
}
const parentMetadata = await parent;
return generateSanityMetadata(client, {
seo: page.seo,
parentMetadata,
type: 'website',
});
}

export async function generateStaticParams() {
const pages = await getPagesSlugs({ client });
const slugs = new Set(pages.map((page) => page.slug.current));
Expand Down Expand Up @@ -58,7 +96,31 @@ export default async function UniversalPage({ params }: PageProps) {
slug,
});

if (!page) return notFound();
if (!page) {
const file = await getFileBySlug({
client,
slug,
});

if (file?.src && file?.fileName) {
const fileDictionary = await getTranslations('fileDownload');
return (
<FileDownload
fileName={file.fileName}
src={file.src}
strings={{
fetching: fileDictionary('fetching'),
clickToDownload: fileDictionary('clickToDownload'),
clickToDownloadAria: fileDictionary('clickToDownloadAria'),
openPdfInNewTab: fileDictionary('openPdfInNewTab'),
openPdfInNewTabAria: fileDictionary('openPdfInNewTabAria'),
}}
/>
);
}

return notFound();
}

return <PortableText body={page.body} className="max-w-screen-md" wrapperComponent="main" />;
}
36 changes: 36 additions & 0 deletions apps/foundation/app/(Site)/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { getPostsSlugs } from '@session/sanity-cms/queries/getPosts';
import { getPostBySlug } from '@session/sanity-cms/queries/getPost';
import logger from '@/lib/logger';
import BlogPost from '@/app/(Site)/blog/[slug]/BlogPost';
import type { Metadata, ResolvingMetadata } from 'next';
import { generateSanityMetadata } from '@session/sanity-cms/lib/metadata';

/**
* Force static rendering and cache the data of a layout or page by forcing `cookies()`, `headers()`
Expand All @@ -17,6 +19,40 @@ export const dynamic = 'force-static';
*/
export const dynamicParams = true;

export async function generateMetadata(
{ params }: PageProps,
parent: ResolvingMetadata
): Promise<Metadata> {
const slug = params.slug;
if (!slug) {
logger.warn(`No slug provided for metadata generation`);
return {};
}

logger.info(`Generating metadata for slug ${slug}`);

const post = await getPostBySlug({ client, slug });

if (!post) {
logger.warn(`No post found for slug ${slug}`);
return {};
}
const parentMetadata = await parent;

const publishedTime = post.date ? new Date(post.date).toISOString() : undefined;
const authors = post.author?.name ? [post.author.name] : undefined;
const tags = post.tags?.length ? post.tags : undefined;

return generateSanityMetadata(client, {
seo: post.seo,
parentMetadata,
type: 'article',
publishedTime,
authors,
tags,
});
}

export async function generateStaticParams() {
const posts = await getPostsSlugs({ client });
const slugs = new Set(posts.map((post) => post.slug.current));
Expand Down
15 changes: 15 additions & 0 deletions apps/foundation/app/(Site)/blog/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import type { ReactNode } from 'react';
import { Footer } from '@/components/Footer';
import { getInitialSiteDataForSSR } from '@/lib/sanity/sanity-server';
import type { Metadata, ResolvingMetadata } from 'next';
import { generateSanityMetadata } from '@session/sanity-cms/lib/metadata';
import { client } from '@/lib/sanity/sanity.client';

export async function generateMetadata(_: object, parent: ResolvingMetadata): Promise<Metadata> {
const { settings } = await getInitialSiteDataForSSR();

return settings.blogSeo
? await generateSanityMetadata(client, {
seo: settings.blogSeo,
parentMetadata: await parent,
type: 'website',
})
: {};
}

export default async function BlogLayout({ children }: { children: ReactNode }) {
const { settings } = await getInitialSiteDataForSSR();
Expand Down
20 changes: 19 additions & 1 deletion apps/foundation/app/(Site)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ import DevSheetServerSide from '@/components/DevSheetServerSide';
import { getInitialSiteDataForSSR } from '@/lib/sanity/sanity-server';
import Head from 'next/head';
import { isProduction } from '@session/util-js/env';
import { client } from '@/lib/sanity/sanity.client';
import { generateSanityMetadata } from '@session/sanity-cms/lib/metadata';
import type { Metadata } from 'next';

export async function generateMetadata(): Promise<Metadata> {
const { settings } = await getInitialSiteDataForSSR();

const generatedMetadata = settings.seo
? await generateSanityMetadata(client, {
seo: settings.seo,
type: 'website',
})
: {};

return {
...generatedMetadata,
manifest: '/site.webmanifest',
};
}

export default async function RootLayout({ children }: { children: ReactNode }) {
const { locale, direction, messages } = await getLocalizationData();
Expand All @@ -25,7 +44,6 @@ export default async function RootLayout({ children }: { children: ReactNode })
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
</Head>
<GlobalProvider messages={messages} locale={locale}>
<body className="bg-session-white font-roboto-flex text-session-text-black mx-4 flex flex-col items-center overflow-x-hidden">
Expand Down
13 changes: 12 additions & 1 deletion apps/foundation/app/(Site)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getLandingPageSlug } from '@/lib/sanity/sanity-server';
import UniversalPage from './[slug]/page';
import UniversalPage, { generateMetadata as generateMetadataUniversalPage } from './[slug]/page';
import UniversalPageLayout from '@/app/(Site)/[slug]/layout';
import type { ResolvingMetadata } from 'next';

/**
* Force static rendering and cache the data of a layout or page by forcing `cookies()`, `headers()`
Expand All @@ -9,6 +10,16 @@ import UniversalPageLayout from '@/app/(Site)/[slug]/layout';
*/
export const dynamic = 'force-static';

export async function generateMetadata(_: object, parent: ResolvingMetadata) {
const slug = await getLandingPageSlug();

if (!slug) {
throw new Error('No landing page set in settings to generate metadata');
}

return generateMetadataUniversalPage({ params: { slug } }, parent);
}

export default async function LandingPage() {
const slug = await getLandingPageSlug();

Expand Down
3 changes: 2 additions & 1 deletion apps/foundation/lib/sanity/sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createSanityConfig } from '@session/sanity-cms/lib/config';
import { NEXT_PUBLIC_SANITY_DATASET, NEXT_PUBLIC_SANITY_PROJECT_ID } from '@/lib/env';
import {
authorSchema,
fileSchema,
pageSchema,
postSchema,
siteSchema,
Expand All @@ -18,6 +19,6 @@ export const sanityConfig = createSanityConfig({
enableDrafts: SANITY_UTIL_PATH.ENABLE_DRAFT,
disableDrafts: SANITY_UTIL_PATH.DISABLE_DRAFT,
},
schemas: [pageSchema, postSchema, authorSchema, socialSchema, specialSchema],
schemas: [pageSchema, postSchema, authorSchema, socialSchema, specialSchema, fileSchema],
singletonSchemas: [siteSchema],
});
7 changes: 7 additions & 0 deletions apps/foundation/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,12 @@
"inThisArticle": "In this article",
"readMore": "Read more",
"morePosts": "More posts"
},
"fileDownload": {
"fetching": "Fetching '{name'}...",
"clickToDownload": "Click to download",
"clickToDownloadAria": "Click to download file",
"openPdfInNewTab": "Open PDF in new tab",
"openPdfInNewTabAria": "Open PDF in new tab"
}
}
2 changes: 1 addition & 1 deletion packages/sanity-cms/api/validate-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const createValidateLinkHandler = ({
});
}

const linksArray = Array.from(links);
const linksArray = Array.from(links).filter((link) => !link.startsWith('mailto:'));

logger.info(`Generating static params for ${links.size} links`);
logger.info(linksArray);
Expand Down
58 changes: 58 additions & 0 deletions packages/sanity-cms/components/SanityFileDownload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use client';

import SanityPdf from './SanityPdf';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { cleanSanityString } from '../lib/string';

type FileDownloadProps = {
fileName: string;
src: string;
strings: {
fetching: string;
clickToDownload: string;
clickToDownloadAria: string;
openPdfInNewTab: string;
openPdfInNewTabAria: string;
};
};

export default function FileDownload({ fileName, src, strings }: FileDownloadProps) {
const [downloaded, setDownloaded] = useState(false);
const router = useRouter();
if (!src || !fileName) return null;

const name = cleanSanityString(fileName);
const srcWithParams = new URL(src);
srcWithParams.searchParams.set('dl', name);

if (src.includes('.pdf')) {
return <SanityPdf src={src} url={srcWithParams} strings={strings} />;
}

// Download file on mount
useEffect(() => {
if (!downloaded) {
setDownloaded(true);
void router.push(srcWithParams.href);
}
}, [src]);

return (
<div className="my-12 flex flex-col items-center justify-center gap-2">
<p className="text-center text-sm">{strings.fetching.replace('{name}', fileName)}</p>
<a
href={srcWithParams.href}
className="group"
target="_blank"
rel="noopener noreferrer"
download
aria-label={strings.clickToDownloadAria}
>
<button className="group-hover:decoration-session-green hover:decoration-session-green decoration-session-black mt-1 w-max text-sm underline group-hover:underline">
{strings.clickToDownload}
</button>
</a>
</div>
);
}
11 changes: 10 additions & 1 deletion packages/sanity-cms/components/SanityImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ import type {
import { cn } from '@session/ui/lib/utils';
import { safeTry } from '@session/util-js/try';
import { Fragment } from 'react';
import type { SanityImageSource } from '@sanity/asset-utils';
import type { CustomImageType } from '../schemas/fields/basic/seo';

export type SanityImageType = ImageFieldsSchemaType | ImageFieldsSchemaTypeWithoutAltText;

export const getSanityImageUrlBuilder = (
client: SessionSanityClient,
value: SanityImageType | SanityImageSource | CustomImageType
) => {
return urlBuilder(client).image(value).fit('max').auto('format');
};

/**
* Build image data from Sanity image schema and the image source.
* @param client Sanity client
* @param value Sanity image schema
*/
async function buildImage(client: SessionSanityClient, value: SanityImageType) {
const src = urlBuilder(client).image(value).fit('max').auto('format').url();
const src = getSanityImageUrlBuilder(client, value).url();
const buffer = await fetch(src).then(async (res) => Buffer.from(await res.arrayBuffer()));

const {
Expand Down
33 changes: 33 additions & 0 deletions packages/sanity-cms/components/SanityPdf.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { NavLink } from '@session/ui/components/NavLink';
import { LinkOutIcon } from '@session/ui/icons/LinkOutIcon';

export type SanityPdfProps = {
src: string;
url: URL;
strings: {
openPdfInNewTab: string;
openPdfInNewTabAria: string;
};
};

export default function SanityPdf({ src, url, strings }: SanityPdfProps) {
if (!url || !src) return null;

return (
<main className="flex h-full min-h-screen w-full max-w-3xl flex-col items-center justify-center gap-4">
<NavLink
href={url.href}
aria-label={strings.openPdfInNewTabAria}
className="inline-flex items-center justify-center gap-2 align-middle"
>
{strings.openPdfInNewTab} <LinkOutIcon className="h-4 w-4" />
</NavLink>
<iframe
src={src}
className="h-full min-h-screen w-full"
style={{ border: 'none' }}
title="PDF"
/>
</main>
);
}
Loading

0 comments on commit 0a986da

Please sign in to comment.