Skip to content

Commit fffbc4e

Browse files
committed
feat: Add blur image placeholder and remove cover-image API endpoint
1 parent e62dc41 commit fffbc4e

File tree

11 files changed

+254
-35
lines changed

11 files changed

+254
-35
lines changed

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"prepare": "husky install"
1212
},
1313
"dependencies": {
14+
"add": "^2.0.6",
1415
"dayjs": "^1.11.8",
1516
"is-url-superb": "^6.1.0",
1617
"jotai": "^2.8.0",
@@ -19,11 +20,14 @@
1920
"notion-to-jsx": "^1.2.7",
2021
"notion-to-utils": "^1.1.0",
2122
"p-map": "^7.0.2",
23+
"plaiceholder": "^3.0.0",
2224
"prismjs": "^1.30.0",
2325
"react": "^18.2.0",
2426
"react-dom": "^18.2.0",
2527
"react-icons": "^4.9.0",
26-
"rss": "^1.2.2"
28+
"rss": "^1.2.2",
29+
"sharp": "^0.34.1",
30+
"yarn": "^1.22.22"
2731
},
2832
"devDependencies": {
2933
"@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: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { categoriesAtom } from '@/atoms/categories';
55
import { postsAtom } from '@/atoms/posts';
66
import Home from '@/features/home';
77
import { Category, PostMeta } from '@/interfaces';
8-
import { getPostsMeta, fetchNotionPostsMeta, getCategories } from '@/utils';
8+
import { getPostsMeta, fetchNotionPostsMeta, getCategories, getBlurImage } from '@/utils';
99
import { siteConfig } from 'site.config';
1010

1111
import PageHead from '@/components/common/PageHead';
@@ -37,7 +37,12 @@ export const getStaticProps: GetStaticProps<Props> = async () => {
3737
throw new Error('NOTION_POST_DATABASE_ID is not defined');
3838

3939
const notionPostsResponse = await fetchNotionPostsMeta(process.env.NOTION_POST_DATABASE_ID);
40-
const posts = getPostsMeta(notionPostsResponse);
40+
const posts = await Promise.all(
41+
getPostsMeta(notionPostsResponse).map(async (post) => ({
42+
...post,
43+
blurImage: await getBlurImage(post.cover),
44+
})),
45+
);
4146
const categories = getCategories(notionPostsResponse);
4247

4348
return {

src/assets/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ export const DEFAULT_CATEGORY_COLOR: SelectColor = 'default';
1919

2020
export const REVALIDATE_TIME = 60;
2121
export const IMAGE_MAX_AGE = 60 * 60;
22+
23+
export const DEFAULT_BLUR_BASE64 =
24+
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiNGM0Y0RjYiLz48L3N2Zz4=';

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/Intro/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { HiMail } from 'react-icons/hi';
77

88
import { siteConfig } from 'site.config';
99

10+
import { DEFAULT_BLUR_BASE64 } from '@/assets/constants';
11+
1012
const profileImage = `/api/profile-image`;
1113

1214
const Intro = () => {
@@ -19,6 +21,8 @@ const Intro = () => {
1921
width={110}
2022
height={110}
2123
alt={'Intro Picture'}
24+
placeholder="blur"
25+
blurDataURL={DEFAULT_BLUR_BASE64}
2226
/>
2327
<section className="flex flex-col justify-around gap-1">
2428
<section className="flex flex-col gap-2">

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { calculate } from '@/features/home/PostList/Post/util';
77
import { PostMeta as Item } from '@/interfaces';
88
import { siteConfig } from 'site.config';
99

10-
import { COLOR_TABLE } from '@/assets/constants';
10+
import { COLOR_TABLE, DEFAULT_BLUR_BASE64 } from '@/assets/constants';
1111

1212
interface Props {
1313
item: Item;
@@ -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 } = item;
19+
const { cover, description, published, category, title, slug, blurImage } = item;
2020

2121
useEffect(() => {
2222
if (!ref.current) return;
@@ -58,6 +58,8 @@ const Post = ({ item }: Props) => {
5858
src={cover}
5959
alt={title}
6060
fill
61+
placeholder="blur"
62+
blurDataURL={blurImage || DEFAULT_BLUR_BASE64}
6163
onError={() => setOnError(true)}
6264
/>
6365
) : (

src/interfaces/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface PostMeta {
1919
description: string;
2020
title: string;
2121
slug: string;
22+
blurImage?: string;
2223
}
2324

2425
export interface Category extends SelectPropertyResponse {

src/utils/getBlurImage.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { getPlaiceholder } from 'plaiceholder';
2+
3+
const getBlurImage = async (imgSrc: string) => {
4+
try {
5+
const buffer = await fetch(imgSrc).then(async (res) => Buffer.from(await res.arrayBuffer()));
6+
const { base64 } = await getPlaiceholder(buffer, { size: 10 });
7+
return base64;
8+
} catch (e) {
9+
console.log(e);
10+
return '';
11+
}
12+
};
13+
14+
export default getBlurImage;

src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { default as fetchIdBySlug } from './fetchIdBySlug';
44
export { default as getCategories } from './getCategories';
55
export { default as getSlugs } from './getSlugs';
66
export { default as getPostsMeta } from './getPostsMeta';
7+
export { default as getBlurImage } from './getBlurImage';

0 commit comments

Comments
 (0)