mirror of
https://github.com/averel10/crypto_clash.git
synced 2026-03-12 19:08:11 +01:00
start fixing frontend
This commit is contained in:
@@ -18,6 +18,16 @@ interface CommitProps {
|
||||
gameDetails: GameDetails | null;
|
||||
setSecret: (secret: string) => void;
|
||||
savePlayMove: (playMove: string) => void;
|
||||
// MinusOne mode props
|
||||
selectedMove1?: string | null;
|
||||
setSelectedMove1?: (move: string | null) => void;
|
||||
selectedMove2?: string | null;
|
||||
setSelectedMove2?: (move: string | null) => void;
|
||||
secret1?: string;
|
||||
setSecret1?: (secret: string) => void;
|
||||
secret2?: string;
|
||||
setSecret2?: (secret: string) => void;
|
||||
isWithdrawPhase?: boolean;
|
||||
}
|
||||
|
||||
type MoveName = "Rock" | "Paper" | "Scissors";
|
||||
@@ -40,10 +50,21 @@ export default function Commit({
|
||||
setSecret,
|
||||
savePlayMove,
|
||||
whoAmI,
|
||||
gameDetails
|
||||
gameDetails,
|
||||
selectedMove1,
|
||||
setSelectedMove1,
|
||||
selectedMove2,
|
||||
setSelectedMove2,
|
||||
secret1,
|
||||
setSecret1,
|
||||
secret2,
|
||||
setSecret2,
|
||||
isWithdrawPhase = false,
|
||||
}: Readonly<CommitProps>) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [playMove, setPlayMove] = useState<string>("");
|
||||
const [playMove1, setPlayMove1] = useState<string>("");
|
||||
const [playMove2, setPlayMove2] = useState<string>("");
|
||||
const [selfPlayed, setSelfPlayed] = useState<string>("");
|
||||
const [opponentPlayed, setOpponentPlayed] = useState<string>("");
|
||||
const [bothPlayed, setBothPlayed] = useState<string>("");
|
||||
@@ -52,17 +73,32 @@ export default function Commit({
|
||||
const [commitTimeLeft, setCommitTimeLeft] = useState<number>(0);
|
||||
const [timeoutExpired, setTimeoutExpired] = useState(false);
|
||||
|
||||
const isMinusOne = gameDetails?.gameMode === "minusone";
|
||||
|
||||
// Update encrypted move when move or secret changes
|
||||
useEffect(() => {
|
||||
if (selectedMove && secret) {
|
||||
if (isMinusOne && !isWithdrawPhase) {
|
||||
// MinusOne initial commit: two moves
|
||||
if (selectedMove1 && secret1) {
|
||||
const clearMove1 = `${selectedMove1}-${secret1}`;
|
||||
const hash1 = Web3.utils.keccak256(clearMove1);
|
||||
setPlayMove1(hash1);
|
||||
}
|
||||
if (selectedMove2 && secret2) {
|
||||
const clearMove2 = `${selectedMove2}-${secret2}`;
|
||||
const hash2 = Web3.utils.keccak256(clearMove2);
|
||||
setPlayMove2(hash2);
|
||||
}
|
||||
} else if (selectedMove && secret) {
|
||||
// Classic mode or withdrawal phase: single move/choice
|
||||
const clearMove = `${selectedMove}-${secret}`;
|
||||
// Use keccak256 (Ethereum's standard hash function)
|
||||
const hash = Web3.utils.keccak256(clearMove);
|
||||
setPlayMove(hash);
|
||||
// Persist to sessionStorage through parent
|
||||
savePlayMove(hash);
|
||||
if (!isWithdrawPhase) {
|
||||
savePlayMove(hash);
|
||||
}
|
||||
}
|
||||
}, [selectedMove, secret, savePlayMove]);
|
||||
}, [selectedMove, secret, selectedMove1, secret1, selectedMove2, secret2, isMinusOne, isWithdrawPhase, savePlayMove]);
|
||||
|
||||
// Auto-check if both players have committed and trigger callback
|
||||
useEffect(() => {
|
||||
@@ -75,9 +111,18 @@ export default function Commit({
|
||||
|
||||
const checkSelfPlayed = async () => {
|
||||
try {
|
||||
const encrMove = gameDetails[whoAmI === "player1" ? "playerA" : "playerB"].encrMove;
|
||||
|
||||
setSelfPlayed(Number(encrMove) !== 0 ? "true" : "false");
|
||||
const player = gameDetails[whoAmI === "player1" ? "playerA" : "playerB"];
|
||||
|
||||
if (isMinusOne && !isWithdrawPhase) {
|
||||
// Check hash1 for initial commit phase
|
||||
setSelfPlayed(player.hash1 && Number(player.hash1) !== 0 ? "true" : "false");
|
||||
} else if (isMinusOne && isWithdrawPhase) {
|
||||
// Check wHash for withdrawal commit phase
|
||||
setSelfPlayed(player.wHash && Number(player.wHash) !== 0 ? "true" : "false");
|
||||
} else {
|
||||
// Classic mode: check encrMove
|
||||
setSelfPlayed(player.encrMove && Number(player.encrMove) !== 0 ? "true" : "false");
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("Auto-check self played failed:", err.message);
|
||||
}
|
||||
@@ -88,8 +133,15 @@ export default function Commit({
|
||||
const checkOpponentPlayed = async () => {
|
||||
try {
|
||||
const opponentKey = whoAmI === "player1" ? "playerB" : "playerA";
|
||||
const encrMove = gameDetails[opponentKey].encrMove;
|
||||
setOpponentPlayed(Number(encrMove) !== 0 ? "true" : "false");
|
||||
const opponent = gameDetails[opponentKey];
|
||||
|
||||
if (isMinusOne && !isWithdrawPhase) {
|
||||
setOpponentPlayed(opponent.hash1 && Number(opponent.hash1) !== 0 ? "true" : "false");
|
||||
} else if (isMinusOne && isWithdrawPhase) {
|
||||
setOpponentPlayed(opponent.wHash && Number(opponent.wHash) !== 0 ? "true" : "false");
|
||||
} else {
|
||||
setOpponentPlayed(opponent.encrMove && Number(opponent.encrMove) !== 0 ? "true" : "false");
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("Auto-check opponent played failed:", err.message);
|
||||
}
|
||||
@@ -100,10 +152,22 @@ export default function Commit({
|
||||
// Check immediately on mount or when dependencies change
|
||||
const checkBothPlayed = async () => {
|
||||
try {
|
||||
const playerAEncrMove = gameDetails.playerA.encrMove;
|
||||
const playerBEncrMove = gameDetails.playerB.encrMove;
|
||||
|
||||
const res = Number(playerAEncrMove) !== 0 && Number(playerBEncrMove) !== 0;
|
||||
let res: boolean = false;
|
||||
|
||||
if (isMinusOne && !isWithdrawPhase) {
|
||||
const playerAHash = gameDetails.playerA.hash1;
|
||||
const playerBHash = gameDetails.playerB.hash1;
|
||||
res = !!(playerAHash && playerBHash && Number(playerAHash) !== 0 && Number(playerBHash) !== 0);
|
||||
} else if (isMinusOne && isWithdrawPhase) {
|
||||
const playerAWHash = gameDetails.playerA.wHash;
|
||||
const playerBWHash = gameDetails.playerB.wHash;
|
||||
res = !!(playerAWHash && playerBWHash && Number(playerAWHash) !== 0 && Number(playerBWHash) !== 0);
|
||||
} else {
|
||||
const playerAEncrMove = gameDetails.playerA.encrMove;
|
||||
const playerBEncrMove = gameDetails.playerB.encrMove;
|
||||
res = !!(playerAEncrMove && playerBEncrMove && Number(playerAEncrMove) !== 0 && Number(playerBEncrMove) !== 0);
|
||||
}
|
||||
|
||||
console.log("Both played check:", res);
|
||||
if (res) {
|
||||
setBothPlayed("true");
|
||||
@@ -118,7 +182,14 @@ export default function Commit({
|
||||
// Check commit timeout
|
||||
const checkCommitTimeout = async () => {
|
||||
try {
|
||||
const timeLeft = await contract.methods.commitTimeLeft(gameDetails.returnGameId).call();
|
||||
let timeLeft;
|
||||
if (isMinusOne) {
|
||||
// MinusOne uses getTimeLeft() for all phases
|
||||
timeLeft = await contract.methods.getTimeLeft(gameDetails.returnGameId).call();
|
||||
} else {
|
||||
// Classic uses commitTimeLeft()
|
||||
timeLeft = await contract.methods.commitTimeLeft(gameDetails.returnGameId).call();
|
||||
}
|
||||
console.log("Commit time left:", timeLeft);
|
||||
setCommitTimeLeft(Number(timeLeft));
|
||||
if (Number(timeLeft) <= 0) {
|
||||
@@ -145,11 +216,35 @@ export default function Commit({
|
||||
|
||||
// Commit phase read-only handlers
|
||||
const handlePlay = async () => {
|
||||
if (!contract || !web3 || !account || !playMove) return;
|
||||
if (!contract || !web3 || !account) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// playMove should be a hex string (bytes32)
|
||||
const tx = contract.methods.play(gameDetails?.returnGameId, playMove);
|
||||
let tx;
|
||||
|
||||
if (isMinusOne && !isWithdrawPhase) {
|
||||
// MinusOne initial commit: commitInitialMoves(gameId, hash1, hash2)
|
||||
if (!playMove1 || !playMove2) {
|
||||
showToast("Please select both moves and enter secrets", "error");
|
||||
return;
|
||||
}
|
||||
tx = contract.methods.commitInitialMoves(gameDetails?.returnGameId, playMove1, playMove2);
|
||||
} else if (isMinusOne && isWithdrawPhase) {
|
||||
// MinusOne withdrawal commit: commitWithdraw(gameId, wHash)
|
||||
if (!playMove) {
|
||||
showToast("Please select withdrawal choice and enter secret", "error");
|
||||
return;
|
||||
}
|
||||
tx = contract.methods.commitWithdraw(gameDetails?.returnGameId, playMove);
|
||||
} else {
|
||||
// Classic mode: play(gameId, hash)
|
||||
if (!playMove) {
|
||||
showToast("Please select a move and enter secret", "error");
|
||||
return;
|
||||
}
|
||||
tx = contract.methods.play(gameDetails?.returnGameId, playMove);
|
||||
}
|
||||
|
||||
const gas = await tx.estimateGas({ from: account });
|
||||
const result = await (globalThis as any).ethereum.request({
|
||||
method: "eth_sendTransaction",
|
||||
@@ -163,10 +258,10 @@ export default function Commit({
|
||||
},
|
||||
],
|
||||
});
|
||||
showToast("Play tx sent: " + result, "success");
|
||||
showToast("Commit tx sent: " + result, "success");
|
||||
setMoveSubmitted(true);
|
||||
} catch (err: any) {
|
||||
showToast("Play failed: " + err.message, "error");
|
||||
showToast("Commit failed: " + err.message, "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -174,16 +269,43 @@ export default function Commit({
|
||||
|
||||
const regenerateSecret = () => {
|
||||
const randomHex = Math.random().toString(16).slice(2, 18);
|
||||
setSecret(randomHex);
|
||||
if (isMinusOne && !isWithdrawPhase) {
|
||||
// For MinusOne, we might want separate secrets
|
||||
// For now, let's keep them simple
|
||||
setSecret1?.(randomHex);
|
||||
} else {
|
||||
setSecret(randomHex);
|
||||
}
|
||||
};
|
||||
|
||||
const regenerateSecret2 = () => {
|
||||
const randomHex = Math.random().toString(16).slice(2, 18);
|
||||
setSecret2?.(randomHex);
|
||||
};
|
||||
|
||||
const handleSecretChange = (value: string) => {
|
||||
setSecret(value);
|
||||
};
|
||||
|
||||
const handleSecret1Change = (value: string) => {
|
||||
setSecret1?.(value);
|
||||
};
|
||||
|
||||
const handleSecret2Change = (value: string) => {
|
||||
setSecret2?.(value);
|
||||
};
|
||||
|
||||
const handleMoveSelect = (move: string) => {
|
||||
setSelectedMove(move);
|
||||
};
|
||||
|
||||
const handleMove1Select = (move: string) => {
|
||||
setSelectedMove1?.(move);
|
||||
};
|
||||
|
||||
const handleMove2Select = (move: string) => {
|
||||
setSelectedMove2?.(move);
|
||||
};
|
||||
|
||||
const handleResolveTimeout = async () => {
|
||||
if (!contract || !web3 || !account) return;
|
||||
@@ -331,76 +453,264 @@ export default function Commit({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Move Selection */}
|
||||
<div className="mb-8">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300 mb-4 font-medium">
|
||||
Choose your move:
|
||||
</p>
|
||||
<div className="flex gap-4 justify-center">
|
||||
{(["1", "2", "3"] as const).map((move) => (
|
||||
<button
|
||||
key={move}
|
||||
onClick={() => handleMoveSelect(move)}
|
||||
className={`flex flex-col items-center justify-center p-6 rounded-lg transition-all transform ${
|
||||
selectedMove === move
|
||||
? "bg-blue-500 text-white shadow-lg scale-110"
|
||||
: "bg-white dark:bg-slate-600 text-slate-700 dark:text-slate-200 shadow hover:shadow-md hover:scale-105"
|
||||
}`}
|
||||
>
|
||||
<span className="text-5xl mb-2">{MOVES[move].icon}</span>
|
||||
<span className="font-semibold text-sm">{MOVES[move].name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* Move Selection - Different UI for MinusOne vs Classic */}
|
||||
{isMinusOne && !isWithdrawPhase ? (
|
||||
// MinusOne Mode: Select TWO different moves
|
||||
<>
|
||||
<div className="mb-6">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300 mb-4 font-medium">
|
||||
Choose your FIRST move:
|
||||
</p>
|
||||
<div className="flex gap-4 justify-center">
|
||||
{(["1", "2", "3"] as const).map((move) => (
|
||||
<button
|
||||
key={move}
|
||||
onClick={() => handleMove1Select(move)}
|
||||
disabled={selectedMove2 === move}
|
||||
className={`flex flex-col items-center justify-center p-6 rounded-lg transition-all transform ${
|
||||
selectedMove1 === move
|
||||
? "bg-blue-500 text-white shadow-lg scale-110"
|
||||
: selectedMove2 === move
|
||||
? "bg-slate-300 dark:bg-slate-600 text-slate-400 dark:text-slate-500 cursor-not-allowed"
|
||||
: "bg-white dark:bg-slate-600 text-slate-700 dark:text-slate-200 shadow hover:shadow-md hover:scale-105"
|
||||
}`}
|
||||
>
|
||||
<span className="text-5xl mb-2">{MOVES[move].icon}</span>
|
||||
<span className="font-semibold text-sm">{MOVES[move].name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Secret Input */}
|
||||
<div className="mb-8 bg-white dark:bg-slate-700 p-4 rounded-lg">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">
|
||||
Secret:
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={secret}
|
||||
onChange={(e) => handleSecretChange(e.target.value)}
|
||||
placeholder="Your secret passphrase"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={regenerateSecret}
|
||||
variant="secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
🔄 New
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-2">
|
||||
Keep this secret safe! It's needed to reveal your move later.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-6 bg-white dark:bg-slate-700 p-4 rounded-lg">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">
|
||||
Secret for first move:
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={secret1 || ""}
|
||||
onChange={(e) => handleSecret1Change(e.target.value)}
|
||||
placeholder="Your secret passphrase"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={regenerateSecret}
|
||||
variant="secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
🔄 New
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300 mb-4 font-medium">
|
||||
Choose your SECOND move (must be different):
|
||||
</p>
|
||||
<div className="flex gap-4 justify-center">
|
||||
{(["1", "2", "3"] as const).map((move) => (
|
||||
<button
|
||||
key={move}
|
||||
onClick={() => handleMove2Select(move)}
|
||||
disabled={selectedMove1 === move}
|
||||
className={`flex flex-col items-center justify-center p-6 rounded-lg transition-all transform ${
|
||||
selectedMove2 === move
|
||||
? "bg-green-500 text-white shadow-lg scale-110"
|
||||
: selectedMove1 === move
|
||||
? "bg-slate-300 dark:bg-slate-600 text-slate-400 dark:text-slate-500 cursor-not-allowed"
|
||||
: "bg-white dark:bg-slate-600 text-slate-700 dark:text-slate-200 shadow hover:shadow-md hover:scale-105"
|
||||
}`}
|
||||
>
|
||||
<span className="text-5xl mb-2">{MOVES[move].icon}</span>
|
||||
<span className="font-semibold text-sm">{MOVES[move].name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-8 bg-white dark:bg-slate-700 p-4 rounded-lg">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">
|
||||
Secret for second move:
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={secret2 || ""}
|
||||
onChange={(e) => handleSecret2Change(e.target.value)}
|
||||
placeholder="Your secret passphrase"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={regenerateSecret2}
|
||||
variant="secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
🔄 New
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-2">
|
||||
Keep both secrets safe! You'll need them to reveal your moves later.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : isWithdrawPhase ? (
|
||||
// Withdrawal Phase: Choose which move to withdraw (1 or 2)
|
||||
<>
|
||||
<div className="mb-6 p-4 bg-yellow-50 dark:bg-yellow-900 border-2 border-yellow-300 dark:border-yellow-700 rounded-lg">
|
||||
<p className="text-yellow-800 dark:text-yellow-200 text-sm mb-2 font-medium">
|
||||
🎯 Withdrawal Phase
|
||||
</p>
|
||||
<p className="text-yellow-700 dark:text-yellow-300 text-sm">
|
||||
Choose which move to WITHDRAW (1 or 2). The remaining move will be used in the final battle!
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300 mb-4 font-medium">
|
||||
Which move do you want to withdraw?
|
||||
</p>
|
||||
<div className="flex gap-4 justify-center">
|
||||
{(["1", "2"] as const).map((choice) => (
|
||||
<button
|
||||
key={choice}
|
||||
onClick={() => handleMoveSelect(choice)}
|
||||
className={`flex flex-col items-center justify-center p-8 rounded-lg transition-all transform ${
|
||||
selectedMove === choice
|
||||
? "bg-red-500 text-white shadow-lg scale-110"
|
||||
: "bg-white dark:bg-slate-600 text-slate-700 dark:text-slate-200 shadow hover:shadow-md hover:scale-105"
|
||||
}`}
|
||||
>
|
||||
<span className="text-6xl mb-2">{choice === "1" ? "1️⃣" : "2️⃣"}</span>
|
||||
<span className="font-semibold">Withdraw Move {choice}</span>
|
||||
<span className="text-xs mt-1 opacity-75">
|
||||
{choice === "1" ? "Keep move 2" : "Keep move 1"}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-8 bg-white dark:bg-slate-700 p-4 rounded-lg">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">
|
||||
Secret for withdrawal:
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={secret}
|
||||
onChange={(e) => handleSecretChange(e.target.value)}
|
||||
placeholder="Your secret passphrase"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={regenerateSecret}
|
||||
variant="secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
🔄 New
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-2">
|
||||
This secret is for your withdrawal choice. Keep it safe!
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
// Classic Mode: Select ONE move
|
||||
<>
|
||||
<div className="mb-8">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300 mb-4 font-medium">
|
||||
Choose your move:
|
||||
</p>
|
||||
<div className="flex gap-4 justify-center">
|
||||
{(["1", "2", "3"] as const).map((move) => (
|
||||
<button
|
||||
key={move}
|
||||
onClick={() => handleMoveSelect(move)}
|
||||
className={`flex flex-col items-center justify-center p-6 rounded-lg transition-all transform ${
|
||||
selectedMove === move
|
||||
? "bg-blue-500 text-white shadow-lg scale-110"
|
||||
: "bg-white dark:bg-slate-600 text-slate-700 dark:text-slate-200 shadow hover:shadow-md hover:scale-105"
|
||||
}`}
|
||||
>
|
||||
<span className="text-5xl mb-2">{MOVES[move].icon}</span>
|
||||
<span className="font-semibold text-sm">{MOVES[move].name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Secret Input */}
|
||||
<div className="mb-8 bg-white dark:bg-slate-700 p-4 rounded-lg">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">
|
||||
Secret:
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={secret}
|
||||
onChange={(e) => handleSecretChange(e.target.value)}
|
||||
placeholder="Your secret passphrase"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={regenerateSecret}
|
||||
variant="secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
🔄 New
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-2">
|
||||
Keep this secret safe! It's needed to reveal your move later.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Encrypted Move Display */}
|
||||
<div className="mb-8 bg-blue-50 dark:bg-blue-900 p-4 rounded-lg">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-blue-200 mb-2">
|
||||
Encrypted Move (to be sent):
|
||||
{isMinusOne && !isWithdrawPhase ? "Encrypted Moves (to be sent):" : "Encrypted Move (to be sent):"}
|
||||
</label>
|
||||
<div className="bg-white dark:bg-slate-700 p-3 rounded border border-blue-200 dark:border-blue-700 overflow-x-auto">
|
||||
<code className="text-xs text-slate-600 dark:text-slate-300 font-mono break-all">
|
||||
{playMove || "Select a move and enter a secret"}
|
||||
</code>
|
||||
</div>
|
||||
{isMinusOne && !isWithdrawPhase ? (
|
||||
<>
|
||||
<div className="bg-white dark:bg-slate-700 p-3 rounded border border-blue-200 dark:border-blue-700 overflow-x-auto mb-2">
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mb-1">Move 1:</p>
|
||||
<code className="text-xs text-slate-600 dark:text-slate-300 font-mono break-all">
|
||||
{playMove1 || "Select first move and enter secret"}
|
||||
</code>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-slate-700 p-3 rounded border border-green-200 dark:border-green-700 overflow-x-auto">
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mb-1">Move 2:</p>
|
||||
<code className="text-xs text-slate-600 dark:text-slate-300 font-mono break-all">
|
||||
{playMove2 || "Select second move and enter secret"}
|
||||
</code>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="bg-white dark:bg-slate-700 p-3 rounded border border-blue-200 dark:border-blue-700 overflow-x-auto">
|
||||
<code className="text-xs text-slate-600 dark:text-slate-300 font-mono break-all">
|
||||
{playMove || "Select a move/choice and enter a secret"}
|
||||
</code>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<Button
|
||||
onClick={handlePlay}
|
||||
disabled={loading || !account || !contract || !selectedMove || !secret}
|
||||
disabled={
|
||||
loading ||
|
||||
!account ||
|
||||
!contract ||
|
||||
(isMinusOne && !isWithdrawPhase ? (!selectedMove1 || !selectedMove2 || !secret1 || !secret2) : (!selectedMove || !secret))
|
||||
}
|
||||
variant="primary"
|
||||
className="w-full py-3 text-lg"
|
||||
>
|
||||
{loading ? "Submitting..." : "Submit Move"}
|
||||
{loading ? "Submitting..." : isWithdrawPhase ? "Submit Withdrawal Choice" : "Submit Move"}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user