Skip to content

Commit 2148b23

Browse files
authored
fix(bump): 2.0.0 (#514)
* fix(bump): 2.0.0 * fix(bump): 2.0.0 * fix(bump): 2.0.0
1 parent dff994c commit 2148b23

15 files changed

+328
-110
lines changed

app/(posts)/[slug]/loading.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { PostLoadingSkeleton } from '@/components/post/loading';
2+
3+
export default function PostLoading() {
4+
return <PostLoadingSkeleton />;
5+
}

app/(posts)/[slug]/page.tsx

+24-58
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import { MarkdownRenderer } from '@/components/shared/markdown-renderer';
2-
import { getAllPosts, getPostBySlug } from '@/lib/notion';
3-
import { format } from 'date-fns';
4-
import { Link } from 'next-view-transitions';
1+
import { PostContent } from '@/components/post/content';
2+
import { PostHeader } from '@/components/post/header';
3+
import { ContentSkeleton, HeaderSkeleton, RelatedPostsSkeleton } from '@/components/post/loading';
4+
import { RelatedPosts } from '@/components/post/related';
5+
import { config } from '@/config';
6+
import { getPostBySlug } from '@/lib/notion';
57
import { notFound } from 'next/navigation';
8+
import { Suspense } from 'react';
69

710
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
811
const { slug } = await params;
@@ -20,67 +23,30 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
2023
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
2124
const { slug } = await params;
2225

26+
// Get just the post ID for related posts
2327
const post = await getPostBySlug(slug);
2428

2529
if (!post) return notFound();
2630

27-
const allPosts = await getAllPosts();
28-
const relatedPosts = allPosts.filter((p) => p.id !== post.id).slice(0, 2);
29-
3031
return (
31-
<>
32-
<header className='mb-10'>
33-
<h1 className='mb-2 font-medium text-3xl text-foreground'>{post.title}</h1>
34-
<div className='flex items-center gap-4 text-muted-foreground text-sm'>
35-
<time dateTime={post.date}>{post.date ? format(new Date(post.date), 'MMMM d, yyyy') : ''}</time>
36-
{post.tag && post.tag.length > 0 && (
37-
<div className='flex gap-1.5'>
38-
{post.tag.map((tag) => (
39-
<span key={tag} className='rounded-md bg-secondary px-2 py-0.5 text-secondary-foreground text-xs'>
40-
{tag}
41-
</span>
42-
))}
43-
</div>
44-
)}
45-
</div>
46-
</header>
32+
<div className='space-y-6'>
33+
<Suspense fallback={<HeaderSkeleton />}>
34+
<PostHeader slug={slug} />
35+
</Suspense>
36+
37+
<Suspense fallback={<ContentSkeleton />}>
38+
<PostContent slug={slug} />
39+
</Suspense>
40+
41+
<div className='border-tertiary border-b' />
4742

48-
<article className='mb-10'>
49-
<MarkdownRenderer content={post.content || ''} className='prose prose-neutral dark:prose-invert max-w-none' />
50-
</article>
43+
<Suspense fallback={<RelatedPostsSkeleton />}>
44+
<RelatedPosts currentPostId={post.id} />
45+
</Suspense>
5146

52-
<footer className='mb-3 border-tertiary border-t pt-6 md:mb-10'>
53-
<div className='flex items-center justify-between'>
54-
<Link href='/' className='text-foreground text-sm hover:underline'>
55-
Back to home
56-
</Link>
57-
</div>
58-
</footer>
47+
<div className='border-tertiary border-b' />
5948

60-
{relatedPosts.length > 0 && (
61-
<section className='flex w-full items-center justify-between'>
62-
{relatedPosts.map((relatedPost, index) => (
63-
<Link key={relatedPost.id} href={`/${relatedPost.slug}`} className='group block'>
64-
<article>
65-
<p className='mb-1 text-muted-foreground text-sm'>{index === 0 ? 'Previous' : 'Next'}</p>
66-
<h3 className='font-medium text-base text-foreground transition-colors group-hover:text-muted-foreground'>{relatedPost.title}</h3>
67-
<div className='mt-1.5 flex items-center gap-2'>
68-
<p className='text-muted-foreground text-xs'>{relatedPost.date ? format(new Date(relatedPost.date), 'MMM d, yyyy') : ''}</p>
69-
{relatedPost.tag && relatedPost.tag.length > 0 && (
70-
<div className='flex gap-1'>
71-
{relatedPost.tag.slice(0, 1).map((tag) => (
72-
<span key={tag} className='rounded-sm bg-secondary px-1.5 py-0.5 text-secondary-foreground text-xs'>
73-
{tag}
74-
</span>
75-
))}
76-
</div>
77-
)}
78-
</div>
79-
</article>
80-
</Link>
81-
))}
82-
</section>
83-
)}
84-
</>
49+
{config.post.footer}
50+
</div>
8551
);
8652
}

