diff --git a/README.md b/README.md index df6ebc1..09cb918 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ NEXT_PUBLIC_SITE_URL= NEXT_PUBLIC_SITE_DOMAIN= NEXT_PUBLIC_HACK_COVERS_DOMAIN= +NEXT_PUBLIC_DOWNLOADS_MILESTONE= # Empty hides the homepage celebration pill S3_ENDPOINT= S3_PORT= diff --git a/src/app/page.tsx b/src/app/page.tsx index 16d1d0c..c09fcfe 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -21,6 +21,7 @@ export const metadata: Metadata = { export const revalidate = 604800; // 1 week in seconds export default async function Home() { + const downloadsMilestone = process.env.NEXT_PUBLIC_DOWNLOADS_MILESTONE?.trim(); const supabase = await createClient(); // Fetch top 6 approved hacks ordered by downloads @@ -152,7 +153,9 @@ export default async function Home() {
- + {downloadsMilestone && ( + + )}

Bring your ROM once.
diff --git a/src/components/Home/MilestoneCelebration.tsx b/src/components/Home/MilestoneCelebration.tsx index 3771802..22ee52b 100644 --- a/src/components/Home/MilestoneCelebration.tsx +++ b/src/components/Home/MilestoneCelebration.tsx @@ -3,11 +3,16 @@ import { useCallback, useEffect, useRef } from "react"; import { PiConfettiBold } from "react-icons/pi"; -const STORAGE_KEY = "hackdex:500k-confetti"; const COLORS = ["#f43f5e", "#f97316", "#f59e0b", "#fb7185"]; const BURST_MS_DESKTOP = 2200; const BURST_MS_MOBILE = 1400; +function formatMilestone(milestone: string): string { + if (!/^\d+$/.test(milestone)) return milestone; + + return `${milestone.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}+`; +} + function prefersReducedMotion(): boolean { return window.matchMedia("(prefers-reduced-motion: reduce)").matches; } @@ -16,8 +21,9 @@ function isMobileViewport(): boolean { return window.matchMedia("(max-width: 640px)").matches; } -export default function MilestoneCelebration() { +export default function MilestoneCelebration({ milestone }: { milestone: string }) { const frameRef = useRef(null); + const storageKey = `hackdex:downloads-milestone:${milestone}:confetti`; const stopConfetti = useCallback(() => { if (frameRef.current !== null) { @@ -71,7 +77,7 @@ export default function MilestoneCelebration() { useEffect(() => { let alreadyShown = false; try { - alreadyShown = localStorage.getItem(STORAGE_KEY) === "1"; + alreadyShown = localStorage.getItem(storageKey) === "1"; } catch { // Private browsing or storage blocked } @@ -81,7 +87,7 @@ export default function MilestoneCelebration() { const timer = window.setTimeout(() => { void fireConfetti(); try { - localStorage.setItem(STORAGE_KEY, "1"); + localStorage.setItem(storageKey, "1"); } catch { // Private browsing or storage blocked } @@ -91,7 +97,7 @@ export default function MilestoneCelebration() { window.clearTimeout(timer); stopConfetti(); }; - }, [fireConfetti, stopConfetti]); + }, [fireConfetti, stopConfetti, storageKey]); return (