Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Tutorial from "./pages/Tutorial";
import CONFIG, { IS_MAINNET } from "./lib/config";
import { constants } from "starknet";
import Boards from "./pages/Boards";
import Checks from "./Checks";

const options = {
theme: "realm-of-ra",
Expand Down Expand Up @@ -68,15 +69,16 @@ export default function App() {
autoConnect
>
<Router>
<Routes>
<Route index element={<Home />} />
<Route path="/lobby" element={<Lobby />} />
<Route path="/games/:gameId" element={<Gameplay />} />
<Route path="/tutorial" element={<Tutorial />} />
<Route path="/boards" element={<Boards />} />
</Routes>
<Checks>
<Routes>
<Route index element={<Home />} />
<Route path="/lobby" element={<Lobby />} />
<Route path="/games/:gameId" element={<Gameplay />} />
<Route path="/tutorial" element={<Tutorial />} />
<Route path="/boards" element={<Boards />} />
</Routes>
</Checks>
</Router>
{isSmallScreen && <SmallScreenWarning />}
</StarknetConfig>
);
}
45 changes: 45 additions & 0 deletions client/src/Checks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useAccount, useBalance, useConnect } from "@starknet-react/core";
import NotEnough from "@/components/not-enough";
import { useEffect, useState } from "react";


const SmallScreenWarning = () => (
<div className="fixed inset-0 z-50 flex items-center justify-center text-white bg-black bg-opacity-75 backdrop-blur-sm">
<div className="p-4 text-center">
<h1 className="text-2xl font-bold">
This game is not optimized for this device screen!
</h1>
</div>
</div>
);

