Skip to content
This repository has been archived by the owner on Mar 12, 2023. It is now read-only.

🚀 Migration Next.js to 13 #139

Merged
merged 39 commits into from Nov 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4f9688a
Install next@latest react@latest react-dom@latest eslint-config-next@…
otoyo Nov 21, 2022
4d94eb7
Update @testing-library/jest-dom abd @testing-library/react
otoyo Nov 22, 2022
4a31b92
Enable appDir
otoyo Nov 22, 2022
0fb9902
Ignore .vscode directory
otoyo Nov 22, 2022
b8de44e
Style tsconfig.json
otoyo Nov 23, 2022
fa3ca1a
Remove next/router and next/head from DocumentHead
otoyo Nov 22, 2022
9b4b887
Add viewport to DocumentHead
otoyo Nov 22, 2022
19ded54
Style DocumentHead
otoyo Nov 22, 2022
857c889
Fix tests for DocumentHead
otoyo Nov 22, 2022
3996531
Move server-constants.js to app/
otoyo Nov 22, 2022
f5bf8a2
Fix server-constants path
otoyo Nov 22, 2022
5515a2d
Use next/link in Footer
otoyo Nov 22, 2022
6b09764
Remove ExtLink component
otoyo Nov 22, 2022
82ff023
Define GoogleAnalytics as a client component
otoyo Nov 22, 2022
145cc1f
Define Header as a client component
otoyo Nov 22, 2022
04fad65
Define RootLayout
otoyo Nov 22, 2022
dcbda4a
Define RootHead
otoyo Nov 22, 2022
7f7ca0d
Define RootPage
otoyo Nov 22, 2022
af209e3
Remove legacy props from Link component
otoyo Nov 22, 2022
4bfd4ba
Add 'use client' to components
otoyo Nov 22, 2022
29931a4
Stop using next/dynamic
otoyo Nov 22, 2022
46cab88
Define getBlock() in Notion client
otoyo Nov 23, 2022
dd3a280
Define /api/blocks/[id]
otoyo Nov 23, 2022
81c2d81
Move ImageBlock to components/notion-blocks/ and redesign
otoyo Nov 23, 2022
ebead2e
Define NotFound component
otoyo Nov 23, 2022
06f8af8
Define BlogHead
otoyo Nov 23, 2022
7851747
Define BlogPage
otoyo Nov 23, 2022
fb026a2
Define BlogSlugHead
otoyo Nov 23, 2022
cdb2091
Define BlogSlugPage
otoyo Nov 23, 2022
6842d24
Define BlogBeforeDateHead
otoyo Nov 23, 2022
a1f1814
Define BlogBeforeDatePage
otoyo Nov 23, 2022
cca8b9c
Define BlogTagHead
otoyo Nov 23, 2022
f581f47
Define BlogTagPage
otoyo Nov 23, 2022
d9f4375
Define BlogTagBeforeDateHead
otoyo Nov 23, 2022
e369c21
BlogTagBeforeDatePage
otoyo Nov 23, 2022
4251dca
Delete old page components
otoyo Nov 23, 2022
1774b0c
Delete /api/blocks API
otoyo Nov 25, 2022
89885f4
Disable dynamicParams temporarily
otoyo Nov 25, 2022
95ecba5
Include GoogleAnalytics component each page.tsx
otoyo Nov 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -29,6 +29,7 @@ yarn-error.log*

.now
.vercel
.vscode

