GitHub Actions
Build: Add Android APK, PWA, and deployment guide
38bd54a
Raw
History Blame Contribute Delete
3.75 kB
/**
* HearthNet Service Worker
*
* Enables offline-first functionality and caching for PWA.
* Installed via manifest.json
*/
const CACHE_NAME = 'hearthnet-v0.1.0';
const STATIC_ASSETS = [
'/',
'/index.html',
'/manifest.json',
'/static/icon-192.png',
'/static/icon-512.png',
'/static/styles.css',
];
/**
* Install event: Pre-cache static assets
*/
self.addEventListener('install', (event) => {
console.log('[Service Worker] Installing...');
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[Service Worker] Caching static assets');
return cache.addAll(STATIC_ASSETS).catch((err) => {
console.warn('[Service Worker] Some assets failed to cache:', err);
// Don't fail on cache errors (some assets may not exist)
});
})
);
self.skipWaiting();
});
/**
* Activate event: Clean up old caches
*/
self.addEventListener('activate', (event) => {
console.log('[Service Worker] Activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
console.log('[Service Worker] Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
self.clients.claim();
});
/**
* Fetch event: Cache-first strategy for static assets, network-first for API calls
*/
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Skip non-GET requests
if (request.method !== 'GET') {
return;
}
// API calls: Network-first (always try server)
if (url.pathname.startsWith('/api/') ||
url.pathname.startsWith('/bus/') ||
url.pathname.startsWith('/trace/')) {
event.respondWith(
fetch(request)
.then((response) => {
// Cache successful responses
if (response.status === 200) {
const cache = caches.open(CACHE_NAME);
cache.then((c) => c.put(request, response.clone()));
}
return response;
})
.catch(() => {
// Fallback to cache if offline
return caches.match(request).then((cached) => {
if (cached) {
console.log('[Service Worker] Serving from cache (offline):', request.url);
return cached;
}
// Return offline page or error
return new Response('Offline - API unavailable', {
status: 503,
statusText: 'Service Unavailable',
});
});
})
);
return;
}
// Static assets: Cache-first
event.respondWith(
caches.match(request).then((cached) => {
if (cached) {
return cached;
}
return fetch(request).then((response) => {
if (response.status === 200) {
caches.open(CACHE_NAME).then((cache) => {
cache.put(request, response.clone());
});
}
return response;
});
})
);
});
/**
* Background sync: Queue API calls when offline
*/
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-messages') {
event.waitUntil(
// Implement message queue sync here
Promise.resolve()
);
}
});
/**
* Push notifications
*/
self.addEventListener('push', (event) => {
const options = {
body: event.data?.text() || 'New notification from HearthNet',
icon: '/static/icon-192.png',
badge: '/static/icon-96.png',
tag: 'hearthnet-notification',
requireInteraction: false,
};
event.waitUntil(
self.registration.showNotification('HearthNet', options)
);
});
console.log('[Service Worker] Loaded and ready');