little bugs

This commit is contained in:
Sewmina 2025-04-18 08:39:18 +05:30
parent 49ed569cb4
commit a230c97078
6 changed files with 189 additions and 96 deletions

View File

@ -0,0 +1,67 @@
import { useEffect, useState } from "react";
interface FirstVisitModalProps {
isOpen: boolean;
onClose: () => void;
}
export function FirstVisitModal({ isOpen, onClose }: FirstVisitModalProps) {
const [shouldRender, setShouldRender] = useState(isOpen);
const [animationClass, setAnimationClass] = useState("");
useEffect(() => {
if (isOpen) {
setShouldRender(true);
setAnimationClass("modal-enter");
} else {
setAnimationClass("modal-exit");
// Wait for animation to finish before unmounting
const timer = setTimeout(() => setShouldRender(false), 200); // match animation duration
return () => clearTimeout(timer);
}
}, [isOpen]);
if (!shouldRender) return null;
return (
<div
className="fixed inset-0 bg-black/70 flex justify-center items-start pt-10 z-50"
onClick={onClose}
>
<div
className={`bg-[rgb(30,30,30)] text-white w-full max-w-lg p-6 rounded-2xl shadow-lg transform transition-transform duration-300 ${animationClass}`}
onClick={(e) => e.stopPropagation()}
>
<h2 className="text-xl font-bold mb-4">Welcome to DuelFi.io!</h2>
<div className="space-y-4">
<p className="text-gray-300">
Challenge your friends (or strangers) in a head-to-head skill games and earn from your victories!
<br />
<br />
Create or join duels, set an entry fee, and the winner takes all.
</p>
{[
{ step: "New here? Check out our", desc: "Whitepaper to understand how everything works." },
{ step: "Got questions or just wanna vibe with the squad?", desc: "Join our Telegram." },
].map(({ step, desc }, index) => (
<div key={index}>
<h3 className="text-[rgb(248,144,22)] font-bold text-lg">
{step}
</h3>
<p className="text-s text-gray-400">{desc}</p>
</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 hover:scale-105"
onClick={onClose}
>
Okay
</button>
</div>
</div>
);
}

View File

@ -51,7 +51,7 @@ export default function GameHistoryModal({
); );
const gameData = res.data || []; const gameData = res.data || [];
setGamesHistory(gameData); setGamesHistory(gameData);
console.log(`history data ${gameData}`);
const opponentIds: string[] = gameData.map((game: GameHistory) => const opponentIds: string[] = gameData.map((game: GameHistory) =>
game.master_id === userId ? game.client_id : game.master_id game.master_id === userId ? game.client_id : game.master_id
); );

View File

@ -141,20 +141,24 @@ export default function Header() {
{/* Mobile Navigation Links */} {/* Mobile Navigation Links */}
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
<Link <a
href="/whitepaper" href={URL_WHITEPAPER}
target="_blank"
rel="noopener noreferrer"
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]" className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]"
onClick={() => setIsDrawerOpen(false)} // Close drawer on link click onClick={() => setIsDrawerOpen(false)}
> >
Whitepaper Whitepaper
</Link> </a>
<Link <button
href="/support" onClick={() => {
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]" setShowSupportModal(true);
onClick={() => setIsDrawerOpen(false)} // Close drawer on link click setIsDrawerOpen(false);
}}
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)] text-left"
> >
Support Support
</Link> </button>
{/* Socials */} {/* Socials */}
<a <a

View File

