From 3962924ec55416a1dc46568d97dba1fa120c2693 Mon Sep 17 00:00:00 2001 From: Glittersup Date: Sat, 27 Jun 2026 17:33:13 +0100 Subject: [PATCH] feat: chain-aware tint system across navbar and network identities --- components/icons/StellarIcon.tsx | 12 +- components/navbar/Navbar.tsx | 29 +++- components/navbar/NetworkSwitcher.tsx | 40 +++-- components/navbar/WalletMenu.tsx | 229 ++++++++++++++------------ lib/network-tint.ts | 43 +++++ 5 files changed, 229 insertions(+), 124 deletions(-) create mode 100644 lib/network-tint.ts diff --git a/components/icons/StellarIcon.tsx b/components/icons/StellarIcon.tsx index fb5f56c..0e45f56 100644 --- a/components/icons/StellarIcon.tsx +++ b/components/icons/StellarIcon.tsx @@ -1,11 +1,17 @@ +import React from 'react'; + type Props = { - className?: string + className?: string; + style?: React.CSSProperties; } const StellarIcon = (props: Props) => { return ( -
- +
+
diff --git a/components/navbar/Navbar.tsx b/components/navbar/Navbar.tsx index ef51100..9cf6a97 100644 --- a/components/navbar/Navbar.tsx +++ b/components/navbar/Navbar.tsx @@ -14,6 +14,8 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import Image from "next/image"; import { mockUser as user, mockNavbarState as navbarState } from "./navbar.mock"; import { WhatsNewDrawer } from "@/components/changelog/WhatsNewDrawer"; +import { getNetworkTint } from "@/lib/network-tint"; +import { useEffect } from "react"; const NAV_ITEMS = [ { name: "Markets", href: "/markets", icon: "trending_up" }, @@ -24,9 +26,20 @@ const NAV_ITEMS = [ export function Navbar() { const pathname = usePathname(); - const [network, setNetwork] = useState(navbarState.networkName); + const [network, setNetwork] = useState(() => { + if (typeof window !== "undefined") { + return localStorage.getItem("predictify_network") || navbarState.networkName; + } + return navbarState.networkName; + }); const { theme, setTheme } = useTheme(); const [isWalletModalOpen, setIsWalletModalOpen] = useState(false); + + useEffect(() => { + localStorage.setItem("predictify_network", network); + }, [network]); + + const activeTint = getNetworkTint(network); const { connected, isLoading } = useWalletContext(); const toggleTheme = () => { @@ -51,9 +64,13 @@ export function Navbar() { href={item.href} className={`text-sm transition-all duration-300 font-medium ${ isActive - ? "text-cyan-400 border-b-2 border-cyan-400 pb-1" + ? "pb-1" : "text-slate-400 hover:text-slate-200" }`} + style={isActive ? { + color: activeTint.tint, + borderBottom: `2px solid ${activeTint.tint}` + } : {}} > {item.name} @@ -82,7 +99,7 @@ export function Navbar() { Loading... ) : connected ? ( - + ) : ( Network - {NETWORKS.map((n) => ( - onChange?.(n)} className="cursor-pointer" role="menuitemradio" aria-checked={n === network}> - - {n} - - ))} + {NETWORKS.map((n) => { + const t = getNetworkTint(n); + return ( + onChange?.(n)} + className="cursor-pointer flex items-center gap-2" + role="menuitemradio" + aria-checked={n === network} + > +
+ {n} + + ); + })} ); diff --git a/components/navbar/WalletMenu.tsx b/components/navbar/WalletMenu.tsx index d7eb9d3..ad6ef46 100644 --- a/components/navbar/WalletMenu.tsx +++ b/components/navbar/WalletMenu.tsx @@ -1,108 +1,123 @@ -"use client"; - - -import { Button } from "@/components/ui/button"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { useWalletContext } from "@/context/WalletContext"; -import { useWallet } from "@/hooks/useWallet.hook"; -import { ConnectWalletModal } from "@/components/connect-wallet-modal"; -import { Copy as CopyIcon, RefreshCcw, LogOut as LogOutIcon } from "lucide-react"; -import ArrowDownIcon from "../icons/ArrowDown"; -import { Switch } from "@/components/ui/switch"; -import { usePrivacy } from "@/context/PrivacyContext"; -import { maskAmount } from "@/utils/maskAmount"; - -function truncateMiddle(address: string, visible = 4) { - if (address.length <= visible * 2) return address; - return `${address.slice(0, visible)}…${address.slice(-visible)}`; -} - -export function WalletMenu() { - const { address, connected, isLoading } = useWalletContext(); - const { disconnectWallet } = useWallet(); - const [isOpen, setIsOpen] = React.useState(false); - const { hideBalances, setHideBalances } = usePrivacy(); - - const display = connected && address ? (hideBalances ? maskAmount(address) : truncateMiddle(address)) : "Connect wallet"; - - if (isLoading) { - return ( - - ); - } - - function handleCopy() { - if (address) navigator.clipboard.writeText(address); - } - - if (!connected) { - return ( - <> - - - - ); - } - - return ( - <> - - - - - - Wallet - - - - Copy - - setIsOpen(true)} className="cursor-pointer" aria-label="Switch wallet"> - - Switch - - { void disconnectWallet(); }} className="cursor-pointer" aria-label="Disconnect wallet"> - - Disconnect - - - - - - - - - ); +"use client"; + +import React from 'react'; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useWalletContext } from "@/context/WalletContext"; +import { useWallet } from "@/hooks/useWallet.hook"; +import { ConnectWalletModal } from "@/components/connect-wallet-modal"; +import { Copy as CopyIcon, RefreshCcw, LogOut as LogOutIcon } from "lucide-react"; +import ArrowDownIcon from "../icons/ArrowDown"; +import { Switch } from "@/components/ui/switch"; +import { usePrivacy } from "@/context/PrivacyContext"; +import { maskAmount } from "@/utils/maskAmount"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { getNetworkTint } from "@/lib/network-tint"; +import { mockUser as user } from "./navbar.mock"; + +function truncateMiddle(address: string, visible = 4) { + if (address.length <= visible * 2) return address; + return `${address.slice(0, visible)}…${address.slice(-visible)}`; +} + +export function WalletMenu({ network }: { network: string }) { + const { address, connected, isLoading } = useWalletContext(); + const { disconnectWallet } = useWallet(); + const [isOpen, setIsOpen] = React.useState(false); + const { hideBalances, setHideBalances } = usePrivacy(); + + const activeTint = getNetworkTint(network); + const display = connected && address ? (hideBalances ? maskAmount(address) : truncateMiddle(address)) : "Connect wallet"; + + if (isLoading) { + return ( + + ); + } + + function handleCopy() { + if (address) navigator.clipboard.writeText(address); + } + + if (!connected) { + return ( + <> + + + + ); + } + + return ( + <> + + + + + + Wallet + + + + Copy + + setIsOpen(true)} className="cursor-pointer" aria-label="Switch wallet"> + + Switch + + { void disconnectWallet(); }} className="cursor-pointer" aria-label="Disconnect wallet"> + + Disconnect + + + + + + + + + ); } diff --git a/lib/network-tint.ts b/lib/network-tint.ts new file mode 100644 index 0000000..cdf5e6e --- /dev/null +++ b/lib/network-tint.ts @@ -0,0 +1,43 @@ +export type NetworkType = 'mainnet' | 'testnet' | 'futurenet' | 'unknown'; + +export interface NetworkTint { + tint: string; // The hex color for accents + bg: string; // Light background tint (rgba or hex with alpha) + border: string; // Border color + text: string; // Text color for the badge/indicator +} + +export const NETWORK_TINTS: Record = { + mainnet: { + tint: '#00cffc', // Cyan + bg: 'rgba(0, 207, 252, 0.15)', + border: '#00cffc', + text: '#69daff', + }, + testnet: { + tint: '#9333ea', // Purple + bg: 'rgba(147, 51, 234, 0.15)', + border: '#9333ea', + text: '#a855f7', + }, + futurenet: { + tint: '#f59e0b', // Amber + bg: 'rgba(245, 158, 11, 0.15)', + border: '#f59e0b', + text: '#fbbf24', + }, + unknown: { + tint: '#64748b', // Slate + bg: 'rgba(100, 116, 139, 0.15)', + border: '#64748b', + text: '#94a3b8', + }, +}; + +export const getNetworkTint = (networkName: string): NetworkTint => { + const name = networkName.toLowerCase(); + if (name.includes('mainnet')) return NETWORK_TINTS.mainnet; + if (name.includes('testnet')) return NETWORK_TINTS.testnet; + if (name.includes('futurenet')) return NETWORK_TINTS.futurenet; + return NETWORK_TINTS.unknown; +};