132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import React, { lazy, Suspense, useEffect, useState } from 'react';
|
|
import { Capacitor } from '@capacitor/core';
|
|
import { StatusBar, Style } from '@capacitor/status-bar';
|
|
import { SplashScreen as CapSplashScreen } from '@capacitor/splash-screen';
|
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
|
import { NavigationProvider, useNavigation } from './contexts/NavigationContext';
|
|
import { TradeNotificationProvider, useTradeNotification } from './contexts/TradeNotificationContext';
|
|
import { useAppSettings, resolveAppDefaults } from './hooks/useAppSettings';
|
|
import TabBar, { type TabId } from './components/TabBar';
|
|
import LiveSignalBridge from './components/LiveSignalBridge';
|
|
import LoginScreen from './screens/LoginScreen';
|
|
import '@frontend/styles/splashScreen.css';
|
|
import './theme/global.css';
|
|
|
|
const VirtualTradingScreen = lazy(() => import('./screens/virtual/VirtualTradingScreen'));
|
|
const StrategyEditorScreen = lazy(() => import('./screens/strategy/StrategyEditorScreen'));
|
|
const NotificationsScreen = lazy(() => import('./screens/notifications/NotificationsScreen'));
|
|
const SettingsScreen = lazy(() => import('./screens/settings/SettingsScreen'));
|
|
|
|
function ToastStack() {
|
|
const { toastNotifications, dismissToast } = useTradeNotification();
|
|
if (toastNotifications.length === 0) return null;
|
|
return (
|
|
<div className="toast-stack">
|
|
{toastNotifications.slice(0, 3).map(t => (
|
|
<div key={t.id} className="toast-item" role="button" tabIndex={0} onClick={() => dismissToast(t.id)}>
|
|
<strong className={t.signalType === 'BUY' ? 'text-green' : 'text-red'}>
|
|
{t.signalType === 'BUY' ? '매수' : '매도'}
|
|
</strong>
|
|
{' '}{t.market} · ₩{t.price?.toLocaleString()}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MainApp() {
|
|
const { tab, setTab, openVirtualFocus, goVirtualList, goNotifyList } = useNavigation();
|
|
const { unreadCount } = useTradeNotification();
|
|
const { sessionKey } = useAuth();
|
|
const { settings, isLoaded } = useAppSettings(sessionKey);
|
|
const defaults = resolveAppDefaults(settings);
|
|
|
|
/** FCM/알림 권한: 로그인·메인 진입 시 호출하지 않음 — 설정 버튼에서만 */
|
|
|
|
useEffect(() => {
|
|
document.documentElement.setAttribute('data-theme', defaults.theme ?? 'dark');
|
|
}, [defaults.theme]);
|
|
|
|
useEffect(() => {
|
|
if (tab === 'virtual') goNotifyList();
|
|
else if (tab === 'notifications') goVirtualList();
|
|
}, [tab, goVirtualList, goNotifyList]);
|
|
|
|
const screens: Record<TabId, React.ReactNode> = {
|
|
virtual: <VirtualTradingScreen />,
|
|
strategy: <StrategyEditorScreen />,
|
|
notifications: <NotificationsScreen />,
|
|
settings: <SettingsScreen />,
|
|
};
|
|
|
|
return (
|
|
<div className="app-shell">
|
|
<LiveSignalBridge />
|
|
<ToastStack />
|
|
<main className="app-content">
|
|
<Suspense fallback={<div className="loading-center">로딩…</div>}>
|
|
{screens[tab]}
|
|
</Suspense>
|
|
</main>
|
|
<TabBar active={tab} onChange={setTab} unreadCount={unreadCount} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function AppRoot() {
|
|
const {
|
|
authReady,
|
|
isAppEntered,
|
|
handleLoginSuccess,
|
|
handleGuestEnter,
|
|
sessionKey,
|
|
} = useAuth();
|
|
const [nativeReady, setNativeReady] = useState(false);
|
|
|
|
useEffect(() => {
|
|
void (async () => {
|
|
if (Capacitor.isNativePlatform()) {
|
|
try {
|
|
await StatusBar.setStyle({ style: Style.Dark });
|
|
await StatusBar.setBackgroundColor({ color: '#0f0f23' });
|
|
await CapSplashScreen.hide();
|
|
} catch { /* web dev */ }
|
|
}
|
|
setNativeReady(true);
|
|
})();
|
|
}, []);
|
|
|
|
if (!authReady || !nativeReady) {
|
|
return <div className="loading-center">GoldenChart</div>;
|
|
}
|
|
|
|
if (!isAppEntered) {
|
|
return (
|
|
<LoginScreen
|
|
onLoginSuccess={handleLoginSuccess}
|
|
onGuest={handleGuestEnter}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<NavigationProvider>
|
|
<TradeNotificationProvider
|
|
soundEnabled
|
|
popupEnabled={false}
|
|
settingsSessionKey={sessionKey}
|
|
>
|
|
<MainApp />
|
|
</TradeNotificationProvider>
|
|
</NavigationProvider>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<AuthProvider>
|
|
<AppRoot />
|
|
</AuthProvider>
|
|
);
|
|
}
|