218 lines
8.5 KiB
TypeScript
218 lines
8.5 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import styles from "../styles/Leaderboard.module.css";
|
|
import {
|
|
TopEarnersResponse,
|
|
TopRaidersResponse,
|
|
PlayerResponse,
|
|
} from "../types/leaderboard";
|
|
import { formatUnits } from "viem";
|
|
|
|
const SUBGRAPH_URL =
|
|
"https://api.studio.thegraph.com/query/75782/slay-the-moloch-base-mainnet/version/latest";
|
|
|
|
const Leaderboard = () => {
|
|
const [topEarners, setTopEarners] = useState<TopEarnersResponse>();
|
|
const [topRaiders, setTopRaiders] = useState<TopRaidersResponse>();
|
|
const [activeTab, setActiveTab] = useState<
|
|
"earners" | "raiders" | "bosses" | "prestige"
|
|
>("earners");
|
|
const [bossesDefeated, setBossesDefeated] = useState<PlayerResponse>();
|
|
const [playerPrestige, setPlayerPrestige] = useState<PlayerResponse>();
|
|
|
|
useEffect(() => {
|
|
const fetchLeaderboards = async () => {
|
|
try {
|
|
// Fetch top earners
|
|
const earnersResponse = await fetch(SUBGRAPH_URL, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
query: `{
|
|
armies(first: 10, orderBy: profitPerSecond, orderDirection: desc) {
|
|
player {
|
|
id
|
|
totalMinted
|
|
currentBalance
|
|
numberOfRaids
|
|
}
|
|
profitPerSecond
|
|
molochDenierLevel
|
|
apprenticeLevel
|
|
anointedLevel
|
|
championLevel
|
|
}
|
|
}`,
|
|
}),
|
|
});
|
|
const earnersData = await earnersResponse.json();
|
|
setTopEarners({ armies: earnersData.data.armies });
|
|
|
|
// Fetch top raiders
|
|
const raidersResponse = await fetch(SUBGRAPH_URL, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
query: `{
|
|
players(first: 10, orderBy: numberOfRaids, orderDirection: desc) {
|
|
id
|
|
numberOfRaids
|
|
totalMinted
|
|
currentBalance
|
|
}
|
|
}`,
|
|
}),
|
|
});
|
|
const raidersData = await raidersResponse.json();
|
|
setTopRaiders({ players: raidersData.data.players });
|
|
|
|
// Fetch bosses defeated
|
|
const bossesDefeatedResponse = await fetch(SUBGRAPH_URL, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
query: `{
|
|
players(first: 10, orderBy: bossesDefeated, orderDirection: desc) {
|
|
id
|
|
bossesDefeated
|
|
}
|
|
}`,
|
|
}),
|
|
});
|
|
const bossesDefeatedData = await bossesDefeatedResponse.json();
|
|
setBossesDefeated({ players: bossesDefeatedData.data.players });
|
|
|
|
// Fetch player prestige
|
|
const playerPrestigeResponse = await fetch(SUBGRAPH_URL, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
query: `{
|
|
players(first: 10, orderBy: prestigeLevel, orderDirection: desc) {
|
|
id
|
|
prestigeLevel
|
|
}
|
|
}`,
|
|
}),
|
|
});
|
|
const playerPrestigeData = await playerPrestigeResponse.json();
|
|
setPlayerPrestige({ players: playerPrestigeData.data.players });
|
|
} catch (error) {
|
|
console.error("Error fetching leaderboard:", error);
|
|
}
|
|
};
|
|
|
|
fetchLeaderboards();
|
|
const interval = setInterval(fetchLeaderboards, 30000); // Refresh every 30 seconds
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return (
|
|
<div className={styles.leaderboard}>
|
|
<h2 className={styles.title}>Leaderboard</h2>
|
|
|
|
<div className={styles.tabs}>
|
|
<button
|
|
className={`${styles.tab} ${
|
|
activeTab === "earners" ? styles.active : ""
|
|
}`}
|
|
onClick={() => setActiveTab("earners")}
|
|
>
|
|
Top Earners
|
|
</button>
|
|
<button
|
|
className={`${styles.tab} ${
|
|
activeTab === "raiders" ? styles.active : ""
|
|
}`}
|
|
onClick={() => setActiveTab("raiders")}
|
|
>
|
|
Top Raiders
|
|
</button>
|
|
<button
|
|
className={`${styles.tab} ${
|
|
activeTab === "bosses" ? styles.active : ""
|
|
}`}
|
|
onClick={() => setActiveTab("bosses")}
|
|
>
|
|
Top Boss Slayers
|
|
</button>
|
|
<button
|
|
className={`${styles.tab} ${
|
|
activeTab === "prestige" ? styles.active : ""
|
|
}`}
|
|
onClick={() => setActiveTab("prestige")}
|
|
>
|
|
Top Players by Prestige
|
|
</button>
|
|
</div>
|
|
|
|
{activeTab === "earners" && (
|
|
<div className={styles.list}>
|
|
{topEarners?.armies.map((army, index) => (
|
|
<div key={army.player.id} className={styles.item}>
|
|
<span className={styles.rank}>#{index + 1}</span>
|
|
<span className={styles.address}>
|
|
{army.player.id.slice(0, 6)}...
|
|
{army.player.id.slice(-4)}
|
|
</span>
|
|
<span className={styles.stat}>
|
|
{formatUnits(BigInt(army.profitPerSecond), 4)}{" "}
|
|
GELD/s
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === "raiders" && (
|
|
<div className={styles.list}>
|
|
{topRaiders?.players.map((player, index) => (
|
|
<div key={player.id} className={styles.item}>
|
|
<span className={styles.rank}>#{index + 1}</span>
|
|
<span className={styles.address}>
|
|
{player.id.slice(0, 6)}...{player.id.slice(-4)}
|
|
</span>
|
|
<span className={styles.stat}>
|
|
{player.numberOfRaids} raids
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === "bosses" && (
|
|
<div className={styles.list}>
|
|
{bossesDefeated?.players.map((player, index) => (
|
|
<div key={player.id} className={styles.item}>
|
|
<span className={styles.rank}>#{index + 1}</span>
|
|
<span className={styles.address}>
|
|
{player.id.slice(0, 6)}...{player.id.slice(-4)}
|
|
</span>
|
|
<span className={styles.stat}>
|
|
{player.bossesDefeated} Bosses Slain
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === "prestige" && (
|
|
<div className={styles.list}>
|
|
{playerPrestige?.players.map((player, index) => (
|
|
<div key={player.id} className={styles.item}>
|
|
<span className={styles.rank}>#{index + 1}</span>
|
|
<span className={styles.address}>
|
|
{player.id.slice(0, 6)}...{player.id.slice(-4)}
|
|
</span>
|
|
<span className={styles.stat}>
|
|
{player.prestigeLevel} Prestige Level
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Leaderboard;
|