diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 4331f7b..487e5c1 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -27,6 +27,8 @@ export default function ProfilePage() { const [badges, setBadges] = useState([]); const [eventsAttended, setEventsAttended] = useState([]); const [profileLoading, setProfileLoading] = useState(true); + const [walletRegistered, setWalletRegistered] = useState(false); + const [walletChecking, setWalletChecking] = useState(true); const [statusMessage, setStatusMessage] = useState(null); const copyAddress = () => { @@ -75,19 +77,44 @@ export default function ProfilePage() { }; useEffect(() => { + let cancelled = false; + const loadProfile = async () => { if (!address) { + setWalletRegistered(false); + setWalletChecking(false); setProfileLoading(false); return; } + setWalletChecking(true); setProfileLoading(true); setStatusMessage(null); try { + const isRegistered = await RegistrationService.isWalletRegistered( + address, + ); + + if (cancelled) return; + + if (!isRegistered) { + setWalletRegistered(false); + setDisplayName("Name"); + setProfileImage(null); + setBadges([]); + setEventsAttended([]); + setStatusMessage("This wallet is not linked yet."); + return; + } + + setWalletRegistered(true); + const registration = await RegistrationService.getRegistrationByWallet(address); + if (cancelled) return; + if (registration) { const displayNameFallback = registration.unique_name ? registration.unique_name.trim() @@ -108,16 +135,30 @@ export default function ProfilePage() { setStatusMessage("No profile data found for this wallet."); } } catch (error) { + if (cancelled) return; + console.error("Unable to load profile data", error); - setStatusMessage("Unable to load profile data."); + setWalletRegistered(false); + setStatusMessage("Unable to check wallet registration."); } finally { - setProfileLoading(false); + if (!cancelled) { + setWalletChecking(false); + setProfileLoading(false); + } } }; if (mounted && isConnected) { loadProfile(); + } else { + setWalletRegistered(false); + setWalletChecking(false); + setProfileLoading(false); } + + return () => { + cancelled = true; + }; }, [address, isConnected, mounted]); const handleNameSave = async () => { @@ -159,11 +200,11 @@ export default function ProfilePage() { ); } - if (!isConnected) { + if (!isConnected || !walletRegistered) { return ( -
+
-
+
WEB3UOA Your Profile -

- Connect your wallet to view your profile. +

+ {walletChecking + ? "Checking your wallet..." + : isConnected + ? "This wallet is not linked yet. Connect with a registered email first." + : "Connect your wallet to view your profile."}

@@ -185,11 +230,11 @@ export default function ProfilePage() { } return ( -
+
{/* Profile Card */} -
+
-
+
{profileImage ? ( ) : ( -
No Image
+
+ No Image +
)} setDisplayName(e.target.value)} - className="w-full md:w-auto text-xl font-bold border rounded px-3 py-2" + className="w-full md:w-auto text-xl font-bold border rounded px-3 py-2 bg-white text-foreground dark:border-white/15 dark:bg-[#2f3136] dark:text-white" onKeyDown={(e) => { if (e.key === "Enter") { handleNameSave(); @@ -234,7 +281,7 @@ export default function ProfilePage() {

{displayName}

@@ -247,7 +294,7 @@ export default function ProfilePage() {
{statusMessage ? ( -
+
{statusMessage}
) : null} @@ -275,51 +322,59 @@ export default function ProfilePage() {
{/* Badges*/} -
+

Badges

{profileLoading ? ( -

Loading badges…

+

+ Loading badges… +

) : badges.length > 0 ? (
{badges.map((badge, index) => (
- + {badge}
))}
) : ( -

No badges yet.

+

+ No badges yet. +

)}
{/* Event History*/} -
+

Events Attended

{profileLoading ? ( -

Loading event history…

+

+ Loading event history… +

) : eventsAttended.length > 0 ? (
{eventsAttended.map((eventName, index) => (

{eventName}

))}
) : ( -

No events attended yet.

+

+ No events attended yet. +

)}
diff --git a/src/components/login-overlay.tsx b/src/components/login-overlay.tsx index c3e0584..5338561 100644 --- a/src/components/login-overlay.tsx +++ b/src/components/login-overlay.tsx @@ -34,7 +34,7 @@ function truncateAddress(address: string) { } export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { - const { address, isConnected, connect } = useWallet(); + const { address, isConnected, connect, disconnect } = useWallet(); const [isMounted, setIsMounted] = useState(false); const [walletStatus, setWalletStatus] = useState("idle"); @@ -116,6 +116,19 @@ export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { } }; + const handleChangeWallet = async () => { + setEmail(""); + setEmailStatus("idle"); + setWalletStatus("connecting"); + setMessage("Opening wallet chooser..."); + + disconnect(); + + window.setTimeout(() => { + connect(); + }, 300); + }; + const handleConnectEmailToWallet = async () => { if (!address) { setEmailStatus("error"); @@ -184,14 +197,14 @@ export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { const overlay = (
-
+
-

Connect Wallet

-

+

+ Connect Wallet +

+

Connect your wallet to access your profile.

@@ -221,32 +236,43 @@ export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { {isConnected && address && (
-
-

Connected wallet

-

{truncateAddress(address)}

+
+

+ Connected wallet +

+
+

{truncateAddress(address)}

+ +
{walletStatus === "checking" && ( -

+

Checking wallet...

)} {walletStatus === "registered" && ( -
+
Wallet is registered. You are logged in.
)} {walletStatus === "not_registered" && (
-
+
This wallet is not linked yet. Enter your registered email to connect it.
-
@@ -281,7 +307,7 @@ export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { )} {walletStatus === "error" && ( -
+
Could not check wallet.
)} @@ -293,10 +319,10 @@ export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { className={`mt-4 text-sm ${ emailStatus === "invalid" || emailStatus === "wallet_not_empty" || - emailStatus === "error" || - walletStatus === "error" - ? "text-red-600" - : "text-muted-foreground" + emailStatus === "error" || + walletStatus === "error" + ? "text-red-600 dark:text-red-200" + : "text-muted-foreground dark:text-white/70" }`} > {message} @@ -311,4 +337,4 @@ export function LoginOverlay({ onClose, onLoginSuccess }: LoginOverlayProps) { } return createPortal(overlay, document.body); -} \ No newline at end of file +} diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index df90909..91ab5f9 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -1,26 +1,20 @@ "use client"; -import { Sun, Moon } from 'lucide-react' + +import { Sun, Moon, Menu, X } from "lucide-react"; import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; -import { Menu, X } from "lucide-react"; import { useAccount } from "wagmi"; import { isAllowedAdminAddress } from "@/lib/admin-auth"; import Link from "next/link"; -import { usePathname } from 'next/navigation'; - +import { usePathname } from "next/navigation"; +import { WalletButton } from "./wallet-button"; const navLinks = [ { label: "About", href: "/pages/about" }, { label: "Events", href: "/pages/events" }, { label: "Partners", href: "/pages/partners" }, - // { label: "Claim your Web3 ID!", href: "/pages/claim-id", isCTA: true }, - // { label: "About", href: "#about" }, - // { label: "Events", href: "#events" }, - // { label: "Partners", href: "#partners" }, - { label: "Search", href: "#search" }, // TODO: add search page - { label: "Join Us", href: "#join_us" }, // TODO: add Us - { label: "Connect Wallet", href: "/pages/claim-id" }, // TODO: add wallet connection - //{ label: "Claim your Web3 ID!", href: "#identity", isCTA: true } // not sure if need + { label: "Search", href: "#search" }, + { label: "Join Us", href: "/pages/join-us" }, ]; export function Navbar() { @@ -29,21 +23,23 @@ export function Navbar() { const [mounted, setMounted] = useState(false); const { address } = useAccount(); + const pathname = usePathname(); + useEffect(() => { setMounted(true); }, []); - // save theme preference to localStorage for persistence across sessions - // read saved preference (client only) useEffect(() => { const saved = localStorage.getItem("theme") as "light" | "dark" | null; - const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + setTheme(saved || preferred); }, []); - // Apply theme + save it whenever it changes for the theme useEffect(() => { - document.documentElement.classList.add('theme-transitioning'); + document.documentElement.classList.add("theme-transitioning"); if (theme === "dark") { document.documentElement.classList.add("dark"); @@ -51,58 +47,64 @@ export function Navbar() { document.documentElement.classList.remove("dark"); } - localStorage.setItem("theme", theme); // ← this saves it + localStorage.setItem("theme", theme); - setTimeout(() => { - document.documentElement.classList.remove('theme-transitioning'); + const timeout = setTimeout(() => { + document.documentElement.classList.remove("theme-transitioning"); }, 300); + + return () => clearTimeout(timeout); }, [theme]); const isAdmin = mounted && isAllowedAdminAddress(address); - // Determine active link based on current pathname. Exact match or sub-route match counts as active. - // Will need to change the code after we add the search page and join us page - // currently they are just anchors on the home page so they won't be active when user is on the home page - const pathname = usePathname(); const isActive = (href: string) => { if (href.startsWith("#")) return false; - // exact match OR sub-route match return pathname === href || pathname.startsWith(href + "/"); }; + const toggleTheme = () => { + setTheme(theme === "light" ? "dark" : "light"); + }; + return ( -