Merge pull request 'fix: LiveStats: show hard error when Ponder is unreachable (not silent skeletons) (#201)' (#238) from fix/issue-201 into master

This commit is contained in:
johba 2026-02-24 23:05:31 +01:00
commit 1372207134

View file

@ -1,5 +1,5 @@
<template>
<div v-if="!error && stats" class="live-stats">
<div v-if="stats" class="live-stats">
<div class="live-header">
<span class="live-dot" :class="{ 'live-dot-error': error }"></span>
<span class="live-text">Live</span>
@ -46,7 +46,7 @@
</div>
<div class="freshness">{{ error ? 'Connection lost' : `Updated ${secondsSinceUpdate}s ago` }}</div>
</div>
<div v-else-if="!error && !stats" class="live-stats">
<div v-else-if="!error" class="live-stats">
<div class="stats-grid">
<div class="stat-item skeleton" v-for="i in 6" :key="i">
<div class="stat-label skeleton-text"></div>
@ -54,6 +54,9 @@
</div>
</div>
</div>
<div v-else class="live-stats live-stats-error" data-testid="livestats-error">
<p class="error-message">Protocol data unavailable</p>
</div>
</template>
<script setup lang="ts">
@ -88,6 +91,8 @@ const changedStats = ref<Set<string>>(new Set());
const secondsSinceUpdate = ref(0);
let freshnessTicker: number | null = null;
let changeTimeout: number | null = null;
let loadingTimer: number | null = null;
const LOADING_TIMEOUT_MS = 10_000; // escalate to error after 10s with no data
// Helper: safely convert a Wei BigInt string to ETH float
function weiToEth(wei: string | null | undefined): number {
@ -417,6 +422,7 @@ async function fetchStats() {
stats.value = s;
secondsSinceUpdate.value = 0;
error.value = false;
if (loadingTimer) { clearTimeout(loadingTimer); loadingTimer = null; }
} else {
throw new Error('No stats data');
}
@ -428,6 +434,10 @@ async function fetchStats() {
}
onMounted(async () => {
// Escalate to error state if no data arrives within the timeout window
loadingTimer = window.setTimeout(() => {
if (!stats.value) error.value = true;
}, LOADING_TIMEOUT_MS);
await fetchStats();
fetchEthPrice();
refreshInterval = window.setInterval(fetchStats, 30000); // Refresh every 30 seconds
@ -440,6 +450,7 @@ onUnmounted(() => {
if (ethPriceInterval) clearInterval(ethPriceInterval);
if (freshnessTicker) clearInterval(freshnessTicker);
if (changeTimeout) clearTimeout(changeTimeout);
if (loadingTimer) clearTimeout(loadingTimer);
});
</script>
@ -610,4 +621,15 @@ onUnmounted(() => {
background-position: 200% 0
100%
background-position: -200% 0
.live-stats-error
display: flex
align-items: center
justify-content: center
.error-message
font-size: 14px
color: #f87171
text-align: center
letter-spacing: 0.3px
</style>