Fix chart tail viewport after 5m/15m timeframe switches.
Snap to latest candles on period change and only preserve scroll position when viewing history. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -178,6 +178,7 @@
|
||||
let exhaustedLeft = false;
|
||||
let loadingLeft = false;
|
||||
let chartDataLoading = false;
|
||||
let chartViewEpoch = 0;
|
||||
let priceTagTimer = null;
|
||||
let tfDigitBuf = "";
|
||||
let tfDigitTimer = null;
|
||||
@@ -2025,6 +2026,22 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
function isViewingChartTail(range, candleCount) {
|
||||
if (!range || candleCount <= 0) return true;
|
||||
const maxTo = candleCount - 1 + RIGHT_OFFSET_BARS;
|
||||
return range.to >= maxTo - 24;
|
||||
}
|
||||
|
||||
function tailVisibleLogicalRange(candleCount) {
|
||||
const n = Math.max(0, Number(candleCount) || 0);
|
||||
if (n <= 0) return null;
|
||||
const visible = Math.min(DEFAULT_VISIBLE_BARS, n);
|
||||
return {
|
||||
from: Math.max(0, n - visible),
|
||||
to: n - 1 + RIGHT_OFFSET_BARS,
|
||||
};
|
||||
}
|
||||
|
||||
function clearChartSeriesData() {
|
||||
lastCandles = [];
|
||||
candleByTime = {};
|
||||
@@ -2149,9 +2166,12 @@
|
||||
if (!exKey || !sym || !lastCandles.length || chartDataLoading) return;
|
||||
if (!lastViewKey || vKey !== lastViewKey) return;
|
||||
const myToken = loadToken;
|
||||
const epochAtStart = chartViewEpoch;
|
||||
const candleCountBefore = lastCandles.length;
|
||||
let savedRange = null;
|
||||
if (chart) savedRange = chart.timeScale().getVisibleLogicalRange();
|
||||
const wasViewingTail =
|
||||
!savedRange || isViewingChartTail(savedRange, candleCountBefore);
|
||||
try {
|
||||
const data = await fetchChartChunk({
|
||||
exchange_key: exKey,
|
||||
@@ -2161,6 +2181,7 @@
|
||||
});
|
||||
if (myToken !== loadToken) return;
|
||||
if (vKey !== lastViewKey) return;
|
||||
if (epochAtStart !== chartViewEpoch) return;
|
||||
if (!data.ok || !data.candles || !data.candles.length) return;
|
||||
if (data.price_tick != null) {
|
||||
priceTick = data.price_tick;
|
||||
@@ -2172,13 +2193,23 @@
|
||||
}
|
||||
}
|
||||
applyCandlesToChart(mergeCandles(lastCandles, alignCandlesToTick(data.candles), { prepend: false }), 0);
|
||||
if (
|
||||
if (epochAtStart !== chartViewEpoch) return;
|
||||
const n = lastCandles.length;
|
||||
const curRange = chart && chart.timeScale().getVisibleLogicalRange();
|
||||
if (wasViewingTail) {
|
||||
const tailRange = tailVisibleLogicalRange(n);
|
||||
if (tailRange) chart.timeScale().setVisibleLogicalRange(tailRange);
|
||||
} else if (
|
||||
savedRange &&
|
||||
isVisibleRangeValidForCandles(savedRange, lastCandles.length) &&
|
||||
Math.abs(lastCandles.length - candleCountBefore) < chartChunkLimit(tf)
|
||||
isVisibleRangeValidForCandles(savedRange, n) &&
|
||||
Math.abs(n - candleCountBefore) < chartChunkLimit(tf)
|
||||
) {
|
||||
chart.timeScale().setVisibleLogicalRange(savedRange);
|
||||
} else if (!isVisibleRangeValidForCandles(curRange, n)) {
|
||||
const tailRange = tailVisibleLogicalRange(n);
|
||||
if (tailRange) chart.timeScale().setVisibleLogicalRange(tailRange);
|
||||
}
|
||||
updateVisibleRangeMarkers();
|
||||
if (posContext) {
|
||||
updateLivePosPnl();
|
||||
refreshPosPnlFromBoard();
|
||||
@@ -2200,18 +2231,17 @@
|
||||
|
||||
function applyDefaultVisibleRange() {
|
||||
if (!chart || !lastCandles.length) return;
|
||||
const n = lastCandles.length;
|
||||
const visible = Math.min(DEFAULT_VISIBLE_BARS, n);
|
||||
const from = Math.max(0, n - visible);
|
||||
const to = n - 1 + RIGHT_OFFSET_BARS;
|
||||
applyChartRightGap();
|
||||
requestAnimationFrame(function () {
|
||||
requestAnimationFrame(function () {
|
||||
if (!chart || !lastCandles.length) return;
|
||||
chart.timeScale().setVisibleLogicalRange({ from: from, to: to });
|
||||
updateVisibleRangeMarkers();
|
||||
});
|
||||
});
|
||||
function applyOnce() {
|
||||
if (!chart || !lastCandles.length) return;
|
||||
const r = tailVisibleLogicalRange(lastCandles.length);
|
||||
if (!r) return;
|
||||
applyChartRightGap();
|
||||
chart.timeScale().setVisibleLogicalRange(r);
|
||||
updateVisibleRangeMarkers();
|
||||
}
|
||||
applyOnce();
|
||||
requestAnimationFrame(applyOnce);
|
||||
setTimeout(applyOnce, 0);
|
||||
}
|
||||
|
||||
function updateVisibleRangeMarkers() {
|
||||
@@ -2429,6 +2459,7 @@
|
||||
const resetView = !!force || vKey !== lastViewKey;
|
||||
chartDataLoading = true;
|
||||
if (resetView) {
|
||||
chartViewEpoch += 1;
|
||||
resetChartHistoryState();
|
||||
lastViewKey = "";
|
||||
clearChartSeriesData();
|
||||
@@ -2592,6 +2623,7 @@
|
||||
if (elFsTf) {
|
||||
elFsTf.addEventListener("change", function () {
|
||||
currentTf = elFsTf.value || "1d";
|
||||
lastViewKey = "";
|
||||
syncMainFromFsToolbar();
|
||||
tickLiveClock();
|
||||
loadChart(false);
|
||||
|
||||
Reference in New Issue
Block a user