export default function Checks({ children }: { children: React.ReactNode }) {
const { isConnected } = useAccount();
const { connect, connectors } = useConnect();
const { data } = useBalance({
address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"
})
const isEnough = Math.round(Number(data?.formatted)) >= 10;
const [isSmallScreen, setIsSmallScreen] = useState(false);

useEffect(() => {
if (!isConnected) {
connect({ connector: connectors[0] })
}
const handleResize = () => {
setIsSmallScreen(window.innerWidth < 1280);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return(
<div>
{isConnected && !isEnough && <NotEnough isEnough={isEnough} />}
{isSmallScreen && <SmallScreenWarning />}
{children}
</div>
)
}
20 changes: 19 additions & 1 deletion client/src/components/gameplay/game-navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import GameMessage from "@/components/gameplay/game-message";
import { formatPlayerName, getPlayer, lookupMissingNames } from "@/lib/utils";
import PlayerProfile from "@/components/gameplay/player-profile";
import { gameStarted } from "@/lib/constants";
import { useEffect, useState } from "react";
import { SetStateAction, Dispatch, useEffect, useState } from "react";

export default function GameNavigation({
game_players,
Expand All @@ -17,6 +17,7 @@ export default function GameNavigation({
setMessage,
action,
setAction,
setPlayers
}: {
game_players: any;
player_names: any;
Expand All @@ -30,6 +31,7 @@ export default function GameNavigation({
setMessage: any;
action: { action: any; message: string };
setAction: any;
setPlayers: Dispatch<SetStateAction<{ name: string, address: string }[] | undefined>>
}) {
const games_data_one = game_players?.player_one?.edges?.[0]?.node;
const games_data_two = game_players?.player_two?.edges?.[0]?.node;
Expand Down Expand Up @@ -107,6 +109,22 @@ export default function GameNavigation({
},
];

useEffect(() => {
// Ensure game_node and player displays are available before setting players
if (game_node && player_one_display && player_two_display) {
setPlayers([
{
name: player_one_display?.name || "",
address: player_one_display?.address || ""
},
{
name: player_two_display?.name || "",
address: player_two_display?.address || ""
}
]);
}
}, [game_node, player_one_display, player_two_display, setPlayers]);

return (
<nav className="relative w-full h-40">
<div className="bg-[url('./assets/left-entry.png')] h-40 w-[45%] bg-cover bg-center bg-no-repeat absolute top-0 left-0">
Expand Down
41 changes: 41 additions & 0 deletions client/src/components/not-enough.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Dialog } from "@material-tailwind/react";
import { useState } from "react";
import { Button } from "./ui/button";
import { BellIcon } from "@heroicons/react/24/solid";

export default function NotEnough({ isEnough }: { isEnough: boolean }) {
const [open, setOpen] = useState(!isEnough);
const handleOpen = () => setOpen(open);
return(
<div className="">
<Dialog
open={false}
handler={handleOpen}
className="flex flex-col items-center justify-center bg-transparent"
>
<div className="w-[575px] h-80 bg-[#0F1116] border-2 border-[#272A32] rounded-2xl p-8">
<div className="w-full h-full flex flex-col items-center justify-center space-y-5">
<h3 className="text-white font-semibold text-2xl">Missing Token</h3>
<p className="text-center text-white text-lg font-medium">This Game is currently available to the general public, Input an email address to be notified when available to all users</p>
<input placeholder="Input Email Address" className="w-full p-3.5 bg-[#1A1E25] rounded-lg outline-none text-white" />
<Button className="bg-[#F58229] hover:bg-[#F58229] font-medium hover:cursor-pointer rounded-3xl" onClick={() => {
setOpen(true)
if (window.location.pathname === "/") {
setOpen(true)
} else {
window.location.href === "/"
}
}}>
<div className="flex flex-row items-center space-x-1">
<BellIcon className="text-[#FCE3AA] w-6 h-6" />
<p className="text-[#FCE3AA] font-semibold">
Get Notified
</p>
</div>
</Button>
</div>
</div>
</Dialog>
</div>
)
}
9 changes: 9 additions & 0 deletions client/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,14 @@ export const gameStarted = (games_data_one: any, games_data_two: any) =>
games_data_two?.pit6 == 4
);


export const normalizeAddress = (address: string) => {
// Remove '0x' prefix, convert to lowercase, and pad with leading zeros if needed
const cleanAddress = address?.toLowerCase()?.replace("0x", "");
// Pad to 64 characters (32 bytes) with leading zeros
return cleanAddress?.padStart(64, "0");
};

export const MancalaBoardModelsQuery = gql`
query mancalaSaltMancalaBoardModels {
mancalaSaltMancalaBoardModels {
Expand Down Expand Up @@ -4685,3 +4693,4 @@ export const TUTORIAL_STEPS: TutorialStep[] = [
],
},
];

14 changes: 12 additions & 2 deletions client/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@ import link from "../assets/link-out.png";
import { Link } from "react-router-dom";
import Bubble from "@/components/ui/svgs/bubble";
import small_logo from "@/assets/small-logo.png";
import { useAccount, useConnect } from "@starknet-react/core";
import { Button } from "@/components/ui/button";

export default function Home() {
const { isConnected } = useAccount();
const { connect, connectors } = useConnect();
return (
<div className="bg-[#0F1116] bg-[url('./assets/bg.png')] bg-cover bg-center w-full h-full min-h-screen flex flex-col items-center justify-center">
<div className="bg-[url('./assets/home-box.png')] bg-cover bg-center bg-no-repeat w-[874px] h-[486px] flex flex-col items-center justify-center space-y-20">
<img src={logo} alt="logo" className="w-56 h-16" />
<div className="flex flex-col space-y-5 text-center">
<Link to="/lobby">
{
isConnected ? <Link to="/lobby">
<button className="bg-[#1A1D25] text-[#F58229] py-2.5 px-7 rounded-full flex flex-row items-center justify-center space-x-1">
<Bubble />
<p>Go to lobby</p>
</button>
</Link>
</Link> : <Button className="bg-[#F58229] hover:bg-[#F58229] font-medium hover:cursor-pointer rounded-3xl" onClick={() => connect({ connector: connectors[0] })}>
<div className="flex flex-row items-center space-x-1">
<p className="text-[#FCE3AA] font-medium">Connect Wallet</p>
</div>
</Button>
}
<Link
to="/"
className="flex flex-row items-center justify-center space-x-1"
Expand Down
67 changes: 64 additions & 3 deletions client/src/pages/games/Gameplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
MancalaBoardModelQuery,
MancalaPlayerNames,
MancalaPlayQuery,
normalizeAddress,
} from "@/lib/constants";
import { useQuery } from "@apollo/client";
import AudioSection from "@/components/gameplay/audio-section";
Expand All @@ -16,6 +17,12 @@ import RestartButton from "@/components/gameplay/restart-button";
import EndgameButton from "@/components/gameplay/end-game-button";
import GameNavigation from "@/components/gameplay/game-navigation";
import TimeoutButton from "@/components/gameplay/timeout-button";
import { Dialog, DialogBackdrop, DialogPanel } from "@headlessui/react";
import winner from "@/assets/win.png";
import lose from "@/assets/lose.png";
import end from "@/assets/end.png";
import createIcon from "@/assets/createIcon.png";
import { Button } from "@/components/ui/button";

export default function Gameplay() {
const { gameId } = useParams();
Expand Down Expand Up @@ -46,9 +53,7 @@ export default function Gameplay() {
)
: 0;
const opponent_position = player_position === 0 ? 1 : 0;
const opposition_address =
game_players?.mancalaSaltPlayerModels.edges[opponent_position]?.node
.address;
const opposition_address = game_players?.mancalaSaltPlayerModels.edges[opponent_position]?.node.address;
startMetadataPolling(100);
startPlayersPolling(100);
const [volume, setVolume] = useState(35);
Expand All @@ -58,6 +63,13 @@ export default function Gameplay() {
message: "",
});

const is_finished = involved && game_node?.status === "Finished";
const [open, setOpen] = useState(is_finished);
const handleClose = () => {
setOpen(false)
};
const user_won = normalizeAddress(game_node?.winner) === normalizeAddress(account.account?.address || "");
const [players, setPlayers] = useState<{ name: string, address: string }[]>()
return (
<main className="min-h-screen w-full bg-[#0F1116] bg-[url('./assets/bg.png')] bg-cover bg-center bg-no-repeat flex flex-col items-center overflow-y-scroll">
<GameNavigation
Expand All @@ -73,6 +85,7 @@ export default function Gameplay() {
action={action}
setAction={setAction}
moveMessage={moveMessage}
setPlayers={setPlayers}
/>
<div className="w-full h-[calc(100vh-200px)] max-w-7xl flex flex-row items-start space-x-10">
<div className="flex flex-col justify-center space-y-5 w-fit">
Expand Down Expand Up @@ -114,6 +127,54 @@ export default function Gameplay() {
</div>
</div>
</div>
<div>
<Dialog
open={open}
onClose={handleClose}
className="fixed inset-0 z-50 bg-transparent shadow-none flex items-center justify-center"
>
<DialogBackdrop
transition
className="fixed inset-0 backdrop-blur-sm transition-opacity data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in"
/>
<DialogPanel
transition
className="relative flex flex-col items-center justify-center transform overflow-hidden rounded-lg text-left shadow-xl transition-all data-[closed]:translate-y-4 data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in data-[closed]:sm:translate-y-0 data-[closed]:sm:scale-95"
>
<div className="bg-[#0F1116] border-2 border-[#272A32] rounded-2xl w-[800px] h-[560px] px-16">
<div className="w-full h-full flex flex-col items-center justify-center">
<div className="flex flex-row items-center justify-end w-full">
<Button
className="p-0 bg-transparent rounded-full absolute top-8 right-5"
onClick={handleClose}
>
<img
src={end}
width={50}
height={50}
alt="cancel"
className="rounded-full"
/>
</Button>
</div>
<div className="flex flex-col items-center justify-center space-y-3.5">
<img src={user_won ? winner : lose} className="w-40 h-52" />
<h3 className="text-2xl text-white font-semibold">{user_won ? "You Won" : "You Lost"}</h3>
<p className="text-[#4F5666] text-lg">{user_won ? `Congratulations you beat ${players?.[opponent_position].name || ""}` : `You couldn't beat ${players?.[opponent_position].name || ""}`}</p>
<Button
className="bg-[#F58229] hover:bg-[#F58229] font-medium hover:cursor-pointer rounded-3xl"
>
<div className="flex flex-row items-center space-x-1">
<img src={createIcon} className="w-5 h-5" />
<p className="text-[#FCE3AA] font-medium">Share</p>
</div>
</Button>
</div>
</div>
</div>
</DialogPanel>
</Dialog>
</div>
</div>
</main>
);
Expand Down
Loading