'use client' import { useState, useEffect, useRef } from 'react' import Link from 'next/link' import { usePathname } from 'next/navigation' import { useLocale } from 'next-intl' import { useAccount, useSignMessage } from 'wagmi' import type { BotTrade, TrumpPost } from '@/types' import { getTrades, getPosts } from '@/lib/api' import { useDashboardStore } from '@/store/dashboard' import { getCachedViewEnvelope, getOrCreateViewEnvelope, type SignedEnvelope } from '@/lib/signedRequest' import TradeTable from '@/components/trades/TradeTable' import OpenPositions from '@/components/positions/OpenPositions' import PageHint from '@/components/ui/PageHint' export default function TradesPageClient() { const intlLocale = useLocale() const isZh = false // i18n shelved — Chinese branches kept as dead code for future revival; see messages/zh.json const { address, isConnected } = useAccount() const { signMessageAsync } = useSignMessage() const { isSubscribed, hlApiKeySet } = useDashboardStore() const pathname = usePathname() const locale = pathname.split('/')[1] || 'en' const [mounted, setMounted] = useState(false) const [trades, setTrades] = useState([]) const [posts, setPosts] = useState([]) const [loading, setLoading] = useState(true) const [loadErr, setLoadErr] = useState('') const [needsUnlock, setNeedsUnlock] = useState(false) const [unlocking, setUnlocking] = useState(false) const aliveRef = useRef(true) useEffect(() => { setMounted(true) aliveRef.current = true return () => { aliveRef.current = false } }, []) // Load public posts always; load private trades only if we have a view // envelope. `forcedEnv` lets the in-page Unlock button pass a freshly-minted // one so the user never has to detour through the Settings page first. async function loadAll(forcedEnv?: SignedEnvelope) { if (!address || !isConnected) { setTrades([]); setPosts([]); setLoading(false); setLoadErr(''); setNeedsUnlock(false) return } setLoading(true) const env = forcedEnv ?? getCachedViewEnvelope('view_trades', address) ?? getCachedViewEnvelope('view_user', address) let failed = false try { const [t, p] = await Promise.all([ env ? getTrades(address, env, 100, 1).catch(e => { failed = true setLoadErr(e instanceof Error ? e.message : 'Failed to load trades') return [] as BotTrade[] }) : Promise.resolve([] as BotTrade[]), getPosts(500, 1).catch(() => [] as TrumpPost[]), ]) if (!aliveRef.current) return setTrades(t) setPosts(p) setNeedsUnlock(!env) if (env && !failed) setLoadErr('') } finally { if (aliveRef.current) setLoading(false) } } useEffect(() => { // On navigation we only use a cached envelope — never auto-popup the // wallet. If none is cached the user unlocks explicitly via the button. void loadAll() // eslint-disable-next-line react-hooks/exhaustive-deps }, [address, isConnected]) // Mint a view envelope right here (one signature) and reload — no Settings detour. async function handleUnlock() { if (!address) return setUnlocking(true) setLoadErr('') try { const env = await getOrCreateViewEnvelope({ action: 'view_trades', wallet: address, signMessageAsync, }) await loadAll(env) } catch (e) { // User rejected the signature or it failed — keep the Unlock CTA visible. const msg = e instanceof Error ? e.message : 'Signature cancelled' if (aliveRef.current) setLoadErr(/reject|denied|cancel/i.test(msg) ? '' : msg) } finally { if (aliveRef.current) setUnlocking(false) } } const needsSetup = mounted && isConnected && (!isSubscribed || !hlApiKeySet) return (

{isZh ? '交易执行' : 'Trades'}

What the bot actually did with your money — currently-open positions on top, closed-trade history with realized P&L below.
{needsSetup && (
{isZh ? '机器人尚未配置完成' : 'Bot not configured'}
{isZh ? '在机器人开始交易前,请先去设置页订阅并绑定 Hyperliquid API。' : 'Subscribe and link your Hyperliquid API wallet on the Settings page before the bot can trade.'}
{isZh ? '前往设置 →' : 'Go to Settings →'}
)} {mounted && isConnected && !loading && needsUnlock && (
🔒 Your trade history is private. Sign once to unlock it on this device (valid for a few minutes, no transaction, no gas).
)} {!loading && loadErr && (
⚠️ {isZh ? `无法加载交易历史:${loadErr}` : `Couldn’t load trade history — ${loadErr}`}
)}
) }