Skip to content

Commit 4c32e04

Browse files
committed
Merge branch 'feature/custom-blocks' into dev
2 parents e51da33 + ab06e46 commit 4c32e04

11 files changed

Lines changed: 304 additions & 67 deletions

File tree

frontend/main/next.config.js

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,45 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
44
enabled: process.env.ANALYZE === 'true',
55
});
66

7-
const withPWA = require("next-pwa");
7+
const withPWA = require('next-pwa');
88

9-
10-
module.exports = withPWA(withBundleAnalyzer({
11-
reactStrictMode: true,
12-
images: {
13-
loader: 'cloudinary',
14-
path: 'https://media.codingcat.dev/image/upload/',
15-
},
16-
async redirects() {
17-
return [
18-
{
19-
source: '/blog/design-systems-with-web-components',
20-
destination: '/tutorial/design-systems-with-web-components',
21-
permanent: true,
22-
},
23-
{
24-
source: '/lessons/:path*',
25-
destination: '/tutorial/:path*',
26-
permanent: true,
27-
},
28-
{
29-
source: '/blog/:path((?!/).*)',
30-
destination: '/post/:path*',
31-
permanent: true,
32-
},
33-
{
34-
source: '/posts',
35-
destination: '/blog',
36-
permanent: true,
37-
},
38-
];
39-
},
40-
pwa: {
41-
dest: "public",
42-
register: true,
43-
skipWaiting: true,
44-
disable: process.env.NODE_ENV === "development",
45-
},
46-
}));
9+
module.exports = withPWA(
10+
withBundleAnalyzer({
11+
reactStrictMode: true,
12+
images: {
13+
domains: ['s3.us-west-2.amazonaws.com'],
14+
loader: 'cloudinary',
15+
path: 'https://media.codingcat.dev/image/upload/',
16+
},
17+
async redirects() {
18+
return [
19+
{
20+
source: '/blog/design-systems-with-web-components',
21+
destination: '/tutorial/design-systems-with-web-components',
22+
permanent: true,
23+
},
24+
{
25+
source: '/lessons/:path*',
26+
destination: '/tutorial/:path*',
27+
permanent: true,
28+
},
29+
{
30+
source: '/blog/:path((?!/).*)',
31+
destination: '/post/:path*',
32+
permanent: true,
33+
},
34+
{
35+
source: '/posts',
36+
destination: '/blog',
37+
permanent: true,
38+
},
39+
];
40+
},
41+
pwa: {
42+
dest: 'public',
43+
register: true,
44+
skipWaiting: true,
45+
disable: process.env.NODE_ENV === 'development',
46+
},
47+
})
48+
);

frontend/main/package-lock.json

