Skip to content

Commit 214e242

Browse files
Merge pull request #54 from ShipFriend0516/feature/youtube-embed
feature/youtube-embed
2 parents f5b37ef + 206c3fa commit 214e242

2 files changed

Lines changed: 214 additions & 1 deletion

File tree

app/entities/post/detail/PostBody.tsx

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,167 @@ const PostBody = ({ content, tags, loading }: Props) => {
3535
}
3636
}
3737
};
38+
39+
const renderOpenGraph = (node: any, index?: number, parent?: Element) => {
40+
if (node.type === 'element' && node.tagName === 'p' && node.children) {
41+
const aTag = node.children.find(
42+
(node: any) => node.type === 'element' && node.tagName === 'a'
43+
);
44+
if (!aTag) return;
45+
46+
const href = aTag.properties?.href;
47+
if (href && href.startsWith('/')) {
48+
// 부모가 존재하고 children 배열이 있는 경우
49+
const opengraph = createOpenGraph(href);
50+
if (
51+
index !== undefined &&
52+
parent &&
53+
parent.children &&
54+
Array.isArray(parent.children)
55+
) {
56+
// 현재 a 태그 다음 위치에 div 삽입 s
57+
parent.children.splice(index + 1, 0, opengraph);
58+
} else return;
59+
}
60+
}
61+
};
62+
63+
const renderYoutubeEmbed = (node: any, index?: number, parent?: Element) => {
64+
if (node.type === 'element' && node.tagName === 'p' && node.children) {
65+
const aTag = node.children.find(
66+
(node: any) => node.type === 'element' && node.tagName === 'a'
67+
);
68+
if (!aTag) return;
69+
70+
const href = aTag.properties?.href;
71+
const isYoutubeLink =
72+
href &&
73+
(href.startsWith('https://www.youtube.com/watch') ||
74+
href.startsWith('https://youtu.be/'));
75+
76+
if (isYoutubeLink) {
77+
const urlType = href.startsWith('https://www.youtube.com/watch')
78+
? 'watch'
79+
: 'be';
80+
81+
const videoId =
82+
urlType === 'watch'
83+
? new URL(href).searchParams.get('v')
84+
: href.split('/').pop();
85+
86+
if (videoId) {
87+
const youtubeEmbed = createYoutubeIframe(videoId, 736, 414);
88+
// 부모가 존재하고 children 배열이 있는 경우
89+
if (
90+
index &&
91+
parent &&
92+
parent.children &&
93+
Array.isArray(parent.children)
94+
) {
95+
parent.children.splice(index + 1, 0, youtubeEmbed);
96+
} else return;
97+
}
98+
}
99+
}
100+
};
101+
102+
const createYoutubeIframe = (
103+
videoId: string,
104+
width: number,
105+
height: number
106+
) => {
107+
return {
108+
type: 'element',
109+
tagName: 'iframe',
110+
properties: {
111+
src: `https://www.youtube.com/embed/${videoId}`,
112+
width: width.toString(),
113+
height: height.toString(),
114+
frameBorder: '0',
115+
allowFullScreen: true,
116+
className: 'youtube-embed',
117+
},
118+
children: [],
119+
};
120+
};
121+
122+
const createOpenGraph = (href: string) => {
123+
return {
124+
type: 'element',
125+
tagName: 'a',
126+
properties: {
127+
className: 'open-graph',
128+
href: href,
129+
},
130+
children: [
131+
{
132+
type: 'element',
133+
tagName: 'img',
134+
properties: {
135+
src: `${href}`,
136+
alt: 'Open Graph Image',
137+
className: 'og-image',
138+
},
139+
children: [],
140+
},
141+
{
142+
type: 'element',
143+
tagName: 'div',
144+
properties: {
145+
className: 'og-container',
146+
},
147+
children: [
148+
{
149+
type: 'element',
150+
tagName: 'h4',
151+
properties: {
152+
className: 'og-title',
153+
},
154+
children: [
155+
{
156+
type: 'text',
157+
value: decodeURIComponent(href.split('/').pop()!).replaceAll(
158+
'-',
159+
' '
160+
),
161+
},
162+
],
163+
},
164+
{
165+
type: 'element',
166+
tagName: 'span',
167+
properties: {
168+
className: 'og-content',
169+
},
170+
children: [
171+
// {
172+
// type: 'text',
173+
// value: decodeURIComponent(href.split('/').pop()!).replaceAll(
174+
// '-',
175+
// ' '
176+
// ),
177+
// },
178+
],
179+
},
180+
{
181+
type: 'element',
182+
tagName: 'span',
183+
properties: {
184+
className: 'og-domain',
185+
},
186+
children: [
187+
{
188+
type: 'text',
189+
value: '',
190+
},
191+
],
192+
},
193+
],
194+
},
195+
],
196+
};
197+
};
198+
38199
return (
39200
<div
40201
className={
@@ -58,8 +219,14 @@ const PostBody = ({ content, tags, loading }: Props) => {
58219
wrapperElement={{
59220
'data-color-mode': theme,
60221
}}
61-
rehypeRewrite={(node) => {
222+
rehypeRewrite={(node, index?, parent?) => {
62223
asideStyleRewrite(node);
224+
// renderOpenGraph(node, index || 0, parent as Element | undefined);
225+
renderYoutubeEmbed(
226+
node,
227+
index || 0,
228+
parent as Element | undefined
229+
);
63230
}}
64231
/>
65232
</>

app/globals.css

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,57 @@ article.post .post-body hr::before {
167167
content: '';
168168
}
169169

170+
article.post .post-body pre {
171+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.05);
172+
}
173+
170174
.shadow-top {
171175
box-shadow:
172176
0 -4px 6px -1px rgba(0, 0, 0, 0.1),
173177
0 -2px 4px -1px rgba(0, 0, 0, 0.06);
174178
}
175179

180+
.post-body .open-graph {
181+
display: flex;
182+
height: 120px;
183+
background-color: #ededed;
184+
overflow: hidden;
185+
flex-direction: row;
186+
justify-content: center;
187+
border-radius: 8px;
188+
margin-bottom: 0.5em;
189+
box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
190+
text-decoration: none;
191+
color: var(--text-default);
192+
}
193+
194+
.post-body .open-graph img.og-image {
195+
flex-grow: 1;
196+
border-radius: 0;
197+
margin: 0;
198+
padding: 0.25em;
199+
background-color: white;
200+
}
201+
202+
.open-graph .og-container {
203+
flex-grow: 2;
204+
display: flex;
205+
flex-direction: column;
206+
justify-content: center;
207+
padding: 0.5em;
208+
gap: 4px;
209+
}
210+
211+
.post-body .youtube-embed {
212+
position: relative;
213+
margin: 1em auto;
214+
width: 100%;
215+
height: auto;
216+
aspect-ratio: 16 / 9;
217+
border-radius: 1em;
218+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.05);
219+
}
220+
176221
.post-body aside {
177222
background-color: rgba(231, 241, 231);
178223
border-radius: 4px;
@@ -193,6 +238,7 @@ article.post .post-body hr::before {
193238
.post-body aside p {
194239
margin: 0;
195240
}
241+
196242
.dark .post-body p > code,
197243
.dark .post-body li > code,
198244
.dark .post-body strong > code,

0 commit comments

Comments
 (0)