Activities v1
This commit is contained in:
parent
40ae481eb6
commit
b073494b2d
|
|
@ -4,7 +4,7 @@ import Footer from "@/components/Footer";
|
|||
import Header from "@/components/Header";
|
||||
import HeroSection from "@/components/HeroSection";
|
||||
import Leaderboard from "@/components/Leaderboard";
|
||||
// import Leaderboard from "@/components/Leaderboard";
|
||||
import Activities from "@/components/Activities";
|
||||
import { PrivyProvider } from "@privy-io/react-auth";
|
||||
import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
|
||||
import { Toaster } from "sonner";
|
||||
|
|
@ -39,6 +39,11 @@ export default function Home() {
|
|||
<Header />
|
||||
<HeroSection />
|
||||
<Leaderboard />
|
||||
<div className="container mt-10"></div>
|
||||
|
||||
<Activities />
|
||||
<div className="container mt-10"></div>
|
||||
|
||||
<Footer />
|
||||
</>
|
||||
</PrivyProvider>
|
||||
|
|
|
|||
148
src/components/Activities.tsx
Normal file
148
src/components/Activities.tsx
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import Image from "next/image";
|
||||
import { useState, useEffect } from "react";
|
||||
import { API_BASE_URL } from "@/data/shared";
|
||||
|
||||
interface Activity {
|
||||
id: string;
|
||||
type: string;
|
||||
owner_id: string;
|
||||
joiner_id: string | null;
|
||||
game: string;
|
||||
amount: string;
|
||||
comments: string | null;
|
||||
time: string;
|
||||
}
|
||||
|
||||
interface UserData {
|
||||
id: string;
|
||||
username: string;
|
||||
bio: string;
|
||||
x_profile_url: string;
|
||||
}
|
||||
|
||||
export default function Activities() {
|
||||
const [activities, setActivities] = useState<Activity[]>([]);
|
||||
const [userData, setUserData] = useState<Record<string, UserData>>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [failedImages, setFailedImages] = useState<Set<string>>(new Set());
|
||||
const defaultPFP = '/duelfiassets/PFP (1).png';
|
||||
|
||||
useEffect(() => {
|
||||
const fetchActivities = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/v1/get_activities.php");
|
||||
const data = await response.json();
|
||||
setActivities(data);
|
||||
|
||||
// Fetch user data for all unique user IDs
|
||||
const userIds = new Set<string>();
|
||||
data.forEach((activity: Activity) => {
|
||||
if (activity.owner_id) userIds.add(activity.owner_id);
|
||||
if (activity.joiner_id) userIds.add(activity.joiner_id);
|
||||
});
|
||||
|
||||
const userDataPromises = Array.from(userIds).map(async (userId) => {
|
||||
try {
|
||||
console.log('Fetching user data for ID:', userId);
|
||||
|
||||
const response = await fetch(`/api/v1/get_user_by_id.php?id=${userId}`);
|
||||
const data = await response.json();
|
||||
console.log('Received user data:', data);
|
||||
return [userId, data];
|
||||
} catch (error) {
|
||||
console.error(`Error fetching user data for ${userId}:`, error);
|
||||
return [userId, null];
|
||||
}
|
||||
});
|
||||
|
||||
const userDataResults = await Promise.all(userDataPromises);
|
||||
console.log('All user data results:', userDataResults);
|
||||
const userDataMap = Object.fromEntries(userDataResults);
|
||||
console.log('Final user data map:', userDataMap);
|
||||
setUserData(userDataMap);
|
||||
} catch (error) {
|
||||
console.error("Error fetching activities:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchActivities();
|
||||
}, []);
|
||||
|
||||
const formatActivityMessage = (activity: Activity) => {
|
||||
const ownerUsername = userData[activity.owner_id]?.username || "Anonymous";
|
||||
const joinerUsername = activity.joiner_id ? (userData[activity.joiner_id]?.username || "Anonymous") : null;
|
||||
const amount = parseFloat(activity.amount);
|
||||
const formattedAmount = amount > 0 ? `${amount} SOL` : "";
|
||||
|
||||
switch (activity.type) {
|
||||
case "create":
|
||||
return `${ownerUsername} Created a new ${activity.game} game${formattedAmount ? ` for ${formattedAmount}` : ""}`;
|
||||
case "close":
|
||||
return `${ownerUsername} Closed ${activity.game} game${formattedAmount ? ` for ${formattedAmount}` : ""}`;
|
||||
case "join":
|
||||
return `${joinerUsername} Joined a ${activity.game} game`;
|
||||
case "won":
|
||||
return `${ownerUsername} won the ${activity.game} game${formattedAmount ? ` and won ${formattedAmount}` : ""}`;
|
||||
default:
|
||||
return "Unknown activity";
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="container mx-auto max-w-screen-xl px-3">
|
||||
<div className="bg-[rgb(30,30,30)] rounded-xl p-6 shadow-lg">
|
||||
<h2 className="text-2xl font-bold text-white mb-4">Recent Activities</h2>
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-[rgb(248,144,22)]"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto max-w-screen-xl px-3 md:px-40">
|
||||
<div className="bg-[rgb(30,30,30)] rounded-xl p-6 shadow-lg">
|
||||
<h2 className="text-2xl font-bold text-white mb-4">Recent Activities</h2>
|
||||
<div className="space-y-4">
|
||||
{activities.map((activity) => {
|
||||
const userId = activity.type === "join" ? activity.joiner_id : activity.owner_id;
|
||||
if (!userId) return null;
|
||||
|
||||
let profileUrl = userData[userId]?.x_profile_url || `${API_BASE_URL}profile_pics/${userId}.jpg`;
|
||||
if (failedImages.has(profileUrl)) {
|
||||
profileUrl = defaultPFP;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={activity.id}
|
||||
className="flex items-center gap-4 p-3 px-6 rounded-lg bg-[rgb(10,10,10)] border border-[rgb(30,30,30)] hover:border-[rgb(248,144,22)] transition-all duration-300"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<p className="text-white">{formatActivityMessage(activity)}</p>
|
||||
<p className="text-sm text-gray-400">{new Date(activity.time).toLocaleString()}</p>
|
||||
</div>
|
||||
<Image
|
||||
src={profileUrl}
|
||||
alt="Profile"
|
||||
width={40}
|
||||
height={40}
|
||||
className="rounded-full border border-gray-700 object-cover"
|
||||
onError={(e) => {
|
||||
// @ts-expect-error - Type mismatch expected
|
||||
e.target.src = defaultPFP;
|
||||
setFailedImages(prev => new Set(prev).add(profileUrl));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -15,4 +15,17 @@ export async function showNewGameNotification(username:string, game:string, wage
|
|||
}catch(error){
|
||||
console.error("Error showing new game notification:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function add_new_activity(type:string, owner_id:string, joiner_id:string, game:string, amount:number ){
|
||||
try{
|
||||
const isDevnet = CLUSTER_URL === clusterApiUrl("devnet");
|
||||
if(isDevnet){
|
||||
await fetch(`${API_URL}add_activity.php?type=${type}&owner_id=${owner_id}&joiner_id=${joiner_id}&game=${game}&amount=${amount}&devnet=${isDevnet ? "1" : "0"}`)
|
||||
}else{
|
||||
await fetch(`${API_URL}add_activity.php?type=${type}&owner_id=${owner_id}&joiner_id=${joiner_id}&game=${game}&amount=${amount}`)
|
||||
}
|
||||
}catch(error){
|
||||
console.error("Error adding new activity:", error);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import idl from "../idl/bets_idl.json";
|
|||
import { Bet } from "@/types/Bet";
|
||||
import { toast } from "sonner";
|
||||
import { CONFIRMATION_THRESHOLD } from "./constants";
|
||||
|
||||
import { add_new_activity } from "./data_fetcher";
|
||||
export async function fetchOpenBets(): Promise<Bet[]> {
|
||||
|
||||
const response = await fetch(`${VALIDATOR_URL}fetchBets`);
|
||||
|
|
@ -78,11 +78,12 @@ export async function closeBet(wallets: ConnectedSolanaWallet, uid:string, betI
|
|||
const betList = await program.account.betsList.fetch(bet_list_pda);
|
||||
|
||||
let chosenBet: PublicKey | null = null;
|
||||
|
||||
let game_id:string = "";
|
||||
for (const bet of betList.bets) {
|
||||
const betAcc = await program.account.betVault.fetch(bet);
|
||||
if (betAcc.owner.toBase58() === wallets.address && betAcc.gameId.toString() === betId) {
|
||||
chosenBet = bet;
|
||||
game_id = betAcc.gameId.toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -112,8 +113,8 @@ export async function closeBet(wallets: ConnectedSolanaWallet, uid:string, betI
|
|||
|
||||
// Send transaction// Replace with correct RPC endpoint
|
||||
const txId = await connection.sendRawTransaction(signedTx.serialize());
|
||||
|
||||
console.log(`Transaction: ${tx}`);
|
||||
add_new_activity("close", uid, "", game_id, 0);
|
||||
console.log(`Transaction: ${txId}`);
|
||||
return txId;
|
||||
} catch (error) {
|
||||
console.error("Error closing bet:", error);
|
||||
|
|
@ -161,7 +162,7 @@ export async function createBet(wallets: ConnectedSolanaWallet, uid: string, sel
|
|||
const txId = await connection.sendRawTransaction(signedTx.serialize());
|
||||
console.log(`Transaction sent: ${txId}`);
|
||||
console.log(`Transaction confirmed: ${txId}`);
|
||||
|
||||
add_new_activity("create", uid, "", selectedGame, selectedPrice);
|
||||
if (get_account_address) {
|
||||
const gameIdBytes = Buffer.from(selectedGame); // selectedGame is a string (game_id)
|
||||
const nonceBytes = new BN(nonce).toArrayLike(Buffer, "le", 8); // _nonce.to_le_bytes()
|
||||
|
|
@ -234,7 +235,7 @@ export async function joinBet(wallets: ConnectedSolanaWallet, uid: string, gameI
|
|||
|
||||
// Send transaction// Replace with correct RPC endpoint
|
||||
const txId = await connection.sendRawTransaction(signedTx.serialize());
|
||||
|
||||
add_new_activity("join", "",uid, gameId, 0);
|
||||
console.log(`Transaction ID: ${txId}`);
|
||||
return txId;
|
||||
} catch (error: unknown) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user