Skip to content

Commit d8fadae

Browse files
committed
Day-7: Completed Integration of React and Smart Contracts
1 parent 1fcf42f commit d8fadae

File tree

19 files changed

+43328
-3431
lines changed

19 files changed

+43328
-3431
lines changed

client/package-lock.json

Lines changed: 15325 additions & 3227 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"@thirdweb-dev/react": "^4.9.4",
14+
"gsap": "^3.12.5",
1315
"react": "^18.3.1",
1416
"react-dom": "^18.3.1",
15-
"react-router-dom": "^7.0.2"
17+
"react-router-dom": "^7.0.2",
18+
"web3": "^4.16.0"
1619
},
1720
"devDependencies": {
1821
"@eslint/js": "^9.15.0",

client/src/App.jsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import React from "react";
2-
import { BrowserRouter , Routes , Route } from "react-router-dom";
3-
import Home from "./pages/Home"
4-
import Navbar from "./components/Navbar";
5-
import Profile from "./pages/Profile";
2+
import { BrowserRouter, Routes, Route } from "react-router-dom";
3+
import Home from "./pages/Home";
4+
import Game from "./pages/Game";
65
import Market from "./pages/Market";
6+
import Profile from "./pages/Profile";
7+
import Navbar from "./components/Navbar";
78

89
const App = () => {
10+
console.log("App is rendering!");
911

1012
return (
1113
<BrowserRouter>
12-
<Navbar />
13-
<Routes>
14-
<Route path="/" element={<Home />} />
15-
<Route path="/profile" element={<Profile />} />
16-
<Route path="/market" element={<Market />} />
17-
</Routes>
14+
<Navbar />
15+
<Routes>
16+
<Route path="/" element={<Home />} />
17+
<Route path="/profile" element={<Profile />} />
18+
<Route path="/game" element={<Game />} />
19+
<Route path="/market" element={<Market />} />
20+
</Routes>
1821
</BrowserRouter>
19-
)
20-
}
22+
);
23+
};
2124

22-
export default App
25+
export default App;

