Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement Montage PushManager sample #44

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/audio/pop_up_alert.mp3
Binary file not shown.
Binary file added assets/icons/icon-100.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-167.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-196.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-29.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-40.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-50.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-55.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-58.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-60.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-72.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-76.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon-87.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions bin/push-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env node

var webpush = require('web-push');
// https://console.firebase.google.com
var SERVER_API_KEY="";
// From push-worker subscription
var pushSubscription = {"endpoint":"...","expirationTime":null,"keys":{"p256dh":"..."}};

webpush.setGCMAPIKey(SERVER_API_KEY);
webpush.sendNotification(pushSubscription, JSON.stringify({
tag: Date.now(),
method:"push",
title: "Montage Popcorn",
icon: '/assets/icons/icon-196.png',
badge:'/assets/icons/icon-128.png',
sound: '/assets/audio/pop_up_alert.mp3',
body: "There's a new trailer!"
})).then(function (res) {
console.log('ok', res);
}).catch(function (err) {
console.error('err', err);
});

92 changes: 92 additions & 0 deletions core/push-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var Promise = require("montage/core/promise").Promise;
var ServiceWorker = require('./service-worker').ServiceWorker;

//
// ServiceWorker
//

var PushManager = {

getWorker: function() {
return ServiceWorker.getWorkerRegistration('push-worker.js');
},

getSubscription: function() {
return this.getWorker().then(function(registration) {
if (!registration.pushManager) {
return Promise.reject('Service worker push not supported.');
} else {
return registration.pushManager.getSubscription();
}
});
},

hasSubscription: function() {
return this.getSubscription().then(function (subscription) {
return subscription && subscription.length > 0;
});
},

subscribe: function() {

return this.getWorker().then(function(registration) {
if (!registration.pushManager) {
return Promise.reject('Service worker push not supported.');
} else {
// Use the PushManager to get the user's subscription to the push service.
return registration.pushManager.subscribe({
userVisibleOnly: true
});
}
});
},

unsubscribe: function() {
var self = this;
return self.getSubscription().then(function (subscription) {
return subscription.unsubscribe().then(function () {
return self.getWorker().then(function (worker) {
return worker.unregister();
});
});
});
},

send: function(subscription, body, options) {

options = Object.assign({
title: document.title,
icon: '/assets/icons/icon-196.png',
badge:'/assets/icons/icon-128.png',
sound: '/assets/audio/pop_up_alert.mp3',
data: location.href,
tag: undefined,
vibrate: undefined,
lang: undefined,
delay: 0,
ttl: 0
}, options || {});

var msg = {
body: body,
title: options.title,
picture: options.picture,
sound: options.sound,
icon: options.icon,
badge: options.badge,
tag: options.tag,
vibrate: options.vibrate,
lang: options.lang,
actions: options.actions,
data: options.data
};

// Send to worker
return this.getWorker().then(function (worker) {
return ServiceWorker.sendWorkerMsg(worker, msg);
});
}
};

exports.PushManager = PushManager;

3 changes: 3 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
"theme_color": "#000",
"background_color": "#000",
"display": "standalone",
"permissions":["notifications"],
"gcm_sender_id":"743128909939",
"gcm_user_visible_only":true,
"montage_manifest_version": 1,
"files": {
"locale": {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"digit": "^3.0.2",
"montage": "montagejs/montage#master",
"query-params": "0.0.1",
"url": "^0.11.0"
"url": "^0.11.0",
"web-push": "^3.3.0"
},
"devDependencies": {
"jshint": "^2.9.5",
Expand Down
218 changes: 218 additions & 0 deletions push-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*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.badge === '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);
event.waitUntil(
showNotification(event.data)
);
});

// 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(
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
})
);
}
});
Loading