Hostitel prida na volne sedadlo bota (+ Bot = heuristika, + AI bot = natrenovana siet) alebo bota odoberie; boti maju vlastnu ikonu a flag is_bot v rosteroch. Surove ucty bot:<kind>-<n> sa vsade zobrazuju cez displayName ako "Bot 2" / "AI bot 1" (stol, kopka, tabulky, historia). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
113 lines
4.6 KiB
TypeScript
113 lines
4.6 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
import { useGameStore } from '../store/gameStore';
|
|
import { emit } from '../lib/socket';
|
|
import { leaveGame } from '../lib/leaveGame';
|
|
import { displayName } from '../lib/names';
|
|
|
|
export default function Lobby() {
|
|
const { gid } = useParams<{ gid: string }>();
|
|
const navigate = useNavigate();
|
|
const myPlayer = useGameStore((s) => s.myPlayer);
|
|
const games = useGameStore((s) => s.games);
|
|
const [showCopied, setShowCopied] = useState(false);
|
|
const copiedTimeout = useRef<ReturnType<typeof setTimeout>>();
|
|
|
|
const game = games.find((g) => g.gid === gid);
|
|
const players = game?.players ?? [];
|
|
const isHost = myPlayer?.order === 0;
|
|
const canStart = players.length === 4 && isHost;
|
|
|
|
const handleLeave = () => leaveGame(navigate);
|
|
|
|
const handleCopyCode = () => {
|
|
if (!gid) return;
|
|
navigator.clipboard.writeText(`${window.location.origin}/lobby/${gid}`);
|
|
setShowCopied(true);
|
|
clearTimeout(copiedTimeout.current);
|
|
copiedTimeout.current = setTimeout(() => setShowCopied(false), 1800);
|
|
};
|
|
|
|
useEffect(() => () => clearTimeout(copiedTimeout.current), []);
|
|
|
|
return (
|
|
<div className="max-w-md mx-auto p-4 pt-8 min-h-screen">
|
|
<div
|
|
className={`fixed top-4 left-1/2 -translate-x-1/2 px-4 py-2 rounded-lg bg-header border border-gold/30 text-gold text-sm shadow-lg transition-opacity duration-300 z-50 ${
|
|
showCopied ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
|
}`}
|
|
>
|
|
Odkaz na hru skopírovaný
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h1 className="font-serif text-2xl text-gold">{game?.name ?? 'Hra'}</h1>
|
|
<button onClick={handleLeave} className="text-sm text-green-dim hover:text-gold">
|
|
Odísť
|
|
</button>
|
|
</div>
|
|
|
|
<div className="bg-header border border-[#142018] rounded-xl p-4 mb-4 flex items-center justify-between">
|
|
<div>
|
|
<p className="text-xs uppercase tracking-[.1em] text-green-dim mb-1">Kód hry</p>
|
|
<p className="font-mono text-sm text-green-score break-all">{gid}</p>
|
|
</div>
|
|
<button
|
|
onClick={handleCopyCode}
|
|
className="ml-3 px-3 py-1 rounded-lg text-sm border border-gold/30 text-gold hover:bg-gold hover:text-table transition-colors"
|
|
>
|
|
Kopírovať URL
|
|
</button>
|
|
</div>
|
|
|
|
<div className="bg-header border border-[#142018] rounded-xl p-4 mb-6 flex flex-col gap-3">
|
|
{[0, 1, 2, 3].map((order) => {
|
|
const p = players.find((pl) => pl.order === order);
|
|
return (
|
|
<div key={order} className="flex items-center gap-3">
|
|
<span className={`text-lg ${p ? 'text-gold' : 'text-[#7a7058]'}`}>
|
|
{p ? (p.is_bot ? '⚙' : '✦') : '○'}
|
|
</span>
|
|
<span className={p ? 'font-serif text-green-score' : 'text-green-dim italic'}>
|
|
{p ? `${displayName(p.name)}${myPlayer?.order === p.order ? ' (ty)' : ''}` : 'Čaká sa…'}
|
|
</span>
|
|
{isHost && p?.is_bot && (
|
|
<button
|
|
onClick={() => gid && emit.removeBot(gid, order)}
|
|
className="ml-auto text-xs text-green-dim hover:text-gold"
|
|
>
|
|
Odobrať
|
|
</button>
|
|
)}
|
|
{isHost && !p && (
|
|
<span className="ml-auto flex gap-2">
|
|
<button
|
|
onClick={() => gid && emit.addBot(gid)}
|
|
className="px-2 py-0.5 rounded-lg text-xs border border-gold/30 text-gold hover:bg-gold hover:text-table transition-colors"
|
|
>
|
|
+ Bot
|
|
</button>
|
|
<button
|
|
onClick={() => gid && emit.addBot(gid, 'neural')}
|
|
className="px-2 py-0.5 rounded-lg text-xs border border-gold/30 text-gold hover:bg-gold hover:text-table transition-colors"
|
|
>
|
|
+ AI bot
|
|
</button>
|
|
</span>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<button
|
|
disabled={!canStart}
|
|
onClick={() => gid && emit.startGame(gid)}
|
|
className="w-full py-3 rounded-xl bg-gold text-table font-serif font-semibold text-lg disabled:opacity-40 disabled:cursor-default hover:bg-gold-bright transition-colors"
|
|
>
|
|
{isHost ? (canStart ? 'Začať hru' : `Čaká sa na hráčov (${players.length}/4)`) : 'Čaká sa na hostiteľa…'}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|