diff --git a/Frontend/src/components/GamePools.tsx b/Frontend/src/components/GamePools.tsx
index f1d86f1..6caaed9 100644
--- a/Frontend/src/components/GamePools.tsx
+++ b/Frontend/src/components/GamePools.tsx
@@ -71,45 +71,33 @@ const PoolsInterface: React.FC = () => {
eventName: "PlayerJoined",
onLogs: (logs) => {
- if (!logs || logs.length === 0) return;
- // Process each log entry
- const processedEvents = logs
- .map((log) => {
- // @ts-ignore
- if (!log.args) return null;
-
- const poolId =
- // @ts-ignore
- typeof log.args.poolId === "bigint"
- // @ts-ignore
- ? Number(log.args.poolId)
- // @ts-ignore
- : typeof log.args.poolId === "number"
- // @ts-ignore
- ? log.args.poolId
- : undefined;
-
- // @ts-ignore
- const player = typeof log.args.playerThatJoined === "string"
- // @ts-ignore
- ? (log.args.playerThatJoined as `0x${string}`)
- : undefined;
-
- if (poolId === undefined || !player) return null;
-
- return { poolId, player, timestamp: Date.now() };
- })
- .filter((event) => event !== null);
-
- if (processedEvents.length === 0) return;
-
- // Update join events - this will trigger the useEffect below
- // @ts-ignore
- setJoinEvents((prev) => [...prev, ...processedEvents]);
-
- // Visual feedback remains the same
- processedEvents.forEach((event) => {
- // Show toast
+ logs.forEach((log) => {
+ console.log("Received logs:", logs);
+ if (!log.args) return;
+
+ const poolId =
+ typeof log.args.poolId === "bigint" ? log.args.poolId : undefined;
+ const player =
+ typeof log.args.playerThatJoined === "string"
+ ? (log.args.playerThatJoined as `0x${string}`)
+ : undefined;
+ if (!poolId || !player) {
+ console.error("Invalid event data structure:", log.args);
+ return;
+ }
+
+ console.log("Player joined pool:", { poolId: Number(poolId), player });
+
+ // Update UI
+ setParticipants((prev) => [...prev, player]);
+ // Update the currentParticipants count for the joined pool
+ setNewPools((prevPools) =>
+ prevPools.map((pool) =>
+ pool.id === Number(poolId)
+ ? { ...pool, currentParticipants: pool.currentParticipants + 1 }
+ : pool
+ )
+ );
toast.custom(
@@ -118,10 +106,8 @@ const PoolsInterface: React.FC = () => {
New Challenger!
- {`${event.player.substring(0, 6)}...${event.player.substring(
- 38
- )}`}{" "}
- joined pool #{event.poolId}
+ {`${player.substring(0, 6)}...${player.substring(38)}`} joined
+ pool #{Number(poolId)}
,
@@ -131,38 +117,34 @@ const PoolsInterface: React.FC = () => {
}
);
- // Show pulse animation
+ // Show pulse animation on the pool card
setShowPulse((prev) => ({
...prev,
- [event.poolId]: true,
+ [Number(poolId)]: true,
}));
- // Remove pulse after animation completes
+ // Remove pulse after 2 seconds
setTimeout(() => {
setShowPulse((prev) => ({
...prev,
- [event.poolId]: false,
+ [Number(poolId)]: false,
}));
}, 2000);
});
},
});
-
useEffect(() => {
if (joinEvents.length === 0) return;
-
- // Group by poolId to handle multiple events for the same pool
const poolUpdates = {};
joinEvents.forEach((event) => {
- // @ts-ignore
+
poolUpdates[event.poolId] = (poolUpdates[event.poolId] || 0) + 1;
});
- // Apply all updates at once
+
setNewPools((prevPools) =>
prevPools.map((pool) => {
- // @ts-ignore
const increment = poolUpdates[pool.id] || 0;
if (increment === 0) return pool;
@@ -187,25 +169,22 @@ const PoolsInterface: React.FC = () => {
eventName: "PointsAwarded",
onLogs: (logs) => {
logs.forEach((log) => {
- // @ts-ignore
+
if (!log.args || typeof log.args !== "object") {
return;
}
// Extract and validate player address
- // @ts-ignore
const player = typeof log.args.player === "string" ? log.args.player : undefined;
if (!player || !isAddress(player)) {
return;
}
// Extract and validate points
- // @ts-ignore
const points = log.args.points !== undefined ? BigInt(log.args.points) : undefined;
if (points === undefined || points < 0n) {
return;
}
setPoints(Number(points));
- // @ts-ignore
const actionType = log.args.reason !== undefined ? Number(log.args.reason) : undefined;
if (actionType === undefined || ![1, 2, 3].includes(actionType)) {
return;
@@ -215,7 +194,6 @@ const PoolsInterface: React.FC = () => {
// Show pulse animation on the pool card
setShowPulse((prev) => ({
...prev,
- // @ts-ignore
[Number(poolId)]: true,
}));
@@ -223,7 +201,6 @@ const PoolsInterface: React.FC = () => {
setTimeout(() => {
setShowPulse((prev) => ({
...prev,
- // @ts-ignore
[Number(poolId)]: false,
}));
}, 2000);
@@ -320,7 +297,7 @@ const PoolsInterface: React.FC = () => {
abi: ABI.abi,
functionName: "joinPool",
args: [BigInt(poolId)],
- //@ts-ignore
+
value: entryFee,
gas: BigInt(300000),
});
@@ -335,7 +312,7 @@ const PoolsInterface: React.FC = () => {
setSelectedPool(pool);
setIsModalOpen(true);
- //@ts-ignore
+
const stakeText = pool.stake?.replace("$", "") || "0";
setStakeAmount(parseInt(stakeText, 10) || 0);
};
diff --git a/Frontend/src/hooks/ContractReadIn.tsx b/Frontend/src/hooks/ContractReadIn.tsx
new file mode 100644
index 0000000..a632778
--- /dev/null
+++ b/Frontend/src/hooks/ContractReadIn.tsx
@@ -0,0 +1,51 @@
+import { useState, useEffect,useCallback } from "react";
+import { useReadContract } from "wagmi";
+import CoinTossABI from "../utils/contract/CoinToss.json";
+import { CORE_CONTRACT_ADDRESS } from "../utils/contract/contract";
+
+export enum PlayerChoice {
+ NONE = 0,
+ HEADS = 1,
+ TAILS = 2,
+}
+
+type PlayerRoundStatus = {
+ hasParticipated: boolean;
+ choice: PlayerChoice;
+ isLoading: boolean;
+ error: Error | null;
+};
+
+type PlayerStatus = [boolean, boolean, boolean, boolean];
+
+// Custom Hook: Game Timer
+// const useGameTimer = (initialTime: number, onTimerEnd: () => void) => {
+// const [timer, setTimer] = useState(initialTime);
+
+// useEffect(() => {
+// if (timer > 0) {
+// const interval = setInterval(() => setTimer((prev) => prev - 1), 1000);
+// return () => clearInterval(interval);
+// } else {
+// onTimerEnd();
+// }
+// }, [timer, onTimerEnd]);
+
+// return { timer };
+// };
+
+// Custom Hook: Contract Interactions
+export const usePlayerStatus = (poolId: bigint, address: `0x${string}`) => {
+ const {
+ data: playerStatus,
+ refetch: refetchPlayerStatus,
+ isLoading: isStatusLoading,
+ } = useReadContract
({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "getPlayerStatus",
+ args: [poolId, address],
+ });
+
+ return { playerStatus, refetchPlayerStatus, isStatusLoading };
+ };
diff --git a/Frontend/src/hooks/ContractWriteIn.tsx b/Frontend/src/hooks/ContractWriteIn.tsx
new file mode 100644
index 0000000..c44f428
--- /dev/null
+++ b/Frontend/src/hooks/ContractWriteIn.tsx
@@ -0,0 +1,67 @@
+import { useState, useEffect,useCallback } from "react";
+import { useWriteContract,useWaitForTransactionReceipt } from "wagmi";
+import CoinTossABI from "../utils/contract/CoinToss.json";
+import { CORE_CONTRACT_ADDRESS } from "../utils/contract/contract";
+
+export enum PlayerChoice {
+ NONE = 0,
+ HEADS = 1,
+ TAILS = 2,
+}
+
+type PlayerRoundStatus = {
+ hasParticipated: boolean;
+ choice: PlayerChoice;
+ isLoading: boolean;
+ error: Error | null;
+};
+
+type PlayerStatus = [boolean, boolean, boolean, boolean];
+
+// Custom Hook: Game Timer
+const useGameTimer = (initialTime: number, onTimerEnd: () => void) => {
+ const [timer, setTimer] = useState(initialTime);
+
+ useEffect(() => {
+ if (timer > 0) {
+ const interval = setInterval(() => setTimer((prev) => prev - 1), 1000);
+ return () => clearInterval(interval);
+ } else {
+ onTimerEnd();
+ }
+ }, [timer, onTimerEnd]);
+
+ return { timer };
+};
+
+// Custom Hook: Contract Interactions
+export const useContractInteraction = () => {
+ const { writeContract, data: hash, isPending, error } = useWriteContract();
+ const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({ hash });
+
+ const makeSelection = useCallback(
+ (poolId: bigint, choice: PlayerChoice) => {
+ writeContract({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "makeSelection",
+ args: [poolId, choice],
+ });
+ },
+ [writeContract]
+ );
+
+ const claimPrize = useCallback(
+ (poolId: bigint) => {
+ writeContract({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "claimPrize",
+ args: [poolId],
+ });
+ },
+ [writeContract]
+ );
+
+ return { makeSelection, claimPrize, isPending, isConfirming, isConfirmed, error };
+};
diff --git a/Frontend/src/hooks/usePlayerRoundStatus.tsx b/Frontend/src/hooks/usePlayerRoundStatus.tsx
deleted file mode 100644
index dd41749..0000000
--- a/Frontend/src/hooks/usePlayerRoundStatus.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { useState, useEffect } from "react";
-import { useReadContract, useAccount } from "wagmi";
-import CoinTossABI from "../utils/contract/CoinToss.json";
-import { CORE_CONTRACT_ADDRESS } from "../utils/contract/contract";
-
-export enum PlayerChoice {
- NONE = 0,
- HEADS = 1,
- TAILS = 2,
-}
-
-type PlayerRoundStatus = {
- hasParticipated: boolean;
- choice: PlayerChoice;
- isLoading: boolean;
- error: Error | null;
-};
-
-export function usePlayerRoundStatus(
- poolId: number,
- round: number
-): PlayerRoundStatus {
- const { address } = useAccount();
- const [status, setStatus] = useState({
- hasParticipated: false,
- choice: PlayerChoice.NONE,
- isLoading: true,
- error: null,
- });
-
- const {
- data: roundParticipation,
- isError,
- error,
- isLoading: isLoadingParticipation,
- } = useReadContract({
- address: CORE_CONTRACT_ADDRESS as `0x${string}`,
- abi: CoinTossABI.abi,
- functionName: "roundParticipation",
- args: [BigInt(poolId), BigInt(round), address],
- query: {
- enabled: !!address && round > 0,
- },
- });
-
- const { data: roundSelection, isLoading: isLoadingSelection } =
- useReadContract({
- address: CORE_CONTRACT_ADDRESS as `0x${string}`,
- abi: CoinTossABI.abi,
- functionName: "roundSelection",
- args: [BigInt(poolId), BigInt(round), address],
- query: {
- enabled: !!address && round > 0 && !!roundParticipation,
- },
- });
-
- useEffect(() => {
- setStatus((prev) => ({
- ...prev,
- hasParticipated: !!roundParticipation,
- choice: roundSelection
- ? (Number(roundSelection) as PlayerChoice)
- : PlayerChoice.NONE,
- isLoading: isLoadingParticipation || isLoadingSelection,
- error: isError ? error : null,
- }));
- }, [
- roundParticipation,
- roundSelection,
- isLoadingParticipation,
- isLoadingSelection,
- isError,
- error,
- ]);
-
- return status;
-}
diff --git a/Frontend/src/hooks/useTimer.tsx b/Frontend/src/hooks/useTimer.tsx
new file mode 100644
index 0000000..ccbf703
--- /dev/null
+++ b/Frontend/src/hooks/useTimer.tsx
@@ -0,0 +1,80 @@
+import { useState, useEffect, useCallback } from "react";
+
+export const useTimer = (initialTime: number, onTimerEnd?: () => void) => {
+ const [timer, setTimer] = useState(initialTime);
+ const [isTimerActive, setIsTimerActive] = useState(false);
+
+
+ const startTimer = useCallback(() => {
+ setIsTimerActive(true);
+ }, []);
+
+
+ const stopTimer = useCallback(() => {
+ setIsTimerActive(false);
+ }, []);
+
+
+ const resetTimer = useCallback((newTime: number) => {
+ setTimer(newTime);
+ setIsTimerActive(true);
+ }, []);
+
+
+ useEffect(() => {
+ if (isTimerActive && timer > 0) {
+ const interval = setInterval(() => {
+ setTimer((prevTimer) => prevTimer - 1);
+ }, 1000);
+ return () => clearInterval(interval);
+ } else if (timer === 0 && isTimerActive) {
+ setIsTimerActive(false);
+ onTimerEnd?.();
+ }
+ }, [isTimerActive, timer, onTimerEnd]);
+
+ return { timer, isTimerActive, startTimer, stopTimer, resetTimer };
+};
+
+
+
+// This should be inside your component
+export const checkPreviousSubmission = useCallback((address,pool,setGameState,gameState,setHasLocalSubmission) => {
+ if (!address || !pool?.id) return false;
+
+ const storageKey = `cointoss_submission_${address}_${pool.id}_${gameState.round}`;
+ const savedSubmission = localStorage.getItem(storageKey);
+
+ if (savedSubmission) {
+ try {
+ const submission = JSON.parse(savedSubmission);
+ if (submission.round === gameState.round) {
+ setGameState((prevState) => ({
+ ...prevState,
+ selectedChoice: submission.choice,
+ hasSubmitted: true
+ }));
+ setHasLocalSubmission(true);
+ return true;
+ }
+ } catch (error) {
+ console.error("Error parsing saved submission:", error);
+ }
+ }
+ return false;
+}, [address, pool?.id, gameState.round, setGameState, setHasLocalSubmission]);
+
+// Function to save submission to local storage
+exportconst saveSubmissionToLocalStorage = (choice: PlayerChoice) => {
+ if (!address || !pool?.id) return;
+
+ const storageKey = `cointoss_submission_${address}_${pool.id}_${gameState.round}`;
+ const submission = {
+ choice,
+ round: gameState.round,
+ timestamp: Date.now()
+ };
+
+ localStorage.setItem(storageKey, JSON.stringify(submission));
+ setHasLocalSubmission(true);
+};
\ No newline at end of file
diff --git a/Frontend/src/page/Play.tsx b/Frontend/src/page/Play.tsx
index d4e242e..69e9a8c 100644
--- a/Frontend/src/page/Play.tsx
+++ b/Frontend/src/page/Play.tsx
@@ -10,7 +10,6 @@ import {
import { useNavigate, useLocation } from "react-router-dom";
import CoinTossABI from "../utils/contract/CoinToss.json";
import { CORE_CONTRACT_ADDRESS } from "../utils/contract/contract";
-
enum PlayerChoice {
NONE = 0,
HEADS = 1,
@@ -18,39 +17,38 @@ enum PlayerChoice {
}
const PlayGame = () => {
+ const [coinRotation, setCoinRotation] = useState(0);
const navigate = useNavigate();
const location = useLocation();
const pool = location.state.pools;
const { address } = useAccount();
+ const [gameState, setGameState] = useState({
+ isTimerActive: true,
+ selectedChoice: null as PlayerChoice | null,
+ round: 1,
+ hasSubmitted: false,
+ isCoinFlipping: false,
+ isSubmitting: false,
+ isWaitingForOthers: false,
+ showClaimInterface: false,
+ showWinnerPopup: false,
+ });
- const [isTimerActive, setIsTimerActive] = useState(true);
- const [selectedChoice, setSelectedChoice] = useState(
- null
- );
- const [round, setRound] = useState(1);
const [timer, setTimer] = useState(20);
- const [isEliminated, setIsEliminated] = useState(false);
- const [hasSubmitted, setHasSubmitted] = useState(false);
- const [isCoinFlipping, setIsCoinFlipping] = useState(false);
- const [coinRotation, setCoinRotation] = useState(0);
- const [isSubmitting, setIsSubmitting] = useState(false);
const [notification, setNotification] = useState({
isVisible: false,
isSuccess: false,
message: "",
subMessage: "",
});
-
const [lastCompletedRound, setLastCompletedRound] = useState(0);
- const [isWaitingForOthers, setIsWaitingForOthers] = useState(false);
- const [showClaimInterface, setShowClaimInterface] = useState(false);
+ const [showWinnerPopup, setShowWinnerPopup] = useState(false);
+
type PlayerStatus = [boolean, boolean, boolean, boolean];
- // Fetch player status
- const [isWinner, setIsWinner] = useState(false);
- const [showWinnerPopup, setShowWinnerPopup] = useState(false);
+ // _____________________________Fetch Player Status____________________________________
const {
data: playerStatus,
@@ -58,139 +56,127 @@ const PlayGame = () => {
isLoading: isStatusLoading,
} = useReadContract({
address: CORE_CONTRACT_ADDRESS as `0x${string}`,
-
- // @ts-ignore
abi: CoinTossABI.abi,
functionName: "getPlayerStatus",
args: [BigInt(pool.id), address],
});
- console.log(playerStatus);
+
+ //___________________________Sending Transaction____________________________________
+
+ const {
+ writeContract,
+ data: hash,
+ isPending: txPending,
+ error
+ } = useWriteContract();
+
+ //__________________________ Wait for transaction confirmation________________________________________
+
+ const {
+ isLoading: isConfirming,
+ isSuccess: isConfirmed,
+ error: receiptError,
+ } = useWaitForTransactionReceipt({ hash });
+
+
+
+
- // @ts-ignore
- const isParticipant = playerStatus ? playerStatus[0] : false;
-
- // @ts-ignore
- const isEliminatedStatus = playerStatus ? playerStatus[1] : false;
+ console.log(playerStatus);
+ console.log(pool)
- // @ts-ignore
- const isWinnerStatus = playerStatus ? playerStatus[2] : false;
+//___________________________Player Status Destructuring____________________________________
- // @ts-ignore
+ const isParticipant = playerStatus ? playerStatus[0] : false;
+ const isEliminated= playerStatus ? playerStatus[1] : false;
+ const isAWinner = playerStatus ? playerStatus[2] : false;
const hasClaimed = playerStatus ? playerStatus[3] : false;
- // Send transaction
- const {
- writeContract,
- data: hash,
- isPending: isWritePending,
- error: writeError,
- } = useWriteContract();
-
- // Wait for transaction confirmation
- const {
- isLoading: isConfirming,
- isSuccess: isConfirmed,
- error: receiptError,
- } = useWaitForTransactionReceipt({ hash });
const coinFlipInterval = useRef(null);
//___________________ Handle timer logic________________________________
+
useEffect(() => {
- if (isTimerActive && timer > 0) {
+ if (gameState.isTimerActive && timer > 0) {
const interval = setInterval(() => {
setTimer((prevTimer) => prevTimer - 1);
- }, 1000);
+ }, 2000);
return () => clearInterval(interval);
- } else if (timer === 0 && !isWaitingForOthers) {
- setIsTimerActive(false);
-
- if (isWritePending || isConfirming) {
+ } else if (timer === 0) {
+ setGameState((prev) => ({ ...prev, isTimerActive: false }));
+ if (isConfirming) {
showNotification(
true,
"Processing...",
"Your choice has been submitted and is being processed"
);
- } else if (writeError || receiptError) {
+ } else if (receiptError) {
showNotification(
false,
"Transaction Failed",
"Your transaction failed to process. Try Again"
);
- // setTimeout(() => {
- // navigate("/explore");
- // }, 3000);
} else if (isConfirmed) {
- setIsWaitingForOthers(true);
+ showNotification(
+ true,
+ "Success!",
+ "Your selection has been recorded!"
+ );
+ setGameState((prev) => ({ ...prev, isWaitingForOthers: true }));
}
}
}, [
- isTimerActive,
+ gameState.isTimerActive,
timer,
- isWaitingForOthers,
- isWritePending,
isConfirming,
- writeError,
- receiptError,
isConfirmed,
+ receiptError
]);
+ //__________________________ Redirect Conditions________________________________
useEffect(() => {
- // Check if we should redirect based on different conditions
if (!pool) {
- navigate("/explore"); // No pool data, redirect
- } else if (hasClaimed) {
- navigate("/explore"); // Already claimed, redirect
- } else if (typeof pool.status === "number" && pool.status === 2) {
- // Pool is CLOSED (status 2)
- // Check if player is a winner before redirecting
- refetchPlayerStatus().then((result) => {
- // @ts-ignore
- if (!(result.data && result.data[2])) {
- // Not a winner, redirect
- navigate("/explore");
- }
- // If winner, let them stay to claim prize
- });
- } else if (
- (typeof pool.status === "number" && pool.status !== 2) ||
- (typeof pool.status === "string" && pool.status !== "ACTIVE")
- ) {
- // Pool is not active and not closed (other status), redirect
navigate("/explore");
+ return;
+ }
+ if (hasClaimed) {
+ navigate("/explore");
+ return;
}
- if (typeof pool?.status === "number" && pool.status === 2) {
+ const poolStatus = pool.status;
+ if (poolStatus === 2) {
refetchPlayerStatus().then((result) => {
- // @ts-ignore
- if (result.data && result.data[2] && !result.data[3]) {
- // Is winner and hasn't claimed
- setShowClaimInterface(true);
-
- // @ts-ignore
- } else if (!(result.data && result.data[2])) {
+ if (result.data) {
+ const [ isWinner, hasClaimed] = result.data;
+ if (isWinner && !hasClaimed) {
+ setShowClaimInterface(true);
+ } else {
+
+ navigate("/explore");
+ }
+ } else {
navigate("/explore");
}
});
}
+ // else {
+ // navigate("/explore");
+ // }
}, [pool, hasClaimed, navigate, refetchPlayerStatus]);
- // Handle player elimination
- useEffect(() => {
- if (playerStatus) {
- // @ts-ignore
- const isPlayerEliminated = playerStatus[1];
- // @ts-ignore
- const isPlayerWinner = playerStatus[2];
- // @ts-ignore
- const hasPlayerClaimed = playerStatus[3];
+ //____________________________ Handle player elimination_____________________________________________
+
+ useEffect(() => {
+ if (playerStatus) {
+
// Update elimination status
- if (isPlayerEliminated && !isEliminated) {
- setIsEliminated(true);
- setIsTimerActive(false);
+ if (isParticipant && isEliminated) {
+ setGameState((prev) => ({ ...prev, isEliminated: true, isTimerActive: false }));
showNotification(
false,
"Eliminated",
@@ -198,65 +184,70 @@ const PlayGame = () => {
);
setTimeout(() => {
navigate("/explore");
- }, 3000);
+ }, 4000);
}
// Update winner status
- if (isPlayerWinner && !isWinner) {
+
+ if (isParticipant && isAWinner) {
setIsWinner(true);
setShowWinnerPopup(true);
}
- // Handle claimed status
- if (hasPlayerClaimed && isPlayerWinner) {
+
+ if (isEliminated && gameState.hasSubmitted && isAWinner) {
navigate("/explore");
}
// Handle pool status changes
- if (pool?.status === 2 && isPlayerWinner && !showWinnerPopup) {
+ if (pool?.status === 2 && isParticipant && !showWinnerPopup) {
setShowWinnerPopup(true);
}
}
- }, [playerStatus, isEliminated, isWinner, showWinnerPopup, pool, navigate]);
+ }, [playerStatus,showWinnerPopup, pool, navigate]);
- // Handle player winning the game
+
+ //____________________________ Handle player winning the game_______________________________________
useEffect(() => {
- if (isWinnerStatus && pool?.status === 2) {
+ if (isAWinner && pool?.status === 2) {
setIsWinner(true);
setShowWinnerPopup(true);
}
- }, [isWinnerStatus, pool]);
+ }, [isAWinner, pool]);
// Add a polling mechanism to ensure we get updates even if events fail
useEffect(() => {
- if (isWaitingForOthers) {
+ if (gameState.isWaitingForOthers) {
const interval = setInterval(() => {
refetchPlayerStatus();
}, 5000);
return () => clearInterval(interval);
}
- }, [isWaitingForOthers, refetchPlayerStatus]);
+ }, [gameState.isWaitingForOthers, refetchPlayerStatus]);
// Handle player choice submission
+
const handleMakeChoice = async (selected: PlayerChoice) => {
- if (!isTimerActive || timer <= 2 || isEliminated || hasSubmitted) return;
+ if ( timer <= 2 || isEliminated || gameState.hasSubmitted) return;
- setSelectedChoice(selected);
- setHasSubmitted(true);
- startCoinAnimation();
+ setGameState((prev) => ({ ...prev, selectedChoice: selected, hasSubmitted: true, isCoinFlipping: true }));
+ startCoinAnimation()
await handleSubmit(selected);
};
+
+ // ____________submitting the choice_____________________
+
+
const handleSubmit = async (selected: PlayerChoice) => {
if (!selected) {
showNotification(false, "Error", "Please select HEADS or TAILS");
return;
}
-
try {
- setIsSubmitting(true);
- writeContract({
+ setGameState((prev) => ({ ...prev, isSubmitting: true }));
+ writeContract({
address: CORE_CONTRACT_ADDRESS as `0x${string}`,
abi: CoinTossABI.abi,
functionName: "makeSelection",
@@ -264,139 +255,98 @@ const PlayGame = () => {
});
} catch (err: any) {
const errorMessage = err.message || "Transaction failed";
- showNotification(false, "Transaction Error", errorMessage);
- setIsSubmitting(false);
+ showNotification(false, "Transaction Error!!!", errorMessage);
+ setGameState((prev) => ({
+ ...prev,
+ isSubmitting: false,
+ hasSubmitted: false,
+ selectedChoice: null,
+ }));
stopCoinAnimation();
}
};
+
// Handle transaction confirmation
useEffect(() => {
if (isConfirmed) {
- setIsSubmitting(false);
- setSelectedChoice(null);
+ setGameState((prev) => ({
+ ...prev,
+ isSubmitting: false,
+ isWaitingForOthers: true,
+ }));
showNotification(true, "Success!", "Your selection has been recorded!");
- setIsWaitingForOthers(true);
- setIsTimerActive(false);
- setTimer(0);
}
-
- if (writeError || receiptError) {
- setIsSubmitting(false);
- showNotification(
- false,
- "Transaction Failed",
- "Your transaction failed to process."
- );
+
+ if (receiptError) {
+ setGameState((prev) => ({
+ ...prev,
+ isSubmitting: false,
+ hasSubmitted: false,
+ selectedChoice: null,
+ }));
+ showNotification(false, "Transaction Failed", "Your transaction failed to process!!.");
}
- }, [isConfirmed, writeError, receiptError]);
-
+ }, [isConfirmed, receiptError]);
// Handle RoundCompleted event
-
useWatchContractEvent({
address: CORE_CONTRACT_ADDRESS as `0x${string}`,
abi: CoinTossABI.abi,
eventName: "RoundCompleted",
onLogs: (logs) => {
console.log("RoundCompleted logs received:", logs);
-
for (const log of logs) {
try {
console.log("Processing log:", log);
-
- // Extract event arguments, handling both named and positional formats
- // @ts-ignore
+
const args = log.args || {};
-
- // Get poolId - try both named and indexed access
+
const eventPoolId =
"poolId" in args
? Number(args.poolId)
: "0" in args
? Number(args[0])
: undefined;
-
- // Get round number
- const roundNumber =
- "round" in args
- ? Number(args.round)
- : "1" in args
- ? Number(args[1])
- : undefined;
-
- // Get winning selection
- const winningSelection =
- "winningSelection" in args
- ? Number(args.winningSelection)
- : "2" in args
- ? Number(args[2])
- : undefined;
-
- console.log("Extracted data:", {
- eventPoolId,
- roundNumber,
- winningSelection,
- });
-
+
+ const roundNumber ="round" in args ? Number(args.round) : "1" in args ? Number(args[1]) : undefined;
+ //Get winning selection
+ const winningSelection ="winningSelection" in args? Number(args.winningSelection) : "2" in args ? Number(args[2]): undefined;
// Skip if we couldn't extract necessary data
- if (
- eventPoolId === undefined ||
- roundNumber === undefined ||
- winningSelection === undefined
+ if (eventPoolId === undefined ||roundNumber === undefined ||winningSelection === undefined
) {
- console.error("Could not extract complete data from event", log);
continue;
}
-
- // Check if this is a new round completion (avoid processing the same round multiple times)
+
if (eventPoolId === pool.id && roundNumber > lastCompletedRound) {
console.log("New round completion detected for current pool!");
setLastCompletedRound(roundNumber);
-
// Stop animations immediately
stopCoinAnimation();
-
// Update UI state to indicate processing
- setIsWaitingForOthers(false);
-
+ setGameState((prev) => ({ ...prev, isWaitingForOthers: false }));
// Determine if user survived based on their choice
- const userChoice = selectedChoice;
+ const userChoice = gameState.selectedChoice;
const userSurvived = userChoice === winningSelection;
-
- console.log("Round result:", {
- userChoice,
- winningSelection,
- userSurvived,
- });
-
// Update eliminated status immediately
if (!userSurvived) {
- setIsEliminated(true);
+ setGameState((prev) => ({ ...prev, isEliminated: true }));
}
-
// Force refresh player status from contract
refetchPlayerStatus().then(() => {
console.log("Player status refreshed after round completion");
});
-
- // Show appropriate notification
- showNotification(
- userSurvived,
+
+ showNotification(userSurvived,
`Round ${roundNumber} Completed!`,
userSurvived
? "You advanced to the next round!"
: "You were eliminated!"
);
-
// Handle game state updates
if (userSurvived) {
- // Set timeout to allow notification to be seen
setTimeout(() => {
- setRound(roundNumber + 1);
setTimer(20);
- setIsTimerActive(true);
- setHasSubmitted(false);
- setSelectedChoice(null);
+ setGameState((prev) => ({ ...prev, isTimerActive: true, hasSubmitted: false,selectedChoice: null,round: roundNumber + 1}));
}, 3000);
}
}
@@ -410,15 +360,12 @@ const PlayGame = () => {
}
},
});
-
// Handle PoolCompleted event
useWatchContractEvent({
address: CORE_CONTRACT_ADDRESS as `0x${string}`,
abi: CoinTossABI.abi,
eventName: "PoolCompleted",
onLogs: (logs) => {
- console.log("PoolCompleted logs received:", logs);
-
for (const log of logs) {
try {
// @ts-ignore
@@ -431,22 +378,17 @@ const PlayGame = () => {
: undefined;
if (eventPoolId === pool.id) {
- console.log("Pool completed!");
-
// Immediately check if the user is a winner
refetchPlayerStatus().then((result) => {
- // @ts-ignore
if (result.data && result.data[2]) {
// index 2 is isWinner
- setIsWinner(true);
setShowWinnerPopup(true);
// Do NOT redirect winners - they need to claim their prize
// Update game state to reflect completion
- setIsTimerActive(false);
+ setGameState((prev) => ({ ...prev, isTimerActive: false,isWaitingForOthers: false}));
setTimer(0);
- setIsWaitingForOthers(false);
-
+
// Show a notification that the game is complete and they won
showNotification(
true,
@@ -486,7 +428,7 @@ const PlayGame = () => {
// Start/stop coin animation
const startCoinAnimation = () => {
- setIsCoinFlipping(true);
+ setGameState((prev) => ({ ...prev, isCoinFlipping: true }));
coinFlipInterval.current = setInterval(() => {
setCoinRotation((prev) => (prev + 36) % 360);
}, 100);
@@ -497,7 +439,11 @@ const PlayGame = () => {
clearInterval(coinFlipInterval.current);
coinFlipInterval.current = null;
}
- setIsCoinFlipping(false);
+ setGameState((prev) => ({
+ ...prev,
+ isCoinFlipping: false,
+ selectedChoice: null,
+ }));
setCoinRotation(0);
};
@@ -513,60 +459,40 @@ const PlayGame = () => {
if (!isSuccess) {
navigate("/explore");
}
- }, 3000);
+ }, 5000);
};
// Handle player winning the game
useEffect(() => {
- if (isWinnerStatus) {
+ if (isAWinner) {
setIsWinner(true);
setShowWinnerPopup(true);
}
- }, [isWinnerStatus]);
+ }, [isAWinner]);
+
+
+ // Handle token claim
+ const handleClaimPrize = async () => {
+ try {
+ setGameState((prev) => ({ ...prev, isSubmitting: true }));
+ writeContract({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "claimPrize",
+ args: [BigInt(pool.id)],
+ });
+ showNotification(true, "Success!", "Your prize has been claimed!");
+ setShowWinnerPopup(false);
+ navigate("/explore");
+ } catch (err: any) {
+ const errorMessage = err.message || "Transaction failed";
+ showNotification(false, "Transaction Error", errorMessage);
+ } finally {
+ setGameState((prev) => ({ ...prev, isSubmitting: false }));
+ }
+ };
- // Handle token claim
- const handleClaimPrize = async () => {
- try {
- setIsSubmitting(true);
- writeContract({
- address: CORE_CONTRACT_ADDRESS as `0x${string}`,
- abi: CoinTossABI.abi,
- functionName: "claimPrize",
- args: [BigInt(pool.id)],
- });
- showNotification(true, "Success!", "Your prize has been claimed!");
- setShowWinnerPopup(false);
- navigate("/explore");
- } catch (err: any) {
- const errorMessage = err.message || "Transaction failed";
- showNotification(false, "Transaction Error", errorMessage);
- } finally {
- setIsSubmitting(false);
- }
- };
- if (showClaimInterface) {
- return (
-
-
-
🏆
-
- Congratulations Winner!
-
-
- This pool has ended and you are a winner! Claim your prize now.
-
-
- {isSubmitting ? "Claiming..." : "Claim Prize"}
-
-
-
- );
- }
return (
{/* Top Game Status Bar */}
@@ -574,11 +500,11 @@ const PlayGame = () => {
- {round}
+ {gameState.round}
ROUND
-
{round}
+
{gameState.round}
@@ -613,7 +539,7 @@ const PlayGame = () => {
{/* Coin Animation Area */}
- {isCoinFlipping && (
+ {gameState.isCoinFlipping && (
{
- {selectedChoice === PlayerChoice.HEADS && (
+ {gameState.selectedChoice === PlayerChoice.HEADS && (
)}
handleMakeChoice(PlayerChoice.HEADS)}
className={`w-36 h-36 text-white rounded-full flex items-center justify-center transition-all transform hover:scale-105 ${
- selectedChoice === PlayerChoice.HEADS
+ gameState.selectedChoice === PlayerChoice.HEADS
? "border-4 border-yellow-500 bg-gradient-to-br from-yellow-900 to-yellow-950 shadow-glow-gold"
: "border border-gray-700 bg-gradient-to-br from-gray-800 to-gray-900 hover:border-yellow-600"
}`}
- disabled={!isTimerActive || isCoinFlipping || isSubmitting}
+ disabled={!gameState.isTimerActive || gameState.isCoinFlipping || gameState.isSubmitting}
>
🪙
HEADS
- {selectedChoice === PlayerChoice.HEADS && (
+ {gameState.selectedChoice === PlayerChoice.HEADS && (
SELECTED
@@ -662,22 +588,22 @@ const PlayGame = () => {
{/* Tails Option */}
- {selectedChoice === PlayerChoice.TAILS && (
+ {gameState.selectedChoice === PlayerChoice.TAILS && (
)}
handleMakeChoice(PlayerChoice.TAILS)}
className={`w-36 h-36 rounded-full flex items-center text-white justify-center transition-all transform hover:scale-105 ${
- selectedChoice === PlayerChoice.TAILS
+ gameState.selectedChoice === PlayerChoice.TAILS
? "border-4 border-yellow-500 bg-gradient-to-br from-yellow-900 to-yellow-950 shadow-glow-gold"
: "border border-gray-700 bg-gradient-to-br from-gray-800 to-gray-900 hover:border-yellow-600"
}`}
- disabled={!isTimerActive || isCoinFlipping || isSubmitting}
+ disabled={!gameState.isTimerActive || gameState.isCoinFlipping || gameState.isSubmitting}
>
💰
TAILS
- {selectedChoice === PlayerChoice.TAILS && (
+ {gameState.selectedChoice === PlayerChoice.TAILS && (
SELECTED
@@ -758,7 +684,7 @@ const PlayGame = () => {
)}
- {isWaitingForOthers && (
+ {gameState.isWaitingForOthers && (
Waiting for other players to make their selections...
@@ -779,10 +705,10 @@ const PlayGame = () => {
- {isSubmitting ? "Claiming..." : "Claim Prize"}
+ {gameState.isSubmitting ? "Claiming..." : "Claim Prize"}
diff --git a/Frontend/src/utils/formatedPlay.tsx b/Frontend/src/utils/formatedPlay.tsx
new file mode 100644
index 0000000..3389679
--- /dev/null
+++ b/Frontend/src/utils/formatedPlay.tsx
@@ -0,0 +1,737 @@
+import { useState, useEffect, useRef } from "react";
+import { formatTime } from "../utils/utilFunction";
+import {
+ useWriteContract,
+ useWaitForTransactionReceipt,
+ useWatchContractEvent,
+ useReadContract,
+ useAccount,
+} from "wagmi";
+import { useNavigate, useLocation } from "react-router-dom";
+import CoinTossABI from "../utils/contract/CoinToss.json";
+import { CORE_CONTRACT_ADDRESS } from "../utils/contract/contract";
+import { useContractInteraction } from "../hooks/ContractWriteIn";
+import {usePlayerStatus} from "../hooks/ContractReadIn";
+enum PlayerChoice {
+ NONE = 0,
+ HEADS = 1,
+ TAILS = 2,
+}
+
+const PlayGame = () => {
+ const [coinRotation, setCoinRotation] = useState(0);
+ const navigate = useNavigate();
+ const location = useLocation();
+ const pool = location.state.pools;
+ const { address } = useAccount();
+ const [gameState, setGameState] = useState({
+ isTimerActive: true,
+ selectedChoice: null as PlayerChoice | null,
+ round: 1,
+ isEliminated: false,
+ hasSubmitted: false,
+ isCoinFlipping: false,
+ isSubmitting: false,
+ isWaitingForOthers: false,
+ showClaimInterface: false,
+ showWinnerPopup: false,
+ });
+
+ const [timer, setTimer] = useState(31);
+ const [notification, setNotification] = useState({
+ isVisible: false,
+ isSuccess: false,
+ message: "",
+ subMessage: "",
+ });
+ const [lastCompletedRound, setLastCompletedRound] = useState(0);
+ const [isWinner, setIsWinner] = useState(false);
+ const [showWinnerPopup, setShowWinnerPopup] = useState(false);
+ const { makeSelection, claimPrize, isPending, isConfirming, isConfirmed, error } = useContractInteraction();
+
+ const{ playerStatus, refetchPlayerStatus, isStatusLoading}=usePlayerStatus(pool.id, address as `0x${string}` );
+
+ type PlayerStatus = [boolean, boolean, boolean, boolean];
+
+ // Fetch player status
+
+ // _____________________________Fetch Player Status____________________________________
+
+
+
+ console.log(playerStatus);
+ console.log(pool)
+
+//___________________________Player Status Destructuring____________________________________
+
+ const isParticipant = playerStatus ? playerStatus[0] : false;
+ const isEliminatedStatus = playerStatus ? playerStatus[1] : false;
+ const isWinnerStatus = playerStatus ? playerStatus[2] : false;
+ const hasClaimed = playerStatus ? playerStatus[3] : false;
+
+
+ const coinFlipInterval = useRef(null);
+
+ //___________________ Handle timer logic________________________________
+
+ useEffect(() => {
+ if (gameState.isTimerActive && timer > 0) {
+ const interval = setInterval(() => {
+ setTimer((prevTimer) => prevTimer - 1);
+ }, 2000);
+ return () => clearInterval(interval);
+ } else if (timer === 0) {
+ setGameState((prev) => ({ ...prev, isTimerActive: false }));
+ if (isPending || isConfirming) {
+ showNotification(
+ true,
+ "Processing...",
+ "Your choice has been submitted and is being processed"
+ );
+ } else if (error) {
+ showNotification(
+ false,
+ "Transaction Failed",
+ "Your transaction failed to process. Try Again"
+ );
+ } else if (isConfirmed) {
+ showNotification(
+ true,
+ "Success!",
+ "Your selection has been recorded!"
+ );
+ setGameState((prev) => ({ ...prev, isWaitingForOthers: true }));
+ }
+ }
+ }, [
+ gameState.isTimerActive,
+ timer,
+ gameState.isWaitingForOthers,
+ isPending,
+ isConfirmed,
+ error
+ ]);
+
+ //__________________________ Redirect Conditions________________________________
+
+
+ useEffect(() => {
+ // Check if we should redirect based on different conditions
+ if (!pool) {
+ navigate("/explore");
+ } else if (hasClaimed) {
+ navigate("/explore"); // Already claimed, redirect
+ } else if (typeof pool.status === "number" && pool.status === 2) {
+ // Pool is CLOSED (status 2)
+ // Check if player is a winner before redirecting
+ refetchPlayerStatus().then((result) => {
+
+ if (!(result.data && result.data[2])) {
+ navigate("/explore");
+ }
+ // If winner, let them stay to claim prize
+ });
+ } else if (
+ (typeof pool.status === "number" && pool.status !== 2) ||
+ (typeof pool.status === "string" && pool.status !== "ACTIVE")
+ ) {
+ // Pool is not active and not closed (other status), redirect
+ navigate("/explore");
+ }
+ if (typeof pool?.status === "number" && pool.status === 2) {
+ refetchPlayerStatus().then((result) => {
+ if (result.data && result.data[2] && !result.data[3]) {
+ // Is winner and hasn't claimed
+ setShowClaimInterface(true);
+ } else if (!(result.data && result.data[2])) {
+ navigate("/explore");
+ }
+ });
+ }
+ }, [pool, hasClaimed, navigate, refetchPlayerStatus]);
+
+
+
+
+
+
+
+ //____________________________ Handle player elimination_____________________________________________
+
+ useEffect(() => {
+ if (playerStatus) {
+
+ const isPlayerEliminated = playerStatus[1];
+ const isPlayerWinner = playerStatus[2];
+ const hasPlayerClaimed = playerStatus[3];
+
+ // Update elimination status
+ if (isPlayerEliminated && gameState.isEliminated) {
+ setGameState((prev) => ({ ...prev, isEliminated: true, isTimerActive: false }));
+ showNotification(
+ false,
+ "Eliminated",
+ "You have been eliminated from the pool."
+ );
+ setTimeout(() => {
+ navigate("/explore");
+ }, 3000);
+ }
+
+ // Update winner status
+
+ if (isPlayerWinner && !isWinner) {
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ }
+
+ // Handle claimed status
+ if (hasPlayerClaimed && isPlayerWinner) {
+ navigate("/explore");
+ }
+
+ // Handle pool status changes
+ if (pool?.status === 2 && isPlayerWinner && !showWinnerPopup) {
+ setShowWinnerPopup(true);
+ }
+ }
+ }, [playerStatus, gameState.isEliminated, isWinner, showWinnerPopup, pool, navigate]);
+
+ //____________________________ Handle player winning the game_______________________________________
+ useEffect(() => {
+ if (isWinnerStatus && pool?.status === 2) {
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ }
+ }, [isWinnerStatus, pool]);
+
+ // Add a polling mechanism to ensure we get updates even if events fail
+ useEffect(() => {
+ if (gameState.isWaitingForOthers) {
+ const interval = setInterval(() => {
+ refetchPlayerStatus();
+ }, 5000);
+
+ return () => clearInterval(interval);
+ }
+ }, [gameState.isWaitingForOthers, refetchPlayerStatus]);
+
+ // Handle player choice submission
+
+ const handleMakeChoice = async (selected: PlayerChoice) => {
+ if (!gameState.isTimerActive || timer <= 2 || gameState.isEliminated || gameState.hasSubmitted) return;
+
+ setGameState((prev) => ({ ...prev, selectedChoice: selected, hasSubmitted: true, isCoinFlipping: true }));
+ startCoinAnimation()
+ await handleSubmit(selected);
+ };
+ // ____________submitting the choice_____________________
+
+ const handleSubmit = async (selected: PlayerChoice) => {
+ if (!selected) {
+ showNotification(false, "Error", "Please select HEADS or TAILS");
+ return;
+ }
+
+ try {
+ setGameState((prev) => ({ ...prev, isSubmitting: true }));
+ makeSelection(BigInt(pool.id), selected);
+ } catch (err: any) {
+ const errorMessage = err.message || "Transaction failed";
+ showNotification(false, "Transaction Error", errorMessage);
+ setGameState((prev) => ({ ...prev, isSubmitting: false }));
+ stopCoinAnimation()
+ }
+ };
+
+ // Handle transaction confirmation
+ useEffect(() => {
+ if (isConfirmed) {
+ setGameState((prev) => ({
+ ...prev,
+ isSubmitting: false,
+ isWaitingForOthers: true,
+ }));
+ showNotification(true, "Success!", "Your selection has been recorded!");
+ }
+
+ if (error) {
+ setGameState((prev) => ({ ...prev, isSubmitting: false }));
+ showNotification(false, "Transaction Failed", "Your transaction failed to process.");
+ }
+ }, [isConfirmed, error]);
+
+ // Handle RoundCompleted event
+
+ useWatchContractEvent({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ eventName: "RoundCompleted",
+ onLogs: (logs) => {
+ console.log("RoundCompleted logs received:", logs);
+
+ for (const log of logs) {
+ try {
+ console.log("Processing log:", log);
+
+ // Extract event arguments, handling both named and positional formats
+
+ const args = log.args || {};
+
+ // Get poolId - try both named and indexed access
+ const eventPoolId =
+ "poolId" in args
+ ? Number(args.poolId)
+ : "0" in args
+ ? Number(args[0])
+ : undefined;
+
+ // Get round number
+ const roundNumber =
+ "round" in args
+ ? Number(args.round)
+ : "1" in args
+ ? Number(args[1])
+ : undefined;
+
+ // Get winning selection
+ const winningSelection =
+ "winningSelection" in args
+ ? Number(args.winningSelection)
+ : "2" in args
+ ? Number(args[2])
+ : undefined;
+
+ console.log("Extracted data:", {
+ eventPoolId,
+ roundNumber,
+ winningSelection,
+ });
+
+ // Skip if we couldn't extract necessary data
+ if (
+ eventPoolId === undefined ||
+ roundNumber === undefined ||
+ winningSelection === undefined
+ ) {
+ console.error("Could not extract complete data from event", log);
+ continue;
+ }
+
+ // Check if this is a new round completion (avoid processing the same round multiple times)
+ if (eventPoolId === pool.id && roundNumber > lastCompletedRound) {
+ console.log("New round completion detected for current pool!");
+ setLastCompletedRound(roundNumber);
+
+ // Stop animations immediately
+ stopCoinAnimation();
+
+ // Update UI state to indicate processing
+ setGameState((prev) => ({ ...prev, isWaitingForOthers: false }));
+
+ // Determine if user survived based on their choice
+ const userChoice = gameState.selectedChoice;
+ const userSurvived = userChoice === winningSelection;
+
+ console.log("Round result:", {
+ userChoice,
+ winningSelection,
+ userSurvived,
+ });
+
+ // Update eliminated status immediately
+ if (!userSurvived) {
+ setGameState((prev) => ({ ...prev, isEliminated: true }));
+ }
+
+ // Force refresh player status from contract
+ refetchPlayerStatus().then(() => {
+ console.log("Player status refreshed after round completion");
+ });
+
+ // Show appropriate notification
+ showNotification(
+ userSurvived,
+ `Round ${roundNumber} Completed!`,
+ userSurvived
+ ? "You advanced to the next round!"
+ : "You were eliminated!"
+ );
+
+ // Handle game state updates
+ if (userSurvived) {
+ // Set timeout to allow notification to be seen
+ setTimeout(() => {
+ setGameState((prev) => ({ ...prev, round: roundNumber + 1 }));
+ setTimer(20);
+ setGameState((prev) => ({ ...prev, isTimerActive: true }));
+ setGameState((prev) => ({ ...prev, hasSubmitted: false }));
+ setGameState((prev) => ({ ...prev, selectedChoice: null }));
+ }, 3000);
+ }
+ }
+ } catch (error) {
+ console.error("Error processing event log:", error);
+ console.error(
+ "Error details:",
+ error instanceof Error ? error.message : String(error)
+ );
+ }
+ }
+ },
+ });
+
+ // Handle PoolCompleted event
+ useWatchContractEvent({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ eventName: "PoolCompleted",
+ onLogs: (logs) => {
+ console.log("PoolCompleted logs received:", logs);
+
+ for (const log of logs) {
+ try {
+ // @ts-ignore
+ const args = log.args || {};
+ const eventPoolId =
+ "poolId" in args
+ ? Number(args.poolId)
+ : "0" in args
+ ? Number(args[0])
+ : undefined;
+
+ if (eventPoolId === pool.id) {
+ console.log("Pool completed!");
+
+ // Immediately check if the user is a winner
+ refetchPlayerStatus().then((result) => {
+ // @ts-ignore
+ if (result.data && result.data[2]) {
+ // index 2 is isWinner
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ // Do NOT redirect winners - they need to claim their prize
+
+ // Update game state to reflect completion
+ setGameState((prev) => ({ ...prev, isTimerActive: false,isWaitingForOthers: false}));
+ setTimer(0);
+
+
+ // Show a notification that the game is complete and they won
+ showNotification(
+ true,
+ "You Won!",
+ "The pool has ended and you are a winner!"
+ );
+ } else {
+ // User didn't win - redirect after notification
+ showNotification(
+ false,
+ "Game Over",
+ "The pool has ended. Better luck next time!"
+ );
+ setTimeout(() => {
+ navigate("/explore");
+ }, 5000);
+ }
+ });
+ }
+ } catch (error) {
+ console.error("Error processing PoolCompleted event:", error);
+ console.error(
+ "Error details:",
+ error instanceof Error ? error.message : String(error)
+ );
+
+ // Show a generic notification in case of error
+ showNotification(
+ false,
+ "Error",
+ "An error occurred while processing the game result"
+ );
+ }
+ }
+ },
+ });
+
+ // Start/stop coin animation
+ const startCoinAnimation = () => {
+ setGameState((prev) => ({ ...prev, isCoinFlipping: true }));
+ coinFlipInterval.current = setInterval(() => {
+ setCoinRotation((prev) => (prev + 36) % 360);
+ }, 100);
+ };
+
+ const stopCoinAnimation = () => {
+ if (coinFlipInterval.current) {
+ clearInterval(coinFlipInterval.current);
+ coinFlipInterval.current = null;
+ }
+ setGameState((prev) => ({ ...prev, isCoinFlipping: false }));
+ setCoinRotation(0);
+ };
+
+ // Show notification
+ const showNotification = (
+ isSuccess: boolean,
+ message: string,
+ subMessage: string
+ ) => {
+ setNotification({ isVisible: true, isSuccess, message, subMessage });
+ setTimeout(() => {
+ setNotification((prev) => ({ ...prev, isVisible: false }));
+ if (!isSuccess) {
+ navigate("/explore");
+ }
+ }, 3000);
+ };
+
+ // Handle player winning the game
+ useEffect(() => {
+ if (isWinnerStatus) {
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ }
+ }, [isWinnerStatus]);
+
+ // Handle token claim
+ const handleClaimPrize = async () => {
+ try {
+ setGameState((prev) => ({ ...prev, isSubmitting: true }));
+ await claimPrize(BigInt(pool.id));
+ showNotification(true, "Success!", "Your prize has been claimed!");
+ setShowWinnerPopup(false);
+ navigate("/explore");
+ } catch (err: any) {
+ const errorMessage = err.message || "Transaction failed";
+ showNotification(false, "Transaction Error", errorMessage);
+ } finally {
+ setGameState((prev) => ({ ...prev, isSubmitting: false }));
+ }
+ };
+
+ return (
+
+ {/* Top Game Status Bar */}
+
+
+
+
+ {gameState.round}
+
+
+
ROUND
+
{gameState.round}
+
+
+
+
+
REMAINING PLAYERS
+
+ {pool.currentParticipants}
+
+
+
+
+
+
+ POTENTIAL
+
+ REWARD
+
+
+
+ +120 Points
+
+
+
+
+
+ {/* Main Game Area */}
+
+
CHOOSE WISELY
+
+ Only the minority will survive
+
+
+
+ {/* Coin Animation Area */}
+ {gameState.isCoinFlipping && (
+
+
+ {coinRotation % 180 < 90 ? "🪙" : "💰"}
+
+
+ )}
+
+
+
+ {gameState.selectedChoice === PlayerChoice.HEADS && (
+
+ )}
+
handleMakeChoice(PlayerChoice.HEADS)}
+ className={`w-36 h-36 text-white rounded-full flex items-center justify-center transition-all transform hover:scale-105 ${
+ gameState.selectedChoice === PlayerChoice.HEADS
+ ? "border-4 border-yellow-500 bg-gradient-to-br from-yellow-900 to-yellow-950 shadow-glow-gold"
+ : "border border-gray-700 bg-gradient-to-br from-gray-800 to-gray-900 hover:border-yellow-600"
+ }`}
+ disabled={!gameState.isTimerActive || gameState.isCoinFlipping || gameState.isSubmitting}
+ >
+
+
🪙
+
HEADS
+ {gameState.selectedChoice === PlayerChoice.HEADS && (
+
+ SELECTED
+
+ )}
+
+
+
+
+ {/* VS Divider */}
+
+
+ {/* Tails Option */}
+
+ {gameState.selectedChoice === PlayerChoice.TAILS && (
+
+ )}
+
handleMakeChoice(PlayerChoice.TAILS)}
+ className={`w-36 h-36 rounded-full flex items-center text-white justify-center transition-all transform hover:scale-105 ${
+ gameState.selectedChoice === PlayerChoice.TAILS
+ ? "border-4 border-yellow-500 bg-gradient-to-br from-yellow-900 to-yellow-950 shadow-glow-gold"
+ : "border border-gray-700 bg-gradient-to-br from-gray-800 to-gray-900 hover:border-yellow-600"
+ }`}
+ disabled={!gameState.isTimerActive || gameState.isCoinFlipping || gameState.isSubmitting}
+ >
+
+
💰
+
TAILS
+ {gameState.selectedChoice === PlayerChoice.TAILS && (
+
+ SELECTED
+
+ )}
+
+
+
+
+
+ {/* Timer Section with Enhanced Drama */}
+
+
+
+
TIME REMAINING
+
+ {formatTime(timer)}
+
+
+
+
+
+
+
+ Tip: The
+ minority option wins!
+
+
+
+
+
+ {/* Notification for round results */}
+ {notification.isVisible && (
+
+
+
+ {notification.isSuccess ? "🏆" : "❌"}
+
+
+ {notification.message}
+
+
+ {notification.subMessage}
+
+
+
+ )}
+
+ {gameState.isWaitingForOthers && (
+
+
+ Waiting for other players to make their selections...
+
+
+ )}
+
+ {/* Winner Pop-up */}
+ {showWinnerPopup && (
+
+
+
🏆
+
+ Congratulations!
+
+
+ You are the winner of this pool! Claim your prize now.
+
+
+ {gameState.isSubmitting ? "Claiming..." : "Claim Prize"}
+
+
+
+ )}
+
+ );
+};
+
+export default PlayGame;
diff --git a/Frontend/src/utils/oldPlay.tsx b/Frontend/src/utils/oldPlay.tsx
new file mode 100644
index 0000000..facf803
--- /dev/null
+++ b/Frontend/src/utils/oldPlay.tsx
@@ -0,0 +1,801 @@
+import { useState, useEffect, useRef } from "react";
+import { formatTime } from "../utils/utilFunction";
+import {
+ useWriteContract,
+ useWaitForTransactionReceipt,
+ useWatchContractEvent,
+ useReadContract,
+ useAccount,
+} from "wagmi";
+import { useNavigate, useLocation } from "react-router-dom";
+import CoinTossABI from "../utils/contract/CoinToss.json";
+import { CORE_CONTRACT_ADDRESS } from "../utils/contract/contract";
+
+enum PlayerChoice {
+ NONE = 0,
+ HEADS = 1,
+ TAILS = 2,
+}
+
+const PlayGame = () => {
+
+ const navigate = useNavigate();
+ const location = useLocation();
+ const pool = location.state.pools;
+ const { address } = useAccount();
+
+ const [isTimerActive, setIsTimerActive] = useState(true);
+ const [selectedChoice, setSelectedChoice] = useState(
+ null
+ );
+
+ const [round, setRound] = useState(1);
+ const [timer, setTimer] = useState(31);
+ const [isEliminated, setIsEliminated] = useState(false);
+ const [hasSubmitted, setHasSubmitted] = useState(false);
+ const [isCoinFlipping, setIsCoinFlipping] = useState(false);
+ const [coinRotation, setCoinRotation] = useState(0);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [notification, setNotification] = useState({
+ isVisible: false,
+ isSuccess: false,
+ message: "",
+ subMessage: "",
+ });
+
+ const [lastCompletedRound, setLastCompletedRound] = useState(0);
+ const [isWaitingForOthers, setIsWaitingForOthers] = useState(false);
+ const [showClaimInterface, setShowClaimInterface] = useState(false);
+
+ type PlayerStatus = [boolean, boolean, boolean, boolean];
+
+ // Fetch player status
+
+ const [isWinner, setIsWinner] = useState(false);
+ const [showWinnerPopup, setShowWinnerPopup] = useState(false);
+
+ // _____________________________Fetch Player Status____________________________________
+
+ const {
+ data: playerStatus,
+ refetch: refetchPlayerStatus,
+ isLoading: isStatusLoading,
+ } = useReadContract({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "getPlayerStatus",
+ args: [BigInt(pool.id), address],
+ });
+
+ console.log(playerStatus);
+ console.log(pool)
+
+//___________________________Player Status Destructuring____________________________________
+
+ const isParticipant = playerStatus ? playerStatus[0] : false;
+ const isEliminatedStatus = playerStatus ? playerStatus[1] : false;
+ const isWinnerStatus = playerStatus ? playerStatus[2] : false;
+ const hasClaimed = playerStatus ? playerStatus[3] : false;
+
+
+//___________________________Sending Transaction____________________________________
+
+ const {
+ writeContract,
+ data: hash,
+ isPending: isWritePending,
+ error: writeError,
+ } = useWriteContract();
+
+ //__________________________ Wait for transaction confirmation________________________________________
+
+ const {
+ isLoading: isConfirming,
+ isSuccess: isConfirmed,
+ error: receiptError,
+ } = useWaitForTransactionReceipt({ hash });
+
+ const coinFlipInterval = useRef(null);
+
+ //___________________ Handle timer logic________________________________
+
+ useEffect(() => {
+ if (isTimerActive && timer > 0) {
+ const interval = setInterval(() => {
+ setTimer((prevTimer) => prevTimer - 1);
+ }, 2000);
+ return () => clearInterval(interval);
+ } else if (timer === 0) {
+ setIsTimerActive(false);
+ if (isWritePending || isConfirming) {
+ showNotification(
+ true,
+ "Processing...",
+ "Your choice has been submitted and is being processed"
+ );
+ } else if (writeError || receiptError) {
+ showNotification(
+ false,
+ "Transaction Failed",
+ "Your transaction failed to process. Try Again"
+ );
+ } else if (isConfirmed) {
+ showNotification(
+ true,
+ "Success!",
+ "Your selection has been recorded!"
+ );
+ setIsWaitingForOthers(true);
+ }
+ }
+ }, [
+ isTimerActive,
+ timer,
+ isWaitingForOthers,
+ isWritePending,
+ isConfirming,
+ writeError,
+ receiptError,
+ isConfirmed,
+ ]);
+
+ //__________________________ Redirect Conditions________________________________
+
+
+ useEffect(() => {
+ // Check if we should redirect based on different conditions
+ if (!pool) {
+ navigate("/explore");
+ } else if (hasClaimed) {
+ navigate("/explore"); // Already claimed, redirect
+ } else if (typeof pool.status === "number" && pool.status === 2) {
+ // Pool is CLOSED (status 2)
+ // Check if player is a winner before redirecting
+ refetchPlayerStatus().then((result) => {
+
+ if (!(result.data && result.data[2])) {
+ navigate("/explore");
+ }
+ // If winner, let them stay to claim prize
+ });
+ } else if (
+ (typeof pool.status === "number" && pool.status !== 2) ||
+ (typeof pool.status === "string" && pool.status !== "ACTIVE")
+ ) {
+ // Pool is not active and not closed (other status), redirect
+ navigate("/explore");
+ }
+ if (typeof pool?.status === "number" && pool.status === 2) {
+ refetchPlayerStatus().then((result) => {
+ if (result.data && result.data[2] && !result.data[3]) {
+ // Is winner and hasn't claimed
+ setShowClaimInterface(true);
+ } else if (!(result.data && result.data[2])) {
+ navigate("/explore");
+ }
+ });
+ }
+ }, [pool, hasClaimed, navigate, refetchPlayerStatus]);
+
+
+
+
+
+
+
+ //____________________________ Handle player elimination_____________________________________________
+
+ useEffect(() => {
+ if (playerStatus) {
+
+ const isPlayerEliminated = playerStatus[1];
+ const isPlayerWinner = playerStatus[2];
+ const hasPlayerClaimed = playerStatus[3];
+
+ // Update elimination status
+ if (isPlayerEliminated && !isEliminated) {
+ setIsEliminated(true);
+ setIsTimerActive(false);
+ showNotification(
+ false,
+ "Eliminated",
+ "You have been eliminated from the pool."
+ );
+ setTimeout(() => {
+ navigate("/explore");
+ }, 3000);
+ }
+
+ // Update winner status
+
+ if (isPlayerWinner && !isWinner) {
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ }
+
+ // Handle claimed status
+ if (hasPlayerClaimed && isPlayerWinner) {
+ navigate("/explore");
+ }
+
+ // Handle pool status changes
+ if (pool?.status === 2 && isPlayerWinner && !showWinnerPopup) {
+ setShowWinnerPopup(true);
+ }
+ }
+ }, [playerStatus, isEliminated, isWinner, showWinnerPopup, pool, navigate]);
+
+ // Handle player winning the game
+ useEffect(() => {
+ if (isWinnerStatus && pool?.status === 2) {
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ }
+ }, [isWinnerStatus, pool]);
+
+ // Add a polling mechanism to ensure we get updates even if events fail
+ useEffect(() => {
+ if (isWaitingForOthers) {
+ const interval = setInterval(() => {
+ refetchPlayerStatus();
+ }, 5000);
+
+ return () => clearInterval(interval);
+ }
+ }, [isWaitingForOthers, refetchPlayerStatus]);
+
+ // Handle player choice submission
+ const handleMakeChoice = async (selected: PlayerChoice) => {
+ if (!isTimerActive || timer <= 2 || isEliminated || hasSubmitted) return;
+
+ setSelectedChoice(selected);
+ setHasSubmitted(true);
+ startCoinAnimation();
+ await handleSubmit(selected);
+ };
+
+ // ____________submitting the choice_____________________
+
+ const handleSubmit = async (selected: PlayerChoice) => {
+ if (!selected) {
+ showNotification(false, "Error", "Please select HEADS or TAILS");
+ return;
+ }
+
+ try {
+ setIsSubmitting(true);
+ writeContract({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "makeSelection",
+ args: [BigInt(pool.id), selected],
+ });
+ } catch (err: any) {
+ const errorMessage = err.message || "Transaction failed";
+ showNotification(false, "Transaction Error", errorMessage);
+ setIsSubmitting(false);
+ stopCoinAnimation();
+ }
+ };
+
+ // Handle transaction confirmation
+ useEffect(() => {
+ if (isConfirmed) {
+ setIsSubmitting(false);
+ setSelectedChoice(null);
+ showNotification(true, "Success!", "Your selection has been recorded!");
+ setIsWaitingForOthers(true);
+ setIsTimerActive(false);
+ }
+
+ if (writeError || receiptError) {
+ setIsSubmitting(false);
+ showNotification(
+ false,
+ "Transaction Failed",
+ "Your transaction failed to process."
+ );
+ }
+ }, [isConfirmed, writeError, receiptError]);
+
+ // Handle RoundCompleted event
+
+ useWatchContractEvent({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ eventName: "RoundCompleted",
+ onLogs: (logs) => {
+ console.log("RoundCompleted logs received:", logs);
+
+ for (const log of logs) {
+ try {
+ console.log("Processing log:", log);
+
+ // Extract event arguments, handling both named and positional formats
+
+ const args = log.args || {};
+
+ // Get poolId - try both named and indexed access
+ const eventPoolId =
+ "poolId" in args
+ ? Number(args.poolId)
+ : "0" in args
+ ? Number(args[0])
+ : undefined;
+
+ // Get round number
+ const roundNumber =
+ "round" in args
+ ? Number(args.round)
+ : "1" in args
+ ? Number(args[1])
+ : undefined;
+
+ // Get winning selection
+ const winningSelection =
+ "winningSelection" in args
+ ? Number(args.winningSelection)
+ : "2" in args
+ ? Number(args[2])
+ : undefined;
+
+ console.log("Extracted data:", {
+ eventPoolId,
+ roundNumber,
+ winningSelection,
+ });
+
+ // Skip if we couldn't extract necessary data
+ if (
+ eventPoolId === undefined ||
+ roundNumber === undefined ||
+ winningSelection === undefined
+ ) {
+ console.error("Could not extract complete data from event", log);
+ continue;
+ }
+
+ // Check if this is a new round completion (avoid processing the same round multiple times)
+ if (eventPoolId === pool.id && roundNumber > lastCompletedRound) {
+ console.log("New round completion detected for current pool!");
+ setLastCompletedRound(roundNumber);
+
+ // Stop animations immediately
+ stopCoinAnimation();
+
+ // Update UI state to indicate processing
+ setIsWaitingForOthers(false);
+
+ // Determine if user survived based on their choice
+ const userChoice = selectedChoice;
+ const userSurvived = userChoice === winningSelection;
+
+ console.log("Round result:", {
+ userChoice,
+ winningSelection,
+ userSurvived,
+ });
+
+ // Update eliminated status immediately
+ if (!userSurvived) {
+ setIsEliminated(true);
+ }
+
+ // Force refresh player status from contract
+ refetchPlayerStatus().then(() => {
+ console.log("Player status refreshed after round completion");
+ });
+
+ // Show appropriate notification
+ showNotification(
+ userSurvived,
+ `Round ${roundNumber} Completed!`,
+ userSurvived
+ ? "You advanced to the next round!"
+ : "You were eliminated!"
+ );
+
+ // Handle game state updates
+ if (userSurvived) {
+ // Set timeout to allow notification to be seen
+ setTimeout(() => {
+ setRound(roundNumber + 1);
+ setTimer(20);
+ setIsTimerActive(true);
+ setHasSubmitted(false);
+ setSelectedChoice(null);
+ }, 3000);
+ }
+ }
+ } catch (error) {
+ console.error("Error processing event log:", error);
+ console.error(
+ "Error details:",
+ error instanceof Error ? error.message : String(error)
+ );
+ }
+ }
+ },
+ });
+
+ // Handle PoolCompleted event
+ useWatchContractEvent({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ eventName: "PoolCompleted",
+ onLogs: (logs) => {
+ console.log("PoolCompleted logs received:", logs);
+
+ for (const log of logs) {
+ try {
+ // @ts-ignore
+ const args = log.args || {};
+ const eventPoolId =
+ "poolId" in args
+ ? Number(args.poolId)
+ : "0" in args
+ ? Number(args[0])
+ : undefined;
+
+ if (eventPoolId === pool.id) {
+ console.log("Pool completed!");
+
+ // Immediately check if the user is a winner
+ refetchPlayerStatus().then((result) => {
+ // @ts-ignore
+ if (result.data && result.data[2]) {
+ // index 2 is isWinner
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ // Do NOT redirect winners - they need to claim their prize
+
+ // Update game state to reflect completion
+ setIsTimerActive(false);
+ setTimer(0);
+ setIsWaitingForOthers(false);
+
+ // Show a notification that the game is complete and they won
+ showNotification(
+ true,
+ "You Won!",
+ "The pool has ended and you are a winner!"
+ );
+ } else {
+ // User didn't win - redirect after notification
+ showNotification(
+ false,
+ "Game Over",
+ "The pool has ended. Better luck next time!"
+ );
+ setTimeout(() => {
+ navigate("/explore");
+ }, 5000);
+ }
+ });
+ }
+ } catch (error) {
+ console.error("Error processing PoolCompleted event:", error);
+ console.error(
+ "Error details:",
+ error instanceof Error ? error.message : String(error)
+ );
+
+ // Show a generic notification in case of error
+ showNotification(
+ false,
+ "Error",
+ "An error occurred while processing the game result"
+ );
+ }
+ }
+ },
+ });
+
+ // Start/stop coin animation
+ const startCoinAnimation = () => {
+ setIsCoinFlipping(true);
+ coinFlipInterval.current = setInterval(() => {
+ setCoinRotation((prev) => (prev + 36) % 360);
+ }, 100);
+ };
+
+ const stopCoinAnimation = () => {
+ if (coinFlipInterval.current) {
+ clearInterval(coinFlipInterval.current);
+ coinFlipInterval.current = null;
+ }
+ setIsCoinFlipping(false);
+ setCoinRotation(0);
+ };
+
+ // Show notification
+ const showNotification = (
+ isSuccess: boolean,
+ message: string,
+ subMessage: string
+ ) => {
+ setNotification({ isVisible: true, isSuccess, message, subMessage });
+ setTimeout(() => {
+ setNotification((prev) => ({ ...prev, isVisible: false }));
+ if (!isSuccess) {
+ navigate("/explore");
+ }
+ }, 3000);
+ };
+
+ // Handle player winning the game
+ useEffect(() => {
+ if (isWinnerStatus) {
+ setIsWinner(true);
+ setShowWinnerPopup(true);
+ }
+ }, [isWinnerStatus]);
+
+ // Handle token claim
+ const handleClaimPrize = async () => {
+ try {
+ setIsSubmitting(true);
+ writeContract({
+ address: CORE_CONTRACT_ADDRESS as `0x${string}`,
+ abi: CoinTossABI.abi,
+ functionName: "claimPrize",
+ args: [BigInt(pool.id)],
+ });
+ showNotification(true, "Success!", "Your prize has been claimed!");
+ setShowWinnerPopup(false);
+ navigate("/explore");
+ } catch (err: any) {
+ const errorMessage = err.message || "Transaction failed";
+ showNotification(false, "Transaction Error", errorMessage);
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ if (showClaimInterface) {
+ return (
+
+
+
🏆
+
+ Congratulations Winner!
+
+
+ This pool has ended and you are a winner! Claim your prize now.
+
+
+ {isSubmitting ? "Claiming..." : "Claim Prize"}
+
+
+
+ );
+ }
+ return (
+
+ {/* Top Game Status Bar */}
+
+
+
+
+
+
REMAINING PLAYERS
+
+ {pool.currentParticipants}
+
+
+
+
+
+
+ POTENTIAL
+
+ REWARD
+
+
+
+ +120 Points
+
+
+
+
+
+ {/* Main Game Area */}
+
+
CHOOSE WISELY
+
+ Only the minority will survive
+
+
+
+ {/* Coin Animation Area */}
+ {isCoinFlipping && (
+
+
+ {coinRotation % 180 < 90 ? "🪙" : "💰"}
+
+
+ )}
+
+
+
+ {selectedChoice === PlayerChoice.HEADS && (
+
+ )}
+
handleMakeChoice(PlayerChoice.HEADS)}
+ className={`w-36 h-36 text-white rounded-full flex items-center justify-center transition-all transform hover:scale-105 ${
+ selectedChoice === PlayerChoice.HEADS
+ ? "border-4 border-yellow-500 bg-gradient-to-br from-yellow-900 to-yellow-950 shadow-glow-gold"
+ : "border border-gray-700 bg-gradient-to-br from-gray-800 to-gray-900 hover:border-yellow-600"
+ }`}
+ disabled={!isTimerActive || isCoinFlipping || isSubmitting}
+ >
+
+
🪙
+
HEADS
+ {selectedChoice === PlayerChoice.HEADS && (
+
+ SELECTED
+
+ )}
+
+
+
+
+ {/* VS Divider */}
+
+
+ {/* Tails Option */}
+
+ {selectedChoice === PlayerChoice.TAILS && (
+
+ )}
+
handleMakeChoice(PlayerChoice.TAILS)}
+ className={`w-36 h-36 rounded-full flex items-center text-white justify-center transition-all transform hover:scale-105 ${
+ selectedChoice === PlayerChoice.TAILS
+ ? "border-4 border-yellow-500 bg-gradient-to-br from-yellow-900 to-yellow-950 shadow-glow-gold"
+ : "border border-gray-700 bg-gradient-to-br from-gray-800 to-gray-900 hover:border-yellow-600"
+ }`}
+ disabled={!isTimerActive || isCoinFlipping || isSubmitting}
+ >
+
+
💰
+
TAILS
+ {selectedChoice === PlayerChoice.TAILS && (
+
+ SELECTED
+
+ )}
+
+
+
+
+
+ {/* Timer Section with Enhanced Drama */}
+
+
+
+
TIME REMAINING
+
+ {formatTime(timer)}
+
+
+
+
+
+
+
+ Tip: The
+ minority option wins!
+
+
+
+
+
+ {/* Notification for round results */}
+ {notification.isVisible && (
+
+
+
+ {notification.isSuccess ? "🏆" : "❌"}
+
+
+ {notification.message}
+
+
+ {notification.subMessage}
+
+
+
+ )}
+
+ {isWaitingForOthers && (
+
+
+ Waiting for other players to make their selections...
+
+
+ )}
+
+ {/* Winner Pop-up */}
+ {showWinnerPopup && (
+
+
+
🏆
+
+ Congratulations!
+
+
+ You are the winner of this pool! Claim your prize now.
+
+
+ {isSubmitting ? "Claiming..." : "Claim Prize"}
+
+
+
+ )}
+
+ );
+};
+
+export default PlayGame;