open matches listing with available players

This commit is contained in:
2025-11-28 15:51:55 +05:30
parent a4fc04ace3
commit cb3f5fb5cf
3 changed files with 99 additions and 1 deletions

View File

@@ -0,0 +1,55 @@
import { Match } from "@heroiclabs/nakama-js";
interface MatchListProps {
matches: Match[];
}
export default function MatchList({ matches }: MatchListProps) {
if (!matches.length) return <p>No open matches</p>;
return (
<div style={{ marginTop: "20px" }}>
<h3>Open Matches</h3>
<table
style={{
width: "100%",
borderCollapse: "collapse",
marginTop: "10px",
}}
>
<thead>
<tr>
<th style={th}>Sr No.</th>
<th style={th}>Match ID</th>
<th style={th}>Label</th>
</tr>
</thead>
<tbody>
{matches
.filter(m => m.size ?? 0 > 0)
.map((m, index) => (
<tr key={m.match_id}>
<td style={td}>{index + 1}</td>
<td style={td}>{m.match_id}</td>
<td style={td}>{m.label ?? "-"}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
const th: React.CSSProperties = {
textAlign: "left",
padding: "8px",
background: "#f2f2f2",
borderBottom: "1px solid #ccc",
};
const td: React.CSSProperties = {
padding: "8px",
borderBottom: "1px solid #eee",
};

View File

@@ -1,6 +1,8 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useNakama } from "./providers/NakamaProvider"; import { useNakama } from "./providers/NakamaProvider";
import { Match } from "@heroiclabs/nakama-js";
import Board from "./Board"; import Board from "./Board";
import MatchList from "./MatchList";
export default function TicTacToe() { export default function TicTacToe() {
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
@@ -11,12 +13,14 @@ export default function TicTacToe() {
]); ]);
const [turn, setTurn] = useState<number>(0); const [turn, setTurn] = useState<number>(0);
const [winner, setWinner] = useState<string | null>(null); const [winner, setWinner] = useState<string | null>(null);
const [openMatches, setOpenMatches] = useState<Match[]>([]);
const { const {
loginOrRegister, loginOrRegister,
joinMatchmaker, joinMatchmaker,
onMatchData, onMatchData,
sendMatchData, sendMatchData,
listOpenMatches,
matchId, matchId,
} = useNakama(); } = useNakama();
@@ -32,6 +36,25 @@ export default function TicTacToe() {
}); });
}, [onMatchData]); }, [onMatchData]);
useEffect(() => {
let active = true;
async function refreshLoop() {
while (active) {
const matches = await listOpenMatches();
setOpenMatches(matches);
await new Promise(res => setTimeout(res, 500)); // 0.5s refresh
}
}
refreshLoop();
return () => {
active = false;
};
}, [listOpenMatches]);
// ------------------------------------------ // ------------------------------------------
// CONNECT // CONNECT
// ------------------------------------------ // ------------------------------------------
@@ -82,6 +105,9 @@ export default function TicTacToe() {
/> />
<button onClick={connect}>Connect</button> <button onClick={connect}>Connect</button>
<button onClick={startQueue}>Join Matchmaking</button> <button onClick={startQueue}>Join Matchmaking</button>
<MatchList
matches={openMatches}
/>
</> </>
)} )}

View File

@@ -3,10 +3,12 @@ import {
Session, Session,
Socket, Socket,
MatchmakerTicket, MatchmakerTicket,
Match,
MatchData, MatchData,
MatchmakerMatched, MatchmakerMatched,
} from "@heroiclabs/nakama-js"; } from "@heroiclabs/nakama-js";
// @ts-ignore
import { ApiMatch } from "@heroiclabs/nakama-js/dist/api.gen"
import React, { createContext, useContext, useState } from "react"; import React, { createContext, useContext, useState } from "react";
function getOrCreateDeviceId(): string { function getOrCreateDeviceId(): string {
@@ -32,6 +34,7 @@ export interface NakamaContextType {
sendMatchData(matchId: string, op: number, data: object): void; sendMatchData(matchId: string, op: number, data: object): void;
onMatchData(cb: (msg: any) => void): void; onMatchData(cb: (msg: any) => void): void;
listOpenMatches(): Promise<ApiMatch[]>;
} }
export const NakamaContext = createContext<NakamaContextType>(null!); export const NakamaContext = createContext<NakamaContextType>(null!);
@@ -128,6 +131,19 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
}; };
} }
async function listOpenMatches(): Promise<ApiMatch[]> {
if (!session) {
console.warn("[Nakama] listOpenMatches called before login");
return [];
}
const result = await client.listMatches(session, 10);
console.log("[Nakama] Open matches:", result.matches);
return result.matches ?? [];
}
return ( return (
<NakamaContext.Provider <NakamaContext.Provider
value={{ value={{
@@ -140,6 +156,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
joinMatch, joinMatch,
sendMatchData, sendMatchData,
onMatchData, onMatchData,
listOpenMatches,
}} }}
> >
{children} {children}