import { useState, useEffect } from 'react';
import { usePrivy } from '@privy-io/react-auth';
import { useNavigate } from 'react-router-dom';
import { usePolymarketAuth } from '../../hooks/usePolymarketAuth';
import { useTheme } from '../../hooks/useTheme';
import { useT } from '../../i18n/LanguageContext';
import { readPusdBalance } from '../../utils/dwApprovals';
export default function TopBar() {
const { isConnected, isAuthenticated, loading, error, deriveApiKey } = usePolymarketAuth();
const { authenticated, user, login, getAccessToken } = usePrivy();
const navigate = useNavigate();
const { theme, toggleTheme } = useTheme();
const { t } = useT();
const emailPrefix = user?.email?.address?.split('@')[0];
// Live free-cash (pUSD) balance for the header chip. Resolve the user's own deposit wallet
// from the backend, then read its pUSD balance; poll every 30s.
const [cashRaw, setCash] = useState<number | null>(null);
// Derived: null when logged out (no synchronous reset inside the effect).
const cash = authenticated ? cashRaw : null;
useEffect(() => {
if (!authenticated) return;
let alive = true;
const load = async () => {
try {
const token = await getAccessToken();
if (!token) return;
const r = await fetch('/api/copy/wallet', { headers: { authorization: `Bearer ${token}` } });
const dw = (await r.json())?.data?.depositWallet;
if (!dw) { if (alive) setCash(null); return; }
const bal = await readPusdBalance(dw);
if (alive) setCash(Number(bal) / 1e6);
} catch { /* leave last-known */ }
};
load();
const id = setInterval(load, 30000);
return () => { alive = false; clearInterval(id); };
}, [authenticated, getAccessToken]);
return (
<>
<header className="topbar">
<div className="topbar-logo">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M3 12c4 0 4-4 8-4s5 4 9 4" />
<path d="M3 17c4 0 4-3 8-3s5 3 9 3" />
<path d="M16 6l3-2v4z" />
</svg>
<span>ะะพะปะธะบะพะฟะธ</span>
</div>
<div className="topbar-actions">
<button className="theme-toggle" onClick={toggleTheme} title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}>
{theme === 'dark' ? (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="18" height="18">
<circle cx="12" cy="12" r="5" />
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" />
</svg>
) : (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="18" height="18">
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" />
</svg>
)}
</button>
{isConnected && !isAuthenticated && (
<button
className="btn-sign"
onClick={deriveApiKey}
disabled={loading}
title={error || 'Sign to enable trading'}
>
{loading ? 'Signingโฆ' : '๐ Sign to Trade'}
</button>
)}
{isConnected && isAuthenticated && (
<span className="trade-ready" title="CLOB API key active">โ Ready</span>
)}
{authenticated ? (
<button className="wallet-cta" onClick={() => navigate('/wallet')} title={emailPrefix || t('nav.wallet')}>
๐ {cash != null ? `$${cash.toFixed(2)}` : t('nav.wallet')}
</button>
) : (
<button className="wallet-cta" onClick={login}>{t('nav.signin')}</button>
)}
</div>
</header>
{isConnected && !isAuthenticated && error && (
<div className="sign-error-banner" onClick={deriveApiKey} role="button">
{error}
</div>
)}
</>
);
}
๐ Git History
6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...