매매 시그널 알림 화면 수정
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { useRef, useEffect, useLayoutEffect, useState, useCallback } from
|
||||
import type { MouseEventParams, Time } from 'lightweight-charts';
|
||||
import type { OHLCVBar, ChartType, Theme, IndicatorConfig, LegendData, Drawing, ChartMode, Timeframe } from '../types';
|
||||
import { ChartManager } from '../utils/ChartManager';
|
||||
import { useChartTimeFormat } from '../utils/chartTimeFormat';
|
||||
import { setIndicatorChartContext } from '../utils/indicatorRegistry';
|
||||
import { DISPLAY_COUNT } from '../hooks/useUpbitData';
|
||||
|
||||
@@ -45,6 +46,7 @@ function canApplyChartBars(
|
||||
}
|
||||
import DrawingCanvas, { hitTestDrawing } from './DrawingCanvas';
|
||||
import PaneLegend, { type PaneLegendProps } from './PaneLegend';
|
||||
import CandlePaneTimeAxis from './CandlePaneTimeAxis';
|
||||
import ChartHoverToolbar from './ChartHoverToolbar';
|
||||
import ChartMagnifier from './ChartMagnifier';
|
||||
import ChartContextMenu from './ChartContextMenu';
|
||||
@@ -216,6 +218,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
const prevSortedPKRef = useRef<string>(''); // 순서 무관 paramKey (reorder 감지용)
|
||||
const prevIndicatorsListRef = useRef<IndicatorConfig[]>(indicators);
|
||||
const indicatorSyncInFlightRef = useRef(false);
|
||||
const pendingIndicatorResyncRef = useRef(false);
|
||||
const indicatorReloadGenRef = useRef(0);
|
||||
const prevMarket = useRef<string>(market);
|
||||
const prevMarketTf = useRef<Timeframe>(timeframe);
|
||||
@@ -228,6 +231,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
const resizeDebounceRef = useRef<number | null>(null);
|
||||
const chartVisibleRef = useRef(chartVisible);
|
||||
|
||||
const chartTimeFormat = useChartTimeFormat();
|
||||
const [chartMgr, setChartMgr] = useState<ChartManager | null>(null);
|
||||
/** 캔들 pane 전체보기 (오버레이 지표 유지, 하단 보조지표 pane 숨김) */
|
||||
const [candleOnlyMode, setCandleOnlyMode] = useState(false);
|
||||
@@ -456,6 +460,27 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
});
|
||||
}, [afterIndicatorPaneMutation, restoreLogicalRange]);
|
||||
|
||||
/** 설정 저장 등으로 연속 갱신이 겹칠 때 마지막 상태로 한 번 더 동기화 */
|
||||
const flushPendingIndicatorResync = useCallback((
|
||||
mgr: ChartManager,
|
||||
completedGen: number,
|
||||
) => {
|
||||
if (!pendingIndicatorResyncRef.current) return;
|
||||
if (completedGen !== indicatorReloadGenRef.current) return;
|
||||
pendingIndicatorResyncRef.current = false;
|
||||
const latest = indicatorsRef.current;
|
||||
indicatorSyncInFlightRef.current = true;
|
||||
const flushGen = ++indicatorReloadGenRef.current;
|
||||
reloadIndicatorsWithCover(mgr, latest, () => {
|
||||
if (flushGen !== indicatorReloadGenRef.current) {
|
||||
indicatorSyncInFlightRef.current = false;
|
||||
return;
|
||||
}
|
||||
syncIndicatorTrackingRefs(latest);
|
||||
indicatorSyncInFlightRef.current = false;
|
||||
});
|
||||
}, [reloadIndicatorsWithCover, syncIndicatorTrackingRefs]);
|
||||
|
||||
const queueFullReload = useCallback(() => {
|
||||
if (!chartVisibleRef.current) return;
|
||||
if (recoveryTimerRef.current) clearTimeout(recoveryTimerRef.current);
|
||||
@@ -573,7 +598,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
}
|
||||
mgr.setTheme(th);
|
||||
mgr.setLogScale(ls);
|
||||
mgr.setDisplayTimezone(displayTimezone, timeframe);
|
||||
mgr.setDisplayTimezone(displayTimezone, timeframe, chartTimeFormat);
|
||||
commitCandleLayout(mgr);
|
||||
onCandlesReady?.();
|
||||
lastAppliedMarketRef.current = market;
|
||||
@@ -996,8 +1021,8 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
managerRef.current?.setDisplayTimezone(displayTimezone, timeframe);
|
||||
}, [displayTimezone, timeframe]);
|
||||
managerRef.current?.setDisplayTimezone(displayTimezone, timeframe, chartTimeFormat);
|
||||
}, [displayTimezone, timeframe, chartTimeFormat]);
|
||||
|
||||
// 종목·타임프레임 변경 시 barsKey 캐시만 초기화 (reloadAll 은 bars effect 에서 1회만)
|
||||
useEffect(() => {
|
||||
@@ -1083,6 +1108,10 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
prevLogScale.current = logScale;
|
||||
mgr.setLogScale(logScale);
|
||||
} else if (indChanged) {
|
||||
if (indicatorSyncInFlightRef.current) {
|
||||
pendingIndicatorResyncRef.current = true;
|
||||
return;
|
||||
}
|
||||
const prevList = prevIndicatorsListRef.current;
|
||||
const prevPK = prevIndKey.current.split('@@')[0] ?? '';
|
||||
const currPK = paramKey(indicators);
|
||||
@@ -1142,12 +1171,18 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
&& addedConfigs.length === 0
|
||||
) {
|
||||
const savedLR = mgr.getVisibleLogicalRange();
|
||||
const syncGen = ++indicatorReloadGenRef.current;
|
||||
indicatorSyncInFlightRef.current = true;
|
||||
void mgr.refreshIndicators(paramsChangedConfigs).then(() => {
|
||||
if (syncGen !== indicatorReloadGenRef.current) {
|
||||
indicatorSyncInFlightRef.current = false;
|
||||
return;
|
||||
}
|
||||
afterIndicatorPaneMutation(mgr);
|
||||
restoreLogicalRange(mgr, savedLR);
|
||||
syncIndicatorTrackingRefs(indicators);
|
||||
indicatorSyncInFlightRef.current = false;
|
||||
flushPendingIndicatorResync(mgr, syncGen);
|
||||
});
|
||||
} else if (isReorderOnly) {
|
||||
reloadIndicatorsWithCover(mgr, indicators, () => {
|
||||
@@ -1165,7 +1200,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [chartMgr, bars, barsMarket, market, chartType, theme, logScale, indicators, chartVisible, reloadIndicatorsWithCover, afterIndicatorPaneMutation, restoreLogicalRange, syncIndicatorTrackingRefs, repairMissingIndicators]);
|
||||
}, [chartMgr, bars, barsMarket, market, chartType, theme, logScale, indicators, chartVisible, reloadIndicatorsWithCover, afterIndicatorPaneMutation, restoreLogicalRange, syncIndicatorTrackingRefs, repairMissingIndicators, flushPendingIndicatorResync]);
|
||||
|
||||
// 데이터는 준비됐으나 메인 시리즈가 없는 빈 차트 자동 복구 (종목 전환·레이아웃 전환 직후)
|
||||
useEffect(() => {
|
||||
@@ -1325,6 +1360,12 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
onClose={() => setCtxMenu(null)}
|
||||
/>
|
||||
)}
|
||||
{chartMgr && (
|
||||
<CandlePaneTimeAxisPortal
|
||||
manager={chartMgr}
|
||||
getContainer={() => containerRef.current}
|
||||
/>
|
||||
)}
|
||||
{chartMgr && paneSeparatorOptions && (
|
||||
<PaneSeparatorOverlay
|
||||
manager={chartMgr}
|
||||
@@ -1397,6 +1438,22 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const CandlePaneTimeAxisPortal: React.FC<{
|
||||
manager: ChartManager;
|
||||
getContainer: () => HTMLElement | null;
|
||||
}> = ({ manager, getContainer }) => {
|
||||
const [el, setEl] = useState<HTMLElement | null>(null);
|
||||
useEffect(() => {
|
||||
const c = getContainer();
|
||||
if (c) { setEl(c); return; }
|
||||
const tid = setTimeout(() => setEl(getContainer()), 100);
|
||||
return () => clearTimeout(tid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
if (!el) return null;
|
||||
return <CandlePaneTimeAxis manager={manager} containerEl={el} />;
|
||||
};
|
||||
|
||||
const CandlePaneControlsPortal: React.FC<{
|
||||
manager: ChartManager;
|
||||
getContainer: () => HTMLElement | null;
|
||||
|
||||
Reference in New Issue
Block a user