@@ -24,6 +24,8 @@ const activeOS = ref<string>('arch');
2424const setupStep = ref <number >(0 );
2525const isScrolled = ref <boolean >(false );
2626const starCount = ref <string >(' 160+' );
27+ const contributorCount = ref <number >(5 );
28+ const REPO = ' LotusInputMethod/fcitx5-lotus' ;
2729
2830// --- CATPPUCCIN THEME LOGIC ---
2931const catppuccinThemes = [' latte' , ' frappe' , ' macchiato' , ' mocha' ] as const ;
@@ -71,7 +73,6 @@ const startTyping = () => {
7173};
7274
7375const fetchGithubStars = async () => {
74- const REPO = ' LotusInputMethod/fcitx5-lotus' ;
7576 const CACHE_KEY = ' lotus_stars_cache' ;
7677 const CACHE_TIME_KEY = ' lotus_stars_timestamp' ;
7778 const TWO_HOURS = 2 * 60 * 60 * 1000 ;
@@ -102,6 +103,60 @@ const fetchGithubStars = async () => {
102103 }
103104};
104105
106+ const fetchContributors = async () => {
107+ const CACHE_KEY = ' lotus_contributors_cache_v2' ;
108+ const CACHE_TIME_KEY = ' lotus_contributors_timestamp_v2' ;
109+ const TWO_HOURS = 2 * 60 * 60 * 1000 ;
110+
111+ const specialRoles: Record <string , string > = {
112+ nhktmdzhg: ' Founder' ,
113+ };
114+
115+ const blacklist = [' thanhpy2009' , ' loccun' ];
116+
117+ try {
118+ const cachedData = localStorage .getItem (CACHE_KEY );
119+ const lastFetch = localStorage .getItem (CACHE_TIME_KEY );
120+ const now = Date .now ();
121+
122+ if (cachedData && lastFetch && now - parseInt (lastFetch ) < TWO_HOURS ) {
123+ const parsed = JSON .parse (cachedData );
124+ // Filter again in case the blacklist was updated and we have stale cache
125+ const filtered = parsed .filter (
126+ (c : any ) => ! blacklist .includes (c .name ) && ! c .name .includes (' [bot]' ),
127+ );
128+ contributors .value = filtered ;
129+ contributorCount .value = filtered .length ;
130+ return ;
131+ }
132+
133+ const response = await fetch (` https://api.github.com/repos/${REPO }/contributors ` );
134+ if (! response .ok ) throw new Error (' GitHub API error' );
135+
136+ const data = await response .json ();
137+ const fetchedContributors: Contributor [] = data
138+ .filter ((item : any ) => ! blacklist .includes (item .login ) && item .type !== ' Bot' )
139+ .map ((item : any ) => ({
140+ name: item .login ,
141+ role: specialRoles [item .login ] || ' Contributor' ,
142+ avatar: item .avatar_url ,
143+ githubUrl: item .html_url ,
144+ }));
145+
146+ contributors .value = fetchedContributors ;
147+ contributorCount .value = fetchedContributors .length ;
148+ localStorage .setItem (CACHE_KEY , JSON .stringify (fetchedContributors ));
149+ localStorage .setItem (CACHE_TIME_KEY , now .toString ());
150+ } catch (error ) {
151+ console .error (' Lỗi khi lấy contributor từ GitHub:' , error );
152+ const oldCache = localStorage .getItem (CACHE_KEY );
153+ if (oldCache ) {
154+ contributors .value = JSON .parse (oldCache );
155+ contributorCount .value = contributors .value .length ;
156+ }
157+ }
158+ };
159+
105160onMounted (() => {
106161 const savedTheme = localStorage .getItem (' catppuccin-theme' );
107162 if (savedTheme && catppuccinThemes .includes (savedTheme as CatppuccinTheme )) {
@@ -119,6 +174,7 @@ onMounted(() => {
119174 window .addEventListener (' scroll' , handleScroll );
120175 startTyping ();
121176 fetchGithubStars ();
177+ fetchContributors ();
122178});
123179
124180onUnmounted (() => {
@@ -269,34 +325,40 @@ interface Contributor {
269325 name: string ;
270326 role: string ;
271327 avatar: string ;
328+ githubUrl: string ;
272329}
273- const contributors: Contributor [] = [
330+ const contributors = ref < Contributor []>( [
274331 {
275332 name: ' Nguyen Hoang Ky' ,
276333 role: ' Founder' ,
277334 avatar: ' https://avatars.githubusercontent.com/u/57983253?v=4' ,
335+ githubUrl: ' https://github.com/khog9' ,
278336 },
279337 {
280338 name: ' Huỳnh Thiện Lộc' ,
281339 role: ' Contributor' ,
282340 avatar: ' https://avatars.githubusercontent.com/u/148019203?v=4' ,
341+ githubUrl: ' https://github.com/hthienloc' ,
283342 },
284343 {
285344 name: ' Nguyễn Hồng Hiệp' ,
286345 role: ' Contributor' ,
287346 avatar: ' https://avatars.githubusercontent.com/u/57614330?v=4' ,
347+ githubUrl: ' https://github.com/Hiep-N' ,
288348 },
289349 {
290350 name: ' Đặng Quang Hiển' ,
291351 role: ' Contributor' ,
292352 avatar: ' https://avatars.githubusercontent.com/u/83270073?v=4' ,
353+ githubUrl: ' https://github.com/dqhien' ,
293354 },
294355 {
295356 name: ' Zebra2711' ,
296357 role: ' Contributor' ,
297358 avatar: ' https://avatars.githubusercontent.com/u/89755535?v=4' ,
359+ githubUrl: ' https://github.com/Zebra2711' ,
298360 },
299- ];
361+ ]) ;
300362
301363// --- COMMANDS ---
302364const commands: Record <string , string > = {
@@ -449,7 +511,7 @@ const copyToClipboard = async (text: string | undefined): Promise<void> => {
449511 <span >Distros hỗ trợ</span >
450512 </div >
451513 <div class =" stat-item" >
452- <strong >5 </strong >
514+ <strong >{{ contributorCount }} </strong >
453515 <span >Người đóng góp</span >
454516 </div >
455517 </div >
@@ -669,7 +731,12 @@ const copyToClipboard = async (text: string | undefined): Promise<void> => {
669731 </div >
670732
671733 <div class =" contributors-flex" >
672- <div v-for =" c in contributors" :key =" c.name" class =" contributor-item" >
734+ <div
735+ v-for =" c in contributors"
736+ :key =" c.name"
737+ class =" contributor-item"
738+ @click =" openLink(c.githubUrl)"
739+ >
673740 <el-avatar :size =" 80" :src =" c.avatar" class =" contributor-avatar" />
674741 <div class =" c-name" >{{ c.name }}</div >
675742 <div class =" c-role" >{{ c.role }}</div >
0 commit comments