seepuls/apps/web/public/sw.js
Till JS 6f77d0095b
Some checks are pending
CI / validate (push) Waiting to run
feat: echte Suche, Amenities/Region ausgespielt, Folgen→Web-Push
Vier Review-Verbesserungen:

- Suche real: /api/v1/search liefert echte unified Events+Orte (war Stub);
  Event-q trifft auch Venue-Namen; Startseite zeigt Orte-Treffer bei Suche.
- Amenities/Öffnungszeiten/Region ausgespielt: Venue-Liste liefert
  openingHours/amenities/smoking/heroUrl, neuer amenity- + q-Filter;
  Orte-Seite mit Ausstattungs-/Region-Chips, „Jetzt geöffnet"-Badge
  (isOpenNow, über-Mitternacht-fest) + Amenity-Badges; Region-Chips auch
  auf der Startseite.
- Folgen → Web Push (login-frei): kanal-agnostischer Kern
  (push_endpoints + venue_follows + notification_outbox, Migration 0005),
  Crawler-Auslöser (nur neue Events, best-effort), notification-worker
  (Drain + Ruhezeiten 22–8h + Pruning toter Endpoints), öffentliche
  /api/v1/push/*-Routen, Service Worker + lib/push.ts + Schalter auf
  /gemerkt (iOS-PWA-Hinweis). web-push-Lib (lädt unter Bun).
  Ohne VAPID-Keys bleibt Push schlafend (subscribe→503). Doku:
  docs/NOTIFICATIONS.md. Offen vor Push-Live: VAPID-Keys+SOPS, VVT-Eintrag.

Tests: 112 API + 34 Web grün; api+web type-check grün; web build grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 15:51:50 +02:00

46 lines
1.3 KiB
JavaScript

/* Seepuls Service Worker — Web Push.
*
* Bewusst minimal: nur Push-Empfang + Klick-Handling. Kein Offline-Cache
* (die App ist SSR, Caching wäre eine eigene Entscheidung). Liegt unter
* public/ und wird mit Scope „/" registriert.
*/
self.addEventListener('push', (event) => {
let data = {};
try {
data = event.data ? event.data.json() : {};
} catch (_e) {
data = { title: 'Seepuls', body: event.data ? event.data.text() : '' };
}
const title = data.title || 'Seepuls';
const options = {
body: data.body || '',
tag: data.tag || undefined,
data: { url: data.url || '/' },
icon: '/logo.svg',
badge: '/logo.svg',
lang: 'de',
};
event.waitUntil(self.registration.showNotification(title, options));
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
const target = (event.notification.data && event.notification.data.url) || '/';
event.waitUntil(
self.clients
.matchAll({ type: 'window', includeUncontrolled: true })
.then((clientList) => {
// Schon offenes Fenster fokussieren + dorthin navigieren.
for (const client of clientList) {
if ('focus' in client) {
if ('navigate' in client) {
client.navigate(target).catch(() => {});
}
return client.focus();
}
}
return self.clients.openWindow(target);
}),
);
});