app/loading.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { WritingSectionSkeleton } from '@/components/home/loading';
2+
import { Skeleton } from '@/components/ui/skeleton';
3+
4+
export default function HomeLoading() {
5+
return (
6+
<div className='space-y-10'>
7+
<section>
8+
<Skeleton className='mb-2 h-5 w-16' />
9+
<Skeleton className='h-8 w-3/4' />
10+
<Skeleton className='mt-2 h-4 w-full' />
11+
<Skeleton className='h-4 w-2/3' />
12+
</section>
13+
14+
<WritingSectionSkeleton />
15+
16+
<section>
17+
<Skeleton className='mb-2 h-5 w-16' />
18+
<Skeleton className='h-4 w-full' />
19+
<Skeleton className='h-4 w-2/3' />
20+
</section>
21+
</div>
22+
);
23+
}

app/page.tsx

+11-52
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,26 @@
1+
import { WritingSectionSkeleton } from '@/components/home/loading';
2+
import { WritingSection } from '@/components/home/writing';
13
import NewsletterForm from '@/components/shared/newsletter-form';
2-
import { getAllPosts } from '@/lib/notion';
3-
import { format } from 'date-fns';
4-
import { Link } from 'next-view-transitions';
5-
6-
export default async function Home() {
7-
const posts = await getAllPosts();
4+
import { config } from '@/config';
5+
import { Suspense } from 'react';
86

7+
export default function Home() {
98
return (
109
<div className='mx-auto min-h-screen max-w-2xl px-4 py-10'>
1110
<header className='mb-10'>
12-
<h1 className='mb-1 font-medium text-2xl text-foreground'>Notion Blog React</h1>
13-
<p className='text-muted-foreground text-sm'>A simple blog powered by Notion</p>
11+
<h1 className='mb-1 font-medium text-2xl text-foreground'>{config.site.name}</h1>
12+
<p className='text-muted-foreground text-sm'>{config.site.description}</p>
1413
</header>
1514

16-
<section className='mb-10'>
17-
<p className='mb-2 font-medium text-muted-foreground text-sm'>Writing</p>
18-
<div className='space-y-6'>
19-
{posts.length > 0 ? (
20-
posts.map((post) => (
21-
<Link key={post.id} href={`/${post.slug}`} className='group block'>
22-
<article>
23-
<h3 className='mb-1 font-medium text-foreground transition-colors group-hover:text-muted-foreground'>{post.title}</h3>
24-
<p className='line-clamp-2 text-muted-foreground text-sm'>{post.description}</p>
25-
<div className='mt-1.5 flex items-center gap-2'>
26-
<p className='text-muted-foreground text-xs'>{post.date ? format(new Date(post.date), 'MMM d, yyyy') : ''}</p>
27-
{post.tag && post.tag.length > 0 && (
28-
<div className='flex gap-1'>
29-
{post.tag.slice(0, 2).map((tag) => (
30-
<span key={tag} className='rounded-sm bg-secondary px-1.5 py-0.5 text-secondary-foreground text-xs'>
31-
{tag}
32-
</span>
33-
))}
34-
{post.tag.length > 2 && <span className='text-muted-foreground text-xs'>+{post.tag.length - 2}</span>}
35-
</div>
36-
)}
37-
</div>
38-
</article>
39-
</Link>
40-
))
41-
) : (
42-
<p className='text-muted-foreground'>No posts found.</p>
43-
)}
44-
</div>
45-
</section>
15+
<Suspense fallback={<WritingSectionSkeleton />}>
16+
<WritingSection />
17+
</Suspense>
4618

4719
<section className='mb-10'>
4820
<NewsletterForm />
4921
</section>
5022

51-
<section>
52-
<p className='mb-2 font-medium text-muted-foreground text-sm'>More</p>
53-
<p className='text-muted-foreground text-sm'>
54-
Follow Harry Yep on{' '}
55-
<a href='https://twitter.com/okisdev' className='text-foreground transition-colors hover:text-muted-foreground'>
56-
Twitter
57-
</a>{' '}
58-
and{' '}
59-
<a href='https://github.com/okisdev' className='text-foreground transition-colors hover:text-muted-foreground'>
60-
GitHub
61-
</a>
62-
.
63-
</p>
64-
</section>
23+
{config.home.extraSection}
6524
</div>
6625
);
6726
}

app/provider.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { Toaster } from '@/components/ui/sonner';
44
import { Analytics } from '@vercel/analytics/react';
55
import { SpeedInsights } from '@vercel/speed-insights/next';
6+
import { AppProgressBar as ProgressBar } from 'next-nprogress-bar';
67
import { ThemeProvider } from 'next-themes';
78

89
export default function BodyProvider({
@@ -14,6 +15,8 @@ export default function BodyProvider({
1415
<ThemeProvider attribute='class' defaultTheme='system' value={{ light: 'light', dark: 'dark' }} disableTransitionOnChange>
1516
{children}
1617

18+
<ProgressBar height='2px' color='hsl(var(--foreground))' options={{ showSpinner: false }} shallowRouting />
19+
1720
<Toaster richColors position='top-right' />
1821

1922
<SpeedInsights />

bun.lock

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"lucide-react": "^0.487.0",
1717
"next": "15.2.4",
1818
"next-mdx-remote": "^5.0.0",
19+
"next-nprogress-bar": "^2.4.7",
1920
"next-themes": "^0.4.6",
2021
"next-view-transitions": "^0.3.4",
2122
"react": "^19.1.0",
@@ -586,6 +587,8 @@
586587

587588
"next-mdx-remote": ["[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.23.5", "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", "unist-util-remove": "^3.1.0", "vfile": "^6.0.1", "vfile-matter": "^5.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ=="],
588589

590+
"next-nprogress-bar": ["[email protected]", "", { "dependencies": { "nprogress-v2": "^1.0.4" } }, "sha512-OeveNQYFBhQhZ+RgrDnvHNUEQfHCmipymmD4AfAVE9pFV4jeWi7/nNK5f0lIk7ODRrtjyyr/n2YpkRbs5kUoMg=="],
591+
589592
"next-themes": ["[email protected]", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
590593

591594
"next-view-transitions": ["[email protected]", "", { "peerDependencies": { "next": ">=14.0.0", "react": "^18.2.0", "react-dom": "^18.2.0" } }, "sha512-SSiskenQ8JkEFGzPjvFwC5LGGoqgTxM5dxexkeugxvcXFLpWI2ZUh4IsCURD3ovW+8Ue7xXlrtrpy8b7XR7IwQ=="],
@@ -594,6 +597,8 @@
594597

595598
"node-releases": ["[email protected]", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
596599

600+
"nprogress-v2": ["[email protected]", "", {}, "sha512-MypWLNIPIM07SS0bAc/oac0vhVFz9vAHm7d1sj//Pnf3J03LQ3CuWrlDteIu6exq0fIvkDJ6tUDRWLaifsIt5w=="],
601+
597602
"parse-entities": ["[email protected]", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
598603

599604
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],

components/home/loading.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Skeleton } from '@/components/ui/skeleton';
2+
3+
export function WritingSectionSkeleton() {
4+
// Using static IDs for skeleton items
5+
const skeletonIds = ['skeleton-1', 'skeleton-2', 'skeleton-3'];
6+
7+
return (
8+
<section className='mb-10'>
9+
<Skeleton className='mb-2 h-5 w-16' />
10+
<div className='space-y-6'>
11+
{skeletonIds.map((id) => (
12+
<div key={id} className='block'>
13+
<Skeleton className='mb-1 h-6 w-3/4' />
14+
<Skeleton className='h-4 w-full' />
15+
<Skeleton className='h-4 w-2/3' />
16+
<div className='mt-1.5 flex items-center gap-2'>
17+
<Skeleton className='h-3 w-20' />
18+
<div className='flex gap-1'>
19+
<Skeleton className='h-3 w-16 rounded-sm' />
20+
<Skeleton className='h-3 w-16 rounded-sm' />
21+
</div>
22+
</div>
23+
</div>
24+
))}
25+
</div>
26+
</section>
27+
);
28+
}

components/home/writing.tsx

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { getAllPosts } from '@/lib/notion';
2+
import { format } from 'date-fns';
3+
import { Link } from 'next-view-transitions';
4+
5+
export async function WritingSection() {
6+
const posts = await getAllPosts();
7+
8+
return (
9+
<section className='mb-10'>
10+
<p className='mb-2 font-medium text-muted-foreground text-sm'>Writing</p>
11+
<div className='space-y-6'>
12+
{posts.length > 0 ? (
13+
posts.map((post) => (
14+
<Link key={post.id} href={`/${post.slug}`} className='group block'>
15+
<article>
16+
<h3 className='mb-1 font-medium text-foreground transition-colors group-hover:text-muted-foreground'>{post.title}</h3>
17+
<p className='line-clamp-2 text-muted-foreground text-sm'>{post.description}</p>
18+
<div className='mt-1.5 flex items-center gap-2'>
19+
<p className='text-muted-foreground text-xs'>{post.date ? format(new Date(post.date), 'MMM d, yyyy') : ''}</p>
20+
{post.tag && post.tag.length > 0 && (
21+
<div className='flex gap-1'>
22+
{post.tag.slice(0, 2).map((tag) => (
23+
<span key={tag} className='rounded-sm bg-secondary px-1.5 py-0.5 text-secondary-foreground text-xs'>
24+
{tag}
25+
</span>
26+
))}
27+
{post.tag.length > 2 && <span className='text-muted-foreground text-xs'>+{post.tag.length - 2}</span>}
28+
</div>
29+
)}
30+
</div>
31+
</article>
32+
</Link>
33+
))
34+
) : (
35+
<p className='text-muted-foreground'>No posts found.</p>
36+
)}
37+
</div>
38+
</section>
39+
);
40+
}

components/post/content.tsx

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { MarkdownRenderer } from '@/components/shared/markdown-renderer';
2+
import { getPostBySlug } from '@/lib/notion';
3+
import { notFound } from 'next/navigation';
4+
5+
export async function PostContent({ slug }: { slug: string }) {
6+
const post = await getPostBySlug(slug);
7+
8+
if (!post) return notFound();
9+
10+
return (
11+
<article>
12+
<MarkdownRenderer content={post.content || ''} className='prose prose-neutral dark:prose-invert max-w-none' />
13+
</article>
14+
);
15+
}

components/post/header.tsx

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getPostBySlug } from '@/lib/notion';
2+
import { format } from 'date-fns';
3+
import { notFound } from 'next/navigation';
4+
5+
export async function PostHeader({ slug }: { slug: string }) {
6+
const post = await getPostBySlug(slug);
7+
8+
if (!post) return notFound();
9+
10+
return (
11+
<header>
12+
<h1 className='mb-2 font-medium text-3xl text-foreground'>{post.title}</h1>
13+
<div className='flex items-center gap-4 text-muted-foreground text-sm'>
14+
<time dateTime={post.date}>{post.date ? format(new Date(post.date), 'MMMM d, yyyy') : ''}</time>
15+
{post.tag && post.tag.length > 0 && (
16+
<div className='flex gap-1.5'>
17+
{post.tag.map((tag) => (
18+
<span key={tag} className='rounded-md bg-secondary px-2 py-0.5 text-secondary-foreground text-xs'>
19+
{tag}
20+
</span>
21+
))}
22+
</div>
23+
)}
24+
</div>
25+
</header>
26+
);
27+
}

0 commit comments

Comments
 (0)