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.

This commit is contained in:
padreug 2025-06-22 19:59:04 +02:00
parent 9e65f05906
commit 0962573176

View file

@ -209,10 +209,21 @@ window.app = Vue.createApp({
}, },
async loadChartData() { async loadChartData() {
// Prevent multiple simultaneous requests // Prevent multiple simultaneous requests
if (this.chartLoading) return if (this.chartLoading) {
console.log('Chart already loading, ignoring request')
return
}
try { try {
this.chartLoading = true 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( const {data} = await LNbits.api.request(
'GET', 'GET',
`/satmachineclient/api/v1/dashboard/analytics?time_range=${this.chartTimeRange}`, `/satmachineclient/api/v1/dashboard/analytics?time_range=${this.chartTimeRange}`,
@ -226,19 +237,19 @@ window.app = Vue.createApp({
} }
this.analyticsData = data 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() this.initDCAChart()
// If chart ref still not available, try again shortly } else {
if (!this.$refs.dcaChart) { console.log('Chart loading was cancelled, skipping initialization')
setTimeout(() => { this.chartLoading = false
this.initDCAChart() }
}, 100)
}
})
} catch (error) { } catch (error) {
console.error('Error loading chart data:', error) console.error('Error loading chart data:', error)
} finally {
this.chartLoading = false this.chartLoading = false
} }
}, },
@ -247,7 +258,14 @@ window.app = Vue.createApp({
console.log('initDCAChart called') console.log('initDCAChart called')
console.log('analyticsData:', this.analyticsData) console.log('analyticsData:', this.analyticsData)
console.log('dcaChart ref:', this.$refs.dcaChart) 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) { if (!this.analyticsData) {
console.log('No analytics data available') console.log('No analytics data available')
return return
@ -255,9 +273,9 @@ window.app = Vue.createApp({
if (!this.$refs.dcaChart) { if (!this.$refs.dcaChart) {
console.log('No chart ref available, waiting for DOM...') 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(() => { this.$nextTick(() => {
if (this.$refs.dcaChart) { if (this.$refs.dcaChart && this.chartLoading) {
this.initDCAChart() this.initDCAChart()
} }
}) })
@ -273,8 +291,9 @@ window.app = Vue.createApp({
console.log('Chart.js version:', Chart.version || 'unknown') console.log('Chart.js version:', Chart.version || 'unknown')
console.log('Chart.js available:', typeof Chart) console.log('Chart.js available:', typeof Chart)
// Destroy existing chart // Destroy existing chart (redundant safety check)
if (this.dcaChart) { if (this.dcaChart) {
console.log('Destroying existing chart in initDCAChart')
this.dcaChart.destroy() this.dcaChart.destroy()
this.dcaChart = null this.dcaChart = null
} }
@ -391,6 +410,8 @@ window.app = Vue.createApp({
} }
} }
}) })
// Clear loading state after creating placeholder chart
this.chartLoading = false
return return
} }
@ -479,13 +500,22 @@ window.app = Vue.createApp({
}, },
createChart(labels, cumulativeSats) { createChart(labels, cumulativeSats) {
console.log('createChart called with loading state:', this.chartLoading)
if (!this.$refs.dcaChart) { if (!this.$refs.dcaChart) {
console.log('Chart ref not available for createChart') console.log('Chart ref not available for createChart')
return 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 // Destroy existing chart
if (this.dcaChart) { if (this.dcaChart) {
console.log('Destroying existing chart in createChart')
this.dcaChart.destroy() this.dcaChart.destroy()
this.dcaChart = null this.dcaChart = null
} }
@ -501,110 +531,125 @@ window.app = Vue.createApp({
// Small delay to ensure Chart.js is fully initialized // Small delay to ensure Chart.js is fully initialized
setTimeout(() => { setTimeout(() => {
this.dcaChart = new Chart(ctx, { try {
type: 'line', // Final check to ensure we're still in the correct loading state
data: { if (!this.chartLoading) {
labels: labels, console.log('Loading state changed during timeout, aborting chart creation')
datasets: [{ return
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`
}
}
} }
},
scales: { this.dcaChart = new Chart(ctx, {
x: { type: 'line',
display: true, data: {
grid: { labels: labels,
display: false 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: { options: {
color: '#666666', responsive: true,
font: { maintainAspectRatio: false,
size: 12, plugins: {
weight: '500' legend: {
} display: false
} },
}, tooltip: {
y: { mode: 'index',
display: true, intersect: false,
beginAtZero: true, backgroundColor: 'rgba(0, 0, 0, 0.8)',
grid: { titleColor: '#FFFFFF',
color: 'rgba(255, 149, 0, 0.1)', bodyColor: '#FFFFFF',
drawBorder: false borderColor: '#FF9500',
}, borderWidth: 2,
ticks: { cornerRadius: 8,
color: '#666666', displayColors: false,
font: { callbacks: {
size: 12, title: function(context) {
weight: '500' return `📅 ${context[0].label}`
}, },
callback: function (value) { label: function (context) {
if (value >= 1000000) { return `${context.parsed.y.toLocaleString()} sats accumulated`
return (value / 1000000).toFixed(1) + 'M sats' }
} else if (value >= 1000) { }
return (value / 1000).toFixed(0) + 'k sats' }
},
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'
} }
} }
} })
}, console.log('Chart created successfully in createChart!')
interaction: { // Chart is now created, clear loading state
mode: 'nearest', this.chartLoading = false
axis: 'x', } catch (error) {
intersect: false console.error('Error in createChart setTimeout:', error)
}, this.chartLoading = false
elements: {
point: {
hoverRadius: 8
}
} }
}
})
console.log('Chart created successfully in createChart!')
}, 50) }, 50)
} catch (error) { } catch (error) {
console.error('Error creating Chart.js chart in createChart:', error) console.error('Error creating Chart.js chart in createChart:', error)
console.log('Chart data that failed:', { labels, cumulativeSats }) 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: { watch: {
analyticsData: { analyticsData: {
handler(newData) { handler(newData) {
if (newData) { if (newData && !this.chartLoading && !this.dcaChart) {
console.log('Analytics data changed, initializing chart...') console.log('Analytics data changed and no chart exists, initializing chart...')
this.$nextTick(() => { 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()
}
}) })
} }
}, },