@@ -12,8 +12,30 @@ interface RssItem {
1212 category ?: string ;
1313 pubDate : string ;
1414 creator ?: string ;
15+ thumbnail ?: string ;
1516}
1617
18+ // 添加骨架屏组件
19+ const SkeletonCard = ( ) => (
20+ < div className = "bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden" >
21+ < div className = "p-4" >
22+ < div className = "flex justify-between items-start mb-3" >
23+ < div className = "h-6 w-20 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" > </ div >
24+ < div className = "h-4 w-16 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" > </ div >
25+ </ div >
26+ < div className = "w-full h-40 bg-gray-200 dark:bg-gray-700 rounded mb-3 animate-pulse" > </ div >
27+ < div className = "h-6 w-full bg-gray-200 dark:bg-gray-700 rounded mb-2 animate-pulse" > </ div >
28+ < div className = "h-6 w-3/4 bg-gray-200 dark:bg-gray-700 rounded mb-3 animate-pulse" > </ div >
29+ < div className = "h-4 w-full bg-gray-200 dark:bg-gray-700 rounded mb-2 animate-pulse" > </ div >
30+ < div className = "h-4 w-3/4 bg-gray-200 dark:bg-gray-700 rounded mb-3 animate-pulse" > </ div >
31+ < div className = "flex justify-between items-center" >
32+ < div className = "h-4 w-1/3 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" > </ div >
33+ < div className = "h-4 w-1/4 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" > </ div >
34+ </ div >
35+ </ div >
36+ </ div >
37+ ) ;
38+
1739export default function RssFeed ( ) {
1840 const { t } = useTranslations ( ) ;
1941 const [ items , setItems ] = useState < RssItem [ ] > ( [ ] ) ;
@@ -24,36 +46,41 @@ export default function RssFeed() {
2446 const fetchRss = async ( ) => {
2547 try {
2648 setLoading ( true ) ;
27- const response = await fetch ( "https://linux.do/latest.rss" , {
28- cache : "no-store" ,
29- } ) ;
49+ const rssToJsonUrl = "https://api.rss2json.com/v1/api.json?rss_url=" ;
50+ const techNewsRss = "https://news.mit.edu/rss/feed" ;
51+ const response = await fetch (
52+ `${ rssToJsonUrl } ${ encodeURIComponent ( techNewsRss ) } ` ,
53+ {
54+ cache : "no-store" ,
55+ }
56+ ) ;
3057
3158 if ( ! response . ok ) {
3259 throw new Error ( `获取RSS失败: ${ response . status } ` ) ;
3360 }
3461
35- const xmlText = await response . text ( ) ;
36- const result = await parseStringPromise ( xmlText , {
37- explicitArray : false ,
38- } ) ;
62+ const data = await response . json ( ) ;
3963
40- if ( result ?. rss ?. channel ?. item ) {
41- const rssItems = Array . isArray ( result . rss . channel . item )
42- ? result . rss . channel . item
43- : [ result . rss . channel . item ] ;
44-
45- const parsedItems = rssItems
64+ // RSS2JSON 返回的是已解析好的JSON格式,不需要进一步解析XML
65+ if ( data . status === "ok" && data . items && data . items . length > 0 ) {
66+ const parsedItems = data . items
4667 . map ( ( item : any ) => ( {
4768 title : item . title ,
4869 link : item . link ,
49- description : item . description ,
50- category : item . category ,
70+ description : item . description || "" ,
71+ category :
72+ item . categories && item . categories . length > 0
73+ ? item . categories [ 0 ]
74+ : "Technology" ,
5175 pubDate : item . pubDate ,
52- creator : item [ "dc:creator" ] ,
76+ creator : item . author ,
77+ thumbnail : item . thumbnail || "" ,
5378 } ) )
5479 . slice ( 0 , 10 ) ; // 只显示前10条
5580
5681 setItems ( parsedItems ) ;
82+ } else {
83+ throw new Error ( "RSS 源返回数据格式不正确" ) ;
5784 }
5885 } catch ( err ) {
5986 console . error ( "获取RSS feed失败:" , err ) ;
@@ -106,11 +133,29 @@ export default function RssFeed() {
106133 if ( loading ) {
107134 return (
108135 < div className = "w-full mt-6" >
109- < div className = "text-center py-8" >
110- < div className = "inline-block animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500" > </ div >
111- < p className = "mt-2 text-gray-600 dark:text-gray-400" >
112- { t ( "rssFeed.loading" ) }
113- </ p >
136+ < h2 className = "text-xl font-semibold mb-4 text-gray-800 dark:text-gray-200 flex items-center" >
137+ < svg
138+ xmlns = "http://www.w3.org/2000/svg"
139+ className = "h-5 w-5 mr-2 text-red-600"
140+ fill = "none"
141+ viewBox = "0 0 24 24"
142+ stroke = "currentColor"
143+ >
144+ < path
145+ strokeLinecap = "round"
146+ strokeLinejoin = "round"
147+ strokeWidth = { 2 }
148+ d = "M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
149+ />
150+ </ svg >
151+ MIT Technology News
152+ </ h2 >
153+ < div className = "grid grid-cols-1 sm:grid-cols-2 gap-6" >
154+ { Array ( 4 )
155+ . fill ( 0 )
156+ . map ( ( _ , index ) => (
157+ < SkeletonCard key = { index } />
158+ ) ) }
114159 </ div >
115160 </ div >
116161 ) ;
@@ -129,7 +174,7 @@ export default function RssFeed() {
129174 < h2 className = "text-xl font-semibold mb-4 text-gray-800 dark:text-gray-200 flex items-center" >
130175 < svg
131176 xmlns = "http://www.w3.org/2000/svg"
132- className = "h-5 w-5 mr-2 text-blue-500 "
177+ className = "h-5 w-5 mr-2 text-red-600 "
133178 fill = "none"
134179 viewBox = "0 0 24 24"
135180 stroke = "currentColor"
@@ -138,13 +183,13 @@ export default function RssFeed() {
138183 strokeLinecap = "round"
139184 strokeLinejoin = "round"
140185 strokeWidth = { 2 }
141- d = "M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-6 0a1 1 0 11-2 0 1 1 0 012 0z "
186+ d = "M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z "
142187 />
143188 </ svg >
144- { t ( "rssFeed.title" ) }
189+ MIT Technology News
145190 </ h2 >
146191
147- < div className = "grid grid-cols-1 md :grid-cols-2 gap-4 " >
192+ < div className = "grid grid-cols-1 sm :grid-cols-2 gap-6 " >
148193 { items . map ( ( item , index ) => (
149194 < motion . div
150195 key = { index }
@@ -154,25 +199,35 @@ export default function RssFeed() {
154199 className = "bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden hover:shadow-md transition-shadow duration-200"
155200 >
156201 < div className = "p-4" >
157- < div className = "flex justify-between items-start mb-2 " >
158- < span className = "inline-block px-2 py-1 text-xs font-medium bg-blue -100 dark:bg-blue -900/40 text-blue -800 dark:text-blue -300 rounded" >
202+ < div className = "flex justify-between items-start mb-3 " >
203+ < span className = "inline-block px-2 py-1 text-xs font-medium bg-red -100 dark:bg-red -900/40 text-red -800 dark:text-red -300 rounded" >
159204 { item . category || t ( "rssFeed.uncategorized" ) }
160205 </ span >
161206 < span className = "text-xs text-gray-500 dark:text-gray-400" >
162207 { formatDate ( item . pubDate ) }
163208 </ span >
164209 </ div >
165210
166- < h3 className = "text-lg font-medium text-gray-900 dark:text-white mb-2 line-clamp-2" >
211+ { item . thumbnail && (
212+ < div className = "mb-3 overflow-hidden rounded" >
213+ < img
214+ src = { item . thumbnail }
215+ alt = { item . title }
216+ className = "w-full h-48 object-cover transform hover:scale-105 transition-transform duration-300"
217+ />
218+ </ div >
219+ ) }
220+
221+ < h3 className = "text-base font-medium text-gray-900 dark:text-white mb-2 line-clamp-2" >
167222 { extractCdata ( item . title ) }
168223 </ h3 >
169224
170- < p className = "text-sm text-gray-600 dark:text-gray-400 mb-3 line-clamp-2 " >
225+ < p className = "text-sm text-gray-600 dark:text-gray-400 mb-3 line-clamp-3 " >
171226 { extractFirstParagraph ( item . description ) }
172227 </ p >
173228
174229 < div className = "flex justify-between items-center" >
175- < span className = "text-xs text-gray-500 dark:text-gray-400" >
230+ < span className = "text-xs text-gray-500 dark:text-gray-400 truncate max-w-[50%] " >
176231 { item . creator
177232 ? `${ t ( "rssFeed.author" ) } : ${ extractCdata ( item . creator ) } `
178233 : "" }
@@ -181,7 +236,7 @@ export default function RssFeed() {
181236 href = { item . link }
182237 target = "_blank"
183238 rel = "noopener noreferrer"
184- className = "text-sm font-medium text-blue -600 dark:text-blue -400 hover:text-blue -800 dark:hover:text-blue -300"
239+ className = "text-sm font-medium text-red -600 dark:text-red -400 hover:text-red -800 dark:hover:text-red -300"
185240 >
186241 { t ( "rssFeed.readMore" ) } →
187242 </ a >
0 commit comments