This commit is contained in:
Sewmina 2025-04-15 11:04:49 +05:30
parent c5b8d6ceea
commit 72c9d3a173
8 changed files with 70 additions and 27 deletions

View File

@ -3,7 +3,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
images: {
domains: ["pbs.twimg.com","vps.playpoolstudios.com"], // ✅ add Twitter's image domain
domains: ["pbs.twimg.com","vps.playpoolstudios.com", "api.duelfi.io"], // ✅ add Twitter's image domain
},
};

View File

@ -141,7 +141,7 @@ export default function GameHistoryModal({
const profileUrl =
opponent?.x_profile_url ||
(opponent?.username
? `https://vps.playpoolstudios.com/duelfi/profile_picture/${opponent.username}.jpg`
? `${API_URL}profile_picture/${opponent.username}.jpg`
: "/duelfiassets/default-avatar.png");
const gameImageUrl = gameImages[game.game] || "/duelfiassets/default-game-thumbnail.png";

View File

@ -158,7 +158,7 @@ export default function Header() {
{/* Socials */}
<a
href="https://t.me/yourchannel"
href={URL_TELEGRAM}
target="_blank"
rel="noopener noreferrer"
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]"
@ -167,7 +167,7 @@ export default function Header() {
<FaTelegram size={24} />
</a>
<a
href="https://twitter.com/yourhandle"
href={URL_TWITTER}
target="_blank"
rel="noopener noreferrer"
className="text-white transition-colors duration-[500ms] hover:text-[rgb(248,144,22)]"

View File

@ -278,7 +278,7 @@ export default function HeroSection() {
<h1 className="text-4xl font-bold text-white">
Instant <span className="text-[rgb(248,144,22)]">Duels</span>, Instant{" "}
<span className="text-[rgb(248,144,22)]">Wins</span>
<span className="text-[rgb(248,144,22)]">Wins</span>.
</h1>
<button

View File

@ -6,7 +6,7 @@ import { joinBet } from "@/shared/solana_helpers";
import { Bet } from "../types/Bet";
import { fetchUserById } from "@/shared/data_fetcher";
import { toast } from "sonner";
import { connection, EXPLORER_TX_TEMPLATE } from "@/data/shared";
import { connection, EXPLORER_TX_TEMPLATE, API_BASE_URL } from "@/data/shared";
import { CONFIRMATION_THRESHOLD, WAGER_PRIZE_MULT } from "@/shared/constants";
interface GameModalProps {
@ -20,6 +20,8 @@ export default function YourGames({ bets }: GameModalProps) {
const { user } = usePrivy();
const [selectedBet, setSelectedBet] = useState<Bet | null>(null); // Track selected bet
const [isProcessing, setIsProcessing] = useState(false); // Track processing state
const [failedImages, setFailedImages] = useState<Set<string>>(new Set());
const defaultPFP = '/duelfiassets/PFP (1).png';
const handleJoinGame = async () => {
if (!selectedBet) return;
@ -78,6 +80,7 @@ export default function YourGames({ bets }: GameModalProps) {
const enrichedBets = await Promise.all(
filteredBets.map(async (bet) => {
const ownerProfile = await fetchUserById(bet.owner_id);
return {
...bet,
ownerProfile,
@ -106,12 +109,18 @@ export default function YourGames({ bets }: GameModalProps) {
<p className="text-gray-400">No open games available</p>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{myBets.map((bet) => {
{myBets.map((bet) => {
console.log(`Bet ${bet}`);
const game = games.find((g) => g.id === bet.id);
let ownerPFP = bet.ownerProfile?.x_profile_url || `${API_BASE_URL}profile_pics/${bet.ownerProfile?.id}.jpg`;
if (!game) return null;
// Check if this image has already failed to load
if (failedImages.has(ownerPFP)) {
ownerPFP = defaultPFP;
}
return (
<div
key={bet.address}
@ -147,11 +156,16 @@ export default function YourGames({ bets }: GameModalProps) {
{bet.ownerProfile && (
<div className="flex items-center gap-2 mt-4">
<Image
src={bet.ownerProfile.x_profile_url || `https://vps.playpoolstudios.com/duelfi/profile_pics/${bet.ownerProfile.id}.jpg`}
src={ownerPFP}
alt={bet.ownerProfile.username}
width={24}
height={24}
className="rounded-full"
onError={(e) => {
// @ts-ignore
e.target.src = defaultPFP;
setFailedImages(prev => new Set(prev).add(ownerPFP));
}}
/>
<p className="text-white text-sm font-mono">{bet.ownerProfile.username}</p>
</div>
@ -208,13 +222,26 @@ export default function YourGames({ bets }: GameModalProps) {
<>
<p className="text-gray-400 mb-2">Offered by:</p>
<div className="flex items-center">
<Image
src={selectedBet.ownerProfile.x_profile_url || `https://vps.playpoolstudios.com/duelfi/profile_pics/${selectedBet.ownerProfile.id}.jpg`}
alt={selectedBet.ownerProfile.username}
width={32}
height={32}
className="w-8 h-8 rounded-full mr-2"
/>
{(() => {
let modalOwnerPFP = selectedBet.ownerProfile.x_profile_url || `${API_BASE_URL}profile_pics/${selectedBet.ownerProfile.id}.jpg`;
if (failedImages.has(modalOwnerPFP)) {
modalOwnerPFP = defaultPFP;
}
return (
<Image
src={modalOwnerPFP}
alt={selectedBet.ownerProfile.username}
width={32}
height={32}
className="w-8 h-8 rounded-full mr-2"
onError={(e) => {
// @ts-ignore
e.target.src = defaultPFP;
setFailedImages(prev => new Set(prev).add(modalOwnerPFP));
}}
/>
);
})()}
<p className="font-bold">{selectedBet.ownerProfile.username}</p>
</div>
</>

View File

@ -5,7 +5,7 @@ import { usePrivy, useSolanaWallets } from "@privy-io/react-auth";
import { Connection, PublicKey } from "@solana/web3.js";
import { toast } from "sonner";
import "react-toastify/dist/ReactToastify.css";
import { CLUSTER_URL, API_URL } from "@/data/shared";
import { CLUSTER_URL, API_URL, API_BASE_URL } from "@/data/shared";
import { useFundWallet } from "@privy-io/react-auth/solana";
import axios from "axios";
import { Game } from "@/types/Game";
@ -40,6 +40,8 @@ export default function PrivyButton() {
const [opponentInfo, setOpponentInfo] = useState<{ [key: string]: Opponent }>({});
const [gameImages, setGameImages] = useState<{ [key: string]: string }>({});
const [loading, setLoading] = useState(false);
const [failedImages, setFailedImages] = useState<Set<string>>(new Set());
const defaultPFP = '/duelfiassets/PFP (1).png';
const [username, setUsername] = useState("Tester");
const [bio, setBio] = useState("");
@ -117,7 +119,7 @@ export default function PrivyButton() {
setBio(data.bio || "");
// Check if the user has a profile picture URL and update in database
const customProfileUrl = `https://vps.playpoolstudios.com/duelfi/profile_pics/${user.id}.jpg`;
const customProfileUrl = `${API_URL}profile_pics/${user.id}.jpg`;
const profilePictureUrl = user?.twitter?.profilePictureUrl ?? customProfileUrl;
if (profilePictureUrl) {
const updatePicUrlApi = `${API_URL}update_x_pic_url.php?id=${user?.id}&url=${profilePictureUrl}`;
@ -240,7 +242,7 @@ export default function PrivyButton() {
};
const twitterProfilePic: string = user?.twitter?.profilePictureUrl ?? "";
const customProfileUrl = `https://vps.playpoolstudios.com/duelfi/profile_pics/${user?.id}.jpg`;
const customProfileUrl = `${API_BASE_URL}profile_pics/${user?.id}.jpg`;
useEffect(() => {
if (isModalOpen && user) {
@ -536,7 +538,7 @@ export default function PrivyButton() {
const profileUrl =
opponent?.x_profile_url ||
(opponent?.username
? `https://vps.playpoolstudios.com/duelfi/profile_picture/${opponent.username}.jpg`
? `${API_URL}profile_picture/${opponent.username}.jpg`
: "/duelfiassets/default-avatar.png");
const gameImageUrl = gameImages[game.game] || "/duelfiassets/default-game-thumbnail.png";
@ -570,18 +572,28 @@ export default function PrivyButton() {
</p>
</div>
<Image
src={profileUrl}
src={failedImages.has(profileUrl) ? defaultPFP : profileUrl}
alt="Profile"
width={40}
height={40}
className="w-10 h-10 rounded-full border border-gray-700 object-cover"
onError={(e) => {
// @ts-ignore
e.target.src = defaultPFP;
setFailedImages(prev => new Set(prev).add(profileUrl));
}}
/>
<Image
src={gameImageUrl}
src={failedImages.has(gameImageUrl) ? "/duelfiassets/default-game-thumbnail.png" : gameImageUrl}
alt="Game Thumbnail"
width={64}
height={64}
className="w-16 h-16 rounded-md object-cover ml-4"
onError={(e) => {
// @ts-ignore
e.target.src = "/duelfiassets/default-game-thumbnail.png";
setFailedImages(prev => new Set(prev).add(gameImageUrl));
}}
/>
<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">

View File

@ -4,14 +4,15 @@ import { Connection, clusterApiUrl } from "@solana/web3.js";
// You can get one from providers like QuickNode, Alchemy, Helius, or GenesysGo
// export const CLUSTER_URL = "https://tiniest-cold-darkness.solana-mainnet.quiknode.pro/72332d636ff78d498b880bd8fdc3eb646c827da8/";
// export const CLUSTER_URL = "https://go.getblock.io/908837801b534ae7a6f0869fc44cc567";
// export const CLUSTER_URL = "https://solana-mainnet.core.chainstack.com/c54e14eef17693283a0323efcc4ce731";
export const CLUSTER_URL = "https://solana-mainnet.core.chainstack.com/c54e14eef17693283a0323efcc4ce731";
// export const CLUSTER_URL = "https://mainnet.helius-rpc.com/?api-key=72332d63-6ff7-4d49-8b88-0bd8fdc3eb64";
export const CLUSTER_URL = clusterApiUrl("devnet");
// export const CLUSTER_URL = clusterApiUrl("devnet");
export const EXPLORER_ADDRESS_TEMPLATE = "https://explorer.solana.com/address/{address}";
export const EXPLORER_TX_TEMPLATE = "https://explorer.solana.com/tx/{address}";
export const connection = new Connection(CLUSTER_URL);
export const API_URL = "https://api.duelfi.io/v1/";
export const API_BASE_URL = "https://api.duelfi.io/";
export const VALIDATOR_URL = "https://validator.duelfi.io/";
export const VALIDATOR_URL_DEV = "https://validatordev.duelfi.io/";

View File

@ -1,6 +1,9 @@
import { API_URL } from "@/data/shared";
export async function fetchUserById(id: string) {
const res = await fetch(`https://vps.playpoolstudios.com/duelfi/api/get_user_by_id.php?id=${id}`);
if (!res.ok) return null;
return await res.json();
}
const url = `${API_URL}get_user_by_id.php?id=${id}`;
const res = await fetch(url);
if (!res.ok) return null;
return await res.json();
}