Skip to content

Commit 893f762

Browse files
committed
UI improvements: dice and roll button in center, conditional button states, favicon and metadata updates, Base Sepolia network support
1 parent 92a8587 commit 893f762

11 files changed

Lines changed: 165 additions & 55 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"use client";
2+
3+
// This layout overrides the default layout for the main page
4+
// It uses ScaffoldEthAppWithProviders with hideHeader=true to remove Header/Footer
5+
// The parent layout (app/layout.tsx) already provides all providers, so we just need to wrap with ScaffoldEthAppWithProviders
6+
import { ScaffoldEthAppWithProviders } from "~~/components/ScaffoldEthAppWithProviders";
7+
8+
export default function MainLayout({ children }: { children: React.ReactNode }) {
9+
// Note: We're using ScaffoldEthAppWithProviders here, which will create duplicate providers
10+
// But this is necessary because we need to override the Header/Footer behavior
11+
// The providers are idempotent, so this should be safe
12+
return <ScaffoldEthAppWithProviders hideHeader={true}>{children}</ScaffoldEthAppWithProviders>;
13+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"use client";
2+
3+
import type { NextPage } from "next";
4+
import { BackgammonBoard } from "~~/components/BackgammonBoard";
5+
import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth/RainbowKitCustomConnectButton";
6+
7+
const Home: NextPage = () => {
8+
return (
9+
<>
10+
<div className="flex flex-col min-h-screen">
11+
{/* Wallet Connect Button - Top Right */}
12+
<div className="flex justify-end p-4">
13+
<RainbowKitCustomConnectButton />
14+
</div>
15+
{/* Main Game Board */}
16+
<div className="flex items-center flex-col grow pt-10">
17+
<div className="px-5 w-auto rounded-lg">
18+
<BackgammonBoard />
19+
</div>
20+
</div>
21+
</div>
22+
</>
23+
);
24+
};
25+
26+
export default Home;

packages/nextjs/app/layout.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import "@rainbow-me/rainbowkit/styles.css";
22
import "@scaffold-ui/components/styles.css";
3-
import { ScaffoldEthAppWithProviders } from "~~/components/ScaffoldEthAppWithProviders";
3+
import { ConditionalLayout } from "~~/components/ConditionalLayout";
44
import { ThemeProvider } from "~~/components/ThemeProvider";
55
import "~~/styles/globals.css";
66
import { getMetadata } from "~~/utils/scaffold-eth/getMetadata";
77

88
export const metadata = getMetadata({
9-
title: "Scaffold-ETH 2 App",
10-
description: "Built with 🏗 Scaffold-ETH 2",
9+
title: "Web3 Backgammon - Solidity University",
10+
description:
11+
"Play Backgammon on the blockchain! A fun and user-friendly game created by Solidity University students during our Web3 development course.",
1112
});
1213

1314
const ScaffoldEthApp = ({ children }: { children: React.ReactNode }) => {
1415
return (
1516
<html suppressHydrationWarning className={``}>
1617
<body>
1718
<ThemeProvider enableSystem>
18-
<ScaffoldEthAppWithProviders>{children}</ScaffoldEthAppWithProviders>
19+
<ConditionalLayout>{children}</ConditionalLayout>
1920
</ThemeProvider>
2021
</body>
2122
</html>

packages/nextjs/app/page.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22

33
import type { NextPage } from "next";
44
import { BackgammonBoard } from "~~/components/BackgammonBoard";
5+
import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth/RainbowKitCustomConnectButton";
56

67
const Home: NextPage = () => {
78
return (
89
<>
9-
<div className="flex items-center flex-col grow pt-10">
10-
<div className="px-5 w-auto rounded-lg">
11-
<BackgammonBoard />
10+
<div className="flex flex-col min-h-screen">
11+
{/* Wallet Connect Button - Top Right */}
12+
<div className="flex justify-end p-4">
13+
<RainbowKitCustomConnectButton />
14+
</div>
15+
{/* Main Game Board */}
16+
<div className="flex items-center flex-col grow pt-10">
17+
<div className="px-5 w-auto rounded-lg">
18+
<BackgammonBoard />
19+
</div>
1220
</div>
1321
</div>
1422
</>

packages/nextjs/components/BackgammonBoard.tsx

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ export const BackgammonBoard = () => {
671671
<div className="flex gap-6">
672672
{/* Board */}
673673
<div className="bg-amber-100 rounded-lg p-6 shadow-xl flex-1">
674-
<h2 className="text-2xl font-bold text-center mb-6 text-amber-900">Backgammon Board</h2>
674+
<h2 className="text-2xl font-bold text-center mb-6 text-amber-900">WEB3 НАРДЫ</h2>
675675

676676
{/* Clickable area for field 0 (dead white checkers) - above top row */}
677677
<div
@@ -724,12 +724,85 @@ export const BackgammonBoard = () => {
724724
</div>
725725

726726
{/* Top half: cells 12-1 (right to left) */}
727-
<div className="flex gap-4 mb-[100px]">
727+
<div className="flex gap-4 mb-4">
728728
{Array.from({ length: 12 }, (_, i) => 12 - i).map(cell => renderPoint(cell, true))}
729729
</div>
730730

731+
{/* Center: Dice images and roll button - always reserve space */}
732+
<div className="flex justify-center items-center gap-2 my-4 min-h-[48px]">
733+
{((isItBlackTurn && blackDiceRolled) || (!isItBlackTurn && whiteDiceRolled)) &&
734+
(isItBlackTurn ? blackAvailableMoveValues : whiteAvailableMoveValues).map((value, index) => (
735+
<Image key={index} src={`/${value}.png`} alt={`Dice ${value}`} width={48} height={48} />
736+
))}
737+
{((isItBlackTurn && !blackDiceRolled) || (!isItBlackTurn && !whiteDiceRolled)) &&
738+
(() => {
739+
// Determine if current user can roll dice
740+
const isCurrentUserBlack =
741+
currentAddress &&
742+
blackPlayer &&
743+
typeof blackPlayer === "string" &&
744+
currentAddress.toLowerCase() === blackPlayer.toLowerCase();
745+
const isCurrentUserWhite =
746+
currentAddress &&
747+
whitePlayer &&
748+
typeof whitePlayer === "string" &&
749+
currentAddress.toLowerCase() === whitePlayer.toLowerCase();
750+
751+
// Determine button state and text
752+
let buttonText = "КИНУТЬ КОСТИ";
753+
let isButtonActive = false;
754+
755+
if (isItBlackTurn && !blackDiceRolled) {
756+
// Black's turn, dice not rolled
757+
if (isCurrentUserBlack) {
758+
buttonText = "КИНУТЬ КОСТИ";
759+
isButtonActive = true;
760+
} else {
761+
buttonText = "ЖДЕМ ЧЕРНОГО";
762+
isButtonActive = false;
763+
}
764+
} else if (!isItBlackTurn && !whiteDiceRolled) {
765+
// White's turn, dice not rolled
766+
if (isCurrentUserWhite) {
767+
buttonText = "КИНУТЬ КОСТИ";
768+
isButtonActive = true;
769+
} else {
770+
buttonText = "ЖДЕМ БЕЛОГО";
771+
isButtonActive = false;
772+
}
773+
}
774+
775+
return (
776+
<button
777+
className={`btn ${isButtonActive ? "btn-primary" : "btn-disabled"}`}
778+
disabled={!isButtonActive}
779+
onClick={async () => {
780+
if (!isButtonActive) return;
781+
try {
782+
if (isItBlackTurn) {
783+
await writeBackgammonAsync({
784+
functionName: "rollDiceBlack",
785+
});
786+
} else {
787+
await writeBackgammonAsync({
788+
functionName: "rollDiceWhite",
789+
});
790+
}
791+
} catch (error) {
792+
console.error("Error rolling dice:", error);
793+
const userMessage = parseErrorMessage(error);
794+
notification.error(userMessage);
795+
}
796+
}}
797+
>
798+
{buttonText}
799+
</button>
800+
);
801+
})()}
802+
</div>
803+
731804
{/* Bottom half: cells 13-24 (left to right) */}
732-
<div className="flex gap-4">
805+
<div className="flex gap-4 mt-4">
733806
{Array.from({ length: 12 }, (_, i) => 13 + i).map(cell => renderPoint(cell, false))}
734807
</div>
735808

@@ -803,7 +876,7 @@ export const BackgammonBoard = () => {
803876
)}
804877
{winner === 0 && (
805878
<div className="flex items-center gap-3">
806-
<div className="text-xl font-bold text-amber-900">ХОДИТ:</div>
879+
<div className="text-xl font-bold text-amber-900">ХОД:</div>
807880
{isItBlackTurn ? (
808881
<div className="w-10 h-10 rounded-full bg-gray-800 border-2 border-gray-900 shadow-md flex items-center justify-center">
809882
<div className="w-6 h-6 rounded-full bg-gray-900"></div>
@@ -828,40 +901,6 @@ export const BackgammonBoard = () => {
828901
⚠️ Сначала верните фишки с бара (поле 25)!
829902
</div>
830903
)}
831-
{((isItBlackTurn && blackDiceRolled) || (!isItBlackTurn && whiteDiceRolled)) && (
832-
<div className="flex flex-col gap-2">
833-
<div className="text-xl font-bold text-amber-900">ХОДЫ:</div>
834-
<div className="flex gap-2">
835-
{(isItBlackTurn ? blackAvailableMoveValues : whiteAvailableMoveValues).map((value, index) => (
836-
<Image key={index} src={`/${value}.png`} alt={`Dice ${value}`} width={48} height={48} />
837-
))}
838-
</div>
839-
</div>
840-
)}
841-
{((isItBlackTurn && !blackDiceRolled) || (!isItBlackTurn && !whiteDiceRolled)) && (
842-
<button
843-
className="btn btn-primary"
844-
onClick={async () => {
845-
try {
846-
if (isItBlackTurn) {
847-
await writeBackgammonAsync({
848-
functionName: "rollDiceBlack",
849-
});
850-
} else {
851-
await writeBackgammonAsync({
852-
functionName: "rollDiceWhite",
853-
});
854-
}
855-
} catch (error) {
856-
console.error("Error rolling dice:", error);
857-
const userMessage = parseErrorMessage(error);
858-
notification.error(userMessage);
859-
}
860-
}}
861-
>
862-
Кинуть кости
863-
</button>
864-
)}
865904
</>
866905
)}
867906
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"use client";
2+
3+
import { usePathname } from "next/navigation";
4+
import { ScaffoldEthAppWithProviders } from "~~/components/ScaffoldEthAppWithProviders";
5+
6+
export const ConditionalLayout = ({ children }: { children: React.ReactNode }) => {
7+
const pathname = usePathname();
8+
// Hide header/footer on main page (/) but show on all other pages
9+
const isMainPage = pathname === "/";
10+
11+
return <ScaffoldEthAppWithProviders hideHeader={isMainPage}>{children}</ScaffoldEthAppWithProviders>;
12+
};

packages/nextjs/components/ScaffoldEthAppWithProviders.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import { Header } from "~~/components/Header";
1212
import { BlockieAvatar } from "~~/components/scaffold-eth";
1313
import { wagmiConfig } from "~~/services/web3/wagmiConfig";
1414

15-
const ScaffoldEthApp = ({ children }: { children: React.ReactNode }) => {
15+
const ScaffoldEthApp = ({ children, hideHeader = false }: { children: React.ReactNode; hideHeader?: boolean }) => {
1616
return (
1717
<>
1818
<div className={`flex flex-col min-h-screen `}>
19-
<Header />
19+
{!hideHeader && <Header />}
2020
<main className="relative flex flex-col flex-1">{children}</main>
21-
<Footer />
21+
{!hideHeader && <Footer />}
2222
</div>
2323
<Toaster />
2424
</>
@@ -33,7 +33,13 @@ export const queryClient = new QueryClient({
3333
},
3434
});
3535

36-
export const ScaffoldEthAppWithProviders = ({ children }: { children: React.ReactNode }) => {
36+
export const ScaffoldEthAppWithProviders = ({
37+
children,
38+
hideHeader = false,
39+
}: {
40+
children: React.ReactNode;
41+
hideHeader?: boolean;
42+
}) => {
3743
const { resolvedTheme } = useTheme();
3844
const isDarkMode = resolvedTheme === "dark";
3945
const [mounted, setMounted] = useState(false);
@@ -50,7 +56,7 @@ export const ScaffoldEthAppWithProviders = ({ children }: { children: React.Reac
5056
theme={mounted ? (isDarkMode ? darkTheme() : lightTheme()) : lightTheme()}
5157
>
5258
<ProgressBar height="3px" color="#2299dd" />
53-
<ScaffoldEthApp>{children}</ScaffoldEthApp>
59+
<ScaffoldEthApp hideHeader={hideHeader}>{children}</ScaffoldEthApp>
5460
</RainbowKitProvider>
5561
</QueryClientProvider>
5662
</WagmiProvider>

packages/nextjs/public/favicon.png

-5.61 KB
Binary file not shown.

packages/nextjs/public/favicon.svg

Lines changed: 6 additions & 0 deletions
Loading

packages/nextjs/scaffold.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const DEFAULT_ALCHEMY_API_KEY = "cR4WnXePioePZ5fFrnSiR";
1515

1616
const scaffoldConfig = {
1717
// The networks on which your DApp is live
18-
targetNetworks: [chains.foundry],
18+
targetNetworks: [chains.foundry, chains.baseSepolia], // Add baseSepolia for testnet deployment
1919
// The interval at which your front-end polls the RPC servers for new data (it has no effect if you only target the local network (default is 4000))
2020
pollingInterval: 30000,
2121
// This is ours Alchemy's default API key.

0 commit comments

Comments
 (0)