import confetti from 'canvas-confetti'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../context/AuthContext'; import { useGame } from '../hooks/useGame'; import { ConnectFourBoard } from './ConnectFourBoard'; import { TicTacToeBoard } from './TicTacToeBoard'; import { XIcon } from './icons'; interface Props { gameId: string; onClose: () => void; } export function GameModal({ gameId, onClose }: Props) { const { t } = useTranslation(); const { game, loading, error, makeMove } = useGame(gameId); const { session: auth } = useAuth(); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); const myUserId = auth?.user.id ?? null; const myPlayerIdx: 0 | 1 | null = !game || !myUserId ? null : game.players[0] === myUserId ? 0 : game.players[1] === myUserId ? 1 : null; const isMyTurn = !!game && game.currentTurnUserId === myUserId; const finished = !!game?.finishedAt; const winnerIdx: 0 | 1 | null = !game?.winnerUserId ? null : game.players[0] === game.winnerUserId ? 0 : game.players[1] === game.winnerUserId ? 1 : null; useEffect(() => { if (finished && winnerIdx !== null && winnerIdx === myPlayerIdx) { // Respect the OS-level reduced-motion preference. if ( typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches ) { return; } // Two bursts from the lower corners for a celebratory feel. void confetti({ particleCount: 80, spread: 60, origin: { x: 0.2, y: 0.9 }, }); void confetti({ particleCount: 80, spread: 60, origin: { x: 0.8, y: 0.9 }, }); } }, [finished, winnerIdx, myPlayerIdx]); const title = game?.gameType === 'c4' ? t('app:game.c4', { defaultValue: 'Vier-Gewinnt' }) : t('app:game.ttt', { defaultValue: 'Tic-Tac-Toe' }); const statusLine = (() => { if (loading) return t('app:game.loading', { defaultValue: 'Lädt…' }); if (error) return error; if (!game) return t('app:game.missing', { defaultValue: 'Spiel nicht gefunden.' }); if (finished) { if (winnerIdx === null) return t('app:game.draw', { defaultValue: 'Unentschieden!' }); if (winnerIdx === myPlayerIdx) return t('app:game.you_win', { defaultValue: 'Du hast gewonnen!' }); return t('app:game.you_lose', { defaultValue: 'Du hast verloren.' }); } if (myPlayerIdx === null) return t('app:game.spectator', { defaultValue: 'Du schaust nur zu.' }); if (isMyTurn) return t('app:game.your_turn', { defaultValue: 'Du bist dran' }); return t('app:game.opponent_turn', { defaultValue: 'Gegner ist dran…' }); })(); return (