Progressive Web Apps Snippets by @firt Slides: http://tinyurl.com/fwd17pwa Snippet #1: Pre-caching assets in a Service Worker ========== var urls = []; self.addEventListener("install", function(event) { console.log("The SW is now installed"); event.waitUntil( caches.open("PWACache") .then(function(cache) { return cache.addAll(urls); }) .then(function() { return self.skipWaiting(); // Optional }) ); }); Snippet #2: Serving with Cache First ========== self.addEventListener("fetch", function(event) { event.respondWith(caches.match(event.request) .then(function(response) { if (response) { // The request is in the cache return response; } else { // We need to go to the network return fetch(event.request); } }) ) }); Snippet #3: Serving from Cache and Update resources =========== self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Even if the response is in the cache, we fetch it // and update the cache for future usage var fetchPromise = fetch(event.request).then( function(networkResponse) { caches.put(event.request, networkResponse.clone()); return networkResponse; }); // We use the currently cached version if it's there return response || fetchPromise; }) ); }); Snippet #4: Requesting Application Server Keys for Push =========== npm install -g web-push web-push generate-vapid-keys Snippet #5: Detecting Push Availability =========== if (!('PushManager' in window)) { // Push not available return; } Snippet #6: Ask for Push Notification Permission =========== function pushAskPermission() { return new Promise(function(resolve, reject) { const permissionResult = Notification.requestPermission(function(result) { resolve(result); }); if (permissionResult) { permissionResult.then(resolve, reject); } }) .then(function(permissionResult) { if (permissionResult !== 'granted') { throw new Error('We weren\'t granted permission.'); } }); } Snippet #7: Subscribe a User =========== function pushSubscribeUser() { navigator.serviceWorker.getRegistration().then( function(registration) { const subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array( '' ) }; return registration.pushManager.subscribe(subscribeOptions); }) .then(function(pushSubscription) { console.log('Push Subscription: ', JSON.stringify(pushSubscription)); return pushSubscription; }); } // Snippet from https://www.npmjs.com/package/web-push function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding) .replace(/\-/g, '+') .replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } Snippet #8: Receiving the Push in the SW and basic notification =========== self.addEventListener("push", function(event) { if (event.data) { console.log('Push Data: ', event.data.text()); } else { console.log('No data :('); } self.registration.showNotification("Push title", { body: event.data.text() }); }); Snippet #9: Detect if Actions are available =========== if ('actions' in Notification.prototype) { const maxVisibleActions = Notification.maxActions; } Snippet #10: Notification Click detection =========== self.addEventListener('notificationclick', function(event) { if (!event.action) { console.log('Notification Click with no action'); return; } else { // event action has the action id } self.registration.showNotification("Push title", { body: event.data.text() }); event.notification.close(); event.waitUntil(doSomething); }); Snippet #11: Notification with all options =========== { body: "Message String", dir: "auto|rtl|ltr", actions: [{ action: 'action-id', title: 'Action Title', icon: 'https://...48dp' }] icon: "https://...64dp", badge: "https://...24dp", image: "https://...", vibrate: [100,0,100,0,100], sound: "https://", tag: "notification-id-tag", data: "", requireInteraction: true, renotify: true, silent: false, }