Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
node-version: "20"

- name: Install dependencies
run: npm install --legacy-peer-deps
run: npm install --force

- name: Run build
run: npm run build
55 changes: 55 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,23 @@
"lint": "next lint"
},
"dependencies": {
"@starknet-react/chains": "^3.1.3",
"@starknet-react/core": "^3.7.4",
"framer-motion": "^12.9.4",
"lucide-react": "^0.507.0",
"next": "15.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.3.1"
"react-dom": "^19.0.0"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.3.1",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4",
"typescript": "^5"
}
}
16 changes: 16 additions & 0 deletions src/app/components/icons/logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function Logo() {
return (
<svg
width="46"
height="39"
viewBox="0 0 57 49"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.5 3.50011L0 25.0001L13.5 49H27.5C22.1667 40.3333 12.029 22.215 11 20.5C9.5 18 5.32918 19.0125 6 17C6.5 15.5 11.5 9 15.5 15C20.6667 24.5 31.5514 43.7617 33 45.5C35.5 48.5 42 49 44.5 45.5L56.5 25.0001L43 0H29C34.8333 10.1667 45 28 47 30C48.7423 31.7423 50.5 31 50.5 31C50.1667 31.8333 49.2 33.8 48 35C45.1602 37.8398 41.5 36 40.9135 35.37C35.1366 26.6725 24.49 7.48016 22.5 3.50011C20.5 -0.499889 14.3333 1.83344 11.5 3.50011Z"
fill="#0FF0FC"
/>
</svg>
);
}
191 changes: 191 additions & 0 deletions src/app/components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
"use client";

import { useState, useRef, useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { MoreVertical, House, Volume2 } from "lucide-react";
import { useWalletContext } from "./walletProvider";
import AnimationWrapper from "../motion/animation-wrapper";
import WalletConnectModal from "./wallet-connect-modal";
import WalletDisconnectModal from "./wallet-disconnect-modal";
import Logo from "./icons/logo";

export default function Navbar() {
const [isConnectModalOpen, setIsConnectModalOpen] = useState(false);
const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const { account, connectWallet, disconnectWallet, connectors } =
useWalletContext();
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsDropdownOpen(false);
}
}

document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen);
const handleWalletSelect = (walletId: string) => {
const connector = connectors.find((c) => c.id === walletId);
if (connector) {
connectWallet(connector); // invoke Starknet-React’s useConnect() :contentReference[oaicite:3]{index=3}
}
setIsConnectModalOpen(false);
};
const handleConnectWallet = () => {
setIsConnectModalOpen(true);
};
const handleWalletClick = () => {
setIsDisconnectModalOpen(true);
};
const handleDisconnect = () => {
disconnectWallet(); // real Starknet-React disconnect :contentReference[oaicite:4]{index=4}
setIsDisconnectModalOpen(false);
};

return (
<>
<header className="fixed w-full z-40 to-[#083234]/50 from-[#010F10] bg-gradient-to-tl py-4 px-5">
<div className="flex items-center justify-between">
<AnimationWrapper variant="slideRight">
<Link href="/" className="flex items-center">
<Logo />
</Link>
</AnimationWrapper>

{/* Wallet Connection Button or Connected Wallet */}

<div className="hidden md:flex items-center gap-2 ">
<div className="border border-[#0E282A] p-2 rounded-[12px]">
<House />
</div>
<div className="border border-[#0E282A] p-2 rounded-[12px]">
<Volume2 />
</div>
<AnimationWrapper variant="slideLeft">
{!account ? (
<button
onClick={handleConnectWallet}
className="px-5 py-2 border rounded-md border-[#0E282A] bg-inherit text-[#0FF0FC] font-medium transition-colors font-orbitron "
>
Connect Wallet
</button>
) : (
<div className="relative" ref={dropdownRef}>
<div
onClick={handleWalletClick}
className="px-2 flex items-center gap-2 py-2 border rounded-md border-[#0E282A] bg-inherit text-[#0FF0FC] font-medium transition-colors font-orbitron"
>
<div className="h-8 w-8 rounded-full border-2 border-teal-500 overflow-hidden">
<Image
src="/Avater.svg"
alt="Wallet Avatar"
width={32}
height={32}
className="object-cover"
/>
</div>

<span className="text-white font-medium">
{account.slice(0, 6)}…{account.slice(-4)}
</span>

<button
onClick={(e) => {
e.stopPropagation();

toggleDropdown();
}}
className="h-8 w-8 p-0 text-gray-400 hover:text-white transition-colors"
>
<MoreVertical size={16} />
</button>
</div>

{/* Custom Dropdown Menu */}

{isDropdownOpen && (
<div className="absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-[#010F10] overflow-hidden border-[#003B3E] border">
<div className="py-1">
<button
onClick={handleWalletClick}
className="w-full text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-800 transition-colors"
>
Disconnect
</button>

<button
className="w-full block text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-800 transition-colors"
>
View Profile
</button>

<button className="w-full text-left px-4 py-2 text-sm text-gray-200 hover:bg-gray-800 transition-colors">
Settings
</button>
</div>
</div>
)}
</div>
)}
</AnimationWrapper>
</div>

{/* Mobile Menu Button */}

<div className="md:hidden flex items-center">
<AnimationWrapper variant="slideLeft">
{!account ? (
<button
onClick={handleConnectWallet}
className="px-5 py-2 border rounded-md border-[#0E282A] bg-inherit text-[#0FF0FC] font-medium transition-colors font-orbitron"
>
Connect Wallet
</button>
) : (
<div
onClick={handleWalletClick}
className="px-2 flex items-center gap-2 py-2 border rounded-md border-[#0E282A] bg-inherit text-[#0FF0FC] font-medium transition-colors font-orbitron"
>
<div className="h-6 w-6 rounded-full border border-teal-500 overflow-hidden">
<Image
src="/icons/braavos.png"
alt="Wallet Avatar"
width={24}
height={24}
className="object-cover"
/>
</div>

<span className="text-white text-xs font-medium">
{account.slice(0, 6)}…{account.slice(-4)}
</span>
</div>
)}
</AnimationWrapper>
</div>
</div>
</header>

<WalletConnectModal
isOpen={isConnectModalOpen}
onClose={() => setIsConnectModalOpen(false)}
onSelect={handleWalletSelect}
/>

<WalletDisconnectModal
isOpen={isDisconnectModalOpen}
onClose={() => setIsDisconnectModalOpen(false)}
onDisconnect={handleDisconnect}
/>
</>
);
}
36 changes: 36 additions & 0 deletions src/app/components/provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import React from "react";

import { sepolia, mainnet } from "@starknet-react/chains";

import {
StarknetConfig,
publicProvider,
argent,
braavos,
useInjectedConnectors,
voyager,
} from "@starknet-react/core";

export function StarknetProvider({ children }: { children: React.ReactNode }) {
const { connectors } = useInjectedConnectors({
// Show these connectors if the user has no connector installed.
recommended: [argent(), braavos()],
// Hide recommended connectors if the user has any connector installed.
includeRecommended: "always",
// Randomize the order of the connectors.
order: "random",
});

return (
<StarknetConfig
chains={[mainnet, sepolia]}
provider={publicProvider()}
connectors={connectors}
explorer={voyager}
>
{children}
</StarknetConfig>
);
}
Loading