Files
Calendarr/frontend/sw.js
Scarriffle 7cabfb10de perf: Event-Cache von ±8 Wochen auf ±10 Monate erweitern
Damit lädt beim Swipen durch Monate erst nach ~10 Monaten in beide
Richtungen erneut Daten nach. Vorher reichte der Cache nur ±2 Monate,
sodass nach 2-3 Wischen ein Spinner kam.

- CACHE_BUF      56  → 300 Tage (initial ±10 Monate)
- PREFETCH_EXT   56  → 180 Tage (Verlängerung bei Edge ~6 Monate)
- PREFETCH_EDGE  28  →  90 Tage (Trigger ~3 Monate vor Cache-Rand)

Version v6 → v7.
2026-05-07 19:23:35 +02:00

87 lines
2.4 KiB
JavaScript

// Calendarr Service Worker
// Cache-first for static assets, network-first for /api/* (graceful offline)
const CACHE_VERSION = 'calendarr-v7';
const STATIC_ASSETS = [
'/',
'/index.html',
'/manifest.json',
'/static/css/app.css',
'/static/favicon.svg',
'/static/js/app.js',
'/static/js/api.js',
'/static/js/calendar.js',
'/static/js/color-picker.js',
'/static/js/date-picker.js',
'/static/js/i18n.js',
'/static/js/utils.js',
'/static/js/version.js',
'/static/js/views/agenda.js',
'/static/js/views/month.js',
'/static/js/views/quarter.js',
'/static/js/views/week.js',
'/icons/icon-192.png',
'/icons/icon-512.png',
'/icons/icon.svg',
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_VERSION).then(cache =>
// Use addAll with a fallback so a single missing file doesn't abort install
Promise.all(
STATIC_ASSETS.map(url =>
cache.add(url).catch(err => console.warn('[SW] skip', url, err))
)
)
).then(() => self.skipWaiting())
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE_VERSION).map(k => caches.delete(k)))
).then(() => self.clients.claim())
);
});
self.addEventListener('fetch', event => {
const req = event.request;
if (req.method !== 'GET') return;
const url = new URL(req.url);
// Network-first for API routes — fail silently if offline
if (url.pathname.startsWith('/api/')) {
event.respondWith(
fetch(req).catch(() =>
new Response(JSON.stringify({ offline: true }), {
status: 503,
headers: { 'Content-Type': 'application/json' },
})
)
);
return;
}
// Cache-first for everything else (static)
event.respondWith(
caches.match(req).then(cached => {
if (cached) return cached;
return fetch(req).then(resp => {
// Only cache successful, basic-origin responses
if (resp && resp.status === 200 && resp.type === 'basic') {
const clone = resp.clone();
caches.open(CACHE_VERSION).then(c => c.put(req, clone)).catch(() => {});
}
return resp;
}).catch(() => {
// Offline fallback for navigation requests
if (req.mode === 'navigate') return caches.match('/index.html');
return new Response('', { status: 503 });
});
})
);
});