matchId check in Board instead of TicTacToe.tsx

This commit is contained in:
2025-11-29 02:52:34 +05:30
parent ebc6906bf6
commit 601048f0e4
2 changed files with 142 additions and 136 deletions

View File

@@ -1,5 +1,6 @@
import React from "react";
import { motion, AnimatePresence } from "framer-motion";
import { useNakama } from "./providers/NakamaProvider";
interface BoardProps {
board: string[][];
@@ -20,6 +21,9 @@ export default function Board({
}: BoardProps) {
const myIndex = players.indexOf(myUserId ?? "");
const gameReady = players.length === 2;
const {
matchId
} = useNakama();
const mySymbol =
myIndex === 0 ? "X" : myIndex === 1 ? "O" : null;
@@ -44,134 +48,138 @@ export default function Board({
}
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
>
<motion.h2
key={status}
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25 }}
style={{ marginBottom: 8 }}
>
{status}
</motion.h2>
{gameReady && mySymbol && (
<>
{matchId && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 0.75 }}
style={{ marginBottom: 8, fontSize: 14 }}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
>
You: <strong>{mySymbol}</strong> Opponent:{" "}
<strong>{opponentSymbol}</strong>
<motion.h2
key={status}
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25 }}
style={{ marginBottom: 8 }}
>
{status}
</motion.h2>
{gameReady && mySymbol && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 0.75 }}
style={{ marginBottom: 8, fontSize: 14 }}
>
You: <strong>{mySymbol}</strong> Opponent:{" "}
<strong>{opponentSymbol}</strong>
</motion.div>
)}
{/* -------------------------
BOARD
-------------------------- */}
<motion.div
layout
style={{
display: "grid",
gridTemplateColumns: "repeat(3, 80px)",
gap: "10px",
marginTop: "6px",
}}
>
{board.map((row, rIdx) =>
row.map((cell, cIdx) => {
const disabled =
!!cell ||
!!winner ||
!gameReady ||
myIndex === -1 ||
!isMyTurn;
return (
<motion.button
key={`${rIdx}-${cIdx}-${cell}`} // rerender when cell changes
layout
whileHover={
!disabled
? {
scale: 1.1,
boxShadow: "0px 0px 10px rgba(255,255,255,0.4)",
}
: {}
}
whileTap={!disabled ? { scale: 0.85 } : {}}
onClick={() => !disabled && onCellClick(rIdx, cIdx)}
style={{
width: "80px",
height: "80px",
fontSize: "2rem",
borderRadius: "10px",
border: "2px solid #333",
background: "#111",
color: "white",
cursor: disabled ? "not-allowed" : "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<AnimatePresence>
{cell && (
<motion.span
key="symbol"
initial={{ scale: 0.3, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.3, opacity: 0 }}
transition={{ type: "spring", stiffness: 200, damping: 12 }}
style={{
color:
winner === cell
? "#f1c40f" // highlight winning symbol
: "white",
textShadow:
winner === cell
? "0 0 12px rgba(241,196,15,0.8)"
: "none",
}}
>
{cell}
</motion.span>
)}
</AnimatePresence>
</motion.button>
);
})
)}
</motion.div>
{/* Winner pulse animation */}
{winner && (
<motion.div
initial={{ opacity: 0 }}
animate={{
opacity: 1,
scale: [1, 1.06, 1],
}}
transition={{
repeat: Infinity,
duration: 1.4,
ease: "easeInOut",
}}
style={{
color: "#f1c40f",
fontSize: "20px",
marginTop: "14px",
fontWeight: 700,
textAlign: "center",
}}
>
🎉 {winner} Wins! 🎉
</motion.div>
)}
</motion.div>
)}
{/* -------------------------
BOARD
-------------------------- */}
<motion.div
layout
style={{
display: "grid",
gridTemplateColumns: "repeat(3, 80px)",
gap: "10px",
marginTop: "6px",
}}
>
{board.map((row, rIdx) =>
row.map((cell, cIdx) => {
const disabled =
!!cell ||
!!winner ||
!gameReady ||
myIndex === -1 ||
!isMyTurn;
return (
<motion.button
key={`${rIdx}-${cIdx}-${cell}`} // rerender when cell changes
layout
whileHover={
!disabled
? {
scale: 1.1,
boxShadow: "0px 0px 10px rgba(255,255,255,0.4)",
}
: {}
}
whileTap={!disabled ? { scale: 0.85 } : {}}
onClick={() => !disabled && onCellClick(rIdx, cIdx)}
style={{
width: "80px",
height: "80px",
fontSize: "2rem",
borderRadius: "10px",
border: "2px solid #333",
background: "#111",
color: "white",
cursor: disabled ? "not-allowed" : "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<AnimatePresence>
{cell && (
<motion.span
key="symbol"
initial={{ scale: 0.3, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.3, opacity: 0 }}
transition={{ type: "spring", stiffness: 200, damping: 12 }}
style={{
color:
winner === cell
? "#f1c40f" // highlight winning symbol
: "white",
textShadow:
winner === cell
? "0 0 12px rgba(241,196,15,0.8)"
: "none",
}}
>
{cell}
</motion.span>
)}
</AnimatePresence>
</motion.button>
);
})
)}
</motion.div>
{/* Winner pulse animation */}
{winner && (
<motion.div
initial={{ opacity: 0 }}
animate={{
opacity: 1,
scale: [1, 1.06, 1],
}}
transition={{
repeat: Infinity,
duration: 1.4,
ease: "easeInOut",
}}
style={{
color: "#f1c40f",
fontSize: "20px",
marginTop: "14px",
fontWeight: 700,
textAlign: "center",
}}
>
🎉 {winner} Wins! 🎉
</motion.div>
)}
</motion.div>
);
</>
)
}