open matches listing with available players
This commit is contained in:
55
src/tictactoe/MatchList.tsx
Normal file
55
src/tictactoe/MatchList.tsx
Normal 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",
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNakama } from "./providers/NakamaProvider";
|
||||
import { Match } from "@heroiclabs/nakama-js";
|
||||
import Board from "./Board";
|
||||
import MatchList from "./MatchList";
|
||||
|
||||
export default function TicTacToe() {
|
||||
const [username, setUsername] = useState("");
|
||||
@@ -11,12 +13,14 @@ export default function TicTacToe() {
|
||||
]);
|
||||
const [turn, setTurn] = useState<number>(0);
|
||||
const [winner, setWinner] = useState<string | null>(null);
|
||||
const [openMatches, setOpenMatches] = useState<Match[]>([]);
|
||||
|
||||
const {
|
||||
loginOrRegister,
|
||||
joinMatchmaker,
|
||||
onMatchData,
|
||||
sendMatchData,
|
||||
listOpenMatches,
|
||||
matchId,
|
||||
} = useNakama();
|
||||
|
||||
@@ -32,6 +36,25 @@ export default function TicTacToe() {
|
||||
});
|
||||
}, [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
|
||||
// ------------------------------------------
|
||||
@@ -82,6 +105,9 @@ export default function TicTacToe() {
|
||||
/>
|
||||
<button onClick={connect}>Connect</button>
|
||||
<button onClick={startQueue}>Join Matchmaking</button>
|
||||
<MatchList
|
||||
matches={openMatches}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ import {
|
||||
Session,
|
||||
Socket,
|
||||
MatchmakerTicket,
|
||||
Match,
|
||||
MatchData,
|
||||
MatchmakerMatched,
|
||||
} from "@heroiclabs/nakama-js";
|
||||
// @ts-ignore
|
||||
import { ApiMatch } from "@heroiclabs/nakama-js/dist/api.gen"
|
||||
|
||||
import React, { createContext, useContext, useState } from "react";
|
||||
|
||||
function getOrCreateDeviceId(): string {
|
||||
@@ -32,6 +34,7 @@ export interface NakamaContextType {
|
||||
sendMatchData(matchId: string, op: number, data: object): void;
|
||||
|
||||
onMatchData(cb: (msg: any) => void): void;
|
||||
listOpenMatches(): Promise<ApiMatch[]>;
|
||||
}
|
||||
|
||||
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 (
|
||||
<NakamaContext.Provider
|
||||
value={{
|
||||
@@ -140,6 +156,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
||||
joinMatch,
|
||||
sendMatchData,
|
||||
onMatchData,
|
||||
listOpenMatches,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user