diff --git a/client/src/App.tsx b/client/src/App.tsx
index e0a1fb0a..33bcdb70 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -15,6 +15,7 @@ import Home from "./pages/Home";
import Leaderboard from "./pages/Leaderboard";
import Lobby from "./pages/Lobby";
import { POLICIES } from "./lib/constants";
+import Profile from "./pages/Profile";
const options: ControllerOptions = {
rpc: "https://api.cartridge.gg/x/mancala-alpha-v8/katana",
@@ -75,6 +76,7 @@ export default function App() {
} />
} />
} />
+ } />
} />
diff --git a/client/src/assets/avatar.png b/client/src/assets/avatar.png
new file mode 100644
index 00000000..3a100446
Binary files /dev/null and b/client/src/assets/avatar.png differ
diff --git a/client/src/assets/image-add.svg b/client/src/assets/image-add.svg
new file mode 100644
index 00000000..d2fcb372
--- /dev/null
+++ b/client/src/assets/image-add.svg
@@ -0,0 +1,5 @@
+
diff --git a/client/src/assets/long-box.png b/client/src/assets/long-box.png
new file mode 100644
index 00000000..7dd33bd3
Binary files /dev/null and b/client/src/assets/long-box.png differ
diff --git a/client/src/assets/telegram.png b/client/src/assets/telegram.png
new file mode 100644
index 00000000..b37e0e23
Binary files /dev/null and b/client/src/assets/telegram.png differ
diff --git a/client/src/assets/twitter.png b/client/src/assets/twitter.png
new file mode 100644
index 00000000..4bc52aac
Binary files /dev/null and b/client/src/assets/twitter.png differ
diff --git a/client/src/assets/wide-box.png b/client/src/assets/wide-box.png
new file mode 100644
index 00000000..de8d4821
Binary files /dev/null and b/client/src/assets/wide-box.png differ
diff --git a/client/src/components/header.tsx b/client/src/components/header.tsx
index 5ab9ca29..4c375b5a 100644
--- a/client/src/components/header.tsx
+++ b/client/src/components/header.tsx
@@ -19,7 +19,7 @@ import { Link } from "react-router-dom";
import { constants } from "starknet";
import { Button } from "@material-tailwind/react";
import { UserIcon, ChevronDownIcon } from "@heroicons/react/24/solid";
-import { useDojo } from "@/dojo/useDojo";
+// import { useDojo } from "@/dojo/useDojo";
import clsx from "clsx";
import controllerSvg from "../assets/controller.svg";
import connectB from "../assets/connect.svg";
@@ -75,7 +75,13 @@ export default function Header() {
setPlaying(!isPlaying);
};
- const { account } = useDojo();
+ // const { account } = useDojo();
+
+ const account = {
+ account: {
+ address: "0x05e01dB693CBF7461a016343042786DaC5A6000104813cF134a1E8B1D0a6810b"
+ }
+ }
const { data, startPolling } = useFetchModelsForHeaderQuery();
startPolling(1000);
@@ -185,36 +191,41 @@ export default function Header() {
{isDropdownClose && (
-
-
-
- Profile
-
-
-
-
-
- Lobby
-
-
-
-
-
- Leaderboard
-
-
-
+
+
+
+
+
+
+
+
+
+
+
)}
@@ -242,28 +253,27 @@ export default function Header() {
{isDropdownOpen && (
-
+
-
+
+
+
+
)}
diff --git a/client/src/components/lobby/duels.tsx b/client/src/components/lobby/duels.tsx
index 97130fab..ca41ed6d 100644
--- a/client/src/components/lobby/duels.tsx
+++ b/client/src/components/lobby/duels.tsx
@@ -60,7 +60,7 @@ export default function Duels({ games, transactions, loading }: { games: any, tr
return
}
else {
- if (data.length === 0) {
+ if (data?.length === 0) {
return
}
else {
@@ -105,8 +105,8 @@ export default function Duels({ games, transactions, loading }: { games: any, tr
- {data.map((item: any, index: number) => {
- const isLast = index === data.length - 1;
+ {data?.map((item: any, index: number) => {
+ const isLast = index === data?.length - 1;
const date = new Date(item.date)
return (
diff --git a/client/src/components/profile/empty-game-history.tsx b/client/src/components/profile/empty-game-history.tsx
new file mode 100644
index 00000000..c3bdbf21
--- /dev/null
+++ b/client/src/components/profile/empty-game-history.tsx
@@ -0,0 +1,13 @@
+import mancala_duels_logo from '../../assets/mancala_duels.png';
+
+export default function EmptyGameHistory({ id }: { id: string }) {
+ return (
+
+
+
+
{id == "all" ? "You have no duels" : id == "won" ? "You have won no duels" : "You have lost no duels"}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/client/src/components/profile/game-history-skeleton.tsx b/client/src/components/profile/game-history-skeleton.tsx
new file mode 100644
index 00000000..f012f47c
--- /dev/null
+++ b/client/src/components/profile/game-history-skeleton.tsx
@@ -0,0 +1,36 @@
+import clsx from "clsx";
+
+export function GameHistorySkeleton() {
+ const arr = Array(6).fill(null);
+ return (
+ <>
+ {arr.map((_, index) => {
+ const isLast = index === arr.length - 1;
+ return (
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+ )
+
+ })}
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/client/src/components/profile/game-history.tsx b/client/src/components/profile/game-history.tsx
new file mode 100644
index 00000000..5b65b739
--- /dev/null
+++ b/client/src/components/profile/game-history.tsx
@@ -0,0 +1,174 @@
+import { duels_header } from "@/lib/constants.ts";
+import { Card, Typography } from "@material-tailwind/react";
+// import { useProvider } from "@starknet-react/core";
+// import { useEffect, useMemo, useState } from "react";
+// import { StarknetIdNavigator } from "starknetid.js";
+// import { constants, StarkProfile } from "starknet";
+import clsx from "clsx";
+import { truncateString } from "@/lib/utils.ts";
+import { GameHistorySkeleton } from "./game-history-skeleton.tsx";
+import { UserIcon } from "@heroicons/react/24/solid";
+import EmptyGameHistory from "./empty-game-history.tsx";
+
+export default function GameHistory({ games, loading, id }: { games: any, loading: boolean, id: string }) {
+ // const { provider } = useProvider();
+ // const starknetIdNavigator = useMemo(() => {
+ // return new StarknetIdNavigator(
+ // provider,
+ // constants.StarknetChainId.SN_SEPOLIA
+ // );
+ // }, [provider])
+ // const [challengers, setChallengers] = useState([]);
+ // const [challenged, setChallenged] = useState();
+ // const [winners, setWinners] = useState();
+ // const challengerAddresses = games?.map((game: any) => game.node.player_one);
+ // const challengedAddresses = games?.map((game: any) => game.node.player_two);
+ // const winnerAddresses = games?.map((game: any) => game.node.winner);
+ // useEffect(() => {
+ // if (!starknetIdNavigator || !challengerAddresses) return;
+ // (async () => {
+ // const challengerData = await starknetIdNavigator?.getStarkProfiles(challengerAddresses)
+ // const challengedData = await starknetIdNavigator?.getStarkProfiles(challengedAddresses)
+ // const winnersData = await starknetIdNavigator?.getStarkProfiles(winnerAddresses)
+ // if (!challengerData) return;
+ // if (challengerData) setChallengers(challengerData);
+ // if (!challengedData) return;
+ // if (challengedData) setChallenged(challengedData);
+ // if (!winnersData) return;
+ // if (winnersData) setWinners(winnersData)
+ // })();
+ // }, [challengerAddresses, challengedAddresses, winnerAddresses, starknetIdNavigator]);
+ // const data = challengers?.map((challenger, index) => {
+ // return {
+ // challenger: challenger,
+ // challenged: challenged ? challenged[index] : null,
+ // winner: winners ? winners[index] : null,
+ // date: transactions[index].node.executedAt,
+ // }
+ // })
+
+ const data = games?.map((data: any, index: number) => {
+ console.log(data)
+ return {
+ challenger: data.node.player_one,
+ challenged: data.node.player_two,
+ winner: data.node.winner,
+ date: data.node.entity.executedAt,
+ }
+ })
+
+
+ if (loading) {
+ return
+ }
+ else {
+ if (data?.length === 0) {
+ return
+ }
+ else {
+ return (
+
+
+
+
+
+ {duels_header.map((head) => (
+
+
+ {head.name}
+
+
+ ))}
+
+
+
+
+
+
+ {duels_header.map((head) => (
+
+
+ {head.name}
+
+ |
+ ))}
+
+
+
+ {data?.map((item: any, index: number) => {
+ const isLast = index === data?.length - 1;
+ const date = new Date(item.date)
+ return (
+
+
+ {/* */}
+
+ {/* */}
+
+
+ {item.challenger.name ? item.challenger.name : truncateString(games[index].node.player_one)}
+
+
+ |
+
+ {
+ games[index].node.player_two !== "0x0" ?
+ {/* */}
+
+
+ {item.challenged.name ? item.challenged.name : truncateString((games[index].node.player_two))}
+
+ : Matchmaking
+ }
+ |
+
+
+ {item.winner.name ? item.winner.name : truncateString((games[index].node.winner))}
+
+ |
+
+
+ {date.toLocaleDateString()}
+
+ |
+
+ );
+ })}
+
+
+
+
+
+
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/profile/user-section.tsx b/client/src/components/profile/user-section.tsx
new file mode 100644
index 00000000..f702a102
--- /dev/null
+++ b/client/src/components/profile/user-section.tsx
@@ -0,0 +1,117 @@
+import avatar from "@/assets/avatar.png";
+import twitter from "@/assets/twitter.png";
+import telegram from "@/assets/telegram.png";
+import image from "@/assets/image-add.svg";
+import { Link } from "react-router-dom";
+import { FormEventHandler, useRef, useState } from "react";
+import { Dialog } from "@material-tailwind/react";
+import { truncateString } from "@/lib/utils";
+import { useAccount } from "@starknet-react/core";
+
+export default function UserSection({ level, wins, losses, total }: { level: number, wins: number, losses: number, total: number }) {
+ const [open, setOpen] = useState(false);
+ const [selectedImage, setSelectedImage] = useState(null);
+ const ref: any = useRef();
+ const handleOpen = () => setOpen(!open);
+ const handleImageChange = (event: React.ChangeEvent) => {
+ if (event.target.files && event.target.files[0]) {
+ setSelectedImage(URL.createObjectURL(event.target.files[0]));
+ }
+ };
+ const account = useAccount();
+ return (
+
+
+
+
+
+
{truncateString(account?.address) || "Guest user"}
+
+
+
+
+
+
+
Level {level}
+
{level}/100
+
+
+
+
Total Played
+
{total}
+
+
+
+
Total Lost
+
{losses}
+
+
+
+
Share
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/client/src/pages/Leaderboard.tsx b/client/src/pages/Leaderboard.tsx
index 2f837b4c..9dfd767b 100644
--- a/client/src/pages/Leaderboard.tsx
+++ b/client/src/pages/Leaderboard.tsx
@@ -49,8 +49,6 @@ export default function Leaderboard() {
})();
}, [addresses]);
- console.log(players);
-
return (
{/* Start of header */}
@@ -61,7 +59,7 @@ export default function Leaderboard() {
diff --git a/client/src/pages/Lobby.tsx b/client/src/pages/Lobby.tsx
index fb3a2ffd..553ec6c1 100644
--- a/client/src/pages/Lobby.tsx
+++ b/client/src/pages/Lobby.tsx
@@ -96,10 +96,6 @@ export default function Lobby() {
connect({ connector: connectors[0] });
};
- const handleConnect = () => {
- connectWallet();
- };
-
const filteredGames = data?.mancalaAlphaMancalaGameModels?.edges?.filter(
(game) =>
game?.node?.player_one === account.address ||
diff --git a/client/src/pages/Profile.tsx b/client/src/pages/Profile.tsx
index e69de29b..b66d3f79 100644
--- a/client/src/pages/Profile.tsx
+++ b/client/src/pages/Profile.tsx
@@ -0,0 +1,103 @@
+import Header from "@/components/header";
+import GameHistory from "@/components/profile/game-history";
+import UserSection from "@/components/profile/user-section";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { MancalaGameEdge, useFetchModelsForHeaderQuery, useMancalaModelsFetchQuery } from "@/generated/graphql";
+import { getPlayer } from "@/lib/utils";
+import { useAccount } from "@starknet-react/core";
+
+export default function Profile() {
+ const { data, startPolling, loading } = useMancalaModelsFetchQuery();
+ startPolling(1000);
+ const account = useAccount();
+ const filteredGames = data?.mancalaAlphaMancalaGameModels?.edges?.filter(
+ (game) =>
+ game?.node?.player_one === account.address ||
+ game?.node?.player_two === account.address
+ );
+
+ // const filteredTransactions = data?.mancalaAlphaMancalaGameModels?.edges?.reduce(
+ // (acc: any[], game: any) => {
+ // if (
+ // (game?.node?.player_one === account.address ||
+ // game?.node?.player_two === account.address) &&
+ // game?.node?.entity?.executedAt
+ // ) {
+ // acc.push({
+ // ...game.node,
+ // executedAt: game?.node?.entity?.executedAt,
+ // });
+ // }
+ // return acc;
+ // },
+ // []
+ // ) || [];
+
+ const filteredWonGames = filteredGames?.filter(game => game?.node?.winner === account.address) || [];
+ const filteredLostGames = filteredGames?.filter(game => game?.node?.winner !== "0x0" && game?.node?.winner !== account.address) || [];
+
+ const { data: playerData, startPolling: startPollingPlayer } = useFetchModelsForHeaderQuery();
+ startPollingPlayer(1000);
+
+ const player = getPlayer(
+ playerData?.mancalaAlphaMancalaGameModels?.edges as MancalaGameEdge[],
+ account.account?.address || "",
+ );
+
+
+ return (
+
+
+
+
+
+
+
+
+
+ All games
+
+
+ Won
+
+
+ Lost
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file