검증게시판 단계 추가
This commit is contained in:
@@ -98,6 +98,12 @@ export function invalidateAppSettingsCache() {
|
||||
_loadPromise = null;
|
||||
}
|
||||
|
||||
/** 서버에서 app-settings 재로드 후 캐시 갱신 (모바일 ↻ 동기화 등) */
|
||||
export function reloadAppSettingsCache(): Promise<AppSettingsDto> {
|
||||
invalidateAppSettingsCache();
|
||||
return ensureLoaded();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/** DB 또는 fallback 기본값 접근 헬퍼 */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 가상매매 공통 상태·동기화 (웹 VirtualTradingPage · 모바일 앱 공유)
|
||||
*/
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { TickerData } from './useMarketTicker';
|
||||
import {
|
||||
loadLiveStrategySettings,
|
||||
@@ -47,7 +47,13 @@ import {
|
||||
virtualTargetLimitMessage,
|
||||
} from '../utils/virtualTargetLimits';
|
||||
import { resolveVirtualTargetNames } from '../utils/virtualTargetNames';
|
||||
import { useAppSettings, resolveAppDefaults } from './useAppSettings';
|
||||
import {
|
||||
useAppSettings,
|
||||
resolveAppDefaults,
|
||||
subscribeAppSettings,
|
||||
reloadAppSettingsCache,
|
||||
} from './useAppSettings';
|
||||
import { hydrateVirtualTargetsIfEmpty } from '../utils/virtualTargetsHydrate';
|
||||
|
||||
export interface UseVirtualTradingCoreOptions {
|
||||
defaultMarket?: string;
|
||||
@@ -71,7 +77,7 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
|
||||
settingsSessionKey = 0,
|
||||
} = options;
|
||||
|
||||
const { settings: appSettings } = useAppSettings(settingsSessionKey);
|
||||
const { settings: appSettings, isLoaded: settingsLoaded } = useAppSettings(settingsSessionKey);
|
||||
const appChartDefaults = resolveAppDefaults(appSettings);
|
||||
const paperTradingEnabled = paperTradingEnabledProp ?? appChartDefaults.paperTradingEnabled;
|
||||
const paperAutoTradeEnabled = paperAutoTradeEnabledProp ?? appChartDefaults.paperAutoTradeEnabled;
|
||||
@@ -86,20 +92,53 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [selectedMarket, setSelectedMarket] = useState(defaultMarket);
|
||||
const [viewMode, setViewMode] = useState<VirtualCardViewMode>(() => loadVirtualCardViewMode());
|
||||
/** 서버 ui_preferences 반영 전 빈 목록을 DB에 저장하지 않음 */
|
||||
const preferencesHydratedRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
const reloadFromUiPreferences = useCallback(() => {
|
||||
setTargets(loadVirtualTargets());
|
||||
setSession(loadVirtualSession());
|
||||
void Promise.all([
|
||||
loadStrategies().then(setStrategies).catch(() => setStrategies([])),
|
||||
loadPaperSummary().then(setSummary),
|
||||
loadPaperTrades().then(setTrades),
|
||||
]).finally(() => setLoading(false));
|
||||
}, [settingsSessionKey]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!settingsLoaded) return;
|
||||
let cancelled = false;
|
||||
preferencesHydratedRef.current = false;
|
||||
setLoading(true);
|
||||
void (async () => {
|
||||
reloadFromUiPreferences();
|
||||
let nextTargets = loadVirtualTargets();
|
||||
if (nextTargets.length === 0) {
|
||||
nextTargets = await hydrateVirtualTargetsIfEmpty();
|
||||
if (!cancelled && nextTargets.length > 0) {
|
||||
setTargets(nextTargets);
|
||||
saveVirtualTargets(nextTargets);
|
||||
}
|
||||
}
|
||||
if (!cancelled) {
|
||||
preferencesHydratedRef.current = true;
|
||||
}
|
||||
await Promise.all([
|
||||
loadStrategies().then(s => { if (!cancelled) setStrategies(s); }).catch(() => { if (!cancelled) setStrategies([]); }),
|
||||
loadPaperSummary().then(s => { if (!cancelled) setSummary(s); }),
|
||||
loadPaperTrades().then(t => { if (!cancelled) setTrades(t); }),
|
||||
]);
|
||||
if (!cancelled) setLoading(false);
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [settingsSessionKey, settingsLoaded, reloadFromUiPreferences]);
|
||||
|
||||
/** DB ui_preferences 반영(웹·모바일 동일 계정) */
|
||||
useEffect(() => {
|
||||
if (!settingsLoaded) return;
|
||||
return subscribeAppSettings(() => {
|
||||
reloadFromUiPreferences();
|
||||
});
|
||||
}, [settingsLoaded, reloadFromUiPreferences]);
|
||||
|
||||
/** DB is_pinned → uiPreferences.virtual.targets 동기화 */
|
||||
useEffect(() => {
|
||||
if (!settingsLoaded) return;
|
||||
let cancelled = false;
|
||||
const initial = loadVirtualTargets();
|
||||
if (initial.length === 0) return;
|
||||
@@ -119,10 +158,16 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
|
||||
if (!cancelled) setTargets(merged);
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
}, [settingsLoaded]);
|
||||
|
||||
useEffect(() => { saveVirtualTargets(targets); }, [targets]);
|
||||
useEffect(() => { saveVirtualSession(session); }, [session]);
|
||||
useEffect(() => {
|
||||
if (!preferencesHydratedRef.current) return;
|
||||
saveVirtualTargets(targets);
|
||||
}, [targets]);
|
||||
useEffect(() => {
|
||||
if (!preferencesHydratedRef.current) return;
|
||||
saveVirtualSession(session);
|
||||
}, [session]);
|
||||
useEffect(() => { saveVirtualCardViewMode(viewMode); }, [viewMode]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -365,11 +410,26 @@ export function useVirtualTradingCore(options: UseVirtualTradingCoreOptions = {}
|
||||
void resyncRunningSession(next);
|
||||
}, [session, resyncRunningSession]);
|
||||
|
||||
/** 서버·DB에서 targets 다시 로드 (탭 포커스 등) */
|
||||
/** 서버·DB에서 targets 다시 로드 (↻ 동기화) */
|
||||
const reloadTargetsFromStorage = useCallback(() => {
|
||||
setTargets(loadVirtualTargets());
|
||||
setSession(loadVirtualSession());
|
||||
}, []);
|
||||
setLoading(true);
|
||||
preferencesHydratedRef.current = false;
|
||||
void reloadAppSettingsCache()
|
||||
.then(async () => {
|
||||
reloadFromUiPreferences();
|
||||
let next = loadVirtualTargets();
|
||||
if (next.length === 0) {
|
||||
next = await hydrateVirtualTargetsIfEmpty();
|
||||
if (next.length > 0) {
|
||||
setTargets(next);
|
||||
saveVirtualTargets(next);
|
||||
}
|
||||
}
|
||||
preferencesHydratedRef.current = true;
|
||||
})
|
||||
.catch(() => reloadFromUiPreferences())
|
||||
.finally(() => setLoading(false));
|
||||
}, [reloadFromUiPreferences]);
|
||||
|
||||
return {
|
||||
targets,
|
||||
|
||||
Reference in New Issue
Block a user