From 73fa5837e67fa55470b80ac29234f7b04873f7b8 Mon Sep 17 00:00:00 2001 From: Jared Schoeny Date: Tue, 25 Aug 2026 17:22:02 -0600 Subject: [PATCH] Hide the homepage download celebration until the next milestone is set. (#80) The 500k thank-you stays live in code but only appears when NEXT_PUBLIC_DOWNLOADS_MILESTONE is set, so we can wait for 1M without another copy change later. Co-authored-by: Cursor --- README.md | 1 + src/app/page.tsx | 5 ++++- src/components/Home/MilestoneCelebration.tsx | 18 ++++++++++++------ 3 files changed, 17 insertions(+), 7 deletions(-) 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 (