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>
  );
}