Spaces:
Running
Running
| "use client"; | |
| import { useState, useEffect, useRef, useCallback } from "react"; | |
| import Link from "next/link"; | |
| import { useRouter } from "next/navigation"; | |
| import { Volume2, VolumeX, Trophy, Star, Award, ShoppingBag } from "lucide-react"; | |
| import { useSound } from "@/hooks/useSound"; | |
| import { getStoredPlayer } from "@/lib/player-store"; | |
| import { getPlayerStarCoins } from "@/lib/supabase/queries"; | |
| import { useCosmeticContext } from "@/components/cosmetics/CosmeticProvider"; | |
| /** | |
| * Fixed top navigation header for Triviaverse. | |
| * Shows the logo (links to home), optionally displays the player name, | |
| * coin balance, trophy room link, shop link, a Leaderboard nav link, | |
| * and a mute/unmute toggle when showNav is true. | |
| */ | |
| interface HeaderProps { | |
| playerName?: string; | |
| showNav?: boolean; | |
| } | |
| export function Header({ playerName, showNav = true }: HeaderProps) { | |
| const { muted, toggleMute, play } = useSound(); | |
| const router = useRouter(); | |
| const [coins, setCoins] = useState<number | null>(null); | |
| const { equipped } = useCosmeticContext(); | |
| const hasTitleEffect = equipped.title_effect !== null; | |
| // Easter egg: triple-click logo within 1.5s → /admin | |
| const clickCountRef = useRef(0); | |
| const clickTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); | |
| const handleLogoClick = useCallback((e: React.MouseEvent) => { | |
| e.preventDefault(); | |
| clickCountRef.current += 1; | |
| if (clickTimerRef.current) clearTimeout(clickTimerRef.current); | |
| if (clickCountRef.current >= 3) { | |
| clickCountRef.current = 0; | |
| router.push("/admin"); | |
| return; | |
| } | |
| clickTimerRef.current = setTimeout(() => { | |
| if (clickCountRef.current === 1) router.push("/"); | |
| clickCountRef.current = 0; | |
| }, 400); | |
| }, [router]); | |
| // Easter egg: TRIVIAMASTER name flag | |
| const [isMaster] = useState( | |
| () => | |
| typeof window !== "undefined" && | |
| localStorage.getItem("triviamaster_unlocked") === "1", | |
| ); | |
| useEffect(() => { | |
| const player = getStoredPlayer(); | |
| if (player?.id) { | |
| getPlayerStarCoins(player.id).then(setCoins); | |
| } | |
| }, []); | |
| return ( | |
| <header className="fixed top-0 left-0 right-0 z-50 border-b border-white/10 bg-navy/80 backdrop-blur-md shadow-[0_1px_0_rgba(233,30,140,0.2)]"> | |
| <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4"> | |
| {/* Logo — single click goes to /, triple-click goes to /admin */} | |
| <button | |
| onClick={handleLogoClick} | |
| className={`bg-clip-text text-xl font-bold text-transparent ${ | |
| hasTitleEffect ? "" : "bg-gradient-to-r from-magenta to-cyan" | |
| }`} | |
| style={hasTitleEffect ? { backgroundImage: "var(--cosmetic-title-gradient)" } : undefined} | |
| > | |
| TRIVIAVERSE | |
| </button> | |
| {/* Right section */} | |
| {showNav && ( | |
| <nav className="flex items-center gap-4"> | |
| {playerName && ( | |
| <span className="text-sm text-white/60"> | |
| Playing as{" "} | |
| <span | |
| className="font-semibold" | |
| style={ | |
| isMaster | |
| ? { | |
| background: "linear-gradient(90deg, #dc2626, #7f1d1d, #dc2626)", | |
| WebkitBackgroundClip: "text", | |
| WebkitTextFillColor: "transparent", | |
| backgroundClip: "text", | |
| } | |
| : { color: "white" } | |
| } | |
| > | |
| {playerName} | |
| </span> | |
| </span> | |
| )} | |
| {/* Coin balance */} | |
| {coins !== null && ( | |
| <span className="flex items-center gap-1 text-sm font-semibold text-gold"> | |
| <Star size={14} /> | |
| {coins.toLocaleString()} | |
| </span> | |
| )} | |
| {/* Trophy room link */} | |
| <Link | |
| href="/trophies" | |
| className="flex items-center justify-center px-2 py-1 rounded text-white/80 hover:bg-white/10 hover:text-white transition-colors" | |
| aria-label="Trophy Room" | |
| > | |
| <Award size={20} /> | |
| </Link> | |
| {/* Shop link */} | |
| <Link | |
| href="/shop" | |
| className="flex items-center justify-center px-2 py-1 rounded text-white/80 hover:bg-white/10 hover:text-white transition-colors" | |
| aria-label="Shop" | |
| > | |
| <ShoppingBag size={20} /> | |
| </Link> | |
| <Link | |
| href="/leaderboard" | |
| className="flex items-center gap-1.5 rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-white/80 transition-colors hover:bg-white/10 hover:text-white" | |
| > | |
| <Trophy size={16} /> | |
| Leaderboard | |
| </Link> | |
| <button | |
| onClick={() => { | |
| toggleMute(); | |
| play("click"); | |
| }} | |
| className="flex items-center justify-center px-2 py-1 rounded text-white/80 hover:bg-white/10 hover:text-white transition-colors" | |
| aria-label={muted ? "Unmute" : "Mute"} | |
| > | |
| {muted ? <VolumeX size={20} /> : <Volume2 size={20} />} | |
| </button> | |
| </nav> | |
| )} | |
| </div> | |
| </header> | |
| ); | |
| } | |