import { useCallback, useEffect, useMemo, useState } from 'react'; import { loadVirtualTargets, VIRTUAL_SESSION_CHANGED_EVENT, } from '../utils/virtualTradingStorage'; import { addVirtualTarget, removeVirtualTarget, type VirtualTargetMeta, } from '../utils/virtualTargetMutations'; import { getVirtualTargetMaxCount } from './useAppSettings'; import { virtualTargetLimitMessage } from '../utils/virtualTargetLimits'; /** 가상투자 투자대상 마켓 집합 (추세검색 카드 등) */ export function useVirtualTargetRegistry() { const [targets, setTargets] = useState(() => loadVirtualTargets()); const [busyMarket, setBusyMarket] = useState(null); const refresh = useCallback(() => { setTargets(loadVirtualTargets()); }, []); useEffect(() => { refresh(); const onChange = () => refresh(); window.addEventListener(VIRTUAL_SESSION_CHANGED_EVENT, onChange); return () => window.removeEventListener(VIRTUAL_SESSION_CHANGED_EVENT, onChange); }, [refresh]); const addedSet = useMemo( () => new Set(targets.map(t => t.market)), [targets], ); const isInTargets = useCallback( (market: string) => addedSet.has(market), [addedSet], ); const isPinned = useCallback( (market: string) => targets.find(t => t.market === market)?.pinned === true, [targets], ); const add = useCallback(async (market: string, meta?: VirtualTargetMeta) => { if (addedSet.has(market) || busyMarket === market) return; const maxCount = getVirtualTargetMaxCount(); if (targets.length >= maxCount) { window.alert(virtualTargetLimitMessage(maxCount)); return; } setBusyMarket(market); try { const next = await addVirtualTarget(market, meta); setTargets(next); } catch (e) { console.warn('[VirtualTarget] add failed', market, e); window.alert('투자대상 추가에 실패했습니다.'); } finally { setBusyMarket(null); } }, [addedSet, busyMarket, targets]); const remove = useCallback(async (market: string) => { if (!addedSet.has(market) || busyMarket === market) return; setBusyMarket(market); try { const next = await removeVirtualTarget(market); setTargets(next); } catch (e) { console.warn('[VirtualTarget] remove failed', market, e); window.alert('투자대상 제거에 실패했습니다.'); } finally { setBusyMarket(null); } }, [addedSet, busyMarket, targets]); return { addedSet, isInTargets, isPinned, add, remove, busyMarket, }; }