-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathpage.tsx
More file actions
125 lines (120 loc) · 4.46 KB
/
Copy pathpage.tsx
File metadata and controls
125 lines (120 loc) · 4.46 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
import type { Metadata } from 'next';
import Link from 'next/link';
import { Suspense, ViewTransition } from 'react';
import { ErrorBoundary } from '~/components/error-boundary';
import { ExternalLink } from '~/components/external-link';
import { BlogStats } from '~/features/blog/components/blog-stats';
import { getBlogMetadata } from '~/features/blog/functions/queries';
import { FEED_ALTERNATE_TYPES, FEED_PATH, WRITING_INTRO } from '~/lib/constants';
import { formatDate, viewTransitionName } from '~/lib/utils';
const socialImage = {
url: '/opengraph-image',
width: 1200,
height: 630,
alt: 'Nikhil S — things I build and what I learn along the way.',
};
export const metadata: Metadata = {
title: 'Writing',
description: 'Things I figured out, things I built, and a few questions along the way.',
alternates: {
canonical: '/blog',
types: FEED_ALTERNATE_TYPES,
},
openGraph: {
title: 'Writing | Nikhil S',
description: 'Things I figured out, things I built, and a few questions along the way.',
url: '/blog',
images: [socialImage],
},
twitter: {
card: 'summary_large_image',
title: 'Writing | Nikhil S',
description: 'Things I figured out, things I built, and a few questions along the way.',
images: [socialImage],
},
};
export default async function BlogsPage() {
const blog = await getBlogMetadata();
const postsByYear = Map.groupBy(blog, (post) => post.metadata.publishedAt.getFullYear());
return (
<section className='space-y-16'>
<header>
<div className='mb-5 flex items-baseline justify-between gap-4'>
<h1 className='text-4xl font-medium tracking-tight sm:text-5xl'>
writing<span className='text-primary'>.</span>
</h1>
<ExternalLink
href={FEED_PATH}
className='text-link inline-flex items-center gap-1 font-mono text-xs'
aria-label='RSS feed'
>
rss
</ExternalLink>
</div>
<p className='text-muted-foreground max-w-lg leading-7 text-pretty'>{WRITING_INTRO}</p>
<div className='mt-5'>
<ErrorBoundary
fallback={
<p className='text-muted-foreground text-xs'>Stats are unavailable right now.</p>
}
>
<Suspense
fallback={
<p className='text-muted-foreground font-mono text-xs leading-6'>loading stats…</p>
}
>
<BlogStats />
</Suspense>
</ErrorBoundary>
</div>
</header>
<div className='space-y-12'>
{[...postsByYear.entries()].map(([year, posts]) => (
<section
key={year}
aria-labelledby={`year-${year}`}
className='grid gap-4 sm:grid-cols-[64px_1fr] sm:gap-8'
>
<h2
id={`year-${year}`}
className='section-label pt-1 tabular-nums sm:sticky sm:top-8 sm:self-start'
>
{year}
</h2>
<ul className='space-y-8'>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`} className='focus-ring group block'>
<ViewTransition
name={viewTransitionName(post.slug)}
default='none'
share='article-title'
>
<h3 className='leading-7 text-pretty underline-offset-4 group-hover:underline'>
{post.metadata.title}
</h3>
</ViewTransition>
<p className='text-muted-foreground mt-2 text-sm leading-6 text-pretty'>
{post.metadata.summary}
</p>
<p className='text-muted-foreground mt-3 flex flex-wrap items-center gap-x-2 font-mono text-[11px]'>
<time dateTime={post.metadata.publishedAt.toISOString()}>
{formatDate(post.metadata.publishedAt)}
</time>
{post.metadata.interactive ? (
<>
<span aria-hidden='true'>·</span>
<span className='text-primary'>interactive</span>
</>
) : null}
</p>
</Link>
</li>
))}
</ul>
</section>
))}
</div>
</section>
);
}