From 1f7a43292be8fefd55605c37e40df3168a8939f1 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Mon, 23 Jan 2023 20:40:08 +0100 Subject: [PATCH] fix PWA offline capability --- public/scripts/network.js | 3 +- public/scripts/ui.js | 9 +++- public/service-worker.js | 86 +++++++++++++++++++++++++-------------- 3 files changed, 64 insertions(+), 34 deletions(-) diff --git a/public/scripts/network.js b/public/scripts/network.js index cc9c2b0..51fbc7c 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -152,10 +152,9 @@ class ServerConnection { _onDisconnect() { console.log('WS: server disconnected'); - Events.fire('notify-user', 'Server connection lost. Retrying...'); + Events.fire('notify-user', 'No server connection. Retry in 5s...'); clearTimeout(this._reconnectTimer); this._reconnectTimer = setTimeout(_ => this._connect(), 5000); - this._connect(); Events.fire('ws-disconnected'); } diff --git a/public/scripts/ui.js b/public/scripts/ui.js index ec6b384..10a2726 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -1220,7 +1220,7 @@ class NetworkStatusUI { Events.on('offline', _ => this._showOfflineMessage()); Events.on('online', _ => this._showOnlineMessage()); Events.on('ws-connected', _ => this._showOnlineMessage()); - Events.on('ws-disconnected', _ => window.animateBackground(false)); + Events.on('ws-disconnected', _ => this._onWsDisconnected()); if (!navigator.onLine) this._showOfflineMessage(); } @@ -1230,12 +1230,17 @@ class NetworkStatusUI { } _showOnlineMessage() { + window.animateBackground(true); if (!this.firstConnect) { this.firstConnect = true; return; } Events.fire('notify-user', 'You are back online'); - window.animateBackground(true); + } + + _onWsDisconnected() { + window.animateBackground(false); + if (!this.firstConnect) this.firstConnect = true; } } diff --git a/public/service-worker.js b/public/service-worker.js index 2f22f8e..04fec47 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,4 +1,5 @@ -const CACHE_NAME = 'pairdrop-cache-v4'; +const cacheVersion = 'v5'; +const cacheTitle = `pairdrop-cache-${cacheVersion}`; const urlsToCache = [ 'index.html', './', @@ -23,15 +24,50 @@ const urlsToCache = [ self.addEventListener('install', function(event) { // Perform install steps event.waitUntil( - caches.open(CACHE_NAME) + caches.open(cacheTitle) .then(function(cache) { - console.log('Opened cache'); - return cache.addAll(urlsToCache); - }) + return cache.addAll(urlsToCache).then(_ => { + console.log('All files cached.'); + }); + }) ); }); +// fetch the resource from the network +const fromNetwork = (request, timeout) => + new Promise((fulfill, reject) => { + const timeoutId = setTimeout(reject, timeout); + fetch(request).then(response => { + clearTimeout(timeoutId); + fulfill(response); + update(request); + }, reject); + }); +// fetch the resource from the browser cache +const fromCache = request => + caches + .open(cacheTitle) + .then(cache => + cache + .match(request) + .then(matching => matching || cache.match('/offline/')) + ); + +// cache the current page to make it available for offline +const update = request => + caches + .open(cacheTitle) + .then(cache => + fetch(request).then(response => { + cache.put(request, response).then(_ => { + console.log("Page successfully cached.") + }) + }) + ); + +// general strategy when making a request (eg if online try to fetch it +// from the network with a timeout, if something fails serve from cache) self.addEventListener('fetch', function(event) { if (event.request.method === "POST") { // Requests related to Web Share Target. @@ -62,34 +98,24 @@ self.addEventListener('fetch', function(event) { } else { // Regular requests not related to Web Share Target. event.respondWith( - caches.match(event.request) - .then(function (response) { - // Cache hit - return response - if (response) { - return response; - } - return fetch(event.request); - } - ) + fromNetwork(event.request, 10000).catch(() => fromCache(event.request)) ); + event.waitUntil(update(event.request)); } }); -self.addEventListener('activate', function(event) { - console.log('Updating Service Worker...') - event.waitUntil( - caches.keys().then(function(cacheNames) { - return Promise.all( - cacheNames.filter(function(cacheName) { - // Return true if you want to remove this cache, - // but remember that caches are shared across - // the whole origin - return true - }).map(function(cacheName) { - return caches.delete(cacheName); +// on activation, we clean up the previously registered service workers +self.addEventListener('activate', evt => + evt.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== cacheTitle) { + return caches.delete(cacheName); + } + }) + ); }) - ); - }) - ); -}); + ) +);