frontend/src/service-worker.ts
2025-02-03 10:30:36 +01:00

144 lines
3.8 KiB
TypeScript

/// <reference types="@sveltejs/kit" />
/// <reference lib="webworker" />
declare let self: ServiceWorkerGlobalScope;
import { build, files, version } from "$service-worker";
// Define cache name and assets
const CACHE = `cache-${version}`;
const ASSETS = [...build, ...files];
// Function to generate icon paths
function getIconPath(size: string) {
return new URL(`/pwa-${size}.png`, self.location.origin).href;
}
// Define icons for notifications
const ICONS = {
DEFAULT: getIconPath('192x192'),
SMALL: getIconPath('64x64'),
LARGE: getIconPath('512x512')
};
// Install event: Cache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE)
.then((cache) => cache.addAll(ASSETS))
.then(() => self.skipWaiting())
);
});
// Activate event: Clean up old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) => {
return Promise.all(
keys.map((key) => {
if (key !== CACHE) {
return caches.delete(key);
}
})
);
}).then(() => self.clients.claim())
);
});
// Fetch event: Serve cached assets or fetch from network
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
return cachedResponse || fetch(event.request);
})
);
});
// Push event: Handle incoming push notifications
self.addEventListener('push', (event: PushEvent) => {
if (!event.data) return;
let title = 'Stocknear';
let body: string;
let url = '/'; // Default URL
try {
const payload = event.data.text();
try {
const jsonData = JSON.parse(payload);
if (jsonData.title) {
title = jsonData.title;
body = jsonData.body;
url = jsonData.url || '/'; // Extract URL from payload
} else {
body = payload;
}
} catch {
body = payload;
}
} catch {
body = 'New notification';
}
const options: NotificationOptions = {
body,
icon: ICONS.DEFAULT,
badge: ICONS.SMALL,
timestamp: Date.now(),
requireInteraction: true,
tag: 'stocknear-notification',
renotify: true,
vibrate: [200, 100, 200],
data: {
suppressNotificationFrom: true, // Custom flag to indicate branding should be suppressed
url: url // Store URL in notification data
}
};
event.waitUntil(self.registration.showNotification(title, options));
});
// Notification click event: Handle user interaction with notifications
self.addEventListener('notificationclick', (event) => {
event.notification.close();
// Get the URL from notification data or use default
const urlPath = event.notification.data.url || '/';
const urlToOpen = new URL(urlPath, self.location.origin).href;
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then((windowClients) => {
// Check for existing matching window
for (const client of windowClients) {
if (client.url === urlToOpen && 'focus' in client) {
return client.focus();
}
}
// Open new window if none found
if (clients.openWindow) {
return clients.openWindow(urlToOpen);
}
})
);
});
// Message event: Handle messages from the main thread
self.addEventListener('message', (event) => {
// Handle skip waiting
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
// Handle cache update
if (event.data && event.data.type === 'CACHE_URLS') {
event.waitUntil(
caches.open(CACHE)
.then((cache) => cache.addAll(event.data.payload))
.catch((error) => console.error('Service worker: Cache update failed:', error))
);
}
});