client/src/components/NFTCard.jsx

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import React, { useState, useEffect } from "react";
2+
import { useGlobalContext } from "../context";
3+
import "../index.css";
4+
5+
const NFTCard = ({ nft, setOwnedNFTs }) => {
6+
const { contracts, accounts, setActiveSkin, activeSkin, setActiveBackground, activeBackground } = useGlobalContext();
7+
8+
const [isListModalOpen, setIsListModalOpen] = useState(false);
9+
const [price, setPrice] = useState(0);
10+
const [isChangesApplied, setIsChangesApplied] = useState(false); // For managing the pop-up visibility
11+
12+
const handleChange = () => {
13+
if (nft.type === "spaceship") setActiveSkin(nft.image);
14+
else setActiveBackground(nft.image);
15+
16+
// Show the pop-up after applying changes
17+
setIsChangesApplied(true);
18+
19+
// Hide the pop-up after 3 seconds
20+
setTimeout(() => {
21+
setIsChangesApplied(false);
22+
}, 3000);
23+
};
24+
25+
useEffect(() => {
26+
console.log("Updated activeSkin:", activeSkin);
27+
}, [activeSkin]);
28+
29+
const handleListNFT = () => {
30+
console.log(price);
31+
console.log(contracts.NFT_MarketPlace._address);
32+
33+
if (price <= 0) return;
34+
35+
const listNFT = async () => {
36+
try {
37+
// Approve the NFT for the marketplace
38+
// await contracts.NFT_MarketPlace.methods
39+
// .approve(contracts.NFT_MarketPlace._address, nft.tokenId)
40+
// .send({ from: accounts[0] });
41+
42+
// List the NFT on the marketplace
43+
await contracts.NFT_MarketPlace.methods
44+
.listNFT(nft.tokenId, price)
45+
.send({ from: accounts[0] });
46+
47+
console.log(`NFT with Token ID ${nft.tokenId} listed for sale.`);
48+
49+
setOwnedNFTs((prevNFTs) =>
50+
prevNFTs.filter((ownedNFT) => ownedNFT.tokenId !== nft.tokenId)
51+
);
52+
53+
setIsListModalOpen(false);
54+
55+
} catch (error) {
56+
console.error("Failed to list NFT for sale:", error);
57+
}
58+
};
59+
60+
listNFT();
61+
};
62+
63+
return (
64+
<div className="w-64 p-4 rounded-lg bg-gradient-to-b from-gray-700 via-gray-800 to-gray-900 shadow-lg mt-5">
65+
<div className="w-fill h-40 md:h-60 flex items-center justify-center bg-gray-600 rounded-md overflow-hidden">
66+
<img
67+
src={nft.image}
68+
alt={nft.name}
69+
className="object-cover w-full h-full"
70+
/>
71+
</div>
72+
<div className="mt-4 text-center">
73+
<h4 className="text-xl font-semibold text-gray-200">{nft.name}</h4>
74+
<p className="text-sm text-gray-400 mt-2">{nft.description}</p>
75+
</div>
76+
<div className="mt-4 flex justify-center gap-5">
77+
<button
78+
className="px-4 py-2 rounded-lg bg-blue-500 hover:bg-blue-600 text-white font-bold"
79+
onClick={() => setIsListModalOpen(!isListModalOpen)}
80+
>
81+
List
82+
</button>
83+
<button
84+
className="px-4 py-2 rounded-lg bg-green-500 hover:bg-green-600 text-white font-bold"
85+
onClick={handleChange}
86+
>
87+
Use
88+
</button>
89+
</div>
90+
91+
{isListModalOpen && (
92+
<div className="fixed inset-0 bg-black bg-opacity-75 flex justify-center items-center z-50">
93+
<div className="bg-gray-800 p-6 rounded-lg shadow-lg text-center w-11/12 md:w-1/3">
94+
<h3 className="text-2xl font-bold mb-4">Select Price</h3>
95+
<input
96+
className="p-3 w-full rounded-md bg-gray-700 text-white text-lg mb-6"
97+
type="number"
98+
value={price}
99+
placeholder="Enter price"
100+
onChange={(e) => setPrice(e.target.value)}
101+
/>
102+
<div className="flex gap-4 justify-center mt-4">
103+
<button
104+
onClick={() => setIsListModalOpen(false)}
105+
className="px-6 py-2 w-36 bg-gradient-to-r from-red-500 to-pink-500 rounded-lg text-white font-bold hover:from-pink-500 hover:to-red-500 transition-all"
106+
>
107+
Cancel
108+
</button>
109+
<button
110+
onClick={handleListNFT}
111+
className="px-6 py-2 w-36 bg-gradient-to-r from-green-500 to-blue-500 rounded-lg text-white font-bold hover:from-blue-500 hover:to-green-500 transition-all"
112+
>
113+
List
114+
</button>
115+
</div>
116+
</div>
117+
</div>
118+
)}
119+
120+
{isChangesApplied && (
121+
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50">
122+
<div className="bg-gray-800 p-6 rounded-lg shadow-lg text-center w-11/12 md:w-1/3">
123+
<h3 className="text-xl font-semibold text-green-500">Changes Applied!</h3>
124+
<p className="text-sm text-gray-300 mt-2">Your spaceship or background has been updated.</p>
125+
</div>
126+
</div>
127+
)}
128+
</div>
129+
);
130+
};
131+
132+
export default NFTCard;

client/src/components/Navbar.jsx

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,72 @@
1-
import React from 'react'
2-
import { NavLink } from 'react-router-dom'
1+
import React, { useEffect } from "react";
2+
import { NavLink, Link } from "react-router-dom";
3+
import { useGlobalContext } from "../context";
34

