147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
'use client'
|
||
|
||
import { useState, useEffect } from 'react'
|
||
import Link from 'next/link'
|
||
import { usePathname } from 'next/navigation'
|
||
import { useAccount, useConnect, useDisconnect } from 'wagmi'
|
||
import { injected } from 'wagmi/connectors'
|
||
|
||
function BrandMark() {
|
||
return <span className="brand-mark">α</span>
|
||
}
|
||
|
||
function ThemeToggle() {
|
||
const [theme, setTheme] = useState<'light' | 'dark'>('light')
|
||
|
||
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 (
|
||
<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>
|
||
)
|
||
}
|
||
|
||
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={t.key}
|
||
href={`/${locale}${t.key === '/' ? '' : t.key}`}
|
||
className={`nav-tab ${isActive(t.key) ? 'active' : ''}`}
|
||
>
|
||
{t.label}
|
||
</Link>
|
||
))}
|
||
</div>
|
||
|
||
<div className="nav-spacer" />
|
||
|
||
<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 className="connect-btn lg" onClick={() => connect({ connector: injected() })}>
|
||
Connect wallet
|
||
</button>
|
||
)}
|
||
</div>
|
||
</nav>
|
||
)
|
||
}
|