위젯 기능 추가
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
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<Props> = ({
|
||||
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<WidgetLayoutSchema>(defaultWidgetLayout);
|
||||
const [strategies, setStrategies] = useState<StrategyDto[]>([]);
|
||||
const [summary, setSummary] = useState<PaperSummaryDto | null>(null);
|
||||
const [trades, setTrades] = useState<PaperTradeDto[]>([]);
|
||||
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 <div className="wd-loading">위젯 대시보드 로딩…</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<WidgetDashboardProvider
|
||||
theme={theme}
|
||||
tickers={tickers}
|
||||
marketInfos={marketInfos}
|
||||
marketLoading={marketLoading}
|
||||
usdRate={usdRate}
|
||||
defaultMarket={defaultMarket}
|
||||
strategies={strategies}
|
||||
summary={summary}
|
||||
trades={trades}
|
||||
refreshPaperData={() => { void refreshPaperData(); }}
|
||||
paperTradingEnabled={paperTradingEnabled}
|
||||
paperAutoTradeEnabled={paperAutoTradeEnabled}
|
||||
onPaperOrderFilled={onPaperOrderFilled}
|
||||
chartRealtimeSource={chartRealtimeSource}
|
||||
>
|
||||
<WidgetDashboardShell
|
||||
theme={theme}
|
||||
layout={layout}
|
||||
onLayoutChange={persistLayout}
|
||||
/>
|
||||
</WidgetDashboardProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetDashboardPage;
|
||||
Reference in New Issue
Block a user