mirror of
https://github.com/Hackdex-App/hackdex-website.git
synced 2026-08-31 04:54:48 -05:00
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 <cursoragent@cursor.com>
This commit is contained in:
@@ -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=
|
||||
|
||||
@@ -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() {
|
||||
<div className="mx-auto max-w-screen-2xl px-6 pt-12 pb-8 sm:py-20">
|
||||
<div className="grid items-center gap-10 lg:grid-cols-[minmax(0,1fr)_minmax(0,34rem)] xl:grid-cols-[minmax(0,1fr)_minmax(0,42rem)]">
|
||||
<div>
|
||||
<MilestoneCelebration />
|
||||
{downloadsMilestone && (
|
||||
<MilestoneCelebration milestone={downloadsMilestone} />
|
||||
)}
|
||||
<h1 className="mt-5 text-4xl font-bold tracking-tight sm:text-6xl text-balance">
|
||||
Bring your ROM once.
|
||||
<br />
|
||||
|
||||
@@ -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<number | null>(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 (
|
||||
<button
|
||||
@@ -100,7 +106,7 @@ export default function MilestoneCelebration() {
|
||||
className="inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-sm ring-1 ring-[var(--accent)]/30 bg-[var(--accent)]/10 text-foreground/90 elevate hover:ring-[var(--accent)]/50 transition cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent)]"
|
||||
>
|
||||
<PiConfettiBold size={16} className="text-[var(--accent)]" aria-hidden="true" />
|
||||
<span className="font-semibold gradient-text">500,000+ downloads</span>
|
||||
<span className="font-semibold gradient-text">{formatMilestone(milestone)} downloads</span>
|
||||
<span className="-ml-0.5">Thank you!</span>
|
||||
<span className="sr-only">. Activate to replay the celebration confetti.</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user