"use client"; import { useEffect, useState } from "react"; import styles from "./GitHubStarButton.module.css"; interface Props { repo: string; // SSR-fed initial count from Nav's server-side fetch. When present // the badge renders the number on first paint; the client-side // refetch below is a best-effort refresh that only fires if it // succeeds (unauthenticated github.com api is rate-limited to // ~60/hr/IP, which is why the count was invisible before). initialStars?: number; } function formatStars(n: number): string { if (n >= 1000) { const k = n / 1000; return k >= 10 ? `${Math.round(k)}k` : `${k.toFixed(1).replace(/\.0$/, "")}k`; } return String(n); } export function GitHubStarButton({ repo, initialStars }: Props) { const [stars, setStars] = useState( typeof initialStars === "number" ? initialStars : null, ); useEffect(() => { let cancelled = false; const cacheKey = `gh-stars:${repo}`; const cached = typeof window !== "undefined" ? window.localStorage.getItem(cacheKey) : null; if (cached) { try { const parsed = JSON.parse(cached) as { value: number; at: number }; if (Date.now() - parsed.at < 30 * 60 * 1000) { setStars(parsed.value); } } catch { /* ignore */ } } (async () => { try { const res = await fetch(`https://api.github.com/repos/${repo}`, { headers: { Accept: "application/vnd.github+json" }, }); if (!res.ok) return; const data = (await res.json()) as { stargazers_count?: number }; if (cancelled) return; if (typeof data.stargazers_count === "number") { setStars(data.stargazers_count); try { window.localStorage.setItem( cacheKey, JSON.stringify({ value: data.stargazers_count, at: Date.now() }), ); } catch { /* ignore */ } } } catch { /* offline / blocked — keep SSR / cached / null */ } })(); return () => { cancelled = true; }; }, [repo]); return ( STAR {stars !== null && {formatStars(stars)}} ); }