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