1
1
import React , { useState , useEffect } from 'react' ;
2
- import {
3
- link ,
4
- card ,
5
- content ,
6
- iconContainer ,
7
- icon ,
8
- title ,
9
- updatedText ,
10
- } from './styles.css' ;
2
+ import * as styles from './styles.css' ;
11
3
12
4
export interface LinkPreviewProps {
13
5
url : string ;
@@ -22,6 +14,12 @@ interface RepoData {
22
14
updated_at : string ;
23
15
}
24
16
17
+ interface FigmaData {
18
+ name : string ;
19
+ url : string ;
20
+ thumbnailUrl ?: string ;
21
+ }
22
+
25
23
// GitHub 레포지토리 데이터를 가져오는 함수
26
24
const fetchGitHubRepoData = async (
27
25
repoPath : string
@@ -42,6 +40,67 @@ const fetchGitHubRepoData = async (
42
40
}
43
41
} ;
44
42
43
+ // Figma 파일 정보 추출 함수
44
+ const extractFigmaData = ( url : string ) : FigmaData | null => {
45
+ try {
46
+ const parsedUrl = new URL ( url ) ;
47
+ if ( parsedUrl . hostname . includes ( 'figma.com' ) ) {
48
+ // URL에서 파일 이름 추출
49
+ const pathSegments = parsedUrl . pathname . split ( '/' ) ;
50
+ const fileSegment = pathSegments . find ( ( segment ) =>
51
+ segment . includes ( 'file' )
52
+ ) ;
53
+
54
+ if ( ! fileSegment ) return null ;
55
+
56
+ // 파일 ID와 이름 파싱
57
+ const fileIdMatch = fileSegment . match ( / f i l e \/ ( [ ^ / ] + ) / ) ;
58
+ const fileId = fileIdMatch ? fileIdMatch [ 1 ] : '' ;
59
+
60
+ // URL에서 파일 이름 추출 (URL 파라미터에서)
61
+ let fileName = '' ;
62
+ let mode = '' ;
63
+
64
+ // URL 경로에서 파일 이름 추출 시도
65
+ if ( pathSegments . length > 3 ) {
66
+ // URL 경로에서 이름 부분 추출 (/file/ID/NAME 형식)
67
+ const encodedName = pathSegments [ 3 ] ;
68
+ if ( encodedName ) {
69
+ // URL 디코딩 및 하이픈을 공백으로 변환
70
+ fileName = decodeURIComponent ( encodedName ) . replace ( / - / g, ' ' ) ;
71
+ }
72
+ }
73
+
74
+ // 파일 이름이 추출되지 않았으면 URL에서 직접 찾기
75
+ if ( ! fileName && parsedUrl . pathname . includes ( '-' ) ) {
76
+ const nameMatch = parsedUrl . pathname . match ( / \/ ( [ ^ / ] + ) (?: \? | $ ) / ) ;
77
+ if ( nameMatch && nameMatch [ 1 ] ) {
78
+ fileName = decodeURIComponent ( nameMatch [ 1 ] . replace ( / - / g, ' ' ) ) ;
79
+ }
80
+ }
81
+
82
+ // 파라미터에서 모드 추출 (dev, design 등)
83
+ if ( parsedUrl . search ) {
84
+ const searchParams = new URLSearchParams ( parsedUrl . search ) ;
85
+ mode = searchParams . get ( 'mode' ) || '' ;
86
+ }
87
+
88
+ // 이름이 추출되지 않았으면 기본값 사용
89
+ fileName = fileName || 'Figma Design' ;
90
+
91
+ return {
92
+ name : fileName ,
93
+ url : url ,
94
+ thumbnailUrl : 'https://static.figma.com/app/icon/1/favicon.svg' ,
95
+ } ;
96
+ }
97
+ return null ;
98
+ } catch ( error ) {
99
+ console . error ( 'Error parsing Figma URL:' , error ) ;
100
+ return null ;
101
+ }
102
+ } ;
103
+
45
104
// GitHub URL에서 레포지토리 경로 추출
46
105
const extractRepoPathFromUrl = ( url : string ) : string | null => {
47
106
try {
@@ -62,6 +121,21 @@ const extractRepoPathFromUrl = (url: string): string | null => {
62
121
}
63
122
} ;
64
123
124
+ // URL이 어떤 타입의 링크인지 확인
125
+ const getLinkType = ( url : string ) : 'github' | 'figma' | 'unknown' => {
126
+ try {
127
+ const parsedUrl = new URL ( url ) ;
128
+ if ( parsedUrl . hostname === 'github.com' ) {
129
+ return 'github' ;
130
+ } else if ( parsedUrl . hostname . includes ( 'figma.com' ) ) {
131
+ return 'figma' ;
132
+ }
133
+ return 'unknown' ;
134
+ } catch {
135
+ return 'unknown' ;
136
+ }
137
+ } ;
138
+
65
139
// 날짜 포맷팅 함수
66
140
const formatUpdatedTime = ( dateString : string ) : string => {
67
141
const date = new Date ( dateString ) ;
@@ -100,22 +174,33 @@ const formatUpdatedTime = (dateString: string): string => {
100
174
101
175
const LinkPreview : React . FC < LinkPreviewProps > = ( { url } ) => {
102
176
const [ repoData , setRepoData ] = useState < RepoData | null > ( null ) ;
177
+ const [ figmaData , setFigmaData ] = useState < FigmaData | null > ( null ) ;
103
178
const [ loading , setLoading ] = useState ( true ) ;
179
+ const [ linkType , setLinkType ] = useState < 'github' | 'figma' | 'unknown' > (
180
+ 'unknown'
181
+ ) ;
104
182
105
183
useEffect ( ( ) => {
106
- const loadRepoData = async ( ) => {
184
+ const loadLinkData = async ( ) => {
107
185
setLoading ( true ) ;
108
- const repoPath = extractRepoPathFromUrl ( url ) ;
186
+ const type = getLinkType ( url ) ;
187
+ setLinkType ( type ) ;
109
188
110
- if ( repoPath ) {
111
- const data = await fetchGitHubRepoData ( repoPath ) ;
112
- setRepoData ( data ) ;
189
+ if ( type === 'github' ) {
190
+ const repoPath = extractRepoPathFromUrl ( url ) ;
191
+ if ( repoPath ) {
192
+ const data = await fetchGitHubRepoData ( repoPath ) ;
193
+ setRepoData ( data ) ;
194
+ }
195
+ } else if ( type === 'figma' ) {
196
+ const data = extractFigmaData ( url ) ;
197
+ setFigmaData ( data ) ;
113
198
}
114
199
115
200
setLoading ( false ) ;
116
201
} ;
117
202
118
- loadRepoData ( ) ;
203
+ loadLinkData ( ) ;
119
204
} , [ url ] ) ;
120
205
121
206
// 레포지토리 이름 추출 (full_name에서 organization/repo 형식)
@@ -129,26 +214,60 @@ const LinkPreview: React.FC<LinkPreviewProps> = ({ url }) => {
129
214
? formatUpdatedTime ( repoData . updated_at )
130
215
: '' ;
131
216
217
+ // 모든 링크 타입을 조건부 렌더링으로 통합
132
218
return (
133
- < a href = { url } target = "_blank" rel = "noopener noreferrer" className = { link } >
134
- < div className = { card } >
135
- < div className = { iconContainer } >
136
- < img
137
- src = {
138
- repoData ?. owner ?. avatar_url ||
139
- 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png'
140
- }
141
- alt = "Repository icon"
142
- className = { icon }
143
- />
219
+ < a
220
+ href = { url }
221
+ target = "_blank"
222
+ rel = "noopener noreferrer"
223
+ className = { styles . link }
224
+ >
225
+ { linkType === 'figma' && figmaData ? (
226
+ // Figma 프리뷰 렌더링
227
+ < div className = { styles . preview } >
228
+ < div className = { styles . iconContainer } >
229
+ < img
230
+ src = {
231
+ figmaData . thumbnailUrl ||
232
+ 'https://static.figma.com/app/icon/1/favicon.svg'
233
+ }
234
+ alt = "Figma icon"
235
+ className = { styles . icon }
236
+ />
237
+ </ div >
238
+ < div className = { styles . content } >
239
+ < div className = { styles . title } > { figmaData . name } </ div >
240
+ < div className = { styles . description } > www.figma.com</ div >
241
+ </ div >
242
+ </ div >
243
+ ) : linkType === 'github' ? (
244
+ // GitHub 프리뷰 렌더링
245
+ < div className = { `${ styles . preview } ${ styles . githubPreview } ` } >
246
+ < div className = { styles . iconContainer } >
247
+ < img
248
+ src = {
249
+ repoData ?. owner ?. avatar_url ||
250
+ 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png'
251
+ }
252
+ alt = "Repository icon"
253
+ className = { styles . icon }
254
+ />
255
+ </ div >
256
+ < div className = { `${ styles . content } ${ styles . githubContent } ` } >
257
+ < div className = { styles . title } > { repoName } </ div >
258
+ < div className = { styles . description } >
259
+ { loading ? 'Loading...' : `${ repoName } • ${ updatedTimeText } ` }
260
+ </ div >
261
+ </ div >
144
262
</ div >
145
- < div className = { content } >
146
- < div className = { title } > { repoName } </ div >
147
- < div className = { updatedText } >
148
- { loading ? 'Loading...' : `${ repoName } • ${ updatedTimeText } ` }
263
+ ) : (
264
+ // 기본 링크 프리뷰 렌더링
265
+ < div className = { styles . preview } >
266
+ < div className = { styles . content } >
267
+ < div className = { styles . title } > { url } </ div >
149
268
</ div >
150
269
</ div >
151
- </ div >
270
+ ) }
152
271
</ a >
153
272
) ;
154
273
} ;
0 commit comments