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 (