importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.5.4/workbox-sw.js'); import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'; import { registerRoute } from 'workbox-routing'; import { clientsClaim, setCacheNameDetails, skipWaiting } from 'workbox-core'; import { NetworkFirst, NetworkOnly, StaleWhileRevalidate, CacheFirst, } from 'workbox-strategies'; const ENABLE_DYNAMIC_CACHING = false; import { CacheableResponsePlugin } from 'workbox-cacheable-response'; import { ExpirationPlugin } from 'workbox-expiration'; const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; self.addEventListener('install', () => self.skipWaiting()); self.addEventListener('activate', (event) => { // console.log('CLAIM'); //event.waitUntil(self.clients.claim()) event.waitUntil(async () => { // Check for a new service worker version const registration = await navigator.serviceWorker.getRegistration(); if (registration && registration.waiting) { // A new service worker is waiting, trigger a page reload await registration.waiting.postMessage({ type: 'SKIP_WAITING' }); self.skipWaiting(); } }); }); const VersioneApp = '1.0.42'; console.log(' [ VER-' + VersioneApp + ' ] _---------________------ PAO: this is my custom service worker'); importScripts('js/idb.js', 'js/storage.js'); let port = self.location.hostname.startsWith('test') ? 3001 : 3000; console.log('SW- app ver ' + VersioneApp + ' on port ' + port); // Function helpers async function writeData(table, data) { console.log('writeData', table, data); await idbKeyval.setdata(table, data); } async function readAllData(table) { return idbKeyval.getalldata(table); } async function clearAllData(table) { await idbKeyval.clearalldata(table); } async function deleteItemFromData(table, id) { await idbKeyval.deletedata(table, id); } if (workbox) { const debug = false; workbox.setConfig({ debug }); const precacheList = self.__WB_MANIFEST || []; setCacheNameDetails({ prefix: self.location.hostname, suffix: 'v1', precache: 'precache', runtime: 'runtime', }); precacheAndRoute(precacheList); // Cache strategy registrations registerRoute( new RegExp(/\.(?:png|gif|jpg|jpeg)$/), new CacheFirst({ cacheName: 'images-upload', plugins: [ new CacheableResponsePlugin({ statuses: [200] }), new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 }), // 30 Days ], }) ); registerRoute( new RegExp(/\.(?:svg)$/), new CacheFirst({ cacheName: 'svg', plugins: [ new CacheableResponsePlugin({ statuses: [200] }), new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 }), // 30 Days ], }) ); registerRoute( new RegExp(/.*(?:googleapis|gstatic)\.com.*$/), new StaleWhileRevalidate({ cacheName: 'google-fonts', plugins: [ new CacheableResponsePlugin({ statuses: [200] }), new ExpirationPlugin({ maxEntries: 30 }), ], }) ); registerRoute( new RegExp(/.*\/(?:icons).*$/), new CacheFirst({ cacheName: 'icon-cache', plugins: [ new CacheableResponsePlugin({ statuses: [200] }), new ExpirationPlugin({ maxAgeSeconds: 30 * 24 * 60 * 60 }), // 30 Days ], }) ); registerRoute( new RegExp(/\.(?:js|css|font)$/), new StaleWhileRevalidate({ cacheName: 'js-css-fonts', plugins: [ new CacheableResponsePlugin({ statuses: [200] }), ], }) ); registerRoute( (routeData) => routeData.event.request.headers.get('accept').includes('text/html'), async (args) => { let response = await caches.match(args.event.request); if (response) return response; try { response = await fetch(args.event.request); const cache = await caches.open('dynamic'); cache.put(args.event.request.url, response.clone()); return response; } catch (err) { return caches.match('/offline'); } } ); registerRoute(new RegExp('/admin/'), new NetworkOnly()); const syncStore = {}; self.addEventListener('message', event => { if (event.data && (event.data.type === 'SKIP_WAITING' || event.data.action === 'skipWaiting')) { window.location.reload(); } if (event.data.type === 'sync') { console.log('addEventListener - message'); const id = uuid(); syncStore[id] = event.data; self.registration.sync.register(id); } console.log(event.data); }); self.addEventListener('fetch', (event) => { if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return; event.respondWith(async () => { let cachedResponse = await caches.match(event.request); if (cachedResponse) return cachedResponse; try { const response = await fetch(event.request); if (!response || response.status !== 200 || response.type !== 'basic') return response; if (ENABLE_DYNAMIC_CACHING) { const cache = await caches.open(DYNAMIC_CACHE); cache.put(event.request, response.clone()); } return response; } catch (e) { return ''; } }); }); self.addEventListener('sync', event => { console.log('[Service Worker V5] Background syncing', event); let mystrparam = event.tag; let multiparams = mystrparam.split('|'); if (multiparams && multiparams.length > 3) { let [cmd, table, method, token, refreshToken] = multiparams; if (cmd === 'sync-todos') { console.log('[Service Worker] Syncing', cmd, table, method); const headers = new Headers(); headers.append('content-Type', 'application/json'); headers.append('Accept', 'application/json'); headers.append('x-auth', token); headers.append('x-refrtok', refreshToken); event.waitUntil( readAllData(table).then(alldata => { const myrecs = [...alldata]; let errorfromserver = false; if (myrecs) { let promiseChain = Promise.resolve(); for (let rec of myrecs) { let link = cfgenv.serverweb + '/todos'; if (method !== 'POST') link += '/' + rec._id; promiseChain = promiseChain.then(() => fetch(link, { method: method, headers: headers, cache: 'no-cache', mode: 'cors', body: JSON.stringify(rec), }).then(() => { deleteItemFromData(table, rec._id); deleteItemFromData('swmsg', mystrparam); }) .catch(err => { if (err.message === 'Failed to fetch') { errorfromserver = true; } }) ); } return promiseChain.then(() => { const mystate = !errorfromserver ? 'online' : 'offline'; writeData('config', { _id: 2, stateconn: mystate }); }); } }) ); } } }); // Notifications self.addEventListener('notificationclick', (event) => { const { notification } = event; const { action } = event; if (action === 'confirm') { notification.close(); } else { event.waitUntil( clients.matchAll().then((clis) => { const client = clis.find((c) => c.visibilityState === 'visible'); if (client) { client.navigate(notification.data.url); client.focus(); } else { clients.openWindow(notification.data.url); } notification.close(); }), ); } }); self.addEventListener('notificationclose', (event) => { console.log('Notification was closed', event); }); self.addEventListener('push', (event) => { console.log('Push Notification received', event); let data = event.data ? event.data.json() : { title: 'New!', content: 'Something new happened!', url: '/' }; const options = { body: data.content, icon: data.icon ? data.icon : '/images/android-chrome-192x192.png', badge: data.badge ? data.badge : '/images/badge-96x96.png', data: { url: data.url }, tag: data.tag, }; event.waitUntil(self.registration.showNotification(data.title, options)); const myid = data.id || '0'; self.registration.sync.register(myid); writeData('notifications', { _id: myid, tag: options.tag }); }); } else { console.warn('Workbox could not be loaded.'); }