@@ -1,14 +1,16 @@
|
|||||||
import { defineQuery, PortableText } from "next-sanity";
|
import { PortableText, defineQuery } from "next-sanity";
|
||||||
import imageUrlBuilder from "@sanity/image-url";
|
import imageUrlBuilder from "@sanity/image-url";
|
||||||
import type { SanityImageSource } from "@sanity/image-url/lib/types/types";
|
|
||||||
import { client, sanityFetch } from "../../../sanity/client";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { Post, SanityImageAsset } from "@/sanity/sanity.types";
|
|
||||||
import {getImageDimensions} from '@sanity/asset-utils'
|
import {getImageDimensions} from '@sanity/asset-utils'
|
||||||
|
import type { SanityImageSource } from "@sanity/image-url/lib/types/types";
|
||||||
|
import { client } from "@/sanity/client";
|
||||||
|
import { sanityFetch } from '@/sanity/client';
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Post, SanityImageAsset } from "@/sanity/sanity.types";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
const POSTS_QUERY = defineQuery(`*[_type == "post"]{ slug }`);
|
||||||
const POST_QUERY = defineQuery(`*[_type == "post" && slug.current == $slug][0]`);
|
const POST_QUERY = defineQuery(`*[_type == "post" && slug.current == $slug][0]`);
|
||||||
const POSTS_QUERY = defineQuery(`*[_type == "post"]{slug}`)
|
|
||||||
|
|
||||||
type PageParams = Promise<{
|
type PageParams = Promise<{
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -22,23 +24,46 @@ const urlFor = (source: SanityImageSource) =>
|
|||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
const posts: Post[] = (await sanityFetch({ query: POSTS_QUERY, stega: false, perspective: "published" })).data;
|
const posts: Post[] = (await sanityFetch({ query: POSTS_QUERY, stega: false, perspective: "published" })).data;
|
||||||
return posts.map((post) => ({ slug: post.slug?.current ?? null })).filter((post) => post.slug);
|
return posts.map((post) => ({
|
||||||
|
slug: post.slug?.current ?? ""
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function PostPage(props: { params: PageParams }) {
|
export async function generateMetadata(props: { params: Promise<PageParams> }): Promise<Metadata> {
|
||||||
const { slug } = await props.params;
|
const params = await props.params;
|
||||||
|
const post: Post = (await sanityFetch({ query: POST_QUERY, params, stega: false })).data;
|
||||||
const post: Post = (await sanityFetch({ query: POST_QUERY, params: { slug } })).data;
|
const ogImage = post.mainImage
|
||||||
|
? urlFor(post.mainImage)?.width(550).height(310).url()
|
||||||
if (!post) {
|
: null;
|
||||||
return (
|
const firstBlock = post.body ? post.body[0] : undefined;
|
||||||
<main>
|
let description: string | undefined = undefined;
|
||||||
<p>Post not found</p>
|
if (firstBlock && firstBlock._type === 'block' && firstBlock.children) {
|
||||||
</main>
|
description = firstBlock.children.map(child => child.text).join(" ");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const postImageUrl = post.mainImage ? urlFor(post.mainImage)?.width(550).height(310).url() : null;
|
return {
|
||||||
|
title: post.title,
|
||||||
|
description,
|
||||||
|
openGraph: {
|
||||||
|
title: post.title,
|
||||||
|
description,
|
||||||
|
type: "article",
|
||||||
|
publishedTime: post.publishedAt,
|
||||||
|
url: `/blog/${post.slug}`,
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: ogImage ?? "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: post.title,
|
||||||
|
description,
|
||||||
|
images: ogImage ? [ogImage] : [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function dynamicHeight(originalHeight: number, originalWidth: number, isInline: boolean) {
|
function dynamicHeight(originalHeight: number, originalWidth: number, isInline: boolean) {
|
||||||
const targetWidth = isInline ? 100 : 768;
|
const targetWidth = isInline ? 100 : 768;
|
||||||
@@ -58,7 +83,7 @@ export default async function PostPage(props: { params: PageParams }) {
|
|||||||
.fit('max')
|
.fit('max')
|
||||||
.auto('format')
|
.auto('format')
|
||||||
.url()}
|
.url()}
|
||||||
width={isInline ? (width >= 100 ? 100 : width) : (width >= 768 ? 768 : width)}
|
width={isInline ? (width >= 100 ? 768 : width) : (width >= 768 ? 768 : width)}
|
||||||
height={dynamicHeight(height, width, isInline)}
|
height={dynamicHeight(height, width, isInline)}
|
||||||
alt={value.altText || ' '}
|
alt={value.altText || ' '}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -77,23 +102,32 @@ export default async function PostPage(props: { params: PageParams }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default async function PostPage(props: { params: Promise<PageParams> }) {
|
||||||
|
const params = await props.params;
|
||||||
|
const post: Post = (await sanityFetch({ query: POST_QUERY, params })).data;
|
||||||
|
const postImageUrl = post.mainImage
|
||||||
|
? urlFor(post.mainImage)?.width(912).height(576).url()
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="container mx-auto min-h-screen max-w-3xl p-8 flex flex-col gap-4">
|
<main className="container mx-auto min-h-screen max-w-3xl p-8 flex flex-col gap-4">
|
||||||
<Link href="/blog" className="hover:underline">
|
<Link href="/blog" className="hover:underline">
|
||||||
← Blog
|
← Back to posts
|
||||||
</Link>
|
</Link>
|
||||||
{postImageUrl && (
|
{postImageUrl && (
|
||||||
<Image
|
<Image
|
||||||
src={postImageUrl}
|
src={postImageUrl}
|
||||||
alt={`Banner for ${post.title}` }
|
alt={post.title ?? `Blog Post Banner Image for ${post.title}`}
|
||||||
className="aspect-video rounded-xl"
|
className="aspect-video rounded-xl w-full"
|
||||||
width="550"
|
width="912"
|
||||||
height="310"
|
height="576"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<h1 className="text-4xl font-bold mb-8">{post.title}</h1>
|
<h1 className="text-4xl font-bold mb-8">{post.title}</h1>
|
||||||
<div className="prose">
|
<p className="text-sm text-neutral-600 dark:text-neutral-400 max-w-full w-fit">
|
||||||
<p>Published: {new Date(post.publishedAt ?? "").toISOString().substring(0, 10)}</p>
|
{post.publishedAt}
|
||||||
|
</p>
|
||||||
|
<div className="items-start mt-2 mb-8 text-sm prose dark:prose-invert max-w-full w-full text-left">
|
||||||
{Array.isArray(post.body) && <PortableText value={post.body} components={ components } />}
|
{Array.isArray(post.body) && <PortableText value={post.body} components={ components } />}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
Reference in New Issue
Block a user