diff --git a/src/routes/_pages/settings/index.html b/src/routes/_pages/settings/index.html index 45503ed..02a3045 100644 --- a/src/routes/_pages/settings/index.html +++ b/src/routes/_pages/settings/index.html @@ -36,7 +36,7 @@ methods: { reload (event) { event.preventDefault() - document.location.reload(true) + location.reload() } } } diff --git a/src/routes/_utils/serviceWorkerClient.js b/src/routes/_utils/serviceWorkerClient.js index d846f08..aed0689 100644 --- a/src/routes/_utils/serviceWorkerClient.js +++ b/src/routes/_utils/serviceWorkerClient.js @@ -1,17 +1,81 @@ import { snackbar } from '../_components/snackbar/snackbar' -function onUpdateFound (registration) { - const newWorker = registration.installing +// A lot of this code is borrowed from https://github.com/GoogleChromeLabs/squoosh/blob/53b46f8/src/lib/offliner.ts +// Service Workers are hard! - newWorker.addEventListener('statechange', async () => { - if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { - snackbar.announce('App update available.', 'Reload', () => document.location.reload(true)) +// Tell the service worker to skip waiting +async function skipWaiting () { + const reg = await navigator.serviceWorker.getRegistration() + if (!reg || !reg.waiting) { + return + } + reg.waiting.postMessage('skip-waiting') +} + +// Wait for an installing worker +async function installingWorker (reg) { + if (reg.installing) { + return reg.installing + } + return new Promise((resolve) => { + reg.addEventListener( + 'updatefound', + () => resolve(reg.installing), + { once: true } + ) + }) +} + +// Wait a service worker to become waiting +async function updateReady (reg) { + if (reg.waiting) { + return + } + const installing = await installingWorker(reg) + return new Promise((resolve) => { + const listener = () => { + if (installing.state === 'installed') { + installing.removeEventListener('statechange', listener) + resolve() + } } + installing.addEventListener('statechange', listener) }) } -if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/service-worker.js').then(registration => { - registration.addEventListener('updatefound', () => onUpdateFound(registration)) - }) -} +(async () => { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/service-worker.js') + + const hasController = !!navigator.serviceWorker.controller + + navigator.serviceWorker.addEventListener('controllerchange', () => { + if (!hasController) { // first install + return + } + + location.reload() + }) + + // If we don't have a controller, we don't need to check for updates – we've just loaded from the + // network. + if (!hasController) { + return + } + + const reg = await navigator.serviceWorker.getRegistration() + if (!reg) { // SW not registered yet + return + } + + // Look for updates + await updateReady(reg) + + // Ask the user if they want to update. + snackbar.announce('App update available.', 'Reload', () => { + // Tell the waiting worker to activate, this will change the controller and cause a reload (see + // 'controllerchange') + /* no await */ skipWaiting() + }) + } +})() diff --git a/src/service-worker.js b/src/service-worker.js index d2b4448..d3716fb 100644 --- a/src/service-worker.js +++ b/src/service-worker.js @@ -35,7 +35,6 @@ self.addEventListener('install', event => { caches.open(WEBPACK_ASSETS).then(cache => cache.addAll(webpackAssets)), caches.open(ASSETS).then(cache => cache.addAll(assets)) ]) - self.skipWaiting() })()) }) @@ -243,3 +242,11 @@ self.addEventListener('notificationclick', event => { } })()) }) + +self.addEventListener('message', (event) => { + switch (event.data) { + case 'skip-waiting': + self.skipWaiting() + break + } +})