|
1 | 1 | <script lang="ts"> |
2 | | - import { onMount } from "svelte"; |
3 | 2 | import createLocaleStorage from "../lib/createLocaleStorage"; |
4 | 3 |
|
5 | 4 | interface StarCountStorageData { |
6 | | - value: number; |
| 5 | + value: string; |
7 | 6 | fetchedAt: number; |
8 | 7 | } |
9 | 8 |
|
10 | | - interface GitHubApiResponse { |
11 | | - stargazers_count?: number; |
| 9 | + interface ShieldsApiResponse { |
| 10 | + schemaVersion: number; |
| 11 | + label: string; |
| 12 | + message: string; |
| 13 | + color: string; |
12 | 14 | } |
13 | 15 |
|
14 | 16 | const REPOSITORY_PATH = "matschik/component-party.dev"; |
15 | | - const STAR_COUNT_EXPIRES_IN_MS = 1000 * 60 * 2; |
| 17 | + const STAR_COUNT_EXPIRES_IN_MS = 1000 * 60 * 5; // Shields.io caches for 5-15 minutes |
16 | 18 |
|
17 | | - const starCountStorage = createLocaleStorage("github-star-count", { |
18 | | - value: 0, |
| 19 | + const starCountStorage = createLocaleStorage("github-star-count-v2", { |
| 20 | + value: "0", |
19 | 21 | fetchedAt: 0, |
20 | 22 | }); |
21 | 23 |
|
22 | | - let starCount: number = $state(0); |
| 24 | + let starCount: string = $state("0"); |
23 | 25 | let isFetchingStarCount: boolean = $state(false); |
24 | 26 |
|
25 | 27 | async function getRepoStarCount(): Promise<void> { |
|
38 | 40 |
|
39 | 41 | isFetchingStarCount = true; |
40 | 42 |
|
41 | | - // Github public API rate limit: 60 requests per hour |
42 | | - const data: GitHubApiResponse = await fetch( |
43 | | - `https://api.github.com/repos/${REPOSITORY_PATH}`, |
44 | | - { |
45 | | - headers: { |
46 | | - Accept: "application/vnd.github.v3.star+json", |
47 | | - Authorization: "", |
| 43 | + try { |
| 44 | + // Using Shields.io JSON endpoint - no rate limits, cached for 5-15 minutes |
| 45 | + const data: ShieldsApiResponse = await fetch( |
| 46 | + `https://img.shields.io/github/stars/${REPOSITORY_PATH}.json`, |
| 47 | + { |
| 48 | + headers: { |
| 49 | + Accept: "application/json", |
| 50 | + }, |
48 | 51 | }, |
49 | | - }, |
50 | | - ) |
51 | | - .finally(() => { |
52 | | - isFetchingStarCount = false; |
53 | | - }) |
54 | | - .then((r) => r.json()); |
| 52 | + ) |
| 53 | + .finally(() => { |
| 54 | + isFetchingStarCount = false; |
| 55 | + }) |
| 56 | + .then((r) => r.json()); |
55 | 57 |
|
56 | | - if (data.stargazers_count) { |
57 | | - starCount = data.stargazers_count; |
58 | | - starCountStorage.setJSON({ |
59 | | - value: starCount, |
60 | | - fetchedAt: Date.now(), |
61 | | - }); |
| 58 | + if (data.message) { |
| 59 | + // Use the formatted string directly from Shields.io (e.g., "3.1k", "500") |
| 60 | + starCount = data.message; |
| 61 | + starCountStorage.setJSON({ |
| 62 | + value: starCount, |
| 63 | + fetchedAt: Date.now(), |
| 64 | + }); |
| 65 | + } |
| 66 | + } catch (error) { |
| 67 | + console.warn("Failed to fetch star count from Shields.io:", error); |
| 68 | + // Keep the existing cached value if available |
62 | 69 | } |
63 | 70 | } |
64 | 71 |
|
65 | | - onMount(() => { |
66 | | - getRepoStarCount(); |
67 | | - }); |
| 72 | + getRepoStarCount(); |
68 | 73 |
|
69 | 74 | function onButtonClick(): void { |
70 | 75 | starCountStorage.remove(); |
|
85 | 90 | ></span> |
86 | 91 | <span class="hidden sm:inline">Star</span> |
87 | 92 | </span> |
88 | | - {#if isFetchingStarCount || starCount !== 0} |
| 93 | + {#if isFetchingStarCount || starCount !== "0"} |
89 | 94 | <div |
90 | 95 | class="hidden h-full items-center justify-center px-3 sm:flex border-[#373b43] sm:border-l" |
91 | 96 | > |
92 | | - {#if isFetchingStarCount && starCount === 0} |
| 97 | + {#if isFetchingStarCount && starCount === "0"} |
93 | 98 | <span |
94 | 99 | class="iconify ph--spinner animate-spin size-4 mx-1" |
95 | 100 | aria-hidden="true" |
|
0 commit comments