popcorn
popcorn copied to clipboard
push service worker
<link rel="manifest" href="/manifest.webapp">
manifest.webapp
{
"splash_screens": [],
"scope": "/",
"start_url": "/?utm_source=homescreen",
"display": "standalone",
"theme_color": "#434343",
"background_color": "#f7f9f9",
"prefer_related_applications": true,
"related_applications": [{
"platform": "ios",
"url": "https://itunes.apple.com/us/app/popcorn/applID"
}, {
"platform": "play",
"id": "com.playstore"
}],
"chrome_related_applications": [{
"platform": "web",
"url": "https://popcorn.com"
}, {
"platform": "android",
"url": "https://play.google.com/store/apps/details?id=com.popcorn"
}, {
"platform": "chrome",
"url": "https://chrome.google.com/webstore/detail/chromeStoreid"
}],
"permissions": ["notifications"],
"gcm_sender_id": "gcmId",
"gcm_user_visible_only": true
}
pushWorker.js
/*global define:false, console, self, Promise */
// https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
// https://developers.google.com/web/fundamentals/engage-and-retain/push-notifications/permissions-subscriptions
// https://github.com/w3c/ServiceWorker/blob/master/explainer.md
// chrome://inspect/#service-workers
// https://serviceworke.rs
//
// Env Setttings
//
// It's replaced unconditionally to preserve the expected behavior
// in programs even if there's ever a native finally.
Promise.prototype['finally'] = function finallyPolyfill(callback) {
var constructor = this.constructor;
return this.then(function(value) {
return constructor.resolve(callback()).then(function() {
return value;
});
}, function(reason) {
return constructor.resolve(callback()).then(function() {
throw reason;
});
});
};
var DEBUG = false;
//
// Utils
//
function log(msg, obj) {
console.log('PushWorker', msg, DEBUG ? obj : undefined);
}
function postMessage(msg) {
if (DEBUG) {
log("postMessage", msg);
}
return self.clients.matchAll().then(function(clients) {
return Promise.all(clients.map(function(client) {
return client.postMessage(msg);
}));
});
}
function showNotification(payload) {
// Cast has object
if (typeof payload === 'string') {
payload = {
title: payload
};
}
// Clear bad icons
if (
typeof payload.icon === 'string' &&
payload.icon.indexOf('https://') !== 0
) {
delete payload.icon;
}
// Clear bad badge
if (
typeof payload.icon === 'string' &&
payload.badge.indexOf('https://') !== 0
) {
delete payload.badge;
}
// Force requireInteraction
if (typeof payload.requireInteraction === 'undefined') {
payload.requireInteraction = true;
}
if (typeof payload.actions === 'undefined') {
// Open/Close payload.data
payload.actions = payload.data ? [
{
action: 'open',
title: 'Open'
},
{
action: 'close',
title: 'Dismiss'
}
// Close (no payload.data)
] : [
{
action: 'close',
title: 'Close'
}
];
}
// Send via postMessage
postMessage({
event: 'push',
data: payload
});
return self.registration.showNotification(payload.title, {
lang: payload.lang || 'en',
body: payload.body || 'Hello!',
tag: payload.tag || payload.title,
icon: payload.icon,
badge: payload.badge,
actions: payload.actions,
data: payload.data,
renotify: !!payload.renotify,
requireInteraction: !!payload.requireInteraction,
vibrate: payload.vibrate,
sound: payload.sound,
silent: (payload.silent || (!payload.sound && !payload.vibrate))
});
}
function openUrl(url) {
return self.clients.matchAll({
includeUncontrolled: true,
type: 'window'
}).then(function(clientList) {
var clientListMatchUrl;
// Look for a match
if (url) {
clientListMatchUrl = clientListMatchUrl && clientListMatchUrl.filter(function (client) {
return String(client.url).indexOf(url) === 0;
});
if (clientListMatchUrl && clientListMatchUrl.length === 0) {
clientListMatchUrl = clientList;
}
}
if (clientList && clientList.length > 0) {
return clientList[0].focus();
} else if (url) {
return self.clients.openWindow(url);
}
});
}
//
// Worker
//
log('Started', self);
self.addEventListener('install', function(event) {
log('Install...', event);
event.waitUntil(self.skipWaiting().finally(function () {
log('Installed', event);
}));
});
self.addEventListener('activate', function(event) {
event.waitUntil(self.skipWaiting().then(function () {
self.clients.claim();
}).finally(function () {
log('Activated', event);
}));
});
self.addEventListener('message', function (event) {
log('Push event received', event);
});
// Register event listener for the 'push' event.
var lastPayload;
self.addEventListener('push', function(event) {
try {
var payload = event.data ? JSON.parse(event.data.text()) : {};
// Keep the service worker alive until the notification is created.
event.waitUntil(
// Show a notification with title 'ServiceWorker Cookbook' and body 'Alea iacta est'.
showNotification(payload)
);
} catch(err) {
log('Push message parse failed', err);
}
});
self.addEventListener('notificationclick', function(event) {
log('Notification clicked', event);
var action = event.action || 'open';
if (action === 'open') {
event.notification.close();
event.waitUntil(openUrl(event.notification.data));
} else if (action === 'close') {
event.notification.close();
}
}, false);
self.addEventListener('message', function (event) {
if (event.data === 'PushTest') {
log('PushTest...', event);
event.waitUntil(
showNotification({
title: 'Push Notification Test',
data: self.location.href
})
);
}
});