From 0962573176386f415d396ff55ed0abcf4e3dcf1b Mon Sep 17 00:00:00 2001 From: padreug Date: Sun, 22 Jun 2025 19:59:04 +0200 Subject: [PATCH] Improve chart loading logic and state management: Enhance loading state handling to prevent conflicts during chart initialization, add detailed logging for better debugging, and ensure proper cleanup of existing charts before creating new ones. Implement checks to skip chart creation if the loading state changes, improving overall user experience. --- static/js/index.js | 273 ++++++++++++++++++++++++++------------------- 1 file changed, 161 insertions(+), 112 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index a038132..352e154 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -209,10 +209,21 @@ window.app = Vue.createApp({ }, async loadChartData() { // Prevent multiple simultaneous requests - if (this.chartLoading) return + if (this.chartLoading) { + console.log('Chart already loading, ignoring request') + return + } try { this.chartLoading = true + + // Destroy existing chart immediately to prevent conflicts + if (this.dcaChart) { + console.log('Destroying existing chart before loading new data') + this.dcaChart.destroy() + this.dcaChart = null + } + const {data} = await LNbits.api.request( 'GET', `/satmachineclient/api/v1/dashboard/analytics?time_range=${this.chartTimeRange}`, @@ -226,19 +237,19 @@ window.app = Vue.createApp({ } this.analyticsData = data - // Use nextTick to ensure DOM is ready, with retry logic - this.$nextTick(() => { + + // Wait for DOM update and ensure we're still in loading state + await this.$nextTick() + + // Double-check we're still the active loading request + if (this.chartLoading) { this.initDCAChart() - // If chart ref still not available, try again shortly - if (!this.$refs.dcaChart) { - setTimeout(() => { - this.initDCAChart() - }, 100) - } - }) + } else { + console.log('Chart loading was cancelled, skipping initialization') + this.chartLoading = false + } } catch (error) { console.error('Error loading chart data:', error) - } finally { this.chartLoading = false } }, @@ -247,7 +258,14 @@ window.app = Vue.createApp({ console.log('initDCAChart called') console.log('analyticsData:', this.analyticsData) console.log('dcaChart ref:', this.$refs.dcaChart) - + console.log('chartLoading state:', this.chartLoading) + + // Skip if we're not in a loading state (indicates this is a stale call) + if (!this.chartLoading && this.dcaChart) { + console.log('Chart already exists and not loading, skipping initialization') + return + } + if (!this.analyticsData) { console.log('No analytics data available') return @@ -255,9 +273,9 @@ window.app = Vue.createApp({ if (!this.$refs.dcaChart) { console.log('No chart ref available, waiting for DOM...') - // Try again after DOM update + // Try again after DOM update, but only if still loading this.$nextTick(() => { - if (this.$refs.dcaChart) { + if (this.$refs.dcaChart && this.chartLoading) { this.initDCAChart() } }) @@ -273,8 +291,9 @@ window.app = Vue.createApp({ console.log('Chart.js version:', Chart.version || 'unknown') console.log('Chart.js available:', typeof Chart) - // Destroy existing chart + // Destroy existing chart (redundant safety check) if (this.dcaChart) { + console.log('Destroying existing chart in initDCAChart') this.dcaChart.destroy() this.dcaChart = null } @@ -391,6 +410,8 @@ window.app = Vue.createApp({ } } }) + // Clear loading state after creating placeholder chart + this.chartLoading = false return } @@ -479,13 +500,22 @@ window.app = Vue.createApp({ }, createChart(labels, cumulativeSats) { + console.log('createChart called with loading state:', this.chartLoading) + if (!this.$refs.dcaChart) { console.log('Chart ref not available for createChart') return } + // Skip if we're not in a loading state (indicates this is a stale call) + if (!this.chartLoading) { + console.log('Not in loading state, skipping createChart') + return + } + // Destroy existing chart if (this.dcaChart) { + console.log('Destroying existing chart in createChart') this.dcaChart.destroy() this.dcaChart = null } @@ -501,110 +531,125 @@ window.app = Vue.createApp({ // Small delay to ensure Chart.js is fully initialized setTimeout(() => { - this.dcaChart = new Chart(ctx, { - type: 'line', - data: { - labels: labels, - datasets: [{ - label: 'Total Sats Accumulated', - data: cumulativeSats, - borderColor: '#FF9500', - backgroundColor: gradient, - borderWidth: 3, - fill: true, - tension: 0.4, - pointBackgroundColor: '#FFFFFF', - pointBorderColor: '#FF9500', - pointBorderWidth: 3, - pointRadius: 6, - pointHoverRadius: 8, - pointHoverBackgroundColor: '#FFFFFF', - pointHoverBorderColor: '#FF7700', - pointHoverBorderWidth: 4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - }, - tooltip: { - mode: 'index', - intersect: false, - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleColor: '#FFFFFF', - bodyColor: '#FFFFFF', - borderColor: '#FF9500', - borderWidth: 2, - cornerRadius: 8, - displayColors: false, - callbacks: { - title: function(context) { - return `📅 ${context[0].label}` - }, - label: function (context) { - return `⚡ ${context.parsed.y.toLocaleString()} sats accumulated` - } - } + try { + // Final check to ensure we're still in the correct loading state + if (!this.chartLoading) { + console.log('Loading state changed during timeout, aborting chart creation') + return } - }, - scales: { - x: { - display: true, - grid: { - display: false + + this.dcaChart = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: 'Total Sats Accumulated', + data: cumulativeSats, + borderColor: '#FF9500', + backgroundColor: gradient, + borderWidth: 3, + fill: true, + tension: 0.4, + pointBackgroundColor: '#FFFFFF', + pointBorderColor: '#FF9500', + pointBorderWidth: 3, + pointRadius: 6, + pointHoverRadius: 8, + pointHoverBackgroundColor: '#FFFFFF', + pointHoverBorderColor: '#FF7700', + pointHoverBorderWidth: 4 + }] }, - ticks: { - color: '#666666', - font: { - size: 12, - weight: '500' - } - } - }, - y: { - display: true, - beginAtZero: true, - grid: { - color: 'rgba(255, 149, 0, 0.1)', - drawBorder: false - }, - ticks: { - color: '#666666', - font: { - size: 12, - weight: '500' - }, - callback: function (value) { - if (value >= 1000000) { - return (value / 1000000).toFixed(1) + 'M sats' - } else if (value >= 1000) { - return (value / 1000).toFixed(0) + 'k sats' + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + }, + tooltip: { + mode: 'index', + intersect: false, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#FFFFFF', + bodyColor: '#FFFFFF', + borderColor: '#FF9500', + borderWidth: 2, + cornerRadius: 8, + displayColors: false, + callbacks: { + title: function(context) { + return `📅 ${context[0].label}` + }, + label: function (context) { + return `⚡ ${context.parsed.y.toLocaleString()} sats accumulated` + } + } + } + }, + scales: { + x: { + display: true, + grid: { + display: false + }, + ticks: { + color: '#666666', + font: { + size: 12, + weight: '500' + } + } + }, + y: { + display: true, + beginAtZero: true, + grid: { + color: 'rgba(255, 149, 0, 0.1)', + drawBorder: false + }, + ticks: { + color: '#666666', + font: { + size: 12, + weight: '500' + }, + callback: function (value) { + if (value >= 1000000) { + return (value / 1000000).toFixed(1) + 'M sats' + } else if (value >= 1000) { + return (value / 1000).toFixed(0) + 'k sats' + } + return value.toLocaleString() + ' sats' + } + } + } + }, + interaction: { + mode: 'nearest', + axis: 'x', + intersect: false + }, + elements: { + point: { + hoverRadius: 8 } - return value.toLocaleString() + ' sats' } } - } - }, - interaction: { - mode: 'nearest', - axis: 'x', - intersect: false - }, - elements: { - point: { - hoverRadius: 8 - } + }) + console.log('Chart created successfully in createChart!') + // Chart is now created, clear loading state + this.chartLoading = false + } catch (error) { + console.error('Error in createChart setTimeout:', error) + this.chartLoading = false } - } - }) - console.log('Chart created successfully in createChart!') }, 50) } catch (error) { console.error('Error creating Chart.js chart in createChart:', error) console.log('Chart data that failed:', { labels, cumulativeSats }) + // Clear loading state on error + this.chartLoading = false } } }, @@ -651,10 +696,14 @@ window.app = Vue.createApp({ watch: { analyticsData: { handler(newData) { - if (newData) { - console.log('Analytics data changed, initializing chart...') + if (newData && !this.chartLoading && !this.dcaChart) { + console.log('Analytics data changed and no chart exists, initializing chart...') this.$nextTick(() => { - this.initDCAChart() + // Only initialize if we don't have a chart and aren't currently loading + if (!this.dcaChart && !this.chartLoading) { + this.chartLoading = true + this.initDCAChart() + } }) } },