Skip to content

Commit 7ce418f

Browse files
authored
Merge pull request #36 from 01-binary/refactor
Refactor
2 parents 1ef8e99 + 14a80ec commit 7ce418f

23 files changed

+296
-342
lines changed

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
"lodash": "^4.17.21",
1818
"next": "^13.4.4",
1919
"notion-to-jsx": "^1.2.7",
20-
"notion-to-utils": "^1.0.2",
20+
"notion-to-utils": "^1.1.0",
2121
"p-map": "^7.0.2",
22+
"plaiceholder": "^3.0.0",
2223
"prismjs": "^1.30.0",
2324
"react": "^18.2.0",
2425
"react-dom": "^18.2.0",
2526
"react-icons": "^4.9.0",
26-
"rss": "^1.2.2"
27+
"rss": "^1.2.2",
28+
"sharp": "^0.34.1",
29+
"yarn": "^1.22.22"
2730
},
2831
"devDependencies": {
2932
"@types/lodash": "^4.14.195",

pages/api/cover-image.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

pages/index.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ import { GetStaticProps } from 'next';
44
import { categoriesAtom } from '@/atoms/categories';
55
import { postsAtom } from '@/atoms/posts';
66
import Home from '@/features/home';
7-
import { Category, Post } from '@/interfaces';
8-
import { parsePosts, getNotionPosts, getCategories } from '@/utils';
9-
import { getPreviewImages } from '@/utils';
7+
import { Category, PostMeta } from '@/interfaces';
8+
import { getPostsMeta, fetchNotionPostsMeta, getCategories, getBlurImage } from '@/utils';
109
import { siteConfig } from 'site.config';
1110

1211
import PageHead from '@/components/common/PageHead';
1312

1413
import { REVALIDATE_TIME } from '@/assets/constants';
1514

1615
interface Props {
17-
posts: Post[];
16+
posts: PostMeta[];
1817
categories: Category[];
1918
}
2019
const HomePage = ({ posts, categories }: Props) => {
@@ -37,15 +36,18 @@ export const getStaticProps: GetStaticProps<Props> = async () => {
3736
if (!process.env.NOTION_POST_DATABASE_ID)
3837
throw new Error('NOTION_POST_DATABASE_ID is not defined');
3938

40-
const notionPostsResponse = await getNotionPosts(process.env.NOTION_POST_DATABASE_ID);
41-
const posts = parsePosts(notionPostsResponse);
42-
const postsWithPreview = await getPreviewImages(posts);
43-
39+
const notionPostsResponse = await fetchNotionPostsMeta(process.env.NOTION_POST_DATABASE_ID);
40+
const posts = await Promise.all(
41+
getPostsMeta(notionPostsResponse).map(async (post) => ({
42+
...post,
43+
blurImage: await getBlurImage(post.cover),
44+
})),
45+
);
4446
const categories = getCategories(notionPostsResponse);
4547

4648
return {
4749
props: {
48-
posts: postsWithPreview,
50+
posts,
4951
categories,
5052
},
5153
revalidate: REVALIDATE_TIME,

pages/posts/[slug].tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ParsedUrlQuery } from 'querystring';
33
import { GetStaticPaths, GetStaticProps } from 'next';
44
import { NotionBlock, Renderer } from 'notion-to-jsx';
55

6-
import { getIdBySlug, getNotionPosts, getSlugs, notionClient } from '@/utils';
6+
import { fetchIdBySlug, fetchNotionPostsMeta, getSlugs, notionClient } from '@/utils';
77

88
import Giscus from '@/components/common/Giscus';
99
import PageHead from '@/components/common/PageHead';
@@ -50,7 +50,7 @@ interface PostParams extends ParsedUrlQuery {
5050

5151
export const getStaticProps: GetStaticProps<Props, PostParams> = async ({ params }) => {
5252
const { slug } = params as PostParams;
53-
const id = await getIdBySlug(slug as string, process.env.NOTION_POST_DATABASE_ID as string);
53+
const id = await fetchIdBySlug(slug as string, process.env.NOTION_POST_DATABASE_ID as string);
5454
const blocks = (await notionClient.getPageBlocks(id)) as NotionBlock[];
5555
const properties = await notionClient.getPageProperties(id);
5656

@@ -71,7 +71,7 @@ export const getStaticProps: GetStaticProps<Props, PostParams> = async ({ params
7171
export const getStaticPaths: GetStaticPaths = async () => {
7272
if (!process.env.NOTION_POST_DATABASE_ID)
7373
throw new Error('NOTION_POST_DATABASE_ID is not defined');
74-
const databaseItems = await getNotionPosts(process.env.NOTION_POST_DATABASE_ID);
74+
const databaseItems = await fetchNotionPostsMeta(process.env.NOTION_POST_DATABASE_ID);
7575
const slugs = getSlugs(databaseItems);
7676

7777
const paths = slugs.map((slug) => ({

pages/rss.xml.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { GetServerSideProps } from 'next';
2+
import RSS from 'rss';
23

3-
import { generateRssFeed, getNotionPosts } from '@/utils';
4+
import { GetPageResponse } from '@/interfaces';
5+
import { fetchNotionPostsMeta, getPostsMeta } from '@/utils';
6+
import { siteConfig } from 'site.config';
47

58
const Rss = () => {
69
return null;
@@ -9,7 +12,7 @@ const Rss = () => {
912
export const getServerSideProps: GetServerSideProps = async ({ res }) => {
1013
if (!process.env.NOTION_POST_DATABASE_ID)
1114
throw new Error('NOTION_POST_DATABASE_ID is not defined');
12-
const databaseItems = await getNotionPosts(process.env.NOTION_POST_DATABASE_ID);
15+
const databaseItems = await fetchNotionPostsMeta(process.env.NOTION_POST_DATABASE_ID);
1316

1417
res.setHeader('Content-Type', 'text/xml');
1518
res.write(generateRssFeed(databaseItems));
@@ -21,3 +24,26 @@ export const getServerSideProps: GetServerSideProps = async ({ res }) => {
2124
};
2225

2326
export default Rss;
27+
28+
const generateRssFeed = (notionPostsResponse: GetPageResponse[]) => {
29+
const feedOptions = {
30+
title: `${siteConfig.homeTitle} | ${siteConfig.blogName}`,
31+
description: siteConfig.seoDefaultDesc,
32+
site_url: siteConfig.url,
33+
feed_url: `${siteConfig.url}/rss.xml`,
34+
pubDate: new Date(),
35+
copyright: `All rights reserved ${new Date().getFullYear()}, Jinsoo`,
36+
};
37+
38+
const feed = new RSS(feedOptions);
39+
getPostsMeta(notionPostsResponse).forEach(({ title, description, slug, published }) => {
40+
feed.item({
41+
title,
42+
description,
43+
url: `${siteConfig.url}/posts/${slug}`,
44+
date: new Date(published),
45+
});
46+
});
47+
48+
return feed.xml();
49+
};

pages/sitemap.xml.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import dayjs from 'dayjs';
12
import { GetServerSideProps } from 'next';
3+
import { GetPageResponse } from 'notion-to-utils';
24

3-
import { generateSitemap, getNotionPosts } from '@/utils';
5+
import { PostMeta } from '@/interfaces';
6+
import { getPostsMeta, fetchNotionPostsMeta } from '@/utils';
7+
8+
type PostIdentifier = Pick<PostMeta, 'slug' | 'published'>;
49

510
const Sitemap = () => {
611
return null;
@@ -9,7 +14,7 @@ const Sitemap = () => {
914
export const getServerSideProps: GetServerSideProps = async ({ res }) => {
1015
if (!process.env.NOTION_POST_DATABASE_ID)
1116
throw new Error('NOTION_POST_DATABASE_ID is not defined');
12-
const databaseItems = await getNotionPosts(process.env.NOTION_POST_DATABASE_ID);
17+
const databaseItems = await fetchNotionPostsMeta(process.env.NOTION_POST_DATABASE_ID);
1318
res.setHeader('Content-Type', 'text/xml');
1419
res.write(generateSitemap(databaseItems));
1520
res.end();
@@ -20,3 +25,55 @@ export const getServerSideProps: GetServerSideProps = async ({ res }) => {
2025
};
2126

2227
export default Sitemap;
28+
29+
const generateSitemap = (notionPostsResponse: GetPageResponse[]) => {
30+
const paths: PostIdentifier[] = getSitemapPostIdentifiers(notionPostsResponse);
31+
32+
const urlSet = paths
33+
.map((path) => {
34+
return `
35+
<url>
36+
<loc>${process.env.BLOG_URL}/posts/${path.slug}</loc>
37+
<changefreq>daily</changefreq>
38+
<priority>1</priority>
39+
<lastmod>${path.published}</lastmod>
40+
</url>
41+
`;
42+
})
43+
.join('');
44+
45+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
46+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
47+
<url>
48+
<loc>${process.env.BLOG_URL}</loc>
49+
<changefreq>daily</changefreq>
50+
<priority>1</priority>
51+
<lastmod>${new Date().toISOString()}</lastmod>
52+
</url>
53+
<url>
54+
<loc>${process.env.BLOG_URL}/about</loc>
55+
<changefreq>daily</changefreq>
56+
<priority>1</priority>
57+
<lastmod>${new Date().toISOString()}</lastmod>
58+
</url>
59+
${urlSet}
60+
</urlset>
61+
`;
62+
63+
return sitemap;
64+
};
65+
66+
const getSitemapPostIdentifiers = (notionPostsResponse: GetPageResponse[]) => {
67+
const posts = getPostsMeta(notionPostsResponse);
68+
const postsMeta = posts
69+
.map((post) => {
70+
const { slug, published } = post;
71+
const formattedPublished = published ? dayjs(published).format('YYYY-MM-DD') : '';
72+
return { slug, published: formattedPublished };
73+
})
74+
.filter((postMeta: PostIdentifier | null): postMeta is PostIdentifier =>
75+
postMeta ? 'slug' in postMeta : false,
76+
);
77+
78+
return postsMeta;
79+
};

src/assets/constants.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export const INITIAL_CATEGORY = 'All';
1717

1818
export const DEFAULT_CATEGORY_COLOR: SelectColor = 'default';
1919

20-
export const DEFAULT_BLUR_BASE64 =
21-
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII=';
22-
2320
export const REVALIDATE_TIME = 60;
2421
export const IMAGE_MAX_AGE = 60 * 60;
22+
23+
export const DEFAULT_BLUR_BASE64 =
24+
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiNGM0Y0RjYiLz48L3N2Zz4=';

src/atoms/posts/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { atom } from 'jotai';
22
import { atomWithReset } from 'jotai/utils';
33

44
import { selectedCategoryAtom } from '@/atoms/categories';
5-
import { Post } from '@/interfaces';
5+
import { PostMeta } from '@/interfaces';
66

77
import { INITIAL_CATEGORY } from '@/assets/constants';
88

9-
export const postsAtom = atom<Post[]>([]);
9+
export const postsAtom = atom<PostMeta[]>([]);
1010

11-
export const postsFilterByCategoryAtom = atom<Post[]>((get) => {
11+
export const postsFilterByCategoryAtom = atom<PostMeta[]>((get) => {
1212
const posts = get(postsAtom);
1313
const selectedCategory = get(selectedCategoryAtom);
1414

src/features/home/CategoryList/index.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { useAtomValue } from 'jotai';
55
import { categoriesAtom, selectedCategoryAtom } from '@/atoms/categories';
66
import { useCategorySelect } from '@/features/home/hooks';
77

8+
import { COLOR_TABLE } from '@/assets/constants';
9+
810
const CategoryList = () => {
911
const categories = useAtomValue(categoriesAtom);
1012
const seletedCategory = useAtomValue(selectedCategoryAtom);
@@ -17,14 +19,14 @@ const CategoryList = () => {
1719
<section className="flex min-h-[65px] gap-3 overflow-x-auto bg-[hsla(0,0%,100%,.8)] p-3">
1820
{categories.map((category) => {
1921
const { id, name, color, count } = category;
22+
const selectedColor = COLOR_TABLE[color as keyof typeof COLOR_TABLE];
23+
2024
return (
2125
<section
22-
className={`cursor-pointer whitespace-nowrap rounded-3xl border-[2px] border-solid bg-[#fff] py-2 px-4 text-[14px] shadow-[0_2px_4px_rgba(0,0,0,.1)] ${
23-
seletedCategory === name ? 'border-[#c0c0c0]' : 'border-transparent'
24-
}`}
26+
className={`cursor-pointer whitespace-nowrap rounded-3xl border-[2px] border-solid bg-[#fff] py-2 px-4 text-[14px] shadow-[0_2px_4px_rgba(0,0,0,.1)]`}
2527
key={id}
26-
style={{ color }}
2728
onClick={handleClickCategory(name)}
29+
style={{ borderColor: seletedCategory === name ? selectedColor : 'transparent' }}
2830
>
2931
{`${name} (${count})`}
3032
</section>

src/features/home/PostList/Post/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Image from 'next/image';
44
import Link from 'next/link';
55

66
import { calculate } from '@/features/home/PostList/Post/util';
7-
import { Post as Item } from '@/interfaces';
7+
import { PostMeta as Item } from '@/interfaces';
88
import { siteConfig } from 'site.config';
99

1010
import { COLOR_TABLE, DEFAULT_BLUR_BASE64 } from '@/assets/constants';
@@ -16,7 +16,7 @@ interface Props {
1616
const Post = ({ item }: Props) => {
1717
const ref = useRef<HTMLDivElement>(null);
1818
const [onError, setOnError] = useState<boolean>(false);
19-
const { cover, description, published, category, title, slug, previewImage } = item;
19+
const { cover, description, published, category, title, slug, blurImage } = item;
2020

2121
useEffect(() => {
2222
if (!ref.current) return;
@@ -59,7 +59,7 @@ const Post = ({ item }: Props) => {
5959
alt={title}
6060
fill
6161
placeholder="blur"
62-
blurDataURL={previewImage?.dataURIBase64 || DEFAULT_BLUR_BASE64}
62+
blurDataURL={blurImage || DEFAULT_BLUR_BASE64}
6363
onError={() => setOnError(true)}
6464
/>
6565
) : (

0 commit comments

Comments
 (0)