Files
esc/packages/client/src/routes/display.$roomCode.tsx

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>
)
}