v1 rc3 fixes
This commit is contained in:
parent
fb0fe3dd2d
commit
71cf1f90d5
|
|
@ -76,17 +76,29 @@ body {
|
|||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes slide-down {
|
||||
0% {
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-enter {
|
||||
animation: slide-down 0.2s ease-out;
|
||||
}
|
||||
|
|
@ -95,7 +107,13 @@ body {
|
|||
animation: slide-up 0.2s ease-in;
|
||||
}
|
||||
|
||||
.modal-slide-down {
|
||||
animation: slideDown 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
.modal-slide-up {
|
||||
animation: slideUp 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
/* Hovering card */
|
||||
/* Ensure the card container is positioned relatively for absolute positioning */
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import Link from "next/link";
|
|||
export default function Footer() {
|
||||
return (
|
||||
<footer className="w-full bg-[rgb(17,17,17)] text-white py-8">
|
||||
<div className="container max-w-[1200px] mx-auto px-6 flex flex-col">
|
||||
<div className="container max-w-[1200px] mx-auto px-6 flex flex-col items-center">
|
||||
{/* Big Logo */}
|
||||
<Image
|
||||
src="/duelfiassets/logo.png"
|
||||
alt="DuelFi Logo"
|
||||
width={400}
|
||||
height={200}
|
||||
width={200}
|
||||
height={80}
|
||||
className="mb-6"
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,15 @@ import { URL_TELEGRAM, URL_TWITTER, URL_WHITEPAPER } from "@/shared/constants";
|
|||
|
||||
export default function Header() {
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [showSupportModal, setShowSupportModal] = useState(false);
|
||||
|
||||
// Close modal when clicking outside
|
||||
const handleModalClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
setShowSupportModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle the nav drawer
|
||||
const toggleDrawer = () => {
|
||||
setIsDrawerOpen(!isDrawerOpen);
|
||||
|
|
@ -16,27 +25,104 @@ export default function Header() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<header className="w-full bg-[rgb(22,22,22)] shadow-md">
|
||||
<div className="container mx-auto flex items-center justify-between p-12 max-w-screen-xl w-full">
|
||||
<header className="w-full bg-[rgb(22,22,22)] shadow-md h-26">
|
||||
<div className="container mx-auto flex items-center justify-between px-12 h-full max-w-screen-xl w-full">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center w-40 mr-20">
|
||||
<Image src="/duelfiassets/logo.png" alt="Logo" width={100} height={40} />
|
||||
<div className="flex items-center">
|
||||
<Image
|
||||
src="/duelfiassets/logo.png"
|
||||
alt="Logo"
|
||||
width={100}
|
||||
height={40}
|
||||
className="w-[100px] h-[40px]"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Navigation Links (hidden on mobile, visible on md+) */}
|
||||
<nav className="hidden md:flex gap-6 ml-6">
|
||||
<Link
|
||||
<a
|
||||
href={URL_WHITEPAPER}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]"
|
||||
>
|
||||
Whitepaper
|
||||
</Link>
|
||||
<Link
|
||||
href="/support"
|
||||
</a>
|
||||
<button
|
||||
onClick={() => setShowSupportModal(true)}
|
||||
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]"
|
||||
>
|
||||
Support
|
||||
</Link>
|
||||
</button>
|
||||
|
||||
{showSupportModal && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/70 flex justify-center items-center z-50"
|
||||
onClick={handleModalClick}
|
||||
>
|
||||
<div className="bg-[rgb(30,30,30)] text-white w-full max-w-lg p-6 rounded-2xl shadow-lg space-y-6 transform transition-all duration-300 ease-out translate-y-0 opacity-100">
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-2xl font-bold">Support</h2>
|
||||
<button
|
||||
onClick={() => setShowSupportModal(false)}
|
||||
className="text-gray-400 hover:text-white"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">Looking for support?</h3>
|
||||
<p className="text-gray-300">
|
||||
Join our{" "}
|
||||
<a
|
||||
href="https://t.me/duelfidotio"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[rgb(248,144,22)] hover:underline"
|
||||
>
|
||||
Telegram group
|
||||
</a>{" "}
|
||||
— our mods and community are always happy to help!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">Interested in a collaboration or partnership?</h3>
|
||||
<p className="text-gray-300 mb-4">Fill out the form below and we'll get back to you as soon as possible.</p>
|
||||
|
||||
<form className="space-y-4">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
className="w-full bg-[rgb(10,10,10)] text-white p-2 rounded-md border border-gray-700"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Subject"
|
||||
className="w-full bg-[rgb(10,10,10)] text-white p-2 rounded-md border border-gray-700"
|
||||
/>
|
||||
<textarea
|
||||
placeholder="Message"
|
||||
rows={4}
|
||||
className="w-full bg-[rgb(10,10,10)] text-white p-2 rounded-md border border-gray-700"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-[rgb(248,144,22)] hover:bg-orange-400 text-black font-semibold px-4 py-2 rounded-md transition duration-500 hover:scale-105"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
{/* Spacer (for centering) */}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export default function YourGames({ bets }: GameModalProps) {
|
|||
className="relative group bg-[rgb(30,30,30)] rounded-2xl p-2 w-50 overflow-hidden cursor-pointer transition-all duration-300"
|
||||
onClick={() => setSelectedBet(bet)} // Open modal
|
||||
>
|
||||
<div className="relative w-full h-40 overflow-hidden rounded-xl">
|
||||
<div className="relative w-full h-60 overflow-hidden rounded-xl">
|
||||
<Image
|
||||
src={game.thumbnail}
|
||||
alt={game.name}
|
||||
|
|
@ -219,7 +219,7 @@ export default function YourGames({ bets }: GameModalProps) {
|
|||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400">Prize:</span>
|
||||
<span className="font-bold">{(selectedBet.wager * 2).toFixed(2)} SOL</span>
|
||||
<span className="font-bold">{(selectedBet.wager * 2 * WAGER_PRIZE_MULT).toFixed(2)} SOL</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,16 +7,39 @@ import { toast } from "sonner";
|
|||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { CLUSTER_URL } from "@/data/shared";
|
||||
import { useFundWallet } from "@privy-io/react-auth/solana";
|
||||
import GameHistoryModal from "./GameHistory";
|
||||
import axios from "axios";
|
||||
import { Game } from "@/types/Game";
|
||||
import { games } from "@/data/games";
|
||||
import { WAGER_PRIZE_MULT } from "@/shared/constants";
|
||||
import { EXPLORER_ADDRESS_TEMPLATE } from "@/data/shared";
|
||||
|
||||
interface GameHistory {
|
||||
address: string;
|
||||
master_score: string;
|
||||
client_score: string;
|
||||
winner: string;
|
||||
wager: string;
|
||||
master_id: string;
|
||||
client_id: string;
|
||||
game: string;
|
||||
}
|
||||
|
||||
interface Opponent {
|
||||
username: string;
|
||||
x_profile_url: string;
|
||||
}
|
||||
|
||||
export default function PrivyButton() {
|
||||
const { login, logout, user, linkTwitter, unlinkTwitter } = usePrivy();
|
||||
const { fundWallet } = useFundWallet();
|
||||
const { wallets, ready } = useSolanaWallets();
|
||||
const { wallets, ready,exportWallet } = useSolanaWallets();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
const [solWallet, setSolWallet] = useState("");
|
||||
const [solBalance, setSolBalance] = useState("--");
|
||||
const [gamesHistory, setGamesHistory] = useState<GameHistory[]>([]);
|
||||
const [opponentInfo, setOpponentInfo] = useState<{ [key: string]: Opponent }>({});
|
||||
const [gameImages, setGameImages] = useState<{ [key: string]: string }>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [username, setUsername] = useState("Tester");
|
||||
const [bio, setBio] = useState("");
|
||||
|
|
@ -219,67 +242,101 @@ export default function PrivyButton() {
|
|||
const twitterProfilePic: string = user?.twitter?.profilePictureUrl ?? "";
|
||||
const customProfileUrl = `https://vps.playpoolstudios.com/duelfi/profile_pics/${user?.id}.jpg`;
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen && user) {
|
||||
const fetchGames = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`https://vps.playpoolstudios.com/duelfi/api/get_game_history.php?uid=${user.id}`
|
||||
);
|
||||
const gameData = res.data || [];
|
||||
setGamesHistory(gameData);
|
||||
|
||||
const opponentIds: string[] = gameData.map((game: GameHistory) =>
|
||||
game.master_id === user.id ? game.client_id : game.master_id
|
||||
);
|
||||
const uniqueOpponentIds: string[] = Array.from(new Set(opponentIds));
|
||||
|
||||
const fetchedOpponentInfo: { [key: string]: Opponent } = {};
|
||||
|
||||
await Promise.all(
|
||||
uniqueOpponentIds.map(async (uid) => {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://vps.playpoolstudios.com/duelfi/api/get_user_by_id.php?id=${uid}`
|
||||
);
|
||||
const { username, x_profile_url } = response.data;
|
||||
fetchedOpponentInfo[uid] = {
|
||||
username,
|
||||
x_profile_url,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch opponent info for", uid, err);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setOpponentInfo(fetchedOpponentInfo);
|
||||
|
||||
const gameDataWithImages: { [key: string]: string } = {};
|
||||
await Promise.all(
|
||||
gameData.map(async (gameHistory: GameHistory) => {
|
||||
try {
|
||||
const gameImage = games.find((game: Game) => game.id == gameHistory.game);
|
||||
gameDataWithImages[gameHistory.game] = gameImage?.thumbnail ?? "";
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch game image for", gameHistory.game, err);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setGameImages(gameDataWithImages);
|
||||
} catch (err) {
|
||||
console.error("Error fetching game history", err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchGames();
|
||||
}
|
||||
}, [isModalOpen, user]);
|
||||
|
||||
const handleViewTxClick = (address: string) => {
|
||||
window.open(`${EXPLORER_ADDRESS_TEMPLATE.replace("{address}",address)}`, "_blank");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<GameHistoryModal
|
||||
userId={user?.id}
|
||||
isOpen={showHistory}
|
||||
onClose={() => setShowHistory(false)
|
||||
}
|
||||
/>
|
||||
{user ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsModalOpen(true);
|
||||
fetchSolBalance();
|
||||
fetchUserData();
|
||||
}}
|
||||
className="bg-[rgb(248,144,22)] hover:bg-[rgb(248,200,100)] text-black px-6 py-3 rounded-md transition duration-300 hover:scale-105"
|
||||
>
|
||||
<span className="font-bold">Connected</span>
|
||||
<p className="text-xs font-mono text-gray-700">
|
||||
{solWallet?.slice(0, 6)}...{solWallet?.slice(-4)}
|
||||
</p>
|
||||
</button>
|
||||
|
||||
<p className="text-s font-mono text-white">
|
||||
<Image
|
||||
src="/duelfiassets/solana logo.png"
|
||||
alt="SOL"
|
||||
width={25}
|
||||
height={25}
|
||||
className="inline mx-2"
|
||||
/>
|
||||
{solBalance}
|
||||
</p>
|
||||
|
||||
{/* History button */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setShowHistory(true)}
|
||||
title="View Game History"
|
||||
className="hover:text-orange-400 transition"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-6 h-6 text-white"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsModalOpen(true);
|
||||
fetchSolBalance();
|
||||
fetchUserData();
|
||||
}}
|
||||
className="bg-[rgb(248,144,22)] hover:bg-[rgb(248,200,100)] text-black px-6 py-3 rounded-md transition duration-300 hover:scale-105"
|
||||
>
|
||||
<span className="font-bold">Connected</span>
|
||||
<p className="text-xs font-mono text-gray-700">
|
||||
{solWallet?.slice(0, 6)}...{solWallet?.slice(-4)}
|
||||
</p>
|
||||
</button>
|
||||
|
||||
<p className="text-s font-mono text-white">
|
||||
<Image
|
||||
src="/duelfiassets/solana logo.png"
|
||||
alt="SOL"
|
||||
width={25}
|
||||
height={25}
|
||||
className="inline mx-2"
|
||||
/>
|
||||
{solBalance}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
|
|
@ -291,12 +348,22 @@ export default function PrivyButton() {
|
|||
)}
|
||||
|
||||
{isModalOpen && user && (
|
||||
<div className="fixed inset-0 bg-black/70 flex justify-center items-start pt-10 z-50">
|
||||
<div className="fixed inset-0 bg-black/70 flex justify-center items-start pt-10 z-50 overflow-y-auto">
|
||||
<div
|
||||
ref={modalRef}
|
||||
className="bg-[rgb(30,30,30)] text-white w-full max-w-2xl p-6 rounded-2xl shadow-lg transform transition-transform duration-300 animate-slide-down space-y-6"
|
||||
className="bg-[rgb(30,30,30)] text-white w-full max-w-2xl p-6 rounded-2xl shadow-lg transform transition-transform duration-300 animate-slide-down space-y-6 mb-10"
|
||||
>
|
||||
<h2 className="text-2xl font-bold mb-2">Your Profile</h2>
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-2xl font-bold mb-2">Your Profile</h2>
|
||||
<button
|
||||
onClick={() => setIsModalOpen(false)}
|
||||
className="text-gray-400 hover:text-white transition duration-300 hover:scale-105"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Avatar + Link Twitter Row */}
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
|
|
@ -306,7 +373,10 @@ export default function PrivyButton() {
|
|||
alt=""
|
||||
className="w-16 h-16 rounded-full border border-gray-500 object-cover"
|
||||
/>
|
||||
{(!user.twitter) ? (<label className="bg-gray-800 hover:bg-gray-700 text-white text-sm px-4 py-2 rounded-md cursor-pointer">
|
||||
{(!user.twitter) ? (<label className="bg-gray-800 hover:bg-gray-700 text-white text-sm px-4 py-2 rounded-md cursor-pointer flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||
</svg>
|
||||
Change
|
||||
<input
|
||||
type="file"
|
||||
|
|
@ -324,21 +394,28 @@ export default function PrivyButton() {
|
|||
</span>
|
||||
<button
|
||||
onClick={() => unlinkTwitter(user.twitter?.subject ?? "")}
|
||||
className="bg-black text-white px-4 py-2 rounded-md text-sm hover:bg-gray-900"
|
||||
className="bg-black text-white px-4 py-2 rounded-md text-sm hover:bg-gray-900 flex items-center gap-2"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
Unlink
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={linkTwitter}
|
||||
className="bg-black text-white px-4 py-2 rounded-md text-sm hover:bg-gray-900"
|
||||
className="bg-black text-white px-4 py-2 rounded-md text-sm hover:bg-gray-900 flex items-center gap-2"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
Link X (Twitter)
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Username */}
|
||||
<div>
|
||||
<label className="block text-xs text-gray-400 mb-1">Username</label>
|
||||
|
|
@ -360,11 +437,15 @@ export default function PrivyButton() {
|
|||
rows={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Save Button */}
|
||||
<button
|
||||
className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-md font-semibold"
|
||||
className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-md font-semibold flex items-center gap-2"
|
||||
onClick={saveProfileChanges}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Save Changes
|
||||
</button>
|
||||
|
||||
|
|
@ -375,35 +456,163 @@ export default function PrivyButton() {
|
|||
<div>
|
||||
<p className="text-gray-400 text-xs font-mono mb-1">Connected Wallet</p>
|
||||
<div className="flex items-center justify-between bg-gray-800 p-2 rounded-md">
|
||||
<p className="font-mono text-sm truncate">{solWallet}</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="font-mono text-sm truncate">{solWallet}</p>
|
||||
{wallets.some(wallet => wallet.type === "solana" && wallet.address === solWallet && wallet.connectorType === "embedded") && (
|
||||
<span title="Embedded Wallet" className="text-gray-400 hover:text-white transition">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M4 4a2 2 0 00-2 2v4a2 2 0 002 2V6h10a2 2 0 00-2-2H4zm2 6a2 2 0 012-2h8a2 2 0 012 2v4a2 2 0 01-2 2H8a2 2 0 01-2-2v-4zm6 4a2 2 0 100-4 2 2 0 000 4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={copyToClipboard}
|
||||
className="text-gray-300 hover:text-white transition p-1"
|
||||
>
|
||||
📋
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-400 text-xs font-mono mt-4">SOL Balance</p>
|
||||
<p className="text-sm mb-3">{solBalance} SOL</p>
|
||||
|
||||
<button
|
||||
onClick={() => { fundWallet(solWallet, {}) }}
|
||||
className="w-full bg-purple-600 hover:bg-purple-700 text-white font-semibold py-2 rounded-xl transition"
|
||||
>
|
||||
Fund Wallet
|
||||
</button>
|
||||
<div className="flex gap-2">
|
||||
{wallets.some(wallet => wallet.type === "solana" && wallet.address === solWallet && wallet.connectorType === "embedded") && (
|
||||
<button
|
||||
onClick={() => {
|
||||
exportWallet({ address: solWallet });
|
||||
}}
|
||||
className="flex-1 bg-[rgb(248,144,22)] hover:bg-[rgb(248,200,100)] text-black font-semibold py-2 rounded-xl transition hover:scale-105 flex items-center justify-center gap-2"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
Export Wallet
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => { fundWallet(solWallet, {}) }}
|
||||
className={`${wallets.some(wallet => wallet.type === "solana" && wallet.address === solWallet && wallet.connectorType === "embedded") ? 'flex-1' : 'w-full'} bg-purple-600 hover:bg-purple-700 text-white font-semibold py-2 rounded-xl transition hover:scale-105 flex items-center justify-center gap-2`}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
Fund Wallet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="mt-6 w-full bg-[rgb(248,144,22)] text-black font-semibold py-2 rounded-xl transition hover:bg-white"
|
||||
onClick={() => {
|
||||
logout();
|
||||
setIsModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Sign Out
|
||||
</button>
|
||||
{/* Divider */}
|
||||
<hr className="border-gray-600" />
|
||||
|
||||
{/* Game History Section */}
|
||||
<div>
|
||||
<h3 className="text-xl font-bold mb-4">Game History</h3>
|
||||
{loading ? (
|
||||
<p className="text-gray-500">Loading...</p>
|
||||
) : gamesHistory.length === 0 ? (
|
||||
<p className="text-gray-500">No games played yet.</p>
|
||||
) : (
|
||||
<div className="space-y-4 max-h-96 overflow-y-auto">
|
||||
{gamesHistory.map((game, idx) => {
|
||||
const isUserMaster = game.master_id === user.id;
|
||||
const userScore = isUserMaster
|
||||
? game.master_score
|
||||
: game.client_score;
|
||||
const opponentScore = isUserMaster
|
||||
? game.client_score
|
||||
: game.master_score;
|
||||
const opponentId = isUserMaster
|
||||
? game.client_id
|
||||
: game.master_id;
|
||||
const didUserWin =
|
||||
(isUserMaster && game.winner === "master") ||
|
||||
(!isUserMaster && game.winner === "client");
|
||||
|
||||
const opponent = opponentInfo[opponentId];
|
||||
const profileUrl =
|
||||
opponent?.x_profile_url ||
|
||||
(opponent?.username
|
||||
? `https://vps.playpoolstudios.com/duelfi/profile_picture/${opponent.username}.jpg`
|
||||
: "/duelfiassets/default-avatar.png");
|
||||
|
||||
const gameImageUrl = gameImages[game.game] || "/duelfiassets/default-game-thumbnail.png";
|
||||
|
||||
const wagerAmount = parseFloat(game.wager);
|
||||
const outcomeText = didUserWin
|
||||
? `+${(wagerAmount / 1e8) * 2 * WAGER_PRIZE_MULT} SOL`
|
||||
: `-${(wagerAmount / 1e8)} SOL`;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={idx}
|
||||
className="relative border border-[rgb(30,30,30)] rounded-xl p-3 flex gap-3 items-center group"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-white">
|
||||
{opponent?.username || "Unknown Opponent"}
|
||||
</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
Score: {userScore} - {opponentScore}
|
||||
</p>
|
||||
<p
|
||||
className={`text-sm font-semibold ${
|
||||
didUserWin ? "text-green-500" : "text-gray-500"
|
||||
}`}
|
||||
>
|
||||
{didUserWin ? "You won" : "You lost"}
|
||||
</p>
|
||||
<p className={`text-xs ${didUserWin ? "text-green-500" : "text-red-500"}`}>
|
||||
{outcomeText}
|
||||
</p>
|
||||
</div>
|
||||
<Image
|
||||
src={profileUrl}
|
||||
alt="Profile"
|
||||
width={40}
|
||||
height={40}
|
||||
className="w-10 h-10 rounded-full border border-gray-700 object-cover"
|
||||
/>
|
||||
<Image
|
||||
src={gameImageUrl}
|
||||
alt="Game Thumbnail"
|
||||
width={64}
|
||||
height={64}
|
||||
className="w-16 h-16 rounded-md object-cover ml-4"
|
||||
/>
|
||||
|
||||
<div className="absolute top-0 right-0 h-full w-28 bg-blue-500 text-white flex items-center justify-center opacity-0 group-hover:opacity-100 group-hover:translate-x-0 transition-all">
|
||||
<button
|
||||
onClick={() => handleViewTxClick(game.address)}
|
||||
className="px-4 py-2 text-sm font-semibold"
|
||||
>
|
||||
View TX
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mt-6">
|
||||
<button
|
||||
className="w-[20%] bg-[rgb(248,22,22)] text-white font-semibold px-4 py-1.5 rounded-lg text-sm transition hover:bg-white hover:text-[rgb(248,22,22)] flex items-center justify-center gap-2"
|
||||
onClick={() => {
|
||||
logout();
|
||||
setIsModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -429,7 +638,6 @@ export default function PrivyButton() {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export default function YourGames({bets}:GameModalProps) {
|
|||
const url = EXPLORER_TX_TEMPLATE.replace("{address}", tx);
|
||||
connection.confirmTransaction(tx, CONFIRMATION_THRESHOLD).finally(()=>{
|
||||
toast.dismiss();
|
||||
|
||||
toast.success(`Closed the bet successfully!`, {
|
||||
action: {
|
||||
label: "View TX",
|
||||
|
|
@ -65,6 +66,8 @@ export default function YourGames({bets}:GameModalProps) {
|
|||
})
|
||||
|
||||
} catch (error) {
|
||||
toast.dismiss();
|
||||
|
||||
console.error("Error closing bet:", error);
|
||||
toast.message("Failed to close the bet. Please try again.");
|
||||
} finally {
|
||||
|
|
@ -90,7 +93,7 @@ export default function YourGames({bets}:GameModalProps) {
|
|||
return (
|
||||
<div key={bet.address} className="relative group bg-[rgb(30,30,30)] rounded-2xl p-2 w-50 overflow-hidden cursor-pointer transition-all duration-300">
|
||||
{/* Game Thumbnail */}
|
||||
<div className="relative w-full h-40 overflow-hidden rounded-xl">
|
||||
<div className="relative w-full h-60 overflow-hidden rounded-xl">
|
||||
<Image
|
||||
src={game.thumbnail}
|
||||
alt={game.name}
|
||||
|
|
@ -122,15 +125,15 @@ export default function YourGames({bets}:GameModalProps) {
|
|||
|
||||
{/* 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">
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-[rgb(30,30,30)] 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)}>
|
||||
<button className="px-4 py-2 bg-gray-600 rounded hover:bg-gray-700 hover:scale-105 transition-all duration-300" onClick={() => setSelectedBet(null)}>
|
||||
Cancel
|
||||
</button>
|
||||
<button className="px-4 py-2 bg-red-600 rounded hover:bg-red-700" onClick={handleCloseBet}>
|
||||
<button className="px-4 py-2 bg-red-600 rounded hover:bg-red-700 hover:scale-105 transition-all duration-300" onClick={handleCloseBet}>
|
||||
Close Bet
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user