1
1
import React , { useState , useEffect } from 'react' ;
2
- import styled from 'styled-components' ;
3
2
import { type RichTextItem } from '../../../../types' ;
4
- import RichTexts from '../RichText/RichTexts' ;
3
+ import { MemoizedRichText } from '../MemoizedComponents' ;
4
+ import {
5
+ link ,
6
+ card ,
7
+ content ,
8
+ previewImage ,
9
+ title ,
10
+ description ,
11
+ siteName ,
12
+ caption ,
13
+ } from './styles.css' ;
5
14
6
15
interface OpenGraphData {
7
16
title : string ;
@@ -15,68 +24,28 @@ export interface BookmarkProps {
15
24
caption ?: RichTextItem [ ] ;
16
25
}
17
26
18
- const Card = styled . div `
19
- margin: ${ ( { theme } ) => theme . spacing . md } 0;
20
- border: 1px solid ${ ( { theme } ) => theme . colors . border } ;
21
- border-radius: ${ ( { theme } ) => theme . borderRadius . md } ;
22
- overflow: hidden;
23
- transition: box-shadow 0.2s ease;
24
-
25
- &:hover {
26
- box-shadow: ${ ( { theme } ) => theme . shadows . md } ;
27
- }
28
- ` ;
29
-
30
- const Content = styled . div `
31
- padding: ${ ( { theme } ) => theme . spacing . md } ;
32
- ` ;
33
-
34
- const PreviewImage = styled . img `
35
- width: 100%;
36
- height: 200px;
37
- object-fit: cover;
38
- background: ${ ( { theme } ) => theme . colors . code . background } ;
39
- ` ;
40
-
41
- const Title = styled . h4 `
42
- margin: 0 0 ${ ( { theme } ) => theme . spacing . xs } ;
43
- font-size: ${ ( { theme } ) => theme . typography . fontSize . base } ;
44
- color: ${ ( { theme } ) => theme . colors . text } ;
45
- ` ;
46
-
47
- const Description = styled . p `
48
- margin: 0;
49
- font-size: ${ ( { theme } ) => theme . typography . fontSize . small } ;
50
- color: ${ ( { theme } ) => theme . colors . secondary } ;
51
- display: -webkit-box;
52
- -webkit-line-clamp: 2;
53
- -webkit-box-orient: vertical;
54
- overflow: hidden;
55
- ` ;
56
-
57
- const SiteName = styled . div `
58
- margin-top: ${ ( { theme } ) => theme . spacing . sm } ;
59
- font-size: ${ ( { theme } ) => theme . typography . fontSize . small } ;
60
- color: ${ ( { theme } ) => theme . colors . primary } ;
61
- ` ;
62
-
63
- const Caption = styled . div `
64
- margin-top: ${ ( { theme } ) => theme . spacing . sm } ;
65
- padding-top: ${ ( { theme } ) => theme . spacing . sm } ;
66
- border-top: 1px solid ${ ( { theme } ) => theme . colors . border } ;
67
- font-size: ${ ( { theme } ) => theme . typography . fontSize . small } ;
68
- color: ${ ( { theme } ) => theme . colors . secondary } ;
69
- ` ;
70
-
71
27
// 실제 프로덕션에서는 서버 사이드에서 처리하거나 전용 API를 사용해야 합니다
72
28
const fetchOpenGraphData = async ( url : string ) : Promise < OpenGraphData > => {
73
- // 임시로 더미 데이터를 반환
74
- return {
75
- title : new URL ( url ) . hostname ,
76
- description : 'No description available' ,
77
- image : '' ,
78
- siteName : new URL ( url ) . hostname . split ( '.' ) [ 1 ] as string ,
79
- } ;
29
+ try {
30
+ const parsedUrl = new URL ( url ) ;
31
+ const domain = parsedUrl . hostname ;
32
+ const siteName = domain . split ( '.' ) [ 1 ] || domain ;
33
+
34
+ // 임시로 더미 데이터를 반환
35
+ return {
36
+ title : domain ,
37
+ description : 'No description available' ,
38
+ image : '' ,
39
+ siteName : siteName ,
40
+ } ;
41
+ } catch {
42
+ return {
43
+ title : url ,
44
+ description : 'Invalid URL' ,
45
+ image : '' ,
46
+ siteName : 'Unknown' ,
47
+ } ;
48
+ }
80
49
} ;
81
50
82
51
const Bookmark : React . FC < BookmarkProps > = ( { url, caption } ) => {
@@ -97,29 +66,31 @@ const Bookmark: React.FC<BookmarkProps> = ({ url, caption }) => {
97
66
} , [ url ] ) ;
98
67
99
68
return (
100
- < a
101
- href = { url }
102
- target = "_blank"
103
- rel = "noopener noreferrer"
104
- style = { { textDecoration : 'none' } }
105
- >
106
- < Card >
69
+ < a href = { url } target = "_blank" rel = "noopener noreferrer" className = { link } >
70
+ < div className = { card } >
107
71
{ ogData ?. image && (
108
- < PreviewImage src = { ogData . image } alt = { ogData . title } loading = "lazy" />
72
+ < img
73
+ className = { previewImage }
74
+ src = { ogData . image }
75
+ alt = { ogData . title }
76
+ loading = "lazy"
77
+ />
109
78
) }
110
- < Content >
111
- < Title > { ogData ?. title || url } </ Title >
79
+ < div className = { content } >
80
+ < h4 className = { title } > { ogData ?. title || url } </ h4 >
112
81
{ ogData ?. description && (
113
- < Description > { ogData . description } </ Description >
82
+ < p className = { description } > { ogData . description } </ p >
114
83
) }
115
- { ogData ?. siteName && < SiteName > { ogData . siteName } </ SiteName > }
116
- { caption && caption . length > 0 && (
117
- < Caption >
118
- < RichTexts richTexts = { caption } />
119
- </ Caption >
84
+ { ogData ?. siteName && (
85
+ < div className = { siteName } > { ogData . siteName } </ div >
120
86
) }
121
- </ Content >
122
- </ Card >
87
+ { /* {caption && caption.length > 0 && (
88
+ <div className={caption}>
89
+ <MemoizedRichText richTexts={caption} />
90
+ </div>
91
+ )} */ }
92
+ </ div >
93
+ </ div >
123
94
</ a >
124
95
) ;
125
96
} ;
0 commit comments