101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { useState } from "react"
|
|
import { createFileRoute } from "@tanstack/react-router"
|
|
import { useWebSocket } from "@/hooks/use-websocket"
|
|
import { useRoomStore } from "@/stores/room-store"
|
|
import { PlayerList } from "@/components/player-list"
|
|
import { RoomHeader } from "@/components/room-header"
|
|
import { ACT_LABELS } from "@celebrate-esc/shared"
|
|
|
|
export const Route = createFileRoute("/display/$roomCode")({
|
|
component: DisplayView,
|
|
})
|
|
|
|
function DisplayView() {
|
|
const { roomCode } = Route.useParams()
|
|
useWebSocket(roomCode)
|
|
const { room, connectionStatus, gameState } = useRoomStore()
|
|
|
|
if (!room) {
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center">
|
|
<p className="text-muted-foreground">
|
|
{connectionStatus === "connecting" ? "Connecting..." : "Room not found"}
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="flex min-h-screen flex-col">
|
|
<RoomHeader roomCode={roomCode} currentAct={room.currentAct} connectionStatus={connectionStatus} />
|
|
<div className="flex flex-1 flex-col items-center justify-center gap-8 p-8">
|
|
{room.currentAct === "lobby" && <LobbyDisplay roomCode={roomCode} />}
|
|
|
|
{room.currentAct === "pre-show" && gameState && (
|
|
<div className="flex flex-col items-center gap-4 py-12">
|
|
<p className="text-2xl text-muted-foreground">Pre-Show — Predictions</p>
|
|
<p className="text-lg text-muted-foreground">
|
|
{Object.values(gameState.predictionSubmitted).filter(Boolean).length} / {Object.keys(gameState.predictionSubmitted).length} predictions submitted
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{room.currentAct !== "lobby" && room.currentAct !== "ended" && room.currentAct !== "pre-show" && (
|
|
<div className="flex flex-col items-center gap-4 py-12">
|
|
<p className="text-2xl text-muted-foreground">{ACT_LABELS[room.currentAct]}</p>
|
|
</div>
|
|
)}
|
|
|
|
{room.currentAct === "ended" && (
|
|
<div className="flex flex-col items-center gap-4 py-12">
|
|
<p className="text-2xl text-muted-foreground">The party has ended. Thanks for playing!</p>
|
|
</div>
|
|
)}
|
|
|
|
<PlayerList
|
|
players={room.players}
|
|
mySessionId={null}
|
|
predictionSubmitted={gameState?.predictionSubmitted}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function LobbyDisplay({ roomCode }: { roomCode: string }) {
|
|
const [copied, setCopied] = useState(false)
|
|
const base = import.meta.env.BASE_URL.replace(/\/$/, "")
|
|
const joinUrl = `${window.location.origin}${base}/play/${roomCode}`
|
|
|
|
function copyCode() {
|
|
navigator.clipboard.writeText(roomCode).then(() => {
|
|
setCopied(true)
|
|
setTimeout(() => setCopied(false), 2000)
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col items-center gap-6">
|
|
<h2 className="text-2xl text-muted-foreground">Join the party!</h2>
|
|
<button
|
|
type="button"
|
|
onClick={copyCode}
|
|
className="cursor-pointer rounded-lg border-4 border-dashed border-muted p-8 transition-colors hover:border-primary/50"
|
|
title="Click to copy room code"
|
|
>
|
|
<span className="font-mono text-8xl font-bold tracking-[0.3em]">{roomCode}</span>
|
|
</button>
|
|
<p className="text-muted-foreground">
|
|
{copied ? (
|
|
<span className="font-medium text-green-600">Copied!</span>
|
|
) : (
|
|
<>Tap the code to copy</>
|
|
)}
|
|
</p>
|
|
<p className="text-muted-foreground">
|
|
Go to <span className="font-mono font-medium">{joinUrl}</span>
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|