Skip to content

Commit fb37793

Browse files
committed
feat: starknet wallet connector
1 parent 35d17c4 commit fb37793

File tree

9 files changed

+831
-103
lines changed

9 files changed

+831
-103
lines changed

package-lock.json

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

package.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,23 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@starknet-react/chains": "^3.1.3",
13+
"@starknet-react/core": "^3.7.4",
14+
"framer-motion": "^12.9.4",
15+
"lucide-react": "^0.507.0",
16+
"next": "15.3.1",
1217
"react": "^19.0.0",
13-
"react-dom": "^19.0.0",
14-
"next": "15.3.1"
18+
"react-dom": "^19.0.0"
1519
},
1620
"devDependencies": {
17-
"typescript": "^5",
21+
"@eslint/eslintrc": "^3",
22+
"@tailwindcss/postcss": "^4",
1823
"@types/node": "^20",
1924
"@types/react": "^19",
2025
"@types/react-dom": "^19",
21-
"@tailwindcss/postcss": "^4",
22-
"tailwindcss": "^4",
2326
"eslint": "^9",
2427
"eslint-config-next": "15.3.1",
25-
"@eslint/eslintrc": "^3"
28+
"tailwindcss": "^4",
29+
"typescript": "^5"
2630
}
2731
}

src/app/components/navbar.tsx

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
"use client";
2+
3+
import { useState, useRef, useEffect } from "react";
4+
import Link from "next/link";
5+
import Image from "next/image";
6+
import { Menu, X, MoreVertical } from "lucide-react";
7+
import { motion, AnimatePresence } from "framer-motion";
8+
import { useWalletContext } from "./walletProvider";
9+
import AnimationWrapper from "../motion/animation-wrapper";
10+
import WalletConnectModal from "./wallet-connect-modal";
11+
import WalletDisconnectModal from "./wallet-disconnect-modal";
12+
13+
export default function Navbar() {
14+
const [isMenuOpen, setIsMenuOpen] = useState(false);
15+
const [isConnectModalOpen, setIsConnectModalOpen] = useState(false);
16+
const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false);
17+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
18+
const dropdownRef = useRef<HTMLDivElement>(null);
19+
const { account, connectWallet, disconnectWallet, connectors } =
20+
useWalletContext();
21+
// Close dropdown when clicking outside
22+
useEffect(() => {
23+
function handleClickOutside(event: MouseEvent) {
24+
if (
25+
dropdownRef.current &&
26+
!dropdownRef.current.contains(event.target as Node)
27+
) {
28+
setIsDropdownOpen(false);
29+
}
30+
}
31+
32+
document.addEventListener("mousedown", handleClickOutside);
33+
return () => {
34+
document.removeEventListener("mousedown", handleClickOutside);
35+
};
36+
}, []);
37+
const toggleMenu = () => setIsMenuOpen(!isMenuOpen);
38+
const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen);
39+
const handleWalletSelect = (walletId: string) => {
40+
const connector = connectors.find((c) => c.id === walletId);
41+
if (connector) {
42+
connectWallet(connector); // invoke Starknet-React’s useConnect() :contentReference[oaicite:3]{index=3}
43+
}
44+
setIsConnectModalOpen(false);
45+
};
46+
const handleConnectWallet = () => {
47+
setIsConnectModalOpen(true);
48+
};
49+
const handleWalletClick = () => {
50+
setIsDisconnectModalOpen(true);
51+
};
52+
const handleDisconnect = () => {
53+
disconnectWallet(); // real Starknet-React disconnect :contentReference[oaicite:4]{index=4}
54+
setIsDisconnectModalOpen(false);
55+
};
56+
const navLinks = [
57+
{ name: "Features", href: "/#features" },
58+
{ name: "How It Works", href: "/#how-it-works" },
59+
{ name: "Benefits", href: "/#benefits" },
60+
{ name: "FAQs", href: "/#faqs" },
61+
];
62+
63+
return (
64+
<>
65+
<header className="mt-5 fixed w-full z-40">
66+
<div className="container mx-auto px-4 py-3 flex items-center justify-between bg-[#0A1330] rounded-[48px]">
67+
<AnimationWrapper variant="slideRight">
68+
<Link href="/" className="flex items-center">
69+
<Image
70+
src="/Logo.png"
71+
alt="blockopoly"
72+
width={120}
73+
height={32}
74+
className="h-8 w-auto"
75+
/>
76+
</Link>
77+
</AnimationWrapper>
78+
79+
{/* Desktop Navigation */}
80+
81+
<nav className="hidden md:flex items-center space-x-8 text-[#FCFCFC]">
82+
{navLinks.map((link) => (
83+
<AnimationWrapper variant="slideRight">
84+
<Link
85+
href={link.href}
86+
key={link.name}
87+
className="text-gray-300 hover:text-white transition-colors"
88+
>
89+
{link.name}
90+
</Link>
91+
</AnimationWrapper>
92+
))}
93+
</nav>
94+
95+
{/* Wallet Connection Button or Connected Wallet */}
96+
97+
<div className="hidden md:block">
98+
<AnimationWrapper variant="slideLeft">
99+
{!account ? (
100+
<button
101+
onClick={handleConnectWallet}
102+
className="px-5 py-2 rounded-full bg-teal-500 text-white font-medium hover:bg-teal-600 transition-colors"
103+
>
104+
Connect Wallet
105+
</button>
106+
) : (
107+
<div className="relative" ref={dropdownRef}>
108+
<div
109+
onClick={handleWalletClick}
110+
className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#0d0e24] border border-gray-800 cursor-pointer hover:border-gray-600 transition-colors"
111+
>
112+
<div className="h-8 w-8 rounded-full border-2 border-teal-500 overflow-hidden">
113+
<Image
114+
src="/Avater.svg"
115+
alt="Wallet Avatar"
116+
width={32}
117+
height={32}
118+
className="object-cover"
119+
/>
120+
</div>
121+
122+
<span className="text-white font-medium">
123+
{account.slice(0, 6)}{account.slice(-4)}
124+
</span>
125+
126+
<button
127+
onClick={(e) => {
128+
e.stopPropagation();
129+
130+
toggleDropdown();
131+
}}
132+
className="h-8 w-8 p-0 text-gray-400 hover:text-white transition-colors"
133+
>
134+
<MoreVertical size={16} />
135+
</button>
136+
</div>
137+
138+
{/* Custom Dropdown Menu */}
139+
140+
{isDropdownOpen && (
141+
<div className="absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-[#0d0e24] border border-gray-800 overflow-hidden">
142+
<div className="py-1">
143+
<button
144+
onClick={handleWalletClick}
145+
className="w-full text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-800 transition-colors"
146+
>
147+
Disconnect
148+
</button>
149+
150+
<Link
151+
href="/dashboard"
152+
className="w-full block text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-800 transition-colors"
153+
>
154+
View Profile
155+
</Link>
156+
157+
<button className="w-full text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-800 transition-colors">
158+
Settings
159+
</button>
160+
</div>
161+
</div>
162+
)}
163+
</div>
164+
)}
165+
</AnimationWrapper>
166+
</div>
167+
168+
{/* Mobile Menu Button */}
169+
170+
<div className="md:hidden flex items-center">
171+
<AnimationWrapper variant="slideLeft">
172+
{!account ? (
173+
<button
174+
onClick={handleConnectWallet}
175+
className="px-4 py-1.5 mr-4 rounded-full bg-teal-500 text-white text-sm font-medium hover:bg-teal-600 transition-colors"
176+
>
177+
Connect Wallet
178+
</button>
179+
) : (
180+
<div
181+
onClick={handleWalletClick}
182+
className="flex items-center gap-2 px-2 py-1 mr-4 rounded-full bg-[#0d0e24] border border-gray-800 cursor-pointer"
183+
>
184+
<div className="h-6 w-6 rounded-full border border-teal-500 overflow-hidden">
185+
<Image
186+
src="/icons/braavos.png"
187+
alt="Wallet Avatar"
188+
width={24}
189+
height={24}
190+
className="object-cover"
191+
/>
192+
</div>
193+
194+
<span className="text-white text-xs font-medium">
195+
{account.slice(0, 6)}{account.slice(-4)}
196+
</span>
197+
</div>
198+
)}
199+
</AnimationWrapper>
200+
201+
<button onClick={toggleMenu} className="text-white">
202+
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
203+
</button>
204+
</div>
205+
</div>
206+
207+
{/* Mobile Navigation */}
208+
209+
<AnimatePresence>
210+
{isMenuOpen && (
211+
<motion.div
212+
initial={{ height: 0, opacity: 0 }}
213+
animate={{ height: "auto", opacity: 1 }}
214+
exit={{ height: 0, opacity: 0 }}
215+
className="md:hidden overflow-hidden bg-[#0a0b1e]"
216+
>
217+
<div className="container mx-auto px-4 py-4 flex flex-col space-y-4">
218+
{navLinks.map((link, index) => (
219+
<AnimationWrapper
220+
key={link.name}
221+
variant="slideRight"
222+
delay={index * 0.1}
223+
>
224+
<Link
225+
href={link.href}
226+
className="text-gray-300 hover:text-white transition-colors block py-2"
227+
onClick={() => setIsMenuOpen(false)}
228+
>
229+
{link.name}
230+
</Link>
231+
</AnimationWrapper>
232+
))}
233+
</div>
234+
</motion.div>
235+
)}
236+
</AnimatePresence>
237+
</header>
238+
239+
<WalletConnectModal
240+
isOpen={isConnectModalOpen}
241+
onClose={() => setIsConnectModalOpen(false)}
242+
onSelect={handleWalletSelect}
243+
/>
244+
245+
<WalletDisconnectModal
246+
isOpen={isDisconnectModalOpen}
247+
onClose={() => setIsDisconnectModalOpen(false)}
248+
onDisconnect={handleDisconnect}
249+
/>
250+
</>
251+
);
252+
}

0 commit comments

Comments
 (0)