"use client"; import { useState, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; import { ArrowLeft, Package, Plus, LogIn, Users, Sparkles, Loader2, ChevronRight, } from "lucide-react"; import { getCachedProfile, isRegistered } from "@/lib/userId"; import AuthModal from "@/components/AuthModal"; import type { UserProfile } from "@/types"; interface RoomSummary { id: string; code: string; name: string; memberCount: number; poolCount: number; members: { id: string; username: string; avatar: string }[]; lastDrawn: { content: string; createdAt: string } | null; } export default function BlindboxLobbyPage() { const router = useRouter(); const [loggedIn, setLoggedIn] = useState(false); const [profile, setProfile] = useState(null); const [showAuth, setShowAuth] = useState(false); const [rooms, setRooms] = useState([]); const [loading, setLoading] = useState(true); const [createName, setCreateName] = useState(""); const [creating, setCreating] = useState(false); const [joinCode, setJoinCode] = useState(""); const [joining, setJoining] = useState(false); const [error, setError] = useState(""); useEffect(() => { const registered = isRegistered(); setLoggedIn(registered); if (registered) { setProfile(getCachedProfile()); } }, []); useEffect(() => { const handler = () => { const registered = isRegistered(); setLoggedIn(registered); setProfile(registered ? getCachedProfile() : null); }; window.addEventListener("nowhatever_auth", handler); return () => window.removeEventListener("nowhatever_auth", handler); }, []); const fetchRooms = useCallback(async () => { const p = getCachedProfile(); if (!p) return; setLoading(true); try { const res = await fetch(`/api/blindbox/rooms?userId=${p.id}`); const data = await res.json(); setRooms(data.rooms ?? []); } catch { /* ignore */ } finally { setLoading(false); } }, []); useEffect(() => { if (loggedIn) fetchRooms(); else setLoading(false); }, [loggedIn, fetchRooms]); const handleAuth = (p: UserProfile) => { setProfile(p); setLoggedIn(true); setShowAuth(false); }; const handleCreate = async () => { if (creating || !profile) return; setCreating(true); setError(""); try { const res = await fetch("/api/blindbox/room", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId: profile.id, name: createName.trim() || undefined }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); router.push(`/blindbox/${data.code}`); } catch (e) { setError(e instanceof Error ? e.message : "创建失败"); } finally { setCreating(false); } }; const handleJoin = async () => { if (joining || !profile || !joinCode.trim()) return; setJoining(true); setError(""); try { const res = await fetch("/api/blindbox/room/join", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId: profile.id, code: joinCode.trim() }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); router.push(`/blindbox/${data.code}`); } catch (e) { setError(e instanceof Error ? e.message : "加入失败"); } finally { setJoining(false); } }; return (
{/* Ambient */}
{/* Header */}

🎁 周末契约

ADVENTURE ROULETTE

{!loggedIn ? ( /* ============ Layer 1: Unauthenticated — Feature intro ============ */ {/* Hero icon */}

和 TA 一起,拆开周末

平日蓄水,周末开奖。把所有"想做但一直没做"的事, 交给命运来决定。

{/* Steps */}
{[ { step: "1", icon: Plus, text: "创建专属房间,邀请 TA 加入" }, { step: "2", icon: Package, text: "平时随时塞入疯狂想法" }, { step: "3", icon: Sparkles, text: "周末一起盲抽,绝不反悔" }, ].map((s) => (
{s.step}

{s.text}

))}
{/* CTA */} setShowAuth(true)} className="mt-10 flex h-12 w-full max-w-xs items-center justify-center gap-2 rounded-2xl bg-linear-to-r from-purple-600 to-indigo-600 text-sm font-bold text-white shadow-lg shadow-purple-900/40 transition-shadow hover:shadow-xl hover:shadow-purple-900/50" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.97 }} > 登录 / 注册,开始你的第一个盲盒

仅需用户名 + 密码,10 秒注册

) : loading ? ( /* ============ Loading ============ */

加载中...

) : rooms.length === 0 ? ( /* ============ Layer 2: Logged in, no rooms — Create first ============ */

还没有盲盒房间

创建第一个房间,邀请 TA 一起玩

{/* Inline create form */}
{ setCreateName(e.target.value.slice(0, 30)); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} maxLength={30} className="h-11 flex-1 rounded-xl border-none bg-surface px-4 text-sm text-foreground outline-none ring-1 ring-border transition-all placeholder:text-dim focus:ring-2 focus:ring-purple-600" />
{/* Join alternative */}
或输入房间号加入
{ setJoinCode(e.target.value.toUpperCase().slice(0, 6)); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin(); }} maxLength={6} className="h-11 flex-1 rounded-xl border-none bg-surface px-4 text-center font-mono text-sm tracking-[0.15em] text-foreground outline-none ring-1 ring-border transition-all placeholder:text-dim focus:ring-2 focus:ring-purple-600" />
{error && ( {error} )}
) : ( /* ============ Layer 3: Logged in, has rooms — Room list ============ */ {/* Create row */}
{ setCreateName(e.target.value.slice(0, 30)); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} maxLength={30} className="h-10 flex-1 rounded-xl border-none bg-surface px-3 text-sm text-foreground outline-none ring-1 ring-border transition-all placeholder:text-dim focus:ring-2 focus:ring-purple-600" />
{/* Join row */}
{ setJoinCode(e.target.value.toUpperCase().slice(0, 6)); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin(); }} maxLength={6} className="h-10 flex-1 rounded-xl border-none bg-surface px-3 text-center font-mono text-sm tracking-[0.15em] text-foreground outline-none ring-1 ring-border transition-all placeholder:font-sans placeholder:tracking-normal placeholder:text-dim focus:ring-2 focus:ring-purple-600" />
{error && ( {error} )} {/* Room list */}
{rooms.map((room, i) => ( router.push(`/blindbox/${room.code}`)} className="group flex w-full items-center gap-3 rounded-2xl bg-surface p-4 text-left ring-1 ring-border transition-all hover:bg-elevated hover:ring-purple-500/30" initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: i * 0.06 }} whileTap={{ scale: 0.98 }} > {/* Icon */}
{/* Info */}

{room.name}

{room.memberCount} {room.poolCount} 待抽
{room.lastDrawn && (

最近抽中:{room.lastDrawn.content}

)}
{/* Members preview */}
{room.members.slice(0, 3).map((m) => (
{m.avatar}
))} {room.memberCount > 3 && (
+{room.memberCount - 3}
)}
))}
)} {/* Auth Modal */} setShowAuth(false)} onAuth={handleAuth} defaultTab="register" />
); }