rmaps-online/public/sw.js

168 lines
4.4 KiB
JavaScript

// rMaps Service Worker for Push Notifications & Background Location
const CACHE_NAME = 'rmaps-v1';
const SYNC_TAG = 'rmaps-location-sync';
// Install event - cache essential assets
self.addEventListener('install', (event) => {
console.log('[SW] Installing service worker...');
self.skipWaiting();
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('[SW] Activating service worker...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
self.clients.claim();
});
// Background Sync - triggered when device comes back online
self.addEventListener('sync', (event) => {
console.log('[SW] Sync event:', event.tag);
if (event.tag === SYNC_TAG) {
event.waitUntil(syncLocation());
}
});
// Sync location to server when coming back online
async function syncLocation() {
try {
// Get stored location data from IndexedDB or localStorage via client
const clients = await self.clients.matchAll({ type: 'window' });
for (const client of clients) {
// Ask the client to send its current location
client.postMessage({ type: 'REQUEST_LOCATION_SYNC' });
}
console.log('[SW] Location sync requested from clients');
} catch (error) {
console.error('[SW] Location sync failed:', error);
}
}
// Request location from any available client
async function requestLocationFromClient() {
const clients = await self.clients.matchAll({
type: 'window',
includeUncontrolled: true
});
if (clients.length > 0) {
// Send message to first available client to get location
clients[0].postMessage({ type: 'REQUEST_LOCATION_UPDATE' });
return true;
}
return false;
}
// Push event - handle incoming push notifications
self.addEventListener('push', (event) => {
console.log('[SW] Push received:', event);
let data = {
title: 'rMaps',
body: 'You have a new notification',
icon: '/icon-192.png',
badge: '/icon-192.png',
tag: 'rmaps-notification',
data: {},
silent: false,
};
if (event.data) {
try {
const payload = event.data.json();
data = { ...data, ...payload };
} catch (e) {
data.body = event.data.text();
}
}
// Handle silent push - request location update without showing notification
if (data.silent || data.data?.type === 'location_request') {
event.waitUntil(
requestLocationFromClient().then((sent) => {
console.log('[SW] Silent push - location request sent:', sent);
})
);
return;
}
const options = {
body: data.body,
icon: data.icon || '/icon-192.png',
badge: data.badge || '/icon-192.png',
tag: data.tag || 'rmaps-notification',
data: data.data || {},
vibrate: [100, 50, 100],
actions: data.actions || [],
requireInteraction: data.requireInteraction || false,
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// Notification click event - handle user interaction
self.addEventListener('notificationclick', (event) => {
console.log('[SW] Notification clicked:', event);
event.notification.close();
const data = event.notification.data || {};
let targetUrl = '/';
// Determine URL based on notification type
if (data.roomSlug) {
targetUrl = `/${data.roomSlug}`;
} else if (data.url) {
targetUrl = data.url;
}
// Handle action buttons
if (event.action === 'view') {
targetUrl = data.url || targetUrl;
} else if (event.action === 'dismiss') {
return;
}
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
// Try to focus an existing window
for (const client of clientList) {
if (client.url.includes(targetUrl) && 'focus' in client) {
return client.focus();
}
}
// Open a new window if none exists
if (clients.openWindow) {
return clients.openWindow(targetUrl);
}
})
);
});
// Handle notification close
self.addEventListener('notificationclose', (event) => {
console.log('[SW] Notification closed:', event);
});
// Handle messages from the main thread
self.addEventListener('message', (event) => {
console.log('[SW] Message received:', event.data);
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});