"use client"; import { createPortal } from "react-dom"; import { useEffect, useMemo, useState } from "react"; import { Maximize2, Minimize2 } from "lucide-react"; import type { PokerPhase } from "@/lib/minigames/poker"; import { describeHandStrength } from "@/lib/minigames/poker/handHint"; import type { PokerSnapshot } from "@/app/(frontend)/minigames/poker/actions"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card as UiCard, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { PokerCardStyleToggle } from "./PokerCardStyleToggle"; import { PokerDevPanel } from "./PokerDevPanel"; import { PokerFeltCanvas, type SeatedSnapshot } from "./PokerFeltCanvas"; import { PokerSoundToggle } from "./PokerSoundToggle"; import type { PokerSoundControls } from "./usePokerSounds"; import type { ActivePokerPopup } from "./usePokerPopups"; import type { PokerActionLogEntry } from "./pokerPopups"; import { RenameDialogGuard } from "./RenameDialogGuard"; interface PokerGameTableProps { readonly snapshot: SeatedSnapshot; readonly onSnapshot: (snapshot: PokerSnapshot) => void; readonly onStart: () => Promise; readonly onLeave: () => Promise; readonly onJoin: () => Promise; readonly sound: PokerSoundControls; readonly popups: readonly ActivePokerPopup[]; readonly actionLog: readonly PokerActionLogEntry[]; readonly isSuperuser: boolean; readonly superuserIds: readonly number[]; } const PHASE_LABELS: Record = { betting: "Betting", showdown: "Showdown", "hand-complete": "Hand complete", "game-complete": "Game complete", }; export function PokerGameTable({ snapshot, onSnapshot, onStart, onLeave, onJoin, sound, popups, actionLog, isSuperuser, superuserIds, }: PokerGameTableProps) { const [starting, setStarting] = useState(false); const [leaving, setLeaving] = useState(false); const [fullscreen, setFullscreen] = useState(false); const [mounted, setMounted] = useState(false); const view = snapshot.you; const handHint = useMemo( () => view.phase === "game-complete" ? null : describeHandStrength(view.yourHoleCards, view.communityCards), [view.communityCards, view.phase, view.yourHoleCards], ); const bestCardKeys = useMemo(() => new Set(handHint?.bestCardKeys ?? []), [handHint]); const handTier = handHint?.tier ?? null; useEffect(() => { setMounted(true); }, []); useEffect(() => { if (!fullscreen) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") setFullscreen(false); }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [fullscreen]); const start = async () => { setStarting(true); await onStart(); setStarting(false); }; const leave = async () => { setLeaving(true); await onLeave(); setLeaving(false); }; const tableName = snapshot.name ?? `Table ${snapshot.tableId}`; const spectator = Boolean(snapshot.spectator); const feltCanvas = ( void start(), onLeave: () => void leave(), }} onActionFeedback={sound.playUiClick} popups={popups} actionLog={actionLog} spectator={spectator} onJoin={onJoin} superuserIds={superuserIds} fullscreen={fullscreen} /> ); if (fullscreen) { if (!mounted) return null; return createPortal(
{tableName} {spectator && Spectating} {PHASE_LABELS[view.phase]}
{isSuperuser && } {view.phase !== "game-complete" && ( )}
{feltCanvas}
, document.body, ); } return (
{tableName} {spectator && Spectating} {PHASE_LABELS[view.phase]}

Hand {view.handNumber} ยท Blinds {view.config.smallBlind}/{view.config.bigBlind}

v{snapshot.version} {isSuperuser && } {view.phase !== "game-complete" && ( )}
{feltCanvas}
); }