bet creation and close complete

This commit is contained in:
warlock 2025-04-03 05:37:29 +05:30
parent 8f80d3c098
commit 609fdcad20
2 changed files with 93 additions and 57 deletions

View File

@ -3,14 +3,12 @@
import { useState } from "react"; import { useState } from "react";
import { usePrivy, useSolanaWallets } from "@privy-io/react-auth"; import { usePrivy, useSolanaWallets } from "@privy-io/react-auth";
import { toast } from "sonner"; import { toast } from "sonner";
import { games } from "../data/games"; // Assuming you have a games data file import { games } from "../data/games";
import { PriceSelection } from "./PriceSelection"; import { PriceSelection } from "./PriceSelection";
import { GameSelection } from "./GameSelection"; import { GameSelection } from "./GameSelection";
import { createBet } from "@/shared/solana_helpers"; import { createBet } from "@/shared/solana_helpers";
import { Game } from "@/types/Game"; import { Game } from "@/types/Game";
// Change to Mainnet when deploying
interface GameModalProps { interface GameModalProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
@ -22,53 +20,67 @@ export default function GameModal({ isOpen, onClose }: GameModalProps) {
const [selectedGame, setSelectedGame] = useState<Game | null>(null); const [selectedGame, setSelectedGame] = useState<Game | null>(null);
const [selectedPrice, setSelectedPrice] = useState<number | null>(null); const [selectedPrice, setSelectedPrice] = useState<number | null>(null);
const [isProcessing, setIsProcessing] = useState(false);
const handleCreateGame = async () => { const handleCreateGame = async () => {
if (!authenticated) { if (!authenticated) {
toast.error("Please log in with Privy."); toast.error("Please log in with Privy.");
return; return;
} }
const wallet = wallets[0]; // Get connected Privy wallet const wallet = wallets[0];
if (!wallet) { if (!wallet) {
toast.error("Please connect your wallet."); toast.error("Please connect your wallet.");
return; return;
} }
if (!selectedGame || selectedPrice === null) { if (!selectedGame || selectedPrice === null) {
toast.error("Please select a game and a price."); toast.error("Please select a game and a price.");
return; return;
} }
await createBet(wallets[0],selectedPrice,selectedGame); setIsProcessing(true);
onClose(); try {
await createBet(wallet, selectedPrice, selectedGame);
onClose();
} catch (error) {
console.error("Error creating bet:", error);
toast.error("Failed to create bet. Please try again.");
} finally {
setIsProcessing(false);
}
}; };
if (!isOpen) return null; if (!isOpen) return null;
return ( return (
<div className="fixed inset-0 bg-black/70 flex justify-center items-start pt-10 z-50" onClick={onClose}> <div className="fixed inset-0 bg-black/70 flex justify-center items-start pt-10 z-50" onClick={onClose}>
<div <div className="bg-[rgb(30,30,30)] text-white w-full max-w-lg p-6 rounded-2xl shadow-lg" onClick={(e) => e.stopPropagation()}>
className="bg-[rgb(30,30,30)] text-white w-full max-w-lg p-6 rounded-2xl shadow-lg" {isProcessing ? (
onClick={(e) => e.stopPropagation()} <div className="flex flex-col items-center">
> <svg className="animate-spin h-8 w-8 text-blue-400 mb-4" viewBox="0 0 24 24" fill="none">
<h2 className="text-xl font-bold mb-4">Create Game</h2> <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
</svg>
<h3 className="text-lg font-semibold">Processing...</h3>
<p className="text-gray-400 text-sm mt-2">Creating your bet, please wait...</p>
</div>
) : (
<>
<h2 className="text-xl font-bold mb-4">Create Game</h2>
<GameSelection games={games} selectedGame={selectedGame} onSelect={setSelectedGame} />
<PriceSelection selectedPrice={selectedPrice} onSelect={setSelectedPrice} />
{/* Game Selection */} <button
<GameSelection games={games} selectedGame={selectedGame} onSelect={setSelectedGame} /> className="mt-6 w-full py-2 rounded-xl font-semibold bg-[rgb(248,144,22)] text-black hover:bg-white hover:scale-105"
onClick={handleCreateGame}
{/* Price Selection */} >
<PriceSelection selectedPrice={selectedPrice} onSelect={setSelectedPrice} /> Confirm & Create Bet
</button>
<button </>
className="mt-6 w-full py-2 rounded-xl font-semibold bg-[rgb(248,144,22)] text-black hover:bg-white hover:scale-105" )}
onClick={handleCreateGame}
>
Confirm & Create Bet
</button>
</div> </div>
</div> </div>
); );

View File

@ -4,7 +4,7 @@ import Image from "next/image";
import { games } from "../data/games"; import { games } from "../data/games";
import { FiTrash } from "react-icons/fi"; import { FiTrash } from "react-icons/fi";
import { useSolanaWallets } from "@privy-io/react-auth"; import { useSolanaWallets } from "@privy-io/react-auth";
import { fetchOpenBets, closeBet } from "@/shared/solana_helpers"; // Import close function import { fetchOpenBets, closeBet } from "@/shared/solana_helpers";
import { Bet } from "../types/Bet"; import { Bet } from "../types/Bet";
export default function YourGames() { export default function YourGames() {
@ -12,40 +12,37 @@ export default function YourGames() {
const [openBets, setOpenBets] = useState<Bet[]>([]); const [openBets, setOpenBets] = useState<Bet[]>([]);
const [myBets, setMyBets] = useState<Bet[]>([]); const [myBets, setMyBets] = useState<Bet[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [selectedBet, setSelectedBet] = useState<Bet | null>(null);
const [isProcessing, setIsProcessing] = useState(false);
// Function to fetch open bets // Fetch bets
const updateBets = async () => { const updateBets = async () => {
const bets: Bet[] = await fetchOpenBets(wallets[0]); const bets: Bet[] = await fetchOpenBets(wallets[0]);
setMyBets(bets.filter((bet) => bet.owner === wallets[0].address)); setMyBets(bets.filter((bet) => bet.owner === wallets[0].address));
setOpenBets(bets); setOpenBets(bets);
setLoading(false); setLoading(false);
console.log(`Got ${bets.length} bets`);
}; };
// Auto-refresh every 10 seconds
useEffect(() => { useEffect(() => {
if (!ready) return; if (!ready) return;
updateBets(); updateBets();
const interval = setInterval(updateBets, 10000); // Refresh every 10s const interval = setInterval(updateBets, 10000);
return () => clearInterval(interval);
return () => clearInterval(interval); // Cleanup on unmount
}, [ready]); }, [ready]);
// Function to handle bet closing // Handle bet closing
const handleCloseBet = async (bet: Bet) => { const handleCloseBet = async () => {
const confirmed = window.confirm( if (!selectedBet) return;
`Are you sure you want to close this bet? You will receive your refund back to your wallet.` setIsProcessing(true);
);
if (!confirmed) return;
try { try {
console.log(`Closing bet ${bet.id}`); await closeBet(wallets[0], selectedBet.id);
await closeBet(wallets[0], bet.id); updateBets();
alert("Bet closed successfully!");
updateBets(); // Refresh bets after closing
} catch (error) { } catch (error) {
console.error("Error closing bet:", error); console.error("Error closing bet:", error);
alert("Failed to close the bet. Please try again."); alert("Failed to close the bet. Please try again.");
} finally {
setIsProcessing(false);
setSelectedBet(null);
} }
}; };
@ -60,16 +57,11 @@ export default function YourGames() {
) : ( ) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{myBets.map((bet) => { {myBets.map((bet) => {
console.log(`Finding game for the id ${bet.id}`); const game = games.find((g) => g.id === bet.id);
const game = games.find((g) => g.id === bet.id); // Match game if (!game) return null;
if (!game) return null; // Skip unmatched bets
return ( return (
<div <div key={bet.id} className="relative group bg-[rgb(30,30,30)] rounded-2xl p-2 w-50 overflow-hidden cursor-pointer transition-all duration-300">
key={bet.id}
className="relative group bg-[rgb(30,30,30)] rounded-2xl p-2 w-50 overflow-hidden cursor-pointer transition-all duration-300"
>
{/* Game Thumbnail */} {/* Game Thumbnail */}
<div className="relative w-full h-40 overflow-hidden rounded-xl"> <div className="relative w-full h-40 overflow-hidden rounded-xl">
<Image <Image
@ -88,10 +80,10 @@ export default function YourGames() {
<p className="text-xs text-white font-mono py-1">{bet.wager} SOL</p> <p className="text-xs text-white font-mono py-1">{bet.wager} SOL</p>
</div> </div>
{/* Close Button (Trash Icon) */} {/* Close Button */}
<button <button
className="absolute bottom-3 right-3 bg-red-600 text-white p-2 rounded-full transition duration-300 hover:bg-red-800" className="absolute bottom-3 right-3 bg-red-600 text-white p-2 rounded-full transition duration-300 hover:bg-red-800"
onClick={() => handleCloseBet(bet)} onClick={() => setSelectedBet(bet)}
> >
<FiTrash size={16} /> <FiTrash size={16} />
</button> </button>
@ -100,6 +92,38 @@ export default function YourGames() {
})} })}
</div> </div>
)} )}
{/* Confirmation Modal */}
{selectedBet && !isProcessing && (
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50">
<div className="bg-gray-900 text-white p-6 rounded-lg shadow-lg w-96">
<h3 className="text-lg font-semibold mb-4">Close Bet</h3>
<p className="mb-6">Are you sure you want to close this bet? You will receive your refund back to your wallet.</p>
<div className="flex justify-end space-x-4">
<button className="px-4 py-2 bg-gray-600 rounded hover:bg-gray-700" onClick={() => setSelectedBet(null)}>
Cancel
</button>
<button className="px-4 py-2 bg-red-600 rounded hover:bg-red-700" onClick={handleCloseBet}>
Close Bet
</button>
</div>
</div>
</div>
)}
{/* Processing Modal */}
{isProcessing && (
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50">
<div className="bg-gray-900 text-white p-6 rounded-lg shadow-lg w-96 flex flex-col items-center">
<svg className="animate-spin h-8 w-8 text-blue-400 mb-4" viewBox="0 0 24 24" fill="none">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
</svg>
<h3 className="text-lg font-semibold">Processing...</h3>
<p className="text-gray-400 text-sm mt-2">Please wait while we close your bet.</p>
</div>
</div>
)}
</section> </section>
); );
} }