Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 168 additions & 1 deletion app/entities/post/detail/PostBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,167 @@ const PostBody = ({ content, tags, loading }: Props) => {
}
}
};

const renderOpenGraph = (node: any, index?: number, parent?: Element) => {
if (node.type === 'element' && node.tagName === 'p' && node.children) {
const aTag = node.children.find(
(node: any) => node.type === 'element' && node.tagName === 'a'
);
if (!aTag) return;

const href = aTag.properties?.href;
if (href && href.startsWith('/')) {
// 부모가 존재하고 children 배열이 있는 경우
const opengraph = createOpenGraph(href);
if (
index !== undefined &&
parent &&
parent.children &&
Array.isArray(parent.children)
) {
// 현재 a 태그 다음 위치에 div 삽입 s
parent.children.splice(index + 1, 0, opengraph);
} else return;
}
}
};

const renderYoutubeEmbed = (node: any, index?: number, parent?: Element) => {
if (node.type === 'element' && node.tagName === 'p' && node.children) {
const aTag = node.children.find(
(node: any) => node.type === 'element' && node.tagName === 'a'
);
if (!aTag) return;

const href = aTag.properties?.href;
const isYoutubeLink =
href &&
(href.startsWith('https://www.youtube.com/watch') ||
href.startsWith('https://youtu.be/'));

if (isYoutubeLink) {
const urlType = href.startsWith('https://www.youtube.com/watch')
? 'watch'
: 'be';

const videoId =
urlType === 'watch'
? new URL(href).searchParams.get('v')
: href.split('/').pop();

if (videoId) {
const youtubeEmbed = createYoutubeIframe(videoId, 736, 414);
// 부모가 존재하고 children 배열이 있는 경우
if (
index &&
parent &&
parent.children &&
Array.isArray(parent.children)
) {
parent.children.splice(index + 1, 0, youtubeEmbed);
} else return;
}
}
}
};

const createYoutubeIframe = (
videoId: string,
width: number,
height: number
) => {
return {
type: 'element',
tagName: 'iframe',
properties: {
src: `https://www.youtube.com/embed/${videoId}`,
width: width.toString(),
height: height.toString(),
frameBorder: '0',
allowFullScreen: true,
className: 'youtube-embed',
},
children: [],
};
};

const createOpenGraph = (href: string) => {
return {
type: 'element',
tagName: 'a',
properties: {
className: 'open-graph',
href: href,
},
children: [
{
type: 'element',
tagName: 'img',
properties: {
src: `${href}`,
alt: 'Open Graph Image',
className: 'og-image',
},
children: [],
},
{
type: 'element',
tagName: 'div',
properties: {
className: 'og-container',
},
children: [
{
type: 'element',
tagName: 'h4',
properties: {
className: 'og-title',
},
children: [
{
type: 'text',
value: decodeURIComponent(href.split('/').pop()!).replaceAll(
'-',
' '
),
},
],
},
{
type: 'element',
tagName: 'span',
properties: {
className: 'og-content',
},
children: [
// {
// type: 'text',
// value: decodeURIComponent(href.split('/').pop()!).replaceAll(
// '-',
// ' '
// ),
// },
],
},
{
type: 'element',
tagName: 'span',
properties: {
className: 'og-domain',
},
children: [
{
type: 'text',
value: '',
},
],
},
],
},
],
};
};

return (
<div
className={
Expand All @@ -58,8 +219,14 @@ const PostBody = ({ content, tags, loading }: Props) => {
wrapperElement={{
'data-color-mode': theme,
}}
rehypeRewrite={(node) => {
rehypeRewrite={(node, index?, parent?) => {
asideStyleRewrite(node);
// renderOpenGraph(node, index || 0, parent as Element | undefined);
renderYoutubeEmbed(
node,
index || 0,
parent as Element | undefined
);
}}
/>
</>
Expand Down
46 changes: 46 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,57 @@ article.post .post-body hr::before {
content: '';
}

article.post .post-body pre {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.05);
}

.shadow-top {
box-shadow:
0 -4px 6px -1px rgba(0, 0, 0, 0.1),
0 -2px 4px -1px rgba(0, 0, 0, 0.06);
}

.post-body .open-graph {
display: flex;
height: 120px;
background-color: #ededed;
overflow: hidden;
flex-direction: row;
justify-content: center;
border-radius: 8px;
margin-bottom: 0.5em;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
text-decoration: none;
color: var(--text-default);
}

.post-body .open-graph img.og-image {
flex-grow: 1;
border-radius: 0;
margin: 0;
padding: 0.25em;
background-color: white;
}

.open-graph .og-container {
flex-grow: 2;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0.5em;
gap: 4px;
}

.post-body .youtube-embed {
position: relative;
margin: 1em auto;
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
border-radius: 1em;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.05);
}

.post-body aside {
background-color: rgba(231, 241, 231);
border-radius: 4px;
Expand All @@ -193,6 +238,7 @@ article.post .post-body hr::before {
.post-body aside p {
margin: 0;
}

.dark .post-body p > code,
.dark .post-body li > code,
.dark .post-body strong > code,
Expand Down