91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
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<string | null>(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;
|
|
if (targets.find(t => t.market === market)?.pinned) {
|
|
window.alert('고정된 종목은 투자대상에서 삭제할 수 없습니다.');
|
|
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,
|
|
};
|
|
}
|