Fix offline capability: Cache first -> fetch from network second. From now on, updates are only possible by increasing cacheVersion

This commit is contained in:
schlagmichdoch 2023-10-12 18:57:26 +02:00
parent 8eb6e5bfab
commit 2131307fc6
6 changed files with 86 additions and 58 deletions

View file

@ -37,7 +37,7 @@ class ServerConnection {
_connect() { _connect() {
clearTimeout(this._reconnectTimer); clearTimeout(this._reconnectTimer);
if (this._isConnected() || this._isConnecting()) return; if (this._isConnected() || this._isConnecting() || this._isOffline()) return;
if (this._isReconnect) { if (this._isReconnect) {
Events.fire('notify-user', { Events.fire('notify-user', {
message: Localization.getTranslation("notifications.connecting"), message: Localization.getTranslation("notifications.connecting"),
@ -246,6 +246,10 @@ class ServerConnection {
return this._socket && this._socket.readyState === this._socket.CONNECTING; return this._socket && this._socket.readyState === this._socket.CONNECTING;
} }
_isOffline() {
return !navigator.onLine;
}
_onError(e) { _onError(e) {
console.error(e); console.error(e);
} }

View file

@ -2258,7 +2258,10 @@ class NetworkStatusUI {
} }
_showOfflineMessage() { _showOfflineMessage() {
Events.fire('notify-user', Localization.getTranslation("notifications.offline")); Events.fire('notify-user', {
message: Localization.getTranslation("notifications.offline"),
persistent: true
});
} }
_showOnlineMessage() { _showOnlineMessage() {

View file

@ -1,16 +1,18 @@
const cacheVersion = 'v1.9.0'; const cacheVersion = 'v1.9.0';
const cacheTitle = `pairdrop-cache-${cacheVersion}`; const cacheTitle = `pairdrop-cache-${cacheVersion}`;
const urlsToCache = [ const urlsToCache = [
'index.html',
'./', './',
'index.html',
'manifest.json',
'styles.css', 'styles.css',
'scripts/localization.js',
'scripts/network.js', 'scripts/network.js',
'scripts/NoSleep.min.js',
'scripts/QRCode.min.js',
'scripts/theme.js',
'scripts/ui.js', 'scripts/ui.js',
'scripts/util.js', 'scripts/util.js',
'scripts/qrcode.js',
'scripts/zip.min.js', 'scripts/zip.min.js',
'scripts/NoSleep.min.js',
'scripts/theme.js',
'sounds/blop.mp3', 'sounds/blop.mp3',
'images/favicon-96x96.png', 'images/favicon-96x96.png',
'images/favicon-96x96-notification.png', 'images/favicon-96x96-notification.png',
@ -53,7 +55,7 @@ const fromNetwork = (request, timeout) =>
fetch(request).then(response => { fetch(request).then(response => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
fulfill(response); fulfill(response);
update(request); update(request).then(() => console.log("Cache successfully updated for", request.url));
}, reject); }, reject);
}); });
@ -62,9 +64,7 @@ const fromCache = request =>
caches caches
.open(cacheTitle) .open(cacheTitle)
.then(cache => .then(cache =>
cache cache.match(request)
.match(request)
.then(matching => matching || cache.match('/offline/'))
); );
// cache the current page to make it available for offline // cache the current page to make it available for offline
@ -72,15 +72,16 @@ const update = request =>
caches caches
.open(cacheTitle) .open(cacheTitle)
.then(cache => .then(cache =>
fetch(request).then(response => { fetch(request)
cache.put(request, response).then(_ => { .then(async response => {
console.log("Page successfully cached.") await cache.put(request, response);
}) })
}) .catch(() => console.log(`Cache could not be updated. ${request.url}`))
); );
// general strategy when making a request (eg if online try to fetch it // 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) // from cache, if something fails fetch from network. Update cache everytime files are fetched.
// This way files should only be fetched if cacheVersion is changed
self.addEventListener('fetch', function(event) { self.addEventListener('fetch', function(event) {
if (event.request.method === "POST") { if (event.request.method === "POST") {
// Requests related to Web Share Target. // Requests related to Web Share Target.
@ -90,27 +91,33 @@ self.addEventListener('fetch', function(event) {
})()); })());
} else { } else {
// Regular requests not related to Web Share Target. // Regular requests not related to Web Share Target.
// FOR DEVELOPMENT: Comment in next line to always update assets instead of using cached versions
// event.respondWith(fromNetwork(event.request, 10000));return;
event.respondWith( event.respondWith(
fromNetwork(event.request, 10000).catch(() => fromCache(event.request)) fromCache(event.request).then(rsp => {
// if fromCache resolves to undefined fetch from network instead
return rsp || fromNetwork(event.request, 10000);
})
); );
event.waitUntil(update(event.request));
} }
}); });
// on activation, we clean up the previously registered service workers // on activation, we clean up the previously registered service workers
self.addEventListener('activate', evt => self.addEventListener('activate', evt => {
evt.waitUntil( return evt.waitUntil(
caches.keys().then(cacheNames => { caches.keys().then(cacheNames => {
return Promise.all( return Promise.all(
cacheNames.map(cacheName => { cacheNames.map(cacheName => {
if (cacheName !== cacheTitle) { if (cacheName !== cacheTitle) {
return caches.delete(cacheName); return caches.delete(cacheName);
} }
}) })
); );
}) })
) )
}
); );
const evaluateRequestData = function (request) { const evaluateRequestData = function (request) {

View file

@ -35,7 +35,7 @@ class ServerConnection {
_connect() { _connect() {
clearTimeout(this._reconnectTimer); clearTimeout(this._reconnectTimer);
if (this._isConnected() || this._isConnecting()) return; if (this._isConnected() || this._isConnecting() || this._isOffline()) return;
if (this._isReconnect) { if (this._isReconnect) {
Events.fire('notify-user', { Events.fire('notify-user', {
message: Localization.getTranslation("notifications.connecting"), message: Localization.getTranslation("notifications.connecting"),
@ -257,6 +257,10 @@ class ServerConnection {
return this._socket && this._socket.readyState === this._socket.CONNECTING; return this._socket && this._socket.readyState === this._socket.CONNECTING;
} }
_isOffline() {
return !navigator.onLine;
}
_onError(e) { _onError(e) {
console.error(e); console.error(e);
} }

View file

@ -2260,7 +2260,10 @@ class NetworkStatusUI {
} }
_showOfflineMessage() { _showOfflineMessage() {
Events.fire('notify-user', Localization.getTranslation("notifications.offline")); Events.fire('notify-user', {
message: Localization.getTranslation("notifications.offline"),
persistent: true
});
} }
_showOnlineMessage() { _showOnlineMessage() {

View file

@ -1,16 +1,18 @@
const cacheVersion = 'v1.9.0'; const cacheVersion = 'v1.9.0';
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`; const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
const urlsToCache = [ const urlsToCache = [
'index.html',
'./', './',
'index.html',
'manifest.json',
'styles.css', 'styles.css',
'scripts/localization.js',
'scripts/network.js', 'scripts/network.js',
'scripts/NoSleep.min.js',
'scripts/QRCode.min.js',
'scripts/theme.js',
'scripts/ui.js', 'scripts/ui.js',
'scripts/util.js', 'scripts/util.js',
'scripts/qrcode.js',
'scripts/zip.min.js', 'scripts/zip.min.js',
'scripts/NoSleep.min.js',
'scripts/theme.js',
'sounds/blop.mp3', 'sounds/blop.mp3',
'images/favicon-96x96.png', 'images/favicon-96x96.png',
'images/favicon-96x96-notification.png', 'images/favicon-96x96-notification.png',
@ -53,7 +55,7 @@ const fromNetwork = (request, timeout) =>
fetch(request).then(response => { fetch(request).then(response => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
fulfill(response); fulfill(response);
update(request); update(request).then(() => console.log("Cache successfully updated for", request.url));
}, reject); }, reject);
}); });
@ -62,9 +64,7 @@ const fromCache = request =>
caches caches
.open(cacheTitle) .open(cacheTitle)
.then(cache => .then(cache =>
cache cache.match(request)
.match(request)
.then(matching => matching || cache.match('/offline/'))
); );
// cache the current page to make it available for offline // cache the current page to make it available for offline
@ -72,15 +72,16 @@ const update = request =>
caches caches
.open(cacheTitle) .open(cacheTitle)
.then(cache => .then(cache =>
fetch(request).then(response => { fetch(request)
cache.put(request, response).then(_ => { .then(async response => {
console.log("Page successfully cached.") await cache.put(request, response);
}) })
}) .catch(() => console.log(`Cache could not be updated. ${request.url}`))
); );
// general strategy when making a request (eg if online try to fetch it // 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) // from cache, if something fails fetch from network. Update cache everytime files are fetched.
// This way files should only be fetched if cacheVersion is changed
self.addEventListener('fetch', function(event) { self.addEventListener('fetch', function(event) {
if (event.request.method === "POST") { if (event.request.method === "POST") {
// Requests related to Web Share Target. // Requests related to Web Share Target.
@ -90,27 +91,33 @@ self.addEventListener('fetch', function(event) {
})()); })());
} else { } else {
// Regular requests not related to Web Share Target. // Regular requests not related to Web Share Target.
// FOR DEVELOPMENT: Comment in next line to always update assets instead of using cached versions
// event.respondWith(fromNetwork(event.request, 10000));return;
event.respondWith( event.respondWith(
fromNetwork(event.request, 10000).catch(() => fromCache(event.request)) fromCache(event.request).then(rsp => {
// if fromCache resolves to undefined fetch from network instead
return rsp || fromNetwork(event.request, 10000);
})
); );
event.waitUntil(update(event.request));
} }
}); });
// on activation, we clean up the previously registered service workers // on activation, we clean up the previously registered service workers
self.addEventListener('activate', evt => self.addEventListener('activate', evt => {
evt.waitUntil( return evt.waitUntil(
caches.keys().then(cacheNames => { caches.keys().then(cacheNames => {
return Promise.all( return Promise.all(
cacheNames.map(cacheName => { cacheNames.map(cacheName => {
if (cacheName !== cacheTitle) { if (cacheName !== cacheTitle) {
return caches.delete(cacheName); return caches.delete(cacheName);
} }
}) })
); );
}) })
) )
}
); );
const evaluateRequestData = function (request) { const evaluateRequestData = function (request) {