feat: add offline indicator and increase CoinGecko cache to avoid rate limits
This commit is contained in:
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user