Files
trumpsignal-frontend/components/nav/Navbar.tsx
T
2026-04-21 19:32:53 +08:00

147 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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>
)
}