"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, ChevronRight, } from "lucide-react"; import { getCachedProfile, isRegistered } from "@/lib/userId"; import AuthModal from "@/components/AuthModal"; import Button from "@/components/Button"; import Input from "@/components/Input"; import { BlindboxListSkeleton } from "@/components/Skeleton"; 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 [hydrated, setHydrated] = useState(false); 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(""); const [loadError, setLoadError] = useState(false); useEffect(() => { const registered = isRegistered(); setLoggedIn(registered); if (registered) { setProfile(getCachedProfile()); } setHydrated(true); }, []); 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); setLoadError(false); try { const res = await fetch(`/api/blindbox/rooms?userId=${p.id}`); if (!res.ok) throw new Error(); const data = await res.json(); setRooms(Array.isArray(data.rooms) ? data.rooms : []); } catch { setLoadError(true); } 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 */}
{/* Back button */} {/* Header */}

周末契约

ADVENTURE ROULETTE

平日蓄水,周末开奖。把所有"想做但一直没做"的事,交给命运来决定。
塞入想法
周末开奖
执行契约
{!hydrated || (loggedIn && loading) ? ( ) : !loggedIn ? ( /* ============ Layer 1: Unauthenticated — Login CTA ============ */ setShowAuth(true)} className="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 秒注册,无需手机号

) : loadError ? (

加载房间失败

) : 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} size="xl" variant="purple" className="flex-1" />
{/* Join alternative */}
或输入房间号加入
{ setJoinCode(e.target.value.toUpperCase().slice(0, 6)); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin(); }} maxLength={6} size="xl" variant="purple" className="flex-1 text-center font-mono tracking-[0.15em]" />
{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} size="lg" variant="purple" className="flex-1" />
{/* Join row */}
{ setJoinCode(e.target.value.toUpperCase().slice(0, 6)); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin(); }} maxLength={6} size="lg" variant="purple" className="flex-1 text-center font-mono tracking-[0.15em] placeholder:font-sans placeholder:tracking-normal" />
{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" />
); }