tmp/
public/notion_images/*
Expand Down
30 changes: 17 additions & 13 deletions __tests__/components/document-head.test.tsx
@@ -1,29 +1,33 @@
import { render } from '@testing-library/react'
import React from 'react';
import { createRoot } from 'react-dom/client';
import DocumentHead from '../../components/document-head'

jest.mock('next/router', () => ({
useRouter() {
return {
asPath: '/',
pathname: '/',
}
},
}))

const mockNextPublicURL = jest.fn()
jest.mock('../../lib/notion/server-constants', () => ({
jest.mock('../../app/server-constants', () => ({
get NEXT_PUBLIC_URL() {
return mockNextPublicURL()
},
}))

let container: Element

beforeEach(() => {
container = document.createElement('div')
document.body.appendChild(container)
})

afterEach(() => {
document.body.removeChild(container);
container = null;
})

describe('DocumentHead', () => {
describe('with NEXT_PUBLIC_URL', () => {
mockNextPublicURL.mockReturnValue('http://localhost')

it('renders the component', () => {
expect(() => {
render(<DocumentHead />)
createRoot(container).render(<DocumentHead />)
}).not.toThrow()
})
})
Expand All @@ -33,7 +37,7 @@ describe('DocumentHead', () => {

it('renders the component', () => {
expect(() => {
render(<DocumentHead />)
createRoot(container).render(<DocumentHead />)
}).not.toThrow()
})
})
Expand Down
19 changes: 19 additions & 0 deletions app/blog/[slug]/head.tsx
@@ -0,0 +1,19 @@
import { NEXT_PUBLIC_URL } from '../../server-constants'
import { getPostBySlug } from '../../../lib/notion/client'
import { getBlogLink } from '../../../lib/blog-helpers'
import DocumentHead from '../../../components/document-head'

const BlogSlugHead = async ({ params: { slug } }) => {
const post = await getPostBySlug(slug)

return (
<DocumentHead
title={post.Title}
description={post.Excerpt}
path={getBlogLink(post.Slug)}
urlOgImage={NEXT_PUBLIC_URL && post.OGImage && new URL(`/api/og-image/${post.Slug}`, NEXT_PUBLIC_URL).toString()}
/>
)
}

export default BlogSlugHead
102 changes: 102 additions & 0 deletions app/blog/[slug]/page.tsx
@@ -0,0 +1,102 @@
import { redirect } from 'next/navigation'
import { NEXT_PUBLIC_URL } from '../../server-constants'
import { Post } from '../../../lib/notion/interfaces'
import GoogleAnalytics from '../../../components/google-analytics'
import {
BlogPostLink,
BlogTagLink,
NoContents,
PostBody,
PostDate,
PostTags,
PostTitle,
} from '../../../components/blog-parts'
import SocialButtons from '../../../components/social-buttons'
import styles from '../../../styles/blog.module.css'
import { getBlogLink } from '../../../lib/blog-helpers'
import {
getPosts,
getAllPosts,
getRankedPosts,
getPostBySlug,
getPostsByTag,
getAllTags,
getAllBlocksByBlockId,
} from '../../../lib/notion/client'

export const revalidate = 30
// TODO: Enable after fixed https://github.com/vercel/next.js/issues/43357
// export const dynamicParams = false

export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map(p => ({ slug: p.Slug }))
}

const BlogSlugPage = async ({ params: { slug } }) => {
const post = await getPostBySlug(slug)

if (!post) {
console.log(`Failed to find post for slug: ${slug}`)
redirect('/blog')
}

const [
blocks,
rankedPosts,
recentPosts,
tags,
sameTagPosts,
] = await Promise.all([
getAllBlocksByBlockId(post.PageId),
getRankedPosts(),
getPosts(5),
getAllTags(),
getPostsByTag(post.Tags[0], 6),
])

const otherPostsHavingSameTag = sameTagPosts.filter((p: Post) => p.Slug !== post.Slug)

return (
<>
<GoogleAnalytics pageTitle={post.Title} />
<div className={styles.container}>
<div className={styles.mainContent}>
<div className={styles.post}>
<PostDate post={post} />
<PostTags post={post} />
<PostTitle post={post} enableLink={false} />

<NoContents contents={blocks} />
<PostBody blocks={blocks} />

<footer>
{NEXT_PUBLIC_URL && (
<SocialButtons
title={post.Title}
url={new URL(
getBlogLink(post.Slug),
NEXT_PUBLIC_URL
).toString()}
id={post.Slug}
/>
)}
</footer>
</div>
</div>

<div className={styles.subContent}>
<BlogPostLink
heading="Posts in the same category"
posts={otherPostsHavingSameTag}
/>
<BlogPostLink heading="Recommended" posts={rankedPosts} />
<BlogPostLink heading="Latest posts" posts={recentPosts} />
<BlogTagLink heading="Categories" tags={tags} />
</div>
</div>
</>
)
}

export default BlogSlugPage
11 changes: 11 additions & 0 deletions app/blog/before/[date]/head.tsx
@@ -0,0 +1,11 @@
import DocumentHead from '../../../../components/document-head'

const BlogBeforeDateHead = async ({ params: { date: encodedDate } }) => {
const date = decodeURIComponent(encodedDate)

return (
<DocumentHead description={`Post before ${date.split('T')[0]}`} />
)
}

export default BlogBeforeDateHead
76 changes: 76 additions & 0 deletions app/blog/before/[date]/page.tsx
@@ -0,0 +1,76 @@
import { notFound } from 'next/navigation'
import { NUMBER_OF_POSTS_PER_PAGE } from '../../../../app/server-constants'
import GoogleAnalytics from '../../../../components/google-analytics'
import {
getRankedPosts,
getPostsBefore,
getFirstPost,
getAllTags,
} from '../../../../lib/notion/client'
import {
BlogPostLink,
BlogTagLink,
NextPageLink,
NoContents,
PostDate,
PostExcerpt,
PostTags,
PostTitle,
ReadMoreLink,
} from '../../../../components/blog-parts'
import styles from '../../../../styles/blog.module.css'

export const revalidate = 3600

const BlogBeforeDatePage = async ({ params: { date: encodedDate } }) => {
const date = decodeURIComponent(encodedDate)

if (!Date.parse(date) || !/^\d{4}-\d{2}-\d{2}/.test(date)) {
notFound()
}

const [posts, firstPost, rankedPosts, tags] = await Promise.all([
getPostsBefore(date, NUMBER_OF_POSTS_PER_PAGE),
getFirstPost(),
getRankedPosts(),
getAllTags(),
])

return (
<>
<GoogleAnalytics pageTitle={`Posts before ${date.split('T')[0]}`} />
<div className={styles.container}>
<div className={styles.mainContent}>
<header>
<h2>Posts before {date.split('T')[0]}</h2>
</header>

<NoContents contents={posts} />

{posts.map(post => {
return (
<div className={styles.post} key={post.Slug}>
<PostDate post={post} />
<PostTags post={post} />
<PostTitle post={post} />
<PostExcerpt post={post} />
<ReadMoreLink post={post} />
</div>
)
})}

<footer>
<NextPageLink firstPost={firstPost} posts={posts} />
</footer>
</div>

<div className={styles.subContent}>
<BlogPostLink heading="Recommended" posts={rankedPosts} />
<BlogTagLink heading="Categories" tags={tags} />
</div>
</div>
</>
)
}

export default BlogBeforeDatePage
7 changes: 7 additions & 0 deletions app/blog/head.tsx
@@ -0,0 +1,7 @@
import DocumentHead from '../../components/document-head'

const BlogHead = () => (
<DocumentHead title="Blog" path="/blog" />
)

export default BlogHead
65 changes: 65 additions & 0 deletions app/blog/page.tsx
@@ -0,0 +1,65 @@
import { NUMBER_OF_POSTS_PER_PAGE } from '../../app/server-constants'
import GoogleAnalytics from '../../components/google-analytics'
import {
BlogPostLink,
BlogTagLink,
NextPageLink,
NoContents,
PostDate,
PostExcerpt,
PostTags,
PostTitle,
ReadMoreLink,
} from '../../components/blog-parts'
import styles from '../../styles/blog.module.css'
import {
getPosts,
getFirstPost,
getRankedPosts,
getAllTags,
} from '../../lib/notion/client'

export const revalidate = 60

const BlogPage = async () => {
const [posts, firstPost, rankedPosts, tags] = await Promise.all([
getPosts(NUMBER_OF_POSTS_PER_PAGE),
getFirstPost(),
getRankedPosts(),
getAllTags(),
])

return (
<>
<GoogleAnalytics pageTitle="Blog" />
<div className={styles.container}>
<div className={styles.mainContent}>
<NoContents contents={posts} />

{posts.map(post => {
return (
<div className={styles.post} key={post.Slug}>
<PostDate post={post} />
<PostTags post={post} />
<PostTitle post={post} />
<PostExcerpt post={post} />
<ReadMoreLink post={post} />
</div>
)
})}

<footer>
<NextPageLink firstPost={firstPost} posts={posts} />
</footer>
</div>

<div className={styles.subContent}>
<BlogPostLink heading="Recommended" posts={rankedPosts} />
<BlogTagLink heading="Categories" tags={tags} />
</div>
</div>
</>
)
}

export default BlogPage
12 changes: 12 additions & 0 deletions app/blog/tag/[tag]/before/[date]/head.tsx
@@ -0,0 +1,12 @@
import DocumentHead from '../../../../../../components/document-head'

const BlogTagBeforeDateHead = async ({ params: { tag: encodedTag, date: encodedDate } }) => {
const tag = decodeURIComponent(encodedTag)
const date = decodeURIComponent(encodedDate)

return (
<DocumentHead description={`Posts in ${tag} before ${date.split('T')[0]}`} />
)
}

export default BlogTagBeforeDateHead