anvilsign in

collin/mahjong

1import { useEffect, useRef } from 'react';
2import type { Prefs } from '../game/prefs';
3import { WIND_EN, WIND_NAMES } from '../game/tiles';
4import type { SeatId } from '../game/types';
5import type { NetSession } from '../net/session';
6import { Bi, BiText } from './Bi';
7import { useLang, useT } from './lang';
8
9const SEATS: SeatId[] = [0, 1, 2, 3];
10
11/**
12 * The room before the deal: who is here, who is sitting where, and — for the
13 * host — the button that starts it. Seats nobody takes go to the computer, so
14 * two friends and two bots is a game, not a wait.
15 */
16export function OnlineLobby({ session, prefs }: { session: NetSession; prefs: Prefs }) {
17 const t = useT();
18 const lang = useLang();
19 const seats = session.seats;
20 const me = session.room.me.id;
21
22 // Walking in unseated: take the first free chair rather than making everyone
23 // pick one. Moving is one tap on another seat — including one somebody
24 // already holds, which is how two people share a hand.
25 const claimed = useRef(false);
26 useEffect(() => {
27 if (claimed.current || session.mySeat !== null) return;
28 if (seats.some((s) => s.ids.length === 0)) {
29 claimed.current = true;
30 session.claimSeat(null);
31 }
32 }, [session, seats]);
33
34 const seated = seats.filter((s) => s.ids.length > 0).length;
35
36 return (
37 <div className="lobby">
38 <h1>{lang === 'en' ? 'Taiwanese Mahjong' : '台灣麻將'}</h1>
39 <p className="sub">
40 {t('線上對戰', 'online game', ' · ')} · {session.playerCount}{' '}
41 {t('人在房', 'in the room')}
42 </p>
43 <p className="hint">
44 <BiText
45 zh="把這頁的連結傳給朋友,他們一開就進同一房。"
46 en="Share this page's invite link — anyone who opens it lands at this table."
47 />
48 </p>
49
50 <div className="pick-grid">
51 {SEATS.map((i) => {
52 const slot = seats[i];
53 const mine = slot.ids.includes(me);
54 const held = slot.ids.length;
55 return (
56 <button
57 key={i}
58 className={`btn big-btn ${mine ? '' : 'ghost'}`}
59 onClick={() => !mine && session.claimSeat(i)}
60 >
61 {t(WIND_NAMES[i], WIND_EN[i])}
62 <em>
63 {held === 0
64 ? t('空位,開局時由電腦代打', 'open — a bot if nobody sits', ' · ')
65 : mine
66 ? `${slot.name || t('你', 'you')}${
67 held > 1 ? ` +${held - 1}` : ''
68 } · ${t('你', 'you')}`
69 : `${slot.name}${held > 1 ? ` +${held - 1}` : ''} · ${t('可同坐', 'join them')}`}
70 </em>
71 </button>
72 );
73 })}
74 </div>
75
76 <div className="lobby-buttons">
77 {session.isHost ? (
78 <button
79 className="btn big-btn"
80 disabled={seated === 0}
81 onClick={() => session.startOnline(prefs.rules)}
82 >
83 <Bi
84 zh="開局"
85 en="Deal"
86 sub={
87 seated < 4
88 ? t(`${4 - seated} 個電腦`, `${4 - seated} bots fill in`)
89 : undefined
90 }
91 />
92 </button>
93 ) : (
94 <p className="hint">{t('等房主開局', 'waiting for the host to deal', ' · ')}</p>
95 )}
96 </div>
97 {session.err && <p className="hint">{session.err}</p>}
98 </div>
99 );
100}