var periodicSyncNewMsgReminderText; const OFFLINE_CACHE = `offline`; const offlineCacheFiles = ['offline.html','config.json']; // on install we download the routes we want to cache for offline self.addEventListener('install', evt => evt.waitUntil(caches.open(OFFLINE_CACHE).then(cache => { console.log("SW Caching offline files"); self.skipWaiting(); return cache.addAll(offlineCacheFiles); }))); self.addEventListener("activate", event => { event.waitUntil(clients.claim()); }); // Notification click event listener self.addEventListener("notificationclick", (e) => { e.notification.close(); e.waitUntil( clients.matchAll({ type: "window" }).then((clientsArr) => { // If a Window tab matching the targeted URL already exists, focus that; const hadWindowToFocus = clientsArr.some((windowClient) => windowClient.url === e.notification.data.url ? (windowClient.focus(), true) : false, ); // Otherwise, open a new tab to the applicable URL and focus it. if (!hadWindowToFocus) clients .openWindow(e.notification.data.url) .then((windowClient) => (windowClient ? windowClient.focus() : null)); }), ); }); self.addEventListener("message", (event) => { periodicSyncNewMsgReminderText = event.data; }); async function checkNewMessages() { const cachedCredentials = await caches.open('cachedCredentials'); // Todo... } // Install PWA in mobile or web to test if periodicSync notification works // see browser compatibility: https://developer.mozilla.org/en-US/docs/Web/API/Web_Periodic_Background_Synchronization_API#browser_compatibility self.addEventListener('periodicsync', (event) => { if (event.tag === 'check-new-messages') { let notificationTitle = periodicSyncNewMsgReminderText || "You may have new messages"; self.registration.showNotification(notificationTitle); event.waitUntil(checkNewMessages()); } }); self.addEventListener("fetch", (event) => { if (event.request.mode === 'navigate') { return event.respondWith( fetch(event.request).catch((e) => { console.log("OFFLINE, serve offline page", e); return serveOfflinePage(); })); } else if (event.request.url.endsWith("config.json")) { return fetch(event.request) .then((response) => { console.log("Caching a new version of config.json"); let responseClone = response.clone(); caches .open(OFFLINE_CACHE) .then((cache) => cache.put(event.request, responseClone)); return response; }) } }); async function serveOfflinePage() { let mirrorUrl = null; const rConfig = await caches.match("config.json", { cacheName: OFFLINE_CACHE}); if (rConfig) { const json = await rConfig.json(); const mirrors = json.mirrors; if (mirrors && Array.isArray(mirrors) && mirrors.length > 0) { mirrorUrl = json.mirrors[Math.floor(Math.random() * mirrors.length)]; } } const offlinePage = await caches.match("offline.html", { cacheName: OFFLINE_CACHE}); if (mirrorUrl && offlinePage) { let text = await offlinePage.text(); text = text.replaceAll("", mirrorUrl); let title = undefined; let message = undefined; let redirect = undefined; await new Promise((resolve, reject) => { var open = indexedDB.open("ServiceWorker", 1); open.onerror = function() { resolve(false); } open.onsuccess = function() { // Start a new transaction var db = open.result; var tx = db.transaction("offline_strings", "readonly"); var store = tx.objectStore("offline_strings"); var get1 = store.get("offline_title"); var get2 = store.get("offline_message"); var get3 = store.get("offline_redirect"); get1.onsuccess = function() { title = get1.result.translation; }; get2.onsuccess = function() { message = get2.result.translation; }; get3.onsuccess = function() { redirect = get3.result.translation; }; // Close the db when the transaction is done tx.oncomplete = function() { db.close(); resolve(true); }; } }); if (title) { text = text.replaceAll(/(.*?)/g, title); } if (message) { text = text.replaceAll(/(.*?)/g, message); } if (redirect) { text = text.replaceAll(/(.*?)/g, redirect); } return new Response(text, { headers: {"content-type": "text/html"}}); } throw new Error("Offline"); }