import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { Theme } from '../types'; import { loadPaperSummary, loadPaperTrades, loadStrategies, type PaperSummaryDto, type PaperTradeDto, type StrategyDto, } from '../utils/backendApi'; import { flushPendingUiPreferences, getUiPreferences, patchUiPreferences, subscribeUiPreferences, } from '../utils/uiPreferencesDb'; import { defaultWidgetLayout, normalizeWidgetLayout, type WidgetLayoutSchema } from '../types/widgetDashboard'; import { WidgetDashboardProvider } from '../widgets/WidgetDashboardContext'; import WidgetDashboardShell from './widgetDashboard/WidgetDashboardShell'; import { PAPER_TRADES_CHANGED_EVENT } from '../utils/paperTradeEvents'; import { useMarketTicker } from '../hooks/useMarketTicker'; import type { ChartRealtimeSource } from '../hooks/useChartRealtimeData'; import { useChartWorkspaceContext } from '../chart/ChartWorkspaceContext'; import { useAppSettings } from '../hooks/useAppSettings'; import '../styles/widgetDashboard.css'; interface Props { theme: Theme; paperTradingEnabled: boolean; paperAutoTradeEnabled: boolean; onPaperOrderFilled?: () => void; chartRealtimeSource?: ChartRealtimeSource; } function readWidgetLayoutFromPrefs(): WidgetLayoutSchema { return normalizeWidgetLayout(getUiPreferences().widgetDashboard ?? defaultWidgetLayout()); } const WidgetDashboardPage: React.FC = ({ theme, paperTradingEnabled, paperAutoTradeEnabled, onPaperOrderFilled, chartRealtimeSource = 'BACKEND_STOMP', }) => { const { isLoaded: appSettingsLoaded } = useAppSettings(); const { symbol: defaultMarket } = useChartWorkspaceContext(); const { tickers, marketInfos, loading: marketLoading, usdRate } = useMarketTicker( useMemo(() => ({ enabled: true, loadFull: true }), []), ); const [layout, setLayout] = useState(defaultWidgetLayout); const [strategies, setStrategies] = useState([]); const [summary, setSummary] = useState(null); const [trades, setTrades] = useState([]); const layoutJsonRef = useRef(''); useEffect(() => { if (!appSettingsLoaded) return; const next = readWidgetLayoutFromPrefs(); const json = JSON.stringify(next); if (json !== layoutJsonRef.current) { layoutJsonRef.current = json; setLayout(next); } }, [appSettingsLoaded]); useEffect(() => { if (!appSettingsLoaded) return; return subscribeUiPreferences(() => { const next = readWidgetLayoutFromPrefs(); const json = JSON.stringify(next); if (json === layoutJsonRef.current) return; layoutJsonRef.current = json; setLayout(next); }); }, [appSettingsLoaded]); useEffect(() => { const flush = () => { flushPendingUiPreferences(); }; window.addEventListener('beforeunload', flush); window.addEventListener('pagehide', flush); return () => { window.removeEventListener('beforeunload', flush); window.removeEventListener('pagehide', flush); flush(); }; }, []); const persistLayout = useCallback(( next: WidgetLayoutSchema, opts?: { immediate?: boolean }, ) => { const normalized = normalizeWidgetLayout(next); layoutJsonRef.current = JSON.stringify(normalized); setLayout(normalized); if (!appSettingsLoaded) return; patchUiPreferences({ widgetDashboard: normalized }, opts?.immediate ?? false); }, [appSettingsLoaded]); const refreshPaperData = useCallback(async () => { try { const [sum, tr] = await Promise.all([loadPaperSummary(), loadPaperTrades()]); setSummary(sum); setTrades(tr ?? []); } catch { /* ignore */ } }, []); useEffect(() => { void loadStrategies().then(list => setStrategies(list ?? [])); void refreshPaperData(); }, [refreshPaperData]); useEffect(() => { const onChanged = () => { void refreshPaperData(); }; window.addEventListener(PAPER_TRADES_CHANGED_EVENT, onChanged); return () => window.removeEventListener(PAPER_TRADES_CHANGED_EVENT, onChanged); }, [refreshPaperData]); if (!appSettingsLoaded) { return
위젯 대시보드 로딩…
; } return ( { void refreshPaperData(); }} paperTradingEnabled={paperTradingEnabled} paperAutoTradeEnabled={paperAutoTradeEnabled} onPaperOrderFilled={onPaperOrderFilled} chartRealtimeSource={chartRealtimeSource} > ); }; export default WidgetDashboardPage;