feat: add offline indicator and increase CoinGecko cache to avoid rate limits

This commit is contained in:
2026-02-04 11:06:16 +01:00
parent 9f3ff3e3cb
commit 96538ab1db
3 changed files with 75 additions and 3 deletions

View File

@@ -88,6 +88,42 @@ export function setupGlobalErrorHandler() {
console.error('Unhandled promise rejection:', event.reason) console.error('Unhandled promise rejection:', event.reason)
showErrorToast(event.reason?.message || 'Ein Fehler ist aufgetreten') showErrorToast(event.reason?.message || 'Ein Fehler ist aufgetreten')
}) })
// Offline/Online detection
setupOfflineIndicator()
}
/**
* Shows/hides offline indicator based on network status
*/
function setupOfflineIndicator() {
const updateStatus = () => {
let indicator = document.querySelector('.offline-indicator')
if (!navigator.onLine) {
if (!indicator) {
indicator = document.createElement('div')
indicator.className = 'offline-indicator'
indicator.innerHTML = `
<span class="offline-icon">📡</span>
<span class="offline-text">Offline</span>
`
document.body.appendChild(indicator)
requestAnimationFrame(() => indicator.classList.add('visible'))
}
} else {
if (indicator) {
indicator.classList.remove('visible')
setTimeout(() => indicator.remove(), 300)
}
}
}
window.addEventListener('online', updateStatus)
window.addEventListener('offline', updateStatus)
// Check on init
updateStatus()
} }
/** /**
@@ -214,6 +250,40 @@ style.textContent = /* css */`
.error-toast-close:hover { .error-toast-close:hover {
color: var(--color-text); color: var(--color-text);
} }
/* Offline Indicator */
.offline-indicator {
position: fixed;
top: var(--space-md);
left: 50%;
transform: translateX(-50%) translateY(-100px);
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-md);
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
box-shadow: var(--shadow-md);
z-index: 10001;
opacity: 0;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.offline-indicator.visible {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
.offline-indicator .offline-icon {
filter: grayscale(1);
}
.offline-indicator .offline-text {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-text);
}
` `
document.head.appendChild(style) document.head.appendChild(style)

View File

@@ -16,8 +16,8 @@ const CURRENCY_SYMBOLS = {
JPY: '¥' JPY: '¥'
} }
const CACHE_DURATION = 10 * 60 * 1000 // 10 minutes (CoinGecko free tier: 10-30 req/min) const CACHE_DURATION = 30 * 60 * 1000 // 30 minutes (CoinGecko free tier is strict)
const MIN_REQUEST_INTERVAL = 6 * 1000 // 6 seconds between requests (max ~10/min) const MIN_REQUEST_INTERVAL = 60 * 1000 // 60 seconds between requests
let cachedRates = null let cachedRates = null
let cacheTimestamp = 0 let cacheTimestamp = 0

View File

@@ -1,4 +1,4 @@
const CACHE_NAME = 'dgray-v27'; const CACHE_NAME = 'dgray-v28';
const STATIC_ASSETS = [ const STATIC_ASSETS = [
'/', '/',
'/index.html', '/index.html',
@@ -16,6 +16,8 @@ const STATIC_ASSETS = [
'/js/components/chat-widget.js', '/js/components/chat-widget.js',
'/js/components/listing-card.js', '/js/components/listing-card.js',
'/js/components/search-box.js', '/js/components/search-box.js',
'/js/components/error-boundary.js',
'/js/services/currency.js',
'/locales/de.json', '/locales/de.json',
'/locales/en.json', '/locales/en.json',
'/locales/fr.json', '/locales/fr.json',