Skip to content

Commit

Permalink
Simplify the refresh banner example.
Browse files Browse the repository at this point in the history
Use a prolyfill of `navigator.serviceWorker.waiting` and `ServiceWorker.skipWaiting()` to simply implementation.

Fixes google#5504
  • Loading branch information
dfabulich authored Dec 14, 2017
1 parent 819eaaf commit 55bf338
Showing 1 changed file with 39 additions and 72 deletions.
111 changes: 39 additions & 72 deletions src/content/en/tools/workbox/guides/advanced-recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,34 @@ description: Advanced recipes to use with Workbox.
A common UX pattern for progressive web apps is to show a banner when a service
worker has updated and waiting to install.

To do this you'll need to add some code to your page and to your service worker.
To do this you'll need to add some polyfill code to your page and to your service worker.

**Add to your page**

```javascript
// polyfill navigator.serviceWorker.waiting
if (!('waiting' in navigator.serviceWorker)) {
navigator.serviceWorker.waiting = new Promise(function(resolve) {
navigator.serviceWorker.ready.then(function(reg) {
function awaitStateChange() {
reg.installing.addEventListener('statechange', function() {
if (this.state === 'installed') resolve(reg);
});
}
if (reg.waiting) resolve(reg);
if (reg.installing) awaitStateChange();
reg.addEventListener('updatefound', awaitStateChange);
})
});
}

// polyfill ServiceWorker.skipWaiting()
if (!('skipWaiting' in ServiceWorker.prototype)) {
ServiceWorker.prototype.skipWaiting = function() {
this.postMessage('skipWaiting');
}
}

function showRefreshUI(registration) {
// TODO: Display a toast or refresh UI.

Expand All @@ -40,79 +63,27 @@ function showRefreshUI(registration) {

button.disabled = true;

registration.waiting.postMessage('force-activate');
registration.waiting.skipWaiting();
});

document.body.appendChild(button);
};

function onNewServiceWorker(registration, callback) {
if (registration.waiting) {
// SW is waiting to activate. Can occur if multiple clients open and
// one of the clients is refreshed.
return callback();
}

function listenInstalledStateChange() {
registration.installing.addEventListener('statechange', function() {
if (event.target.state === 'installed') {
// A new service worker is available, inform the user
callback();
}
});
};

if (registration.installing) {
return listenInstalledStateChange();
}

// We are currently controlled so a new SW may be found...
// Add a listener in case a new SW is found,
registration.addEventListener('updatefound', listenInstalledStateChange);
}

window.addEventListener('load', function() {
// When the user asks to refresh the UI, we'll need to reload the window
navigator.serviceWorker.addEventListener('message', function(event) {
if (!event.data) {
return;
}

switch (event.data) {
case 'reload-window':
window.location.reload();
break;
default:
// NOOP
break;
}
});

navigator.serviceWorker.register('/sw.js')
.then(function (registration) {
// Track updates to the Service Worker.
if (!navigator.serviceWorker.controller) {
// The window client isn't currently controlled so it's a new service
// worker that will activate immediately
return;
}

onNewServiceWorker(registration, function() {
showRefreshUI(registration);
});
});
navigator.serviceWorker.addEventListener('controllerchange', function() {
window.location.reload();
});

navigator.serviceWorker.waiting.then(showRefreshUI);
```

This code handles the verious possible lifecycles of the service worker
The polyfill handles the verious possible lifecycles of the service worker
and detects when a new service worker has become installed and is waiting to
activate.

When a waiting service worker is found we set up a message listener on
`navigator.serviceWorker` so we know when to reload the window. When the
user clicks on the UI to refresh the page, we post a message to the new
service worker telling it to force an activation. After this a message is
posted to all windows telling them to reload.
When a waiting service worker is found we we tell the service worker to skip
waiting (posting a message if necessary). After this, the `controllerchange`
event will fire. The page should reload when the controller changes to
ensure that all tabs are running the same version of the code.

Note: This is one possible approach to refreshing the page on a new service
worker. For a more thorough answer as well as an explanation of alternative
Expand All @@ -122,19 +93,18 @@ discuess a range of options.

**Add to your service worker**

This code supports the polyfill while we wait for `ServiceWorker.skipWaiting`
to be available in all browsers.

```javascript
self.addEventListener('message', (event) => {
if (!event.data){
return;
}

switch (event.data) {
case 'force-activate':
case 'skipWaiting':
self.skipWaiting();
self.clients.claim();
self.clients.matchAll().then((clients) => {
clients.forEach((client) => client.postMessage('reload-window'));
});
break;
default:
// NOOP
Expand All @@ -143,10 +113,7 @@ self.addEventListener('message', (event) => {
});
```

This will receive a the 'force-activate' message and call `skipWaiting()` and
`clients.claim()` so that the service worker activates immediately and controls
all of the currently open windows. We then message each window with a
'reload-window' message so each tab is refreshed with the latest content.
This will receive a 'skipWaiting' message and call `skipWaiting()`.

## "Warm" the runtime cache

Expand Down

0 comments on commit 55bf338

Please sign in to comment.