Files
ActionBlock/actionBlocks_website/src/pages/LobbyPage.tsx
T
2026-10-23 19:23:03 +02:00

125 lines
4.0 KiB
TypeScript

import { useState } from 'react';
import { Logo } from '../components/Logo';
import { Button } from '../components/ui/Button';
import { Panel } from '../components/ui/Panel';
import { controlsHelp } from '../config/keybindings';
import type { Lobby } from '../net/types';
import type { GameActions } from '../net/useGameConnection';
import { itemVisuals } from '../theme/assets';
import styles from './LobbyPage.module.css';
interface Props {
lobby: Lobby;
playerId: string;
actions: GameActions;
}
export function LobbyPage({ lobby, playerId, actions }: Props) {
const [error, setError] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
const isHost = lobby.hostId === playerId;
const copyLink = async () => {
const url = `${window.location.origin}${window.location.pathname}?code=${lobby.code}`;
try {
await navigator.clipboard.writeText(url);
setCopied(true);
window.setTimeout(() => setCopied(false), 1500);
} catch {
setError(url);
}
};
const start = async () => {
setError(null);
try {
await actions.startGame();
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
};
return (
<main className={styles.page}>
<header className={styles.header}>
<Logo small />
<Button variant="secondary" onClick={() => void actions.leaveLobby()}>
Leave
</Button>
</header>
<Panel title="Lobby code" className={styles.codePanel}>
<div className={styles.code}>{lobby.code}</div>
<Button variant="secondary" onClick={copyLink}>
{copied ? 'Copied!' : 'Copy invite link'}
</Button>
</Panel>
<div className={styles.grid}>
<Panel title={`Players ${lobby.players.length}/${lobby.maxPlayers}`}>
<ol className={styles.players}>
{lobby.players.map((p) => (
<li key={p.id} className={p.id === playerId ? styles.me : ''}>
<span>{p.name}</span>
{p.isHost && <span className={styles.badge}>HOST</span>}
{p.id === playerId && <span className={styles.badge}>YOU</span>}
</li>
))}
</ol>
{isHost ? (
<Button onClick={start} className={styles.start}>
Start game
</Button>
) : (
<p className={styles.waiting}>Waiting for the host to start...</p>
)}
{error && <p className={styles.error}>{error}</p>}
</Panel>
{lobby.lastResults && (
<Panel title="Last round">
<table className={styles.results}>
<tbody>
{lobby.lastResults.map((r) => (
<tr key={r.id}>
<td className={r.place === 1 ? styles.winner : ''}>#{r.place}</td>
<td>{r.name}</td>
<td className={styles.num}>{r.score}</td>
<td className={styles.num}>{r.lines}L</td>
</tr>
))}
</tbody>
</table>
</Panel>
)}
<Panel title="Controls">
<dl className={styles.controls}>
{controlsHelp.map((c) => (
<div key={c.action}>
<dt>{c.keys}</dt>
<dd>{c.action}</dd>
</div>
))}
</dl>
</Panel>
<Panel title="Items">
<p className={styles.hint}>Items hide inside blocks. Clear their line to collect them.</p>
<ul className={styles.items}>
{Object.entries(itemVisuals).map(([id, v]) => (
<li key={id}>
<img src={v.icon} alt="" width={20} height={20} />
<div>
<div style={{ color: v.color }}>{v.name}</div>
<div className={styles.itemDescription}>{v.description}</div>
</div>
</li>
))}
</ul>
</Panel>
</div>
</main>
);
}