1
+ import { useState , useEffect } from 'react' ;
2
+ import { GithubOutlined } from '@ant-design/icons' ;
3
+ import { Tooltip } from 'antd' ;
4
+
5
+ interface GitHubStarCounterProps {
6
+ owner : string ;
7
+ repo : string ;
8
+ cacheTime ?: number ; // 缓存时间,单位:毫秒,默认30分钟
9
+ }
10
+
11
+ const CACHE_KEY = 'github-stars-cache' ;
12
+
13
+ interface StarCache {
14
+ count : number ;
15
+ timestamp : number ;
16
+ owner : string ;
17
+ repo : string ;
18
+ }
19
+
20
+ /**
21
+ * GitHub Star计数器组件
22
+ * 显示项目的star数,并支持缓存
23
+ */
24
+ const GitHubStarCounter : React . FC < GitHubStarCounterProps > = ( {
25
+ owner,
26
+ repo,
27
+ cacheTime = 30 * 60 * 1000 // 默认30分钟
28
+ } ) => {
29
+ const [ starCount , setStarCount ] = useState < number | null > ( null ) ;
30
+ const [ loading , setLoading ] = useState ( true ) ;
31
+
32
+ useEffect ( ( ) => {
33
+ const fetchStarCount = async ( ) => {
34
+ try {
35
+ // 尝试从缓存中获取
36
+ const cachedData = localStorage . getItem ( CACHE_KEY ) ;
37
+ if ( cachedData ) {
38
+ const cache : StarCache = JSON . parse ( cachedData ) ;
39
+
40
+ // 检查是否是同一仓库的缓存
41
+ if ( cache . owner === owner && cache . repo === repo ) {
42
+ // 检查缓存是否过期
43
+ const now = Date . now ( ) ;
44
+ if ( now - cache . timestamp < cacheTime ) {
45
+ setStarCount ( cache . count ) ;
46
+ setLoading ( false ) ;
47
+ return ;
48
+ }
49
+ }
50
+ }
51
+
52
+ // 缓存不存在或已过期,发起请求
53
+ const response = await fetch ( `https://api.github.com/repos/${ owner } /${ repo } ` ) ;
54
+
55
+ if ( ! response . ok ) {
56
+ throw new Error ( 'Failed to fetch repo data' ) ;
57
+ }
58
+
59
+ const data = await response . json ( ) ;
60
+ const count = data . stargazers_count ;
61
+
62
+ // 更新缓存
63
+ const cacheData : StarCache = {
64
+ count,
65
+ timestamp : Date . now ( ) ,
66
+ owner,
67
+ repo
68
+ } ;
69
+
70
+ localStorage . setItem ( CACHE_KEY , JSON . stringify ( cacheData ) ) ;
71
+ setStarCount ( count ) ;
72
+ } catch ( error ) {
73
+ console . error ( 'Error fetching GitHub stars:' , error ) ;
74
+ // 如果请求失败但有缓存,使用缓存数据,不管是否过期
75
+ const cachedData = localStorage . getItem ( CACHE_KEY ) ;
76
+ if ( cachedData ) {
77
+ const cache : StarCache = JSON . parse ( cachedData ) ;
78
+ if ( cache . owner === owner && cache . repo === repo ) {
79
+ setStarCount ( cache . count ) ;
80
+ }
81
+ }
82
+ } finally {
83
+ setLoading ( false ) ;
84
+ }
85
+ } ;
86
+
87
+ fetchStarCount ( ) ;
88
+ } , [ owner , repo , cacheTime ] ) ;
89
+
90
+ // 样式
91
+ const containerStyle = {
92
+ display : 'inline-flex' ,
93
+ alignItems : 'center' ,
94
+ justifyContent : 'center' ,
95
+ fontWeight : 500 ,
96
+ fontSize : '14px' ,
97
+ padding : '4px 8px' ,
98
+ border : '1px solid #e1e4e8' ,
99
+ borderRadius : '6px' ,
100
+ color : '#24292e' ,
101
+ backgroundColor : '#f6f8fa' ,
102
+ cursor : 'pointer' ,
103
+ transition : 'all 0.2s ease' ,
104
+ height : '28px' ,
105
+ lineHeight : '20px'
106
+ } ;
107
+
108
+ // 当鼠标悬停时的样式
109
+ const hoverStyle = {
110
+ backgroundColor : '#e1e4e8'
111
+ } ;
112
+
113
+ return (
114
+ < Tooltip title = "Star us on GitHub" >
115
+ < a
116
+ href = { `https://github.com/${ owner } /${ repo } ` }
117
+ target = "_blank"
118
+ rel = "noopener noreferrer"
119
+ style = { containerStyle }
120
+ onMouseOver = { ( e ) => {
121
+ Object . assign ( e . currentTarget . style , hoverStyle ) ;
122
+ } }
123
+ onMouseOut = { ( e ) => {
124
+ Object . assign ( e . currentTarget . style , containerStyle ) ;
125
+ } }
126
+ >
127
+ < GithubOutlined style = { { marginRight : '6px' } } />
128
+ { loading ? '...' : `${ starCount || 0 } ` }
129
+ </ a >
130
+ </ Tooltip >
131
+ ) ;
132
+ } ;
133
+
134
+ export default GitHubStarCounter ;
0 commit comments