Lines changed: 22 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/main/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"lint": "next lint"
1616
},
1717
"dependencies": {
18-
"@9gustin/react-notion-render": "^3.8.3",
18+
"@9gustin/react-notion-render": "^3.9.0-beta.2",
1919
"@notionhq/client": "^1.0.4",
2020
"@tailwindcss/forms": "^0.5.2",
2121
"@tanem/react-nprogress": "^5.0.2",
@@ -29,6 +29,7 @@
2929
"next-pwa": "^5.4.0",
3030
"next-seo": "^4.28.1",
3131
"next-themes": "^0.2.0",
32+
"prism-react-renderer": "^1.3.3",
3233
"prismjs": "^1.28.0",
3334
"react": "^17.0.2",
3435
"react-dom": "^17.0.2",

frontend/main/src/components/PostMedia.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export default function PostMedia({
8080
width="480"
8181
height="270"
8282
layout="responsive"
83+
priority
8384
/>
8485
) : (
8586
<></>

frontend/main/src/components/RenderBlocks.tsx

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

frontend/main/src/components/authors/AuthorCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Image from 'next/image';
22
import { Author } from '@/models/user.model';
3-
import { renderBlocks } from '@/components/RenderBlocks';
3+
import { renderBlocks } from '@/components/notion-custom-blocks/RenderBlocks';
44

55
export default function AuthorCard({
66
author,
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import Highlight, { defaultProps, Language } from 'prism-react-renderer';
2+
import dracula from 'prism-react-renderer/themes/vsDark';
3+
import { useState } from 'react';
4+
5+
const CodeHighlight = ({
6+
plainText,
7+
className,
8+
lang,
9+
highlight,
10+
}: {
11+
plainText: string;
12+
className?: string;
13+
lang?: string;
14+
highlight?: string[];
15+
}) => {
16+
const language =
17+
lang || className?.replace(/language-/, '') || ('bash' as any);
18+
19+
const children = plainText;
20+
21+
const getTokenSetup = ({ getTokenProps, token, key }: any) => {
22+
const tokenProps = getTokenProps({ token, key });
23+
// if (highlight && highlight.includes(token.content)) {
24+
// return <WordHighlight>{token.content}</WordHighlight>;
25+
// }
26+
return <span key={key} {...tokenProps} />;
27+
};
28+
29+
const Button = (props: any) => (
30+
<button
31+
style={{
32+
position: 'absolute',
33+
top: 0,
34+
right: 0,
35+
border: 'none',
36+
boxShadow: 'none',
37+
textDecoration: 'none',
38+
margin: '8px',
39+
padding: '8px 12px',
40+
background: '#E2E8F022',
41+
color: 'white',
42+
borderRadius: '8px',
43+
cursor: 'pointer',
44+
fontSize: '14px',
45+
fontFamily: 'sans-serif',
46+
lineHeight: '1',
47+
}}
48+
{...props}
49+
/>
50+
);
51+
52+
const [isCopied, setIsCopied] = useState(false);
53+
const copyToClipboard = (str: string) => {
54+
const el = document.createElement('textarea');
55+
el.value = str;
56+
el.setAttribute('readonly', '');
57+
el.style.position = 'absolute';
58+
el.style.left = '-9999px';
59+
document.body.appendChild(el);
60+
el.select();
61+
document.execCommand('copy');
62+
document.body.removeChild(el);
63+
};
64+
65+
return (
66+
<Highlight
67+
{...defaultProps}
68+
code={children}
69+
language={language}
70+
theme={dracula}
71+
>
72+
{({ className, style, tokens, getLineProps, getTokenProps }) => (
73+
<pre
74+
className={className}
75+
style={{ ...style, padding: '2rem', position: 'relative' }}
76+
>
77+
<Button
78+
onClick={() => {
79+
copyToClipboard(children);
80+
setIsCopied(true);
81+
setTimeout(() => setIsCopied(false), 3000);
82+
}}
83+
>
84+
{isCopied ? '🎉 Copied!' : 'Copy'}
85+
</Button>
86+
{tokens.map((line, i) => (
87+
<div key={i} {...getLineProps({ line, key: i })}>
88+
{line.map((token, key) =>
89+
getTokenSetup({ getTokenProps, token, key })
90+
)}
91+
</div>
92+
))}
93+
</pre>
94+
)}
95+
</Highlight>
96+
);
97+
};
98+
export default CodeHighlight;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
NotionBlock,
3+
Render,
4+
withContentValidation,
5+
} from '@9gustin/react-notion-render';
6+
import CodeHighlight from '@/components/notion-custom-blocks/CodeHighlight';
7+
import Image from 'next/image';
8+
9+
const myMapper = {
10+
image: withContentValidation(({ className, media }) => {
11+
return media ? (
12+
<div
13+
style={{
14+
display: 'flex',
15+
flexDirection: 'column',
16+
position: 'relative',
17+
flexShrink: '0',
18+
boxSizing: 'border-box',
19+
marginTop: '20px',
20+
width: 'auto',
21+
height: 'auto',
22+
minHeight: '20px',
23+
maxWidth: 'calc(100vw - 40px)',
24+
minWidth: '20px',
25+
overflow: 'hidden',
26+
}}
27+
>
28+
<Image
29+
loader={({ src }) => src}
30+
className={`${className || ''} object-contain `}
31+
src={media.src}
32+
alt={media?.alt || media?.name || ''}
33+
layout="fill"
34+
height="100%"
35+
width="100%"
36+
/>
37+
<div
38+
style={{
39+
width: '100%',
40+
paddingTop: '70%',
41+
pointerEvents: 'none',
42+
fontSize: '0px',
43+
}}
44+
></div>
45+
</div>
46+
) : (
47+
<></>
48+
);
49+
}),
50+
code: withContentValidation((props) => CodeHighlight(props)),
51+
};
52+
53+
export const renderBlocks = (blocks: NotionBlock[]) => {
54+
return (
55+
<Render blocks={blocks} emptyBlocks blockComponentsMapper={myMapper} />
56+
);
57+
};

0 commit comments

Comments
 (0)