45
const Navbar = () => {
6+
const { isRegistered, updateRegistrationStatus } = useGlobalContext();
7+
8+
useEffect(() => {
9+
updateRegistrationStatus();
10+
}, [updateRegistrationStatus]);
11+
512
return (
6-
<div className='bg-gradient-to-b from-gray-900 to-black px-6 py-4 flex justify-between items-center shadow-lg'>
7-
<div className='text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500'>
8-
SpaceWars
13+
<nav className="bg-gradient-to-b from-gray-900 to-black px-6 py-4 flex justify-between items-center shadow-lg">
14+
{isRegistered ? (
15+
<div
16+
className="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500"
17+
>
18+
SpaceWars
919
</div>
10-
<div className='flex space-x-8'>
11-
<NavLink to="/profile" className={({isActive}) => `text-lg font-semibold ${isActive ? "text-white underline underline-offset-4" : "text-gray-400 hover:text-white"} transition-all duration-300`} >
12-
Profile
13-
</NavLink>
14-
<NavLink to="/market" className={({isActive}) => `text-lg font-semibold ${isActive ? "text-white underline underline-offset-4" : "text-gray-400 hover:text-white"} transition-all duration-300`} >
15-
Market
16-
</NavLink>
17-
<NavLink to="/game" className={({isActive}) => `text-lg font-semibold ${isActive ? "text-white underline underline-offset-4" : "text-gray-400 hover:text-white"} transition-all duration-300`} >
18-
Game
19-
</NavLink>
20+
) : (
21+
<Link
22+
to="/"
23+
className="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500"
24+
>
25+
SpaceWars
26+
</Link>
27+
)}
28+
{isRegistered && (
29+
<div className="flex space-x-6">
30+
<NavLink
31+
to="/profile"
32+
className={({ isActive }) =>
33+
`text-lg font-semibold ${
34+
isActive
35+
? "text-white underline underline-offset-4"
36+
: "text-gray-400 hover:text-white"
37+
} transition-all duration-300`
38+
}
39+
>
40+
Profile
41+
</NavLink>
42+
<NavLink
43+
to="/market"
44+
className={({ isActive }) =>
45+
`text-lg font-semibold ${
46+
isActive
47+
? "text-white underline underline-offset-4"
48+
: "text-gray-400 hover:text-white"
49+
} transition-all duration-300`
50+
}
51+
>
52+
Market
53+
</NavLink>
54+
<NavLink
55+
to="/game"
56+
className={({ isActive }) =>
57+
`text-lg font-semibold ${
58+
isActive
59+
? "text-white underline underline-offset-4"
60+
: "text-gray-400 hover:text-white"
61+
} transition-all duration-300`
62+
}
63+
>
64+
Game
65+
</NavLink>
2066
</div>
21-
</div>
22-
)
23-
}
67+
)}
68+
</nav>
69+
);
70+
};
2471

25-
export default Navbar
72+
export default Navbar;

client/src/context/index.jsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { createContext, useContext, useEffect, useState } from "react";
2+
import defaultSpaceshipImage from "../assets/spaceship.png";
3+
import gameBackgroundImage from "../assets/background.jpg";
4+
import {
5+
NFT_MarketPlace_ADDRESS,
6+
NFT_MarketPlace_ABI,
7+
SpaceWars_ADDRESS,
8+
SpaceWars_ABI,
9+
} from "../contract/contractConfig.js";
10+
import Web3 from "web3";
11+
12+
const GlobalContext = createContext();
13+
14+
export const GlobalContextProvider = ({ children }) => {
15+
const [accounts, setAccounts] = useState([]);
16+
const [contracts, setContracts] = useState({
17+
NFT_MarketPlace: "",
18+
SpaceWars: "",
19+
});
20+
const [isRegistered, setIsRegistered] = useState(false);
21+
const [activeSkin, setActiveSkin] = useState(defaultSpaceshipImage);
22+
const [activeBackground, setActiveBackground] = useState(gameBackgroundImage);
23+
24+
const updateRegistrationStatus = async () => {
25+
const name = await contracts.SpaceWars.methods
26+
.getPlayerName()
27+
.call({ from: accounts[0] });
28+
29+
setIsRegistered(!!name);
30+
};
31+
useEffect(() => {
32+
try {
33+
const web3 = new Web3(window.ethereum);
34+
35+
const NFT_MarketPlace_Contract = new web3.eth.Contract(
36+
NFT_MarketPlace_ABI,
37+
NFT_MarketPlace_ADDRESS
38+
);
39+
const SpaceWars_Contract = new web3.eth.Contract(
40+
SpaceWars_ABI,
41+
SpaceWars_ADDRESS
42+
);
43+
44+
setContracts({
45+
NFT_MarketPlace: NFT_MarketPlace_Contract,
46+
SpaceWars: SpaceWars_Contract,
47+
});
48+
49+
console.log(contracts);
50+
51+
const getAccounts = async () => {
52+
await window.eth_requestAccounts;
53+
const accounts = await web3.eth.getAccounts();
54+
setAccounts(accounts);
55+
console.log(accounts);
56+
};
57+
58+
getAccounts();
59+
} catch (error) {
60+
console.log(error);
61+
}
62+
}, []);
63+
64+
return (
65+
<GlobalContext.Provider
66+
value={{
67+
accounts,
68+
contracts,
69+
setAccounts,
70+
isRegistered,
71+
updateRegistrationStatus,
72+
activeSkin,
73+
setActiveSkin,
74+
activeBackground,
75+
setActiveBackground
76+
}}
77+
>
78+
{children}
79+
</GlobalContext.Provider>
80+
);
81+
};
82+
83+
export const useGlobalContext = () => useContext(GlobalContext);

0 commit comments

Comments
 (0)