From b84102ea4bf1798d9a0879cc3a6b36b1bc24e2a8 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Sun, 20 Oct 2024 12:05:59 -0400 Subject: [PATCH] Modernizing service worker --- static/serviceworker.js | 130 +++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/static/serviceworker.js b/static/serviceworker.js index 04d5b15..38ee813 100644 --- a/static/serviceworker.js +++ b/static/serviceworker.js @@ -4,34 +4,60 @@ let offlinePage = '/offline'; let offlineFundamentals = [offlinePage, '/']; let maxItems = 100; + +function addFundamentals() { + return caches.open(version + cacheName) + .then(cache => cache.addAll(offlineFundamentals)) +} + +// Cache the page(s) that initiate the service worker +function cacheClients() { + return clients.matchAll({ + includeUncontrolled: true + }) + .then(allClients => allClients.map(client => client.url)) + .then(pages => [pages, caches.open(cacheName)]) + .then(([pages, cache]) => cache.addAll(pages)) +} + +// Remove caches whose name is no longer valid +function clearInvalidatedCaches() { + return caches.keys() + .then( keys => { + return Promise.all(keys + .filter(key => !key.includes(version)) + .map(key => caches.delete(key)) + ); + }); +} + +function trimCache(name, maxItems) { + return caches.open(name) + .then(cache => [cache, cache.keys()]) + // Make sure offlineFundamentals don't get deleted + .then(([cache, keys]) => [cache, keys.filter(key => !offlineFundamentals.includes(key))]) + .then(([cache, possibleDelete]) => { + // Trim cache until we are of the right size + deleteInProgress = [] + for (let i = 0; i < keys.length - maxItems; i++) { + // Keep track of each delete + deleteInProgress.push(cache.delete(possibleDelete[i])); + } + + // Return when everything is resolved + return Promise.all(deleteInProgress); + }) +} + self.addEventListener('install', function (event) { // Cache offline fundamentals event.waitUntil( - caches.open(version + cacheName).then(function (cache) { - return cache.addAll(offlineFundamentals); - })); + addFundamentals() + .then(() => cacheClients()) + .then(() => skipWaiting()) + ) }); -function trimCache(name, maxItems) { - caches.open(name).then(function(cache) { - cache.keys().then(function(keys) { - // Make sure offlineFundamentals don't get deleted - possibleDelete = keys.filter(function(key) { - !offlineFundamentals.includes(key) - }); - - // Keep track of each delete - deleteInProgress = [] - for (let i = 0; i < keys.length - maxItems; i++) { - deleteInProgress.push(cache.delete(possibleDelete[i])); - } - - // Return when everything is resolved - return Promise.all(deleteInProgress); - }) - }) -} - // Listens for trimCache command which occurs on page load self.addEventListener('message', event => { if (event.data.command == 'trimCache') { @@ -42,32 +68,28 @@ self.addEventListener('message', event => { // If the version changes, invalidate the cache self.addEventListener('activate', function (event) { event.waitUntil( - caches.keys() - .then(function (keys) { - return Promise.all(keys - // Find all caches that aren't the current version - .filter(function (key) { - return !key.includes(version) - }) - // Delete the cache - .map(function (key) { - return caches.delete(key); - }) - ); - }) + clearInvalidatedCaches() + .then(() => clients.claim()) ); }); +if (registration.navigationPreload) { + addEventListener('activate', event => { + event.waitUntil( + registration.navigationPreload.enable() + ); + }); +} + // Listen for request events self.addEventListener('fetch', function (event) { - let request = event.request; + const request = event.request; // Always fetch non-GET requests from the network if (request.method !== 'GET') { - event.respondWith(fetch(request) - .catch(function () { - return caches.match(offlinePage); - }) + event.respondWith( + fetch(request) + .catch(() => caches.match(offlinePage)) ); return; } @@ -81,26 +103,26 @@ self.addEventListener('fetch', function (event) { // Network-first Approach event.respondWith( // Attepmt to grab the latest copy from the network - fetch(request).then(function (response) { + Promise.resolve(event.preloadResponse) + .then(preloadResponse => preloadResponse || fetch(request)) + .then(response => { // If successful, create a copy of the response // and save it to the cache // Note: Ignore badges if (!request.url.includes("/badges")) { let cacheCopy = response.clone(); - event.waitUntil(caches.open(version + cacheName).then(function (cache) { - return cache.put(request, cacheCopy); - })); + event.waitUntil( + caches.open(version + cacheName) + .then(cache => cache.put(request, cacheCopy)) + ); } return response; - }).catch(function (error) { - // Otherwise, check the cache. - return caches.match(request).then(function (response) { - // Show offline page if cache misses for HTML pages. - if (isRequestType('text/html')) { - return response || caches.match(offlinePage); - } - return response; - }); }) + // Check the cache + .catch(error => + caches.match(request) + // Show offline page for HTML pages if cache miss + .then(response => isRequestType('text/html')? response || caches.match(offlinePage) : response) + ) ); }); \ No newline at end of file