-
-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathpage.tsx
More file actions
129 lines (117 loc) · 3.57 KB
/
page.tsx
File metadata and controls
129 lines (117 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import type { Metadata, ResolvingMetadata } from "next";
import { type PortableTextBlock } from "next-sanity";
import { notFound } from "next/navigation";
import { Suspense } from "react";
import Avatar from "@/components/avatar";
import DateComponent from "@/components/date";
import MoreContent from "@/components/more-content";
import PortableText from "@/components/portable-text";
import type { PostQueryResult } from "@/sanity/types";
import { sanityFetch } from "@/sanity/lib/live";
import { postQuery } from "@/sanity/lib/queries";
import { resolveOpenGraphImage } from "@/sanity/lib/utils";
import CoverMedia from "@/components/cover-media";
import MoreHeader from "@/components/more-header";
import { BreadcrumbLinks } from "@/components/breadrumb-links";
import SponsorCard from "@/components/sponsor-card";
type Params = Promise<{ slug: string }>;
export async function generateMetadata(
{ params }: { params: Params },
parent: ResolvingMetadata,
): Promise<Metadata> {
const { slug } = await params;
const post = (
await sanityFetch({
query: postQuery,
params: { slug },
stega: false,
})
).data as PostQueryResult;
const previousImages = (await parent).openGraph?.images || [];
const ogImage = resolveOpenGraphImage(post?.coverImage);
return {
authors:
post?.author?.map((a) => {
return { name: a.title };
}) || [],
title: post?.title,
description: post?.excerpt,
openGraph: {
images: ogImage ? ogImage : previousImages,
},
} satisfies Metadata;
}
export default async function PostPage({ params }: { params: Params }) {
const { slug } = await params;
const [post] = (
await Promise.all([
sanityFetch({
query: postQuery,
params: { slug },
}),
])
).map((res) => res.data) as [PostQueryResult];
if (!post?._id) {
return notFound();
}
return (
<div className="container px-5 mx-auto">
<BreadcrumbLinks links={[{ title: "Blog", href: "/blog/page/1" }]} />
<article>
<h1 className="mb-12 text-4xl font-bold leading-tight tracking-tighter text-balance md:text-7xl md:leading-none lg:text-8xl">
{post.title}
</h1>
<div className="mb-8 sm:mx-0 md:mb-16">
<CoverMedia
cloudinaryImage={post?.coverImage}
cloudinaryVideo={post?.videoCloudinary}
youtube={post?.youtube}
/>
</div>
<div className="flex flex-wrap justify-between">
<div className="max-w-2xl sm:max-w-none flex-1">
<div className="mb-6">
{post?.author && (
<div className="flex flex-wrap gap-2">
{post?.author?.map((a) => (
<Avatar
key={a._id}
name={a.title}
href={`/author/${a?.slug}`}
coverImage={a?.coverImage}
/>
))}
</div>
)}
</div>
<div className="mb-4 text-lg">
<DateComponent dateString={post.date} />
</div>
</div>
</div>
{post?.sponsor?.length && (
<section className="flex flex-col mt-10 mb-10">
<h2 className="mb-4 text-2xl font-bold">Sponsors</h2>
<hr className="border-accent-2" />
<div className="my-12 ">
<SponsorCard sponsors={post.sponsor} />
</div>
<hr className="border-accent-2" />
</section>
)}
{post.content?.length && (
<PortableText
className="mx-auto prose-violet lg:prose-xl dark:prose-invert"
value={post.content as PortableTextBlock[]}
/>
)}
</article>
<aside>
<MoreHeader title="Recent Posts" href="/blog/page/1" />
<Suspense>
<MoreContent type={post._type} skip={post._id} limit={2} />
</Suspense>
</aside>
</div>
);
}