Files
myprojplanet_vite/src-pwa/custom-service-worker.js
paoloar77 ce20daed6d Notification with Service Workers now is working!
When a Notification arrives, it save into the IndexDb,
then in Vue.js call a polling to check in the db if there is a different record count.
If is different then call a get to update the notification.
2022-08-07 02:01:20 +02:00

633 lines
17 KiB
JavaScript
Executable File

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'
// Used for filtering matches based on status code, header, or both
import { CacheableResponsePlugin } from 'workbox-cacheable-response'
// Used to limit entries in cache, remove entries after a certain period of time
import { ExpirationPlugin } from 'workbox-expiration'
console.log(
' [ VER-0.3.23b ] _---------________------ PAO: this is my custom service worker')
importScripts('js/idb.js')
importScripts('js/storage.js')
// importScripts('js/workbox-sw.js')
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.4/workbox-sw.js');
// importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0/workbox-sw.js');
// importScripts('./ChabokSDKWorker.js', 'https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js');
let port = 3000
if (self.location.hostname.startsWith('test')) {
port = 3001
}
console.log('SW-06 1')
const cfgenv = {
serverweb: `${self.location.protocol}//${self.location.hostname}:${port}`,
dbname: 'mydb3',
dbversion: 11,
}
// console.log('serverweb', cfgenv.serverweb)
async function writeData (table, data) {
console.log('writeData', table, data);
await idbKeyval.setdata(table, data)
}
async function readAllData (table) {
// console.log('readAllData', table);
return idbKeyval.getalldata(table)
}
async function clearAllData (table) {
// console.log('clearAllData', table);
await idbKeyval.clearalldata(table)
}
async function deleteItemFromData (table, id) {
// console.log('deleteItemFromData', table, 'ID:', id);
await idbKeyval.deletedata(table, id)
}
// self.addEventListener('activate', function(event) {
// event.waitUntil(
// // createDB()
// );
// });
if (!workbox) {
const workbox = new self.WorkboxSW()
}
if (workbox) {
const debug = false
workbox.setConfig({ debug })
const precacheList = self.__WB_MANIFEST || [];
setCacheNameDetails({
prefix: self.location.hostname,
suffix: 'v1',
precache: 'precache',
runtime: 'runtime'
})
// clientsClaim()
// skipWaiting()
precacheAndRoute(precacheList)
// cleanupOutdatedCaches()
registerRoute(
new RegExp(/\.(?:png|gif|jpg|jpeg)$/),
new CacheFirst({
cacheName: 'images-upload',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
}),
)
registerRoute(
new RegExp(/\.(?:svg)$/),
new CacheFirst({
cacheName: 'svg',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
}),
)
// Per Articoli....
const articleHandler = new NetworkFirst({
cacheName: 'articles-cache',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxEntries: 50,
}),
],
})
registerRoute(
new RegExp(/(.*)article(.*)\.html/), args => articleHandler.handle(args),
)
registerRoute(
new RegExp(/.*(?:googleapis|gstatic)\.com.*$/),
new StaleWhileRevalidate({
cacheName: 'google-fonts',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxEntries: 30,
}),
],
}),
)
// console.log(' routing.registerRoute function declaration:')
function Execute_Fetch (table, args) {
console.log('Execute_Fetch registerRoute! ',
`${cfgenv.serverweb}/${table}/`)
// console.log('DATABODY:', args.event.request.body)
let myres = null
// return fetch(args.event.request, args.event.headers)
return fetch(args.event.request, args.event.headers)
.then((res) => {
myres = res
if (res.status === 200) {
const clonedRes = res.clone()
let secondatab = ''
if (table === 'todos') {
secondatab = 'categories'
}
console.log('1) clearAllData: ', table)
return clearAllData(table)
.then(() => {
if (secondatab !== '') {
// console.log('2) clearAllData(todos)')
return clearAllData(secondatab)
.then(() =>
// console.log('3) ....return clonedRes')
clonedRes)
}
return clonedRes
})
}
})
.then((clonedRes) => {
// console.log(' 3) ')
if (clonedRes) return clonedRes.json()
return null
})
.then(data => {
// console.log(' 4) data = ', data)
if (data) {
const myarr = idbKeyval.getArrayByTable(table, data)
if (myarr) {
let promiseChain = Promise.resolve()
console.log('*********+++++++++++++++++********** Records ',
`${table} Received from Server [`, myarr.length, 'record]', myarr)
if (table === 'todos') {
for (const cat in data.categories) {
promiseChain = promiseChain.then(() => writeData('categories', {
_id: cat,
valore: data.categories[cat],
}))
}
for (const arrsing of myarr) {
for (const rec of arrsing) {
promiseChain = promiseChain.then(() => writeData(table, rec))
}
}
} else {
// Others tables
for (const rec of myarr) {
promiseChain = promiseChain.then(() => writeData(table, rec))
}
}
// console.log('promiseChain', promiseChain)
return promiseChain
}
}
})
.then(() => myres)
.catch(err => {
console.log('ERROR registerRoute FETCH:', err)
return myres
})
}
for (const table of MainTables) {
registerRoute(
new RegExp(`${cfgenv.serverweb}/${table}/`),
(args) => {
Execute_Fetch(table, args)
},
)
}
registerRoute(
(routeData) => (routeData.event.request.headers.get('accept')
.includes('text/html')), (args) => caches.match(args.event.request)
.then((response) => {
if (response) {
return response
}
return fetch(args.event.request)
.then((res) => caches.open('dynamic')
.then((cache) => {
console.log('cache', args.event.request.url)
cache.put(args.event.request.url, res.clone())
return res
}))
.catch((err) => caches.match('/offline')
.then((res) => res))
}),
)
registerRoute(
new RegExp(/.*\/(?:icons).*$/),
new CacheFirst({
cacheName: 'icon-cache',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxAgeSeconds: 30 * 24 * 60 * 60,
}),
],
}),
)
registerRoute(
new RegExp(/\.(?:js|css|font)$/),
new StaleWhileRevalidate({
cacheName: 'js-css-fonts',
// Ensure that only requests that result in a 200 status are cached
plugins: [
new CacheableResponsePlugin({
statuses: [200],
}),
],
}),
)
// Storage
registerRoute(
new RegExp(/.*(?:storage)/),
new StaleWhileRevalidate({
cacheName: 'storage',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxAgeSeconds: 30 * 24 * 60 * 60,
// Only cache 10 requests.
maxEntries: 200,
}),
],
}),
)
registerRoute(
new RegExp(/.*\/(?:public).*$/),
new CacheFirst({
cacheName: 'public',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
new ExpirationPlugin({
maxAgeSeconds: 10 * 24 * 60 * 60,
// Only cache 10 requests.
}),
],
}),
)
registerRoute(
new RegExp('/admin/'),
new NetworkOnly(),
)
/*registerRoute(
new RegExp('/owa/'),
new NetworkOnly(),
)
*/
}
if ('serviceWorker' in navigator) {
// console.log('***************** Entering in custom-service-worker.js:')
}
self.addEventListener('fetch', (event) => {
if (event.request.url === '/') {
const StaleWhileRevalidate = new StaleWhileRevalidate();
event.respondWith(StaleWhileRevalidate.handle({ event }));
}
});
self.addEventListener('fetch', function (event) {
console.log('[Service Worker] Fetching something ....', event);
console.log('event.request.cache=', event.request.cache)
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
console.log('SAME ORIGIN!', event);
return;
}
event.respondWith(caches.match(event.request));
});
const syncStore = {}
self.addEventListener('message', event => {
if (event.data.type === 'sync') {
console.log('addEventListener - message')
// get a unique id to save the data
const id = uuid()
syncStore[id] = event.data
// register a sync and pass the id as tag for it to get the data
self.registration.sync.register(id)
console.log('self.registration.sync.register(id)')
}
console.log(event.data)
})
addEventListener('fetch', event => {
// Prevent the default, and handle the request ourselves.
event.respondWith(async function() {
// Try to get the response from a cache.
const cachedResponse = await caches.match(event.request);
// Return it if we found one.
if (cachedResponse && (event.request.cache !== 'no-cache'))
return cachedResponse;
// If we didn't find a match in the cache, use the network.
return fetch(event.request);
}());
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request).then(function (response) {
return response ||
fetch(event.request, event.headers)
.catch(err => {
console.log('_______________________ ERRORE FETCH SW: ', event.request, err)
writeData('config', { _id: 2, stateconn: 'offline' })
return caches.match(event.request);
})
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
fetch(event.request, event.headers)
.catch(err => {
console.log('_______________________ ERRORE FETCH SW: ', event.request, err)
writeData('config', {_id: 2, stateconn: 'offline'})
return caches.match(event.request);
})
);
});
self.addEventListener('sync', function (event) {
console.log('[Service Worker V5] Background syncing', event);
console.log('event:', event);
let mystrparam = event.tag
let multiparams = mystrparam.split('|')
if (multiparams) {
if (multiparams.length > 3) {
let cmd = multiparams[0]
let table = multiparams[1]
let method = multiparams[2]
let token = multiparams[3]
// let lang = multiparams[3]
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)
// console.log('A1) INIZIO.............................................................');
event.waitUntil(
readAllData(table)
.then(function (alldata) {
const myrecs = [...alldata]
console.log('----------------------- LEGGO QUALCOSA DAL WAITUNTIL ')
let errorfromserver = false
if (myrecs) {
for (let rec of myrecs) {
//console.log('syncing', table, '', rec.descr)
let link = cfgenv.serverweb + '/todos'
if (method !== 'POST')
link += '/' + rec._id
console.log('++++++++++++++++++ SYNCING !!!! ', rec.descr, table, 'FETCH: ', method, link, 'data:')
// console.log('DATATOSAVE:', JSON.stringify(rec))
// Insert/Delete/Update table to the server
fetch(link, {
method: method,
headers: headers,
cache: 'no-cache',
mode: 'cors', // 'no-cors',
body: JSON.stringify(rec)
})
.then(() => {
deleteItemFromData(table, rec._id)
})
.then(() => {
deleteItemFromData('swmsg', mystrparam)
})
.catch(function (err) {
console.log('!!!!!!!!!!!!!!! Error while sending data', err, err.message);
if (err.message === 'Failed to fetch') {
errorfromserver = true
}
})
}
return errorfromserver
}
})
.then((errorfromserver) => {
const mystate = !errorfromserver ? 'online' : 'offline'
writeData('config', { _id: 2, stateconn: mystate })
})
);
// console.log('A2) ?????????????????????????? ESCO DAL LOOP !!!!!!!!! err=')
}
}
}
})
;
/*
// send message to serviceWorker
function sync (url, options) {
navigator.serviceWorker.controller.postMessage({type: 'sync', url, options})
}
const syncStore = {}
self.addEventListener('message', event => {
if(event.data.type === 'sync') {
// get a unique id to save the data
const id = uuid()
syncStore[id] = event.data
// register a sync and pass the id as tag for it to get the data
self.registration.sync.register(id)
}
console.log(event.data)
})
self.addEventListener('sync', event => {
// get the data by tag
const {url, options} = syncStore[event.tag]
event.waitUntil(fetch(url, options))
})
*/
self.addEventListener('notificationclick', (event) => {
const { notification } = event
const { action } = event
console.log(notification)
if (action === 'confirm') {
console.log('Confirm was chosen')
notification.close()
} else {
console.log(action)
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 = {
title: 'New!',
content: 'Something new happened!',
url: '/',
}
try {
let isobj = false
if (event.data) {
try {
data = JSON.parse(event.data.text())
isobj = true
} catch (e) {
data = event.data.text()
}
}
console.log('event.data', data)
let options = {}
let myid = '0'
if (isobj) {
if (data.id) {
myid = data.id
}
options = {
body: data.content,
icon: data.icon ? data.icon : '/images/android-chrome-192x192.png',
badge: data.badge ? data.badge : '/images/android-chrome-192x192.png',
data: {
url: data.url,
},
tag: data.tag,
renitify: true, // vibrate also with others messages.
}
event.waitUntil(
self.registration.showNotification(data.title ? data.title : data.content, options),
)
} else {
let text = data;
options = {
body: text,
icon: '/images/android-chrome-192x192.png',
badge: '/images/android-chrome-192x192.png',
data: {
url: '/',
},
tag: 'notif',
renitify: true, // vibrate also with others messages.
}
event.waitUntil(
self.registration.showNotification('', options),
)
}
self.registration.sync.register(myid)
writeData('notifications', { _id: myid, tag: options.tag })
} catch (e) {
console.log('Error on event push:', e)
}
})