import { useEffect, useRef, useState } from 'react' import { Link, useParams } from 'react-router-dom' import { loadLiveRoom, subscribeLiveRoom } from '../lib/api' import type { LiveRoomDetail } from '../types' type RoomSpectatorPageProps = { onConfirmFinished: () => void } const ROOM_POLL_MS = 1500 type RoomClosedDialog = { message: string title: string } | null export function RoomSpectatorPage({ onConfirmFinished }: RoomSpectatorPageProps) { const { roomId = '' } = useParams() const [error, setError] = useState('') const [loading, setLoading] = useState(true) const [room, setRoom] = useState(null) const [showFinishedDialog, setShowFinishedDialog] = useState(false) const [roomClosedDialog, setRoomClosedDialog] = useState(null) const previousStatusRef = useRef(null) const hasRoomRef = useRef(false) useEffect(() => { if (!roomId) { return } let active = true hasRoomRef.current = false previousStatusRef.current = null const applyRoomUpdate = (nextRoom: LiveRoomDetail) => { if (!active) { return } if (nextRoom.status === 'finished' && previousStatusRef.current !== 'finished') { setShowFinishedDialog(true) } previousStatusRef.current = nextRoom.status hasRoomRef.current = true setRoom(nextRoom) setError('') setLoading(false) } const load = async (showLoadError = true) => { try { const nextRoom = await loadLiveRoom(roomId) applyRoomUpdate(nextRoom) } catch (loadError) { if (!active) { return } if (showLoadError || !hasRoomRef.current) { setError(loadError instanceof Error ? loadError.message : '載入觀戰房間失敗。') setLoading(false) } } } void load() const unsubscribe = subscribeLiveRoom( roomId, (nextRoom) => { applyRoomUpdate(nextRoom) }, () => { if (!active) { return } if (!hasRoomRef.current) { setError('觀戰連線中斷,請稍後重試。') setLoading(false) } }, (payload) => { if (!active) { return } if (payload.status === 'released') { setRoomClosedDialog({ title: '房間已關閉', message: '房主已重整頁面、離開記分板或重新選隊伍,本房間已結束觀戰。', }) setLoading(false) } }, ) const timer = window.setInterval(() => { void load(false) }, ROOM_POLL_MS) return () => { active = false window.clearInterval(timer) unsubscribe() } }, [roomId]) if (loading) { return (

正在載入觀戰房間...

) } if (!room && error) { return (

Spectator

無法進入房間

{error}

返回房間列表
) } return ( <>

Spectator

房號 {room?.roomId ?? roomId}

這是觀戰模式,只會即時同步比分,不提供任何操作。

{room ? (
{room.leftTeamName} {room.scoreState.scoreLeft}
:
{room.rightTeamName} {room.scoreState.scoreRight}
目標分數 {room.scoreState.targetScore} 房間狀態 {room.status === 'finished' ? '已結束' : '進行中'} 最後更新 {new Date(room.updatedAt).toLocaleTimeString('zh-TW', { hour12: false })}
) : (

房間已不存在,請返回房間列表。

)}
{showFinishedDialog && room ? (

比賽結束

{room.winnerTeamName ?? '已有獲勝隊伍'}

{room.winnerTeamName ? `${room.winnerTeamName} 已獲勝,按下確定後返回房間列表。` : '比賽已結束,按下確定後返回房間列表。'}

) : null} {roomClosedDialog ? (

觀戰提醒

{roomClosedDialog.title}

{roomClosedDialog.message}

) : null} ) }