import { useState, useEffect } from "react"; import Web3 from "web3"; import { Button } from "./Button"; import { Input } from "./Input"; import { GameDetails } from "./GameModal"; import { showSuccessToast, showErrorToast } from "@/app/lib/toast"; interface GameListProps { account: string; contract: any; config: Config | null; web3: Web3 | null; setStatus: (status: string) => void; onPlayClick?: (gameId: number) => void; } export default function GameList({ account, contract, config, web3, setStatus, onPlayClick, }: Readonly) { const [games, setGames] = useState([]); const [loading, setLoading] = useState(false); const [newGameBet, setNewGameBet] = useState("0.01"); const [newGameNickname, setNewGameNickname] = useState(""); const [joinNicknames, setJoinNicknames] = useState>(new Map()); const [refreshInterval, setRefreshInterval] = useState(null); const [userGameIds, setUserGameIds] = useState>(new Set()); // Fetch all active games const fetchActiveGames = async () => { if (!contract || !web3) return; try { const activeGameIds = await contract.methods.getActiveGameIds().call(); const gameDetails: GameDetails[] = []; for (const gameId of activeGameIds) { const details = await contract.methods.getGameDetails(gameId).call(); gameDetails.push(details); } setGames(gameDetails); // Check which games the user is participating in if (account) { const userGames = new Set(); for (const game of gameDetails) { if ( game.playerA.addr.toLowerCase() === account.toLowerCase() || game.playerB.addr.toLowerCase() === account.toLowerCase() ) { userGames.add(game.returnGameId); } } setUserGameIds(userGames); } } catch (err: any) { console.error("Failed to fetch games:", err.message); } }; // Auto-refresh games every 2 seconds useEffect(() => { fetchActiveGames(); const interval = setInterval(() => { fetchActiveGames(); }, 2000); setRefreshInterval(interval); return () => { if (interval) clearInterval(interval); }; }, [contract, web3, account]); // Join an existing game const handleJoinGame = async (gameId: number, bet: string) => { if (!contract || !web3 || !account) return; const nickname = joinNicknames.get(gameId) || ""; if (!nickname.trim()) { showErrorToast("Please enter a nickname"); return; } if (nickname.length > 20) { showErrorToast("Nickname too long (max 20 characters)"); return; } setLoading(true); try { const betWei = bet; const tx = contract.methods.register(gameId, nickname); const gas = await tx.estimateGas({ from: account, value: betWei }); const result = await (globalThis as any).ethereum.request({ method: "eth_sendTransaction", params: [ { from: account, to: config?.GAME_CONTRACT_ADDRESS, data: tx.encodeABI(), value: web3.utils.numberToHex(betWei), gas: web3.utils.toHex(gas), chainId: web3.utils.toHex(await web3.eth.net.getId()), }, ], }); showSuccessToast("Joined game! Transaction: " + result); // Clear the nickname input for this game const updatedNicknames = new Map(joinNicknames); updatedNicknames.delete(gameId); setJoinNicknames(updatedNicknames); await new Promise((resolve) => setTimeout(resolve, 2000)); fetchActiveGames(); } catch (err: any) { showErrorToast("Failed to join game: " + err.message); console.error(err); } finally { setLoading(false); } }; // Create a new game const handleCreateGame = async () => { if (!contract || !web3 || !account) return; if (!newGameNickname.trim()) { showErrorToast("Please enter a nickname"); return; } if (newGameNickname.length > 20) { showErrorToast("Nickname too long (max 20 characters)"); return; } setLoading(true); try { const betWei = web3.utils.toWei(newGameBet || "0.01", "ether"); const tx = contract.methods.register(0, newGameNickname); // 0 means create new game const gas = await tx.estimateGas({ from: account, value: betWei }); const result = await (globalThis as any).ethereum.request({ method: "eth_sendTransaction", params: [ { from: account, to: config?.GAME_CONTRACT_ADDRESS, data: tx.encodeABI(), value: web3.utils.numberToHex(betWei), gas: web3.utils.toHex(gas), chainId: web3.utils.toHex(await web3.eth.net.getId()), }, ], }); showSuccessToast("Created new game! Transaction: " + result); setNewGameBet("0.01"); setNewGameNickname(""); await new Promise((resolve) => setTimeout(resolve, 2000)); fetchActiveGames(); } catch (err: any) { showErrorToast("Failed to create game: " + err.message); console.error(err); } finally { setLoading(false); } }; const formatAddress = (addr: string) => { if (!addr || addr === "0x0000000000000000000000000000000000000000") return "-"; return `${addr.slice(0, 6)}...${addr.slice(-4)}`; }; const getGamePhase = (game: GameDetails) => { const playerARevealed = Number(game.playerA.move) !== 0; const playerBRevealed = Number(game.playerB.move) !== 0; const playerACommitted = Number(game.playerA.encrMove) !== 0; const playerBCommitted = Number(game.playerB.encrMove) !== 0; if (playerARevealed && playerBRevealed) { return { phase: "Outcome", color: "bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200" }; } else if (playerACommitted && playerBCommitted) { return { phase: "Reveal", color: "bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200" }; } else { return { phase: "Commit", color: "bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200" }; } }; return (
{/* Create New Game Section */}

➕ Create New Game

setNewGameNickname(e.target.value)} maxLength={20} className="flex-1 min-w-[200px]" /> setNewGameBet(e.target.value)} className="flex-1 min-w-[200px]" />

Enter your nickname and bet amount in ETH (e.g., 0.01 for 0.01 ETH).

{/* Active Games List */}

🎮 Available Games ({games.length})

{games.length === 0 ? (

No active games available.

Create a new game to get started!

) : (
{games.map((game) => { const isUserInGame = userGameIds.has(game.returnGameId); return (
{/* Game ID Header */}

Game #{game.returnGameId}

{getGamePhase(game).phase}

{web3 ? web3.utils.fromWei(game.initialBet, "ether") : "-"} ETH

{/* Players VS Layout */}
{/* Player A */}

Player A

{game.playerA.nickname || "Unknown"}

{formatAddress(game.playerA.addr)}

{/* VS */}

VS

{/* Player B */}

Player B

{game.playerB.addr === "0x0000000000000000000000000000000000000000" ? (

⏳ Waiting...

) : ( <>

{game.playerB.nickname || "Unknown"}

{formatAddress(game.playerB.addr)}

)}
{/* Join/Play Button */}
{userGameIds.has(game.returnGameId) ? ( ) : game.playerB.addr === "0x0000000000000000000000000000000000000000" ? (
{ const updatedNicknames = new Map(joinNicknames); updatedNicknames.set(game.returnGameId, e.target.value); setJoinNicknames(updatedNicknames); }} maxLength={20} className="flex-1" />
) : ( )}
); })}
)}
); }