File size: 4,687 Bytes
383d246 945e815 383d246 945e815 9ff7e0c 945e815 9ff7e0c 945e815 383d246 945e815 383d246 945e815 5f5514e 945e815 5f5514e 945e815 383d246 945e815 383d246 945e815 5f5514e 383d246 945e815 5f5514e 945e815 5f5514e 945e815 5f5514e 945e815 5f5514e 383d246 945e815 5f5514e 9ff7e0c 5f5514e 945e815 5f5514e 945e815 383d246 5f5514e 945e815 5f5514e 383d246 5f5514e 945e815 5f5514e 945e815 5f5514e 383d246 5f5514e 945e815 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | import React, { useState, useEffect } from 'react';
import './types';
import { ToastProvider, useToast } from './components/ui/index';
import LockScreen from './pages/LockScreen';
import OnboardingScreen from './pages/OnboardingScreen';
import Dashboard from './pages/Dashboard';
import SendPage from './pages/SendPage';
import HistoryPage from './pages/HistoryPage';
import VoicePage from './pages/VoicePage';
import SecurityPage from './pages/SecurityPage';
import SettingsPage from './pages/SettingsPage';
import ContactsPage from './pages/ContactsPage';
import ScanPage from './pages/ScanPage';
import Sidebar from './components/Sidebar';
import TopBar from './components/TopBar';
type Page = 'dashboard' | 'send' | 'history' | 'voice' | 'security' | 'settings' | 'contacts' | 'scan';
type AppState = 'loading' | 'onboarding' | 'locked' | 'unlocked';
function AppContent() {
const [appState, setAppState] = useState<AppState>('loading');
const [currentPage, setCurrentPage] = useState<Page>('dashboard');
const [publicKey, setPublicKey] = useState<string | null>(null);
const [balance, setBalance] = useState({ sol: 0, usdt: 0 });
const [aiStatus, setAiStatus] = useState<any>(null);
const { addToast } = useToast();
useEffect(() => { init(); }, []);
useEffect(() => { if (window.solvox) return window.solvox.on.locked(() => setAppState('locked')); }, []);
const init = async () => {
try {
if (!window.solvox) { setAppState('unlocked'); return; }
const exists = await window.solvox.wallet.exists();
if (!exists) { setAppState('onboarding'); return; }
const unlocked = await window.solvox.wallet.isUnlocked();
setAppState(unlocked ? 'unlocked' : 'locked');
if (unlocked) { setPublicKey(await window.solvox.wallet.getPublicKey()); refreshBalance(); }
} catch { setAppState('onboarding'); }
};
const refreshBalance = async () => {
try { if (!window.solvox) return; const r = await window.solvox.wallet.getBalance(); if (r.success) setBalance({ sol: r.sol || 0, usdt: r.usdt || 0 }); } catch {}
};
const handleUnlock = (pk: string) => {
setPublicKey(pk); setAppState('unlocked'); refreshBalance();
addToast({ type: 'success', title: 'Wallet unlocked' });
if (window.solvox) window.solvox.ai.initialize().then(r => { if (r.success) window.solvox.ai.getStatus().then(setAiStatus); });
};
const handleLock = async () => { if (window.solvox) await window.solvox.wallet.lock(); setAppState('locked'); };
if (appState === 'loading') return <Loading />;
if (appState === 'onboarding') return <OnboardingScreen onComplete={pk => { setPublicKey(pk); setAppState('unlocked'); refreshBalance(); }} />;
if (appState === 'locked') return <LockScreen onUnlock={handleUnlock} />;
const pages: Record<Page, JSX.Element> = {
dashboard: <Dashboard balance={balance} publicKey={publicKey} onRefresh={refreshBalance} onNavigate={setCurrentPage} />,
send: <SendPage balance={balance} onSent={refreshBalance} />,
history: <HistoryPage />,
voice: <VoicePage aiStatus={aiStatus} />,
contacts: <ContactsPage />,
scan: <ScanPage />,
security: <SecurityPage />,
settings: <SettingsPage onLock={handleLock} />,
};
return (
<div className="flex h-screen bg-canvas">
<Sidebar currentPage={currentPage} onNavigate={setCurrentPage} />
<div className="flex-1 flex flex-col overflow-hidden">
<TopBar publicKey={publicKey} balance={balance} aiStatus={aiStatus} onLock={handleLock} />
<main className="flex-1 overflow-y-auto">
<div className="page-enter" key={currentPage}>{pages[currentPage]}</div>
</main>
</div>
</div>
);
}
export default function App() { return <ToastProvider><AppContent /></ToastProvider>; }
function Loading() {
return (
<div className="h-screen flex flex-col items-center justify-center bg-canvas">
<div className="w-12 h-12 rounded-full bg-primary flex items-center justify-center mb-6">
<span className="text-on-primary font-bold text-title-md">SV</span>
</div>
<div className="display-text text-display-sm text-ink mb-2">SolVox</div>
<div className="text-body-md text-body">Initializing local AI engine…</div>
<div className="mt-8 w-48 h-1 bg-surface-strong rounded-pill overflow-hidden">
<div className="h-full bg-primary rounded-pill" style={{ width: '60%', animation: 'shimmer 1.5s infinite', background: 'linear-gradient(90deg, #0052ff 0%, #003ecc 50%, #0052ff 100%)', backgroundSize: '200% 100%' }} />
</div>
<div className="text-caption text-muted mt-4">Powered by QVAC SDK · 100% Local AI</div>
</div>
);
}
|