mirror of
https://github.com/myronblair/tomsjavajive-app
synced 2026-06-30 09:40:59 -05:00
270 lines
7.0 KiB
JavaScript
270 lines
7.0 KiB
JavaScript
const CACHE_NAME = 'tomsjavajive-v1';
|
|
const STATIC_CACHE = 'tomsjavajive-static-v1';
|
|
const DYNAMIC_CACHE = 'tomsjavajive-dynamic-v1';
|
|
|
|
// Static assets to cache immediately
|
|
const STATIC_ASSETS = [
|
|
'/',
|
|
'/shop.php',
|
|
'/cart.php',
|
|
'/assets/css/style.css',
|
|
'/assets/js/main.js',
|
|
'/assets/images/logo.png',
|
|
'/manifest.json',
|
|
'/offline.html'
|
|
];
|
|
|
|
// Install event - cache static assets
|
|
self.addEventListener('install', (event) => {
|
|
console.log('[Service Worker] Installing...');
|
|
|
|
event.waitUntil(
|
|
caches.open(STATIC_CACHE)
|
|
.then((cache) => {
|
|
console.log('[Service Worker] Caching static assets');
|
|
return cache.addAll(STATIC_ASSETS.map(url => {
|
|
return new Request(url, { cache: 'no-cache' });
|
|
})).catch(err => {
|
|
console.log('[Service Worker] Some assets failed to cache:', err);
|
|
return Promise.resolve();
|
|
});
|
|
})
|
|
.then(() => 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
|
|
.filter((cacheName) => {
|
|
return cacheName !== STATIC_CACHE &&
|
|
cacheName !== DYNAMIC_CACHE &&
|
|
cacheName.startsWith('tomsjavajive-');
|
|
})
|
|
.map((cacheName) => {
|
|
console.log('[Service Worker] Deleting old cache:', cacheName);
|
|
return caches.delete(cacheName);
|
|
})
|
|
);
|
|
})
|
|
.then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
// Fetch event - serve from cache or network
|
|
self.addEventListener('fetch', (event) => {
|
|
const { request } = event;
|
|
const url = new URL(request.url);
|
|
|
|
// Skip non-GET requests
|
|
if (request.method !== 'GET') {
|
|
return;
|
|
}
|
|
|
|
// Skip admin panel
|
|
if (url.pathname.startsWith('/admin')) {
|
|
return;
|
|
}
|
|
|
|
// Skip API requests (always network)
|
|
if (url.pathname.startsWith('/api')) {
|
|
return;
|
|
}
|
|
|
|
// Handle navigation requests
|
|
if (request.mode === 'navigate') {
|
|
event.respondWith(
|
|
fetch(request)
|
|
.then((response) => {
|
|
// Clone and cache the response
|
|
const responseClone = response.clone();
|
|
caches.open(DYNAMIC_CACHE).then((cache) => {
|
|
cache.put(request, responseClone);
|
|
});
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
// Try to serve from cache
|
|
return caches.match(request)
|
|
.then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
// Serve offline page
|
|
return caches.match('/offline.html');
|
|
});
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Handle static assets (cache-first strategy)
|
|
if (isStaticAsset(url.pathname)) {
|
|
event.respondWith(
|
|
caches.match(request)
|
|
.then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
// Fetch in background to update cache
|
|
fetch(request).then((response) => {
|
|
if (response.ok) {
|
|
caches.open(STATIC_CACHE).then((cache) => {
|
|
cache.put(request, response);
|
|
});
|
|
}
|
|
}).catch(() => {});
|
|
|
|
return cachedResponse;
|
|
}
|
|
|
|
return fetch(request).then((response) => {
|
|
if (response.ok) {
|
|
const responseClone = response.clone();
|
|
caches.open(STATIC_CACHE).then((cache) => {
|
|
cache.put(request, responseClone);
|
|
});
|
|
}
|
|
return response;
|
|
});
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Handle images (cache-first with network fallback)
|
|
if (isImageRequest(request)) {
|
|
event.respondWith(
|
|
caches.match(request)
|
|
.then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
|
|
return fetch(request)
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
const responseClone = response.clone();
|
|
caches.open(DYNAMIC_CACHE).then((cache) => {
|
|
cache.put(request, responseClone);
|
|
});
|
|
}
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
// Return placeholder image
|
|
return caches.match('/assets/images/placeholder-product.svg');
|
|
});
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Default: network-first with cache fallback
|
|
event.respondWith(
|
|
fetch(request)
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
const responseClone = response.clone();
|
|
caches.open(DYNAMIC_CACHE).then((cache) => {
|
|
cache.put(request, responseClone);
|
|
});
|
|
}
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
return caches.match(request);
|
|
})
|
|
);
|
|
});
|
|
|
|
// Helper functions
|
|
function isStaticAsset(pathname) {
|
|
return pathname.match(/\.(css|js|woff|woff2|ttf|eot)$/i);
|
|
}
|
|
|
|
function isImageRequest(request) {
|
|
return request.destination === 'image' ||
|
|
request.url.match(/\.(png|jpg|jpeg|gif|svg|webp|ico)$/i);
|
|
}
|
|
|
|
// Push notification handling
|
|
self.addEventListener('push', (event) => {
|
|
console.log('[Service Worker] Push received');
|
|
|
|
let data = { title: "Tom's Java Jive", body: 'You have a new notification!' };
|
|
|
|
if (event.data) {
|
|
try {
|
|
data = event.data.json();
|
|
} catch (e) {
|
|
data.body = event.data.text();
|
|
}
|
|
}
|
|
|
|
const options = {
|
|
body: data.body,
|
|
icon: '/assets/icons/icon-192.png',
|
|
badge: '/assets/icons/badge-72.png',
|
|
vibrate: [100, 50, 100],
|
|
data: {
|
|
url: data.url || '/'
|
|
},
|
|
actions: [
|
|
{ action: 'view', title: 'View' },
|
|
{ action: 'close', title: 'Close' }
|
|
]
|
|
};
|
|
|
|
event.waitUntil(
|
|
self.registration.showNotification(data.title, options)
|
|
);
|
|
});
|
|
|
|
// Notification click handling
|
|
self.addEventListener('notificationclick', (event) => {
|
|
console.log('[Service Worker] Notification clicked');
|
|
|
|
event.notification.close();
|
|
|
|
if (event.action === 'close') {
|
|
return;
|
|
}
|
|
|
|
const url = event.notification.data?.url || '/';
|
|
|
|
event.waitUntil(
|
|
clients.matchAll({ type: 'window', includeUncontrolled: true })
|
|
.then((clientList) => {
|
|
// Focus existing window if available
|
|
for (const client of clientList) {
|
|
if (client.url === url && 'focus' in client) {
|
|
return client.focus();
|
|
}
|
|
}
|
|
// Open new window
|
|
if (clients.openWindow) {
|
|
return clients.openWindow(url);
|
|
}
|
|
})
|
|
);
|
|
});
|
|
|
|
// Background sync for cart/orders
|
|
self.addEventListener('sync', (event) => {
|
|
console.log('[Service Worker] Background sync:', event.tag);
|
|
|
|
if (event.tag === 'sync-cart') {
|
|
event.waitUntil(syncCart());
|
|
}
|
|
});
|
|
|
|
async function syncCart() {
|
|
// Get pending cart actions from IndexedDB
|
|
// and sync with server
|
|
console.log('[Service Worker] Syncing cart...');
|
|
}
|