@ -5,6 +5,7 @@ import Image from "next/image";
import OpenGames from "./OpenGames"; import OpenGames from "./OpenGames";
import GameModal from "./GameModal"; import GameModal from "./GameModal";
import { HowItWorksModal } from "./HowItWorksModal"; import { HowItWorksModal } from "./HowItWorksModal";
import { FirstVisitModal } from "./FirstVisitModal";
import YourGames from "./YourGames"; import YourGames from "./YourGames";
import { Bet } from "@/types/Bet"; import { Bet } from "@/types/Bet";
import { fetchOpenBets, createBet, getVaultByAddress, fetchOpenBetsDev } from "@/shared/solana_helpers"; import { fetchOpenBets, createBet, getVaultByAddress, fetchOpenBetsDev } from "@/shared/solana_helpers";
@ -16,6 +17,7 @@ import { clusterApiUrl } from "@solana/web3.js";
export default function HeroSection() { export default function HeroSection() {
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [isGameModalOpen, setIsGameModalOpen] = useState(false); const [isGameModalOpen, setIsGameModalOpen] = useState(false);
const [isFirstVisitModalOpen, setIsFirstVisitModalOpen] = useState(false);
const [bets, setBets] = useState<Bet[]>([]); const [bets, setBets] = useState<Bet[]>([]);
const [solWallet, setSolWallet] = useState<ConnectedSolanaWallet>(); const [solWallet, setSolWallet] = useState<ConnectedSolanaWallet>();
const [myActiveBet, setMyActiveBet] = useState<Bet>(); const [myActiveBet, setMyActiveBet] = useState<Bet>();
@ -28,6 +30,17 @@ export default function HeroSection() {
const { user } = usePrivy(); const { user } = usePrivy();
const iframeRef = useRef<HTMLIFrameElement>(null); const iframeRef = useRef<HTMLIFrameElement>(null);
// Check if this is the user's first visit
useEffect(() => {
if (typeof window !== 'undefined') {
const hasVisited = localStorage.getItem('duelfi_has_visited');
if (!hasVisited) {
setIsFirstVisitModalOpen(true);
localStorage.setItem('duelfi_has_visited', 'true');
}
}
}, []);
const game_close_signal = (status: number) => { const game_close_signal = (status: number) => {
setRematch(status == 1); setRematch(status == 1);
setMyActiveBet(undefined); setMyActiveBet(undefined);
@ -315,6 +328,7 @@ export default function HeroSection() {
<GameModal isOpen={isGameModalOpen} onClose={() => setIsGameModalOpen(false)} /> <GameModal isOpen={isGameModalOpen} onClose={() => setIsGameModalOpen(false)} />
<HowItWorksModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} /> <HowItWorksModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />
<FirstVisitModal isOpen={isFirstVisitModalOpen} onClose={() => setIsFirstVisitModalOpen(false)} />
</> </>
); );
} }

View File

@ -6,9 +6,9 @@ interface PriceSelectionProps {
} }
export function PriceSelection({ selectedPrice, onSelect }: PriceSelectionProps) { export function PriceSelection({ selectedPrice, onSelect }: PriceSelectionProps) {
const presets = [0.05, 0.1, 0.2, 0.5, 1.0]; const presets = [0.01, 0.05, 0.1, 0.2, 0.5, 1.0];
const [inputValue, setInputValue] = useState<string>(""); const [inputValue, setInputValue] = useState<string>("");
const MIN_AMOUNT = 0.05; const MIN_AMOUNT = 0.01;
useEffect(() => { useEffect(() => {
if (selectedPrice !== null) { if (selectedPrice !== null) {

View File

@ -14,6 +14,7 @@ import { WAGER_PRIZE_MULT } from "@/shared/constants";
import { EXPLORER_ADDRESS_TEMPLATE } from "@/data/shared"; import { EXPLORER_ADDRESS_TEMPLATE } from "@/data/shared";
interface GameHistory { interface GameHistory {
ended_time: string;
address: string; address: string;
master_score: string; master_score: string;
client_score: string; client_score: string;
@ -249,8 +250,10 @@ export default function PrivyButton() {
const fetchGames = async () => { const fetchGames = async () => {
setLoading(true); setLoading(true);
try { try {
const url= `${API_URL}get_game_history.php?uid=${user.id}`;
console.log(`history data ${url}`);
const res = await axios.get( const res = await axios.get(
`${API_URL}get_game_history.php?uid=${user.id}` url
); );
const gameData = res.data || []; const gameData = res.data || [];
setGamesHistory(gameData); setGamesHistory(gameData);
@ -522,7 +525,9 @@ export default function PrivyButton() {
<p className="text-gray-500">No games played yet.</p> <p className="text-gray-500">No games played yet.</p>
) : ( ) : (
<div className="space-y-4 max-h-96 overflow-y-auto"> <div className="space-y-4 max-h-96 overflow-y-auto">
{gamesHistory.map((game, idx) => { {gamesHistory
.sort((a, b) => new Date(b.ended_time).getTime() - new Date(a.ended_time).getTime())
.map((game, idx) => {
const isUserMaster = game.master_id === user.id; const isUserMaster = game.master_id === user.id;
const userScore = isUserMaster const userScore = isUserMaster
? game.master_score ? game.master_score
@ -563,6 +568,9 @@ export default function PrivyButton() {
<p className="text-xs text-gray-400"> <p className="text-xs text-gray-400">
Score: {userScore} - {opponentScore} Score: {userScore} - {opponentScore}
</p> </p>
<p className="text-xs text-gray-400">
{new Date(game.ended_time).toLocaleString()}
</p>
<p <p
className={`text-sm font-semibold ${ className={`text-sm font-semibold ${
didUserWin ? "text-green-500" : "text-gray-500" didUserWin ? "text-green-500" : "text-gray-500"