done
This commit is contained in:
+120
-51
@@ -1,74 +1,143 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { useAccount } from 'wagmi'
|
||||
import { useConnectModal } from '@rainbow-me/rainbowkit'
|
||||
import { shortenAddress } from '@/lib/utils'
|
||||
import { useAccount, useConnect, useDisconnect } from 'wagmi'
|
||||
import { injected } from 'wagmi/connectors'
|
||||
|
||||
const navItems = [
|
||||
{ key: 'overview', href: '' },
|
||||
{ key: 'posts', href: '/posts' },
|
||||
{ key: 'trades', href: '/trades' },
|
||||
{ key: 'analytics', href: '/analytics' },
|
||||
] as const
|
||||
|
||||
interface NavbarProps {
|
||||
locale: string
|
||||
function BrandMark() {
|
||||
return <span className="brand-mark">α</span>
|
||||
}
|
||||
|
||||
export default function Navbar({ locale }: NavbarProps) {
|
||||
const t = useTranslations('nav')
|
||||
const pathname = usePathname()
|
||||
const { address, isConnected } = useAccount()
|
||||
const { openConnectModal } = useConnectModal()
|
||||
function ThemeToggle() {
|
||||
const [theme, setTheme] = useState<'light' | 'dark'>('light')
|
||||
|
||||
function isActive(href: string) {
|
||||
const full = `/${locale}${href}`
|
||||
if (href === '') return pathname === `/${locale}` || pathname === `/${locale}/`
|
||||
return pathname.startsWith(full)
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem('theme') as 'light' | 'dark' | null
|
||||
const t = stored || 'light'
|
||||
setTheme(t)
|
||||
document.documentElement.dataset.theme = t
|
||||
}, [])
|
||||
|
||||
function toggle() {
|
||||
const next = theme === 'dark' ? 'light' : 'dark'
|
||||
setTheme(next)
|
||||
localStorage.setItem('theme', next)
|
||||
document.documentElement.dataset.theme = next
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 h-14 bg-[#000000] border-b border-[#141414] flex items-center px-6">
|
||||
{/* Logo */}
|
||||
<Link href={`/${locale}`} className="flex items-center gap-2 mr-10 shrink-0">
|
||||
<span className="text-[#f97316] font-bold text-lg leading-none">TS</span>
|
||||
<span className="text-white font-medium text-sm">TrumpSignal</span>
|
||||
</Link>
|
||||
<button className="icon-btn theme-toggle" onClick={toggle}>
|
||||
{theme === 'dark' ? (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<circle cx="12" cy="12" r="4" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" stroke="currentColor" strokeWidth="2" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
{/* Center nav */}
|
||||
<div className="flex items-center gap-6 flex-1">
|
||||
{navItems.map((item) => (
|
||||
export default function Navbar() {
|
||||
const pathname = usePathname()
|
||||
const { address, isConnected } = useAccount()
|
||||
const { connect } = useConnect()
|
||||
const { disconnect } = useDisconnect()
|
||||
const [mounted, setMounted] = useState(false)
|
||||
useEffect(() => { setMounted(true) }, [])
|
||||
|
||||
const locale = pathname.split('/')[1] || 'en'
|
||||
const path = '/' + pathname.split('/').slice(2).join('/')
|
||||
|
||||
const tabs = [
|
||||
{ key: '/', label: 'Overview' },
|
||||
{ key: '/posts', label: 'Signals' },
|
||||
{ key: '/trades', label: 'Trades' },
|
||||
{ key: '/analytics', label: 'Analytics' },
|
||||
{ key: '/settings', label: 'Settings' },
|
||||
]
|
||||
|
||||
function isActive(key: string) {
|
||||
if (key === '/') return path === '/' || path === '//'
|
||||
return path.startsWith(key)
|
||||
}
|
||||
|
||||
const shortAddr = address ? `${address.slice(0, 6)}…${address.slice(-4)}` : null
|
||||
|
||||
return (
|
||||
<nav className="nav">
|
||||
<div className="brand">
|
||||
<BrandMark />
|
||||
<span>Trump Alpha</span>
|
||||
</div>
|
||||
|
||||
<div className="nav-tabs">
|
||||
{tabs.map(t => (
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.href}`}
|
||||
className={`text-[13px] transition-colors ${
|
||||
isActive(item.href) ? 'text-[#f97316]' : 'text-[#555555] hover:text-white'
|
||||
}`}
|
||||
key={t.key}
|
||||
href={`/${locale}${t.key === '/' ? '' : t.key}`}
|
||||
className={`nav-tab ${isActive(t.key) ? 'active' : ''}`}
|
||||
>
|
||||
{t(item.key)}
|
||||
{t.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right side */}
|
||||
<div className="flex items-center gap-3 shrink-0">
|
||||
<button className="text-[13px] text-[#555555] hover:text-white border border-[#141414] rounded-full px-3 py-1 transition-colors">
|
||||
{t('language')} ▾
|
||||
</button>
|
||||
<div className="nav-spacer" />
|
||||
|
||||
{isConnected && address ? (
|
||||
<button className="text-[13px] bg-[#141414] text-white border border-[#1a1a1a] rounded-full px-3 py-1">
|
||||
{shortenAddress(address)}
|
||||
</button>
|
||||
<div className="nav-right">
|
||||
<ThemeToggle />
|
||||
{!mounted ? (
|
||||
<button className="connect-btn lg" suppressHydrationWarning>Connect wallet</button>
|
||||
) : isConnected && shortAddr ? (
|
||||
<div className="wallet-menu-wrap" style={{ position: 'relative' }}>
|
||||
<button
|
||||
className="wallet-chip"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
const menu = (e.currentTarget.nextElementSibling as HTMLElement | null)
|
||||
if (menu) menu.style.display = menu.style.display === 'block' ? 'none' : 'block'
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
const menu = e.currentTarget.nextElementSibling as HTMLElement | null
|
||||
setTimeout(() => { if (menu) menu.style.display = 'none' }, 150)
|
||||
}}
|
||||
>
|
||||
<span className="ava" />
|
||||
<span className="mono">{shortAddr}</span>
|
||||
</button>
|
||||
<div
|
||||
style={{
|
||||
display: 'none', position: 'absolute', right: 0, top: 'calc(100% + 6px)',
|
||||
background: 'var(--bg-elev)', border: '1px solid var(--line)', borderRadius: 'var(--r-sm)',
|
||||
minWidth: 180, padding: 6, zIndex: 1000, boxShadow: '0 4px 16px rgba(0,0,0,0.12)',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
if (address) navigator.clipboard?.writeText(address)
|
||||
}}
|
||||
style={{ width: '100%', padding: '8px 10px', fontSize: 13, textAlign: 'left', borderRadius: 6, color: 'var(--ink)' }}
|
||||
>
|
||||
Copy address
|
||||
</button>
|
||||
<button
|
||||
onMouseDown={(e) => { e.preventDefault(); disconnect() }}
|
||||
style={{ width: '100%', padding: '8px 10px', fontSize: 13, textAlign: 'left', borderRadius: 6, color: 'var(--down)' }}
|
||||
>
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={openConnectModal}
|
||||
className="text-[13px] bg-[#f97316] text-black font-medium rounded-full px-4 py-1.5 hover:bg-[#fb923c] transition-colors"
|
||||
>
|
||||
{t('connectWallet')}
|
||||
<button className="connect-btn lg" onClick={() => connect({ connector: injected() })}>
|
||||
Connect wallet
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user