@PitaJ
My bad, your wish is my command
I tried to clean up the three files to just what's relevant about PWA. Besides having to have a plugin you may also need a theme plugin because a button needs to be in place to be act as a registration for web-push.
---- Library.js ----
"use strict";
// Dependencies
var async = module.parent.require('async'),
plugin = {},
user= require.main.require('./src/user'),
groups_pwa = require.main.require('./src/groups'),
socketPlugins = require.main.require('./src/socket.io/plugins'),
messaging = require.main.require('./src/messaging'),
db = require.main.require('./src/database');
try{
var webPush = module.parent.require('web-push');
// Use the web-push library to hide the implementation details of the communication between the application server and the push service.
// For details, see https://tools.ietf.org/html/draft-ietf-webpush-protocol and https://tools.ietf.org/html/draft-ietf-webpush-encryption.
webPush.setGCMAPIKey('API KEY FROM FIREBASE');
}catch(err){
console.log("{!} Web Push dependency is not installed, PUSHING WILL NOT WORK " + err);
console.log("{!} install: npm install web-push --save ");
}
/**
* Initialize the plugin's pages
* -> All socket.io communication needs to be established here
* -> Hijacking of SW fetch communication done here
**/
plugin.init = function(data, callback) {
var controllers = require('./controllers');
// We create two routes for every view. One API call, and the actual route itself.
// Just add the buildHeader middleware to your route and NodeBB will take care of everything for you.
data.router.get('/admin/plugins/pwa', data.middleware.admin.buildHeader, controllers.renderAdminPage);
data.router.get('/api/admin/plugins/pwa', controllers.renderAdminPage);
/**
* Socket.io Comms
* - Create a socket namespace
**/
socketPlugins.pwa = {};
// OVERRRIDE: app.post(route + 'sendNotification', function(req, res) ...
socketPlugins.pwa.sendNotification = function(socket, req, callback) {
/**
* notification_options = {
endpoint: endpoint,
key: key,
authSecret: authSecret,
payload: message,
delay: pushDelay,
ttl: pushTTL,
url: url
};
**/
console.log("[+] -sendNotification- received from client - req: ", req);
// Send notification to the push service.
// Remove the endpoint from the subscriptions array if the push service responds with an error. Subscription has been cancelled or expired.
// SHould loop through Database of each FR
//subscriptions.forEach( plugin.sendNotification );
plugin.sendTodataSetOfUsers(req, callback);
callback(null, { received: true });
};
// OVERRIDE: app.post(route + 'register', function(req, res) ...
socketPlugins.pwa.register = function(socket, req, callback) {
console.log("[+] -register- received from client - req.body: ", req);
// Save the subscription to the Database
var subscription = {
user: req.user,
key: req.key,
authSecret: req.authSecret,
endpoint: req.endpoint
};
plugin.saveSubscription(subscription, function(err){
if(err){
console.log("{!} Error saving subscription: ",err);
return callback(err);
}
});
console.log('\n[+] Subscription registered: ' + req.user );
callback(null, { received: true });
};
// OVERRIDE: app.post(route + 'unregister', function(req, res) ...
socketPlugins.pwa.unregister = function(socket, req, callback) {
console.log("[+] -UNregister- received from client - req.body: ", req);
// Unregister a subscription by removing it from the subscriptions array
async.waterfall([
function(next){
plugin.isSubscribed( req.user );
},
function(boolean, next){
if( boolean ){
// subscriptions.splice(subscriptions.indexOf(endpoint), 1);
plugin.removeSubscription(req.uid);
console.log('\n[+] Subscription unregistered: ', req.user);
}
else{
console.log('\n[!] Cannot unregister, no subscription found for uid:', req.user);
}
}
], callback);
callback(null, { received: true });
};
callback();
};
/**
* Check Subscription
* data == uid
**/
plugin.isSubscribed = function(data, callback)
{
var retBoolean = false;
async.waterfall([
function(next){
db.isObjectField("user:"+data, "subscription",next);
},
function(exits, next){
if(exits)
{
async.waterfall([
function(next){
db.getObjectField("user:"+data, "subscription",next);
},
function(boolean, next){
retBoolean = boolean;
}
], callback);
}
}
], callback);
return retBoolean;
};
/**
* Send Notifications to dataSetOfUsers
* https://caolan.github.io/async/docs.html#waterfall
* The scope for async functions are within where they are created, not executed.
* https://stackoverflow.com/questions/27415400/lost-of-scope-with-async-waterfall
**/
plugin.sendToUsers = function(data, callback)
{
dataSetOfUsers.forEach(function (uid){
console.log(" Checking for subscription user: ", uid);
async.waterfall([
function(next){
db.isObjectField("user:"+uid, "subscription",next);
},
function(exits, next){
if(exits)
{
async.waterfall([
function(next){
db.getObjectField("user:"+uid, "subscription",next);
},
function(boolean, next){
// There is a subscription and it's set to true
if(boolean)
{
console.log("Subscription Found! ",uid);
var smallscope = {
endpoint: '',
key: '',
authSecret: '',
TTL: '',
payload: '',
uid: '',
userslug: ''
};
async.waterfall([
function(next){
db.getObjectField("user:"+uid, "endpoint", next);
},
function(saveme, next){
smallscope.endpoint = saveme;
db.getObjectField("user:"+uid, "authSecret", next);
},
function(saveme, next){
smallscope.authSecret = saveme;
db.getObjectField("user:"+uid, "key", next);
},
function(saveme, next){
smallscope.key = saveme;
db.getObjectField("user:"+uid,"userslug",next);
},
function(saveme,next){
smallscope.userslug = saveme;
smallscope.TTL = data.ttl;
smallscope.payload = data.payload; // BOOKMARK TODO
smallscope.uid = uid;
var sendUrl = url; // BOOKMARK TODO
sendUrl += '/user/';
sendUrl += smallscope.userslug;
sendUrl += '/chats/';
sendUrl += chatID;
plugin.sendNotification(smallscope,callback);
},
function(err, result){
console.log("\n{!push} error: ", err);
}
], next);
}
else{
console.log("{!push} Subscription is set to False for: ", uid);
smallscope = {};
return;
}
}
], callback);
}
else{
console.log("{!push} No Subscription Data: ", uid);
//sendTo = {};
return;
}
}
], callback);
});
};
/**
* Save subscription after registration
**/
plugin.saveSubscription = function(data, callback)
{
// Async all the things, Database abstractions do already test for errors
async.waterfall([
function(next){
db.setObjectField("user:"+data.user, "subscription", true, next);
db.setObjectField("user:"+data.user,"key",data.key, next);
db.setObjectField("user:"+data.user,"authSecret",data.authSecret, next);
db.setObjectField("user:"+data.user,"endpoint",data.endpoint, next);
}
], callback );
};
/**
* Remove subscription by matching the endpoint
**/
plugin.removeSubscription = function(data)
{
console.log("\n[*] Removing from DB: ", data);
};
/**
* Send Notification to endpoint
*
* https://serviceworke.rs/push-subscription-management.html
* +
* https://serviceworke.rs/push-payload.html
* -> data.payload (a string or NodeJS buffer) is all that is allowed to be sent to the webPush API
**/
plugin.sendNotification = function (data, callback)
{
webPush.sendNotification(
{
endpoint: data.endpoint,
TTL: data.ttl,
keys: {
p256dh: data.key,
auth: data.authSecret
}
},
data.payload ).then(function() {
console.log('\n[web-push] Firbase given: ' + data.key, ' for uid: ', data.uid);
}).catch(function(err) {
console.log('\n{!} ERROR in sending Notification: ', err);
console.log('\n{!} endpoint removed: ' + data.endpoint);
plugin.removeSubscription(data.endpoint);
});
};
// Register Plugin to NodeBB
module.exports = plugin;
---- Client.js ----
/* globals $, navigator, socket, app */
"use strict";
var key;
var authSecret;
var endpoint;
var debug = false; // from the initial pwa messages to be quiet
var pushDelay = 500; // time in miliseconds to delay sending the push notification out (on the service worker)
var pushTTL = 60 * 60 * 24; // is a value in seconds that determines retention by the push service (a.k.a firebase endpoint) to continue to send message
$(document).ready(function() {
// Not using because so many elements are loaded in the window that need to be used for subscribing SW
});
/**
* All elments inside the DOM, such as divs are loaded
**/
$(window).on('load', function(){
// Subscription Object that returns a promise
function getSubscription() {
console.log("[1] If #2 doesn't follow - reset the entire page(s) cache/cookies/tab_close ");
return navigator.serviceWorker.ready.then(function(registration){
console.log("[2] #2, whole service worker is cleanly loaded! ");
return registration.pushManager.getSubscription();
});
}
/**
* Service Worker Registration
* https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
* -> adding Push subscription
**/
// Register service worker and check the initial subscription state.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('[ServiceWorker] registration successful with scope: ', registration.scope);
getSubscription().then(function(subscription) {
if (subscription) {
console.log('[!] Notifications: Already subscribed', subscription.endpoint);
} else {
console.log('[!] Notifications: User is not subscribed! ');
}
});
}).catch(function(err) {
// registration failed :(
console.log('{!} ERROR: ServiceWorker registration failed: ', err);
});
} // END if('serviceWorker' in navigator)
/**
* Subscription For web Push Notifications
* Push Subscription; Button comes from templates/partials/subscribe.tpl
* https://serviceworke.rs/push-subscription-management_index_doc.html
**/
//Get the registration from service worker and create a new subscription using registration.pushManager.subscribe. Then register received new subscription by sending a POST request with its endpoint to the server.
function subscribe() {
navigator.serviceWorker.ready.then(function(registration) {
return registration.pushManager.subscribe({ userVisibleOnly: true });
}).then(function(subscription) {
console.log('\n[*] Subscribed ', subscription.endpoint);
// Retrieve the user’s public key.
var rawKey = subscription.getKey ? subscription.getKey('p256dh') : '';
key = rawKey ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : '';
var rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : '';
authSecret = rawAuthSecret ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : '';
endpoint = subscription.endpoint;
var subscription_object = {
endpoint: endpoint,
key: key,
authSecret: authSecret,
user: app.user.uid
};
// fetch('register') Override
return socket.emit('plugins.pwa.register', subscription_object, function(err, data) {
if (err) {
// BIG USER FAIL ERROR! MORE CLOSER ATTENTION FOR HANDELING!
console.log("[!] plugins.pwa.register ", err);
return app.alertError(err.message);
}
console.log("[+] plugins.pwa.register received: ", data.received);
});
// Original register code
// return fetch('register', {
// method: 'post',
// headers: {
// 'Content-type': 'application/json'
// },
// body: JSON.stringify({
// endpoint: subscription.endpoint,
// key: key,
// authSecret: authSecret,
// }),
// });
}).then(setUnsubscribeButton);
}
// Get existing subscription from service worker, unsubscribe (subscription.unsubscribe()) and unregister it in the server with a POST request to stop sending push messages to unexisting endpoint.
function unsubscribe() {
getSubscription().then(function(subscription) {
return subscription.unsubscribe().then(function() {
console.log('\n[!] Unsubscribed ', subscription.endpoint);
var subscription_object = {
endpoint: subscription.endpoint,
key: key,
authSecret: authSecret,
user: app.user.uid
};
// fetch('unregister') Override
return socket.emit('plugins.pwa.unregister', subscription_object, function(err, data) {
if (err) {
// BIG USER FAIL ERROR! MORE CLOSER ATTENTION FOR HANDELING!
console.log("[!] plugins.pwa.unregister ", err);
return app.alertError(err.message);
}
console.log("[+] plugins.pwa.unregister received: ", data.received);
});
// Original unregister code
// return fetch('unregister', {
// method: 'post',
// headers: {
// 'Content-type': 'application/json'
// },
// body: JSON.stringify({
// endpoint: subscription.endpoint,
// key: key,
// authSecret: authSecret,
// })
// });
});
}).then(setSubscribeButton);
}
if('serviceWorker' in navigator)
{
getSubscription().then(function(subscription){
if (subscription){
$('.subscriptionbtn').prop('checked', true);
}
else{
$('.subscriptionbtn').prop('checked', false);
}
});
}
else{
console.log('{!Cli_SW} Error Subscribing ');
}
/**
* Subscribe Button
* If clicked then subscribe; should show disable if already subscribed
* clicking on disabled button would unsubscribe and then need to click again
* to resubscribe.
**/
$('.subscriptionbtn').change(function() {
console.log("[Cli_SW] Subscribe Button Clicked ");
if($(this).prop('checked')){
//true subscribed
console.log("[Cli_SW] Subscribe Button Subscribing");
subscribe();
}
else{
//false unsubscribe
console.log("[Cli_SW] Subscribe Button Unsubscribing");
unsubscribe();
}
});
});
---- templates/partials/subscribe.tpl ----
<h4>Device Notifications</h4>
<div class="well">
<div class="checkbox">
<div class="btn-group btn-toggle">
<button class="btn btn-sm btn-default">On</button>
<button class="btn btn-sm btn-default active">Off</button>
</div>
<strong>Suscribe for device push notifications</strong>
</div>
<p class="help-block">If enabled, subscription to device notifications and recieve push messages sent from the application.</p>
</div>
---- manifest.json src/controllers/index.js:303 ----
var manifest = {
name: meta.config.title || 'NodeBB',
start_url: nconf.get('relative_path') + '/',
display: 'standalone',
orientation: 'portrait',
description: 'A description',
background_color: '#ABC123',
gcm_sender_id: '############', // I used firebase
icons: [
{
src: 'icon-192.png',
sizes: '192x192',
type: 'image/png'
}
],
related_applications: [
{
platform: 'play',
id: 'com.google.samples.apps.iosched'
}
]
};
if (meta.config['brand:touchIcon']) {
manifest.icons.push({
src: nconf.get('relative_path') + '/uploads/system/touchicon-36.png',
sizes: '36x36',
type: 'image/png',
density: 0.75
}, {
src: nconf.get('relative_path') + '/uploads/system/touchicon-48.png',
sizes: '48x48',
type: 'image/png',
density: 1.0
}, {
src: nconf.get('relative_path') + '/uploads/system/touchicon-72.png',
sizes: '72x72',
type: 'image/png',
density: 1.5
}, {
src: nconf.get('relative_path') + '/uploads/system/touchicon-96.png',
sizes: '96x96',
type: 'image/png',
density: 2.0
}, {
src: nconf.get('relative_path') + '/uploads/system/touchicon-144.png',
sizes: '144x144',
type: 'image/png',
density: 3.0
}, {
src: nconf.get('relative_path') + '/uploads/system/touchicon-192.png',
sizes: '192x192',
type: 'image/png',
density: 4.0
});
}
res.status(200).json(manifest);
---- ServiceWorker.js ----
// a change in the cache name will force update on clients older SW version. Per google say so
var CACHE_NAME = 'v1.00a';
/**
* There must be an easier way to do this but automatic *.js creates session errors
* Not implmented auto *.tpl, *.png etc. yet
**/
var urlsToCache = [
// template caches & safe files
'/assets/language/en-GB/login.json',
'/assets/language/en-GB/user.json',
'/assets/language/en-GB/register.json',
'/assets/vendor/jquery/timeago/locales/jquery.timeago.en.js',
'/assets/templates/alert.tpl',
'/assets/templates/categories.tpl',
'/assets/templates/category.tpl',
'/assets/templates/chats.tpl',
'/assets/templates/chat.tpl',
'/assets/templates/confirm.tpl',
'/assets/templates/footer.tpl',
'/assets/templates/header.tpl',
'/assets/templates/login.tpl',
'/assets/templates/notifications.tpl',
'/assets/templates/outgoing.tpl',
'/assets/templates/popular.tpl',
'/assets/templates/recent.tpl',
'/assets/templates/registerComplete.tpl',
'/assets/templates/register.tpl',
'/assets/templates/reset_code.tpl',
'/assets/templates/reset.tpl',
'/assets/templates/search.tpl',
'/assets/templates/tags.tpl',
'/assets/templates/tag.tpl',
'/assets/templates/topic.tpl',
'/assets/templates/tos.tpl',
'/assets/templates/new_chats.tpl',
'/assets/templates/unread.tpl',
'/assets/templates/users.tpl',
'/assets/templates/partials/topic/badge.tpl',
'/assets/templates/partials/topic/post-editor.tpl',
'/assets/templates/partials/topic/post-menu-list.tpl',
'/assets/templates/partials/topic/post-menu.tpl',
'/assets/templates/partials/topic/post.tpl',
'/assets/templates/partials/topic/quickreply.tpl',
'/assets/templates/partials/topic/reply-button.tpl',
'/assets/templates/partials/topic/sort.tpl',
'/assets/templates/partials/topic/stats.tpl',
'/assets/templates/partials/topic/topic-menu-list.tpl',
'/assets/templates/partials/topic/watch.tpl',
'/assets/templates/partials/noscript/warning.tpl',
'/assets/templates/partials/modals/change_picture_modal.tpl',
'/assets/templates/partials/modals/flag_post_modal.tpl',
'/assets/templates/partials/modals/upload_file_modal.tpl',
'/assets/templates/partials/modals/upload_picture_from_url_modal.tpl',
'/assets/templates/partials/modals/votes_modal.tpl',
'/assets/templates/partials/groups/list.tpl',
'/assets/templates/partials/groups/memberlist.tpl',
'/assets/templates/partials/chats/dropdown.tpl',
'/assets/templates/partials/chats/messages.tpl',
'/assets/templates/partials/chats/message.tpl',
'/assets/templates/partials/chats/recent_room.tpl',
'/assets/templates/partials/chats/user.tpl',
'/assets/templates/partials/category/sort.tpl',
'/assets/templates/partials/category/subcategory.tpl',
'/assets/templates/partials/category/tags.tpl',
'/assets/templates/partials/category/tools.tpl',
'/assets/templates/partials/category/watch.tpl',
'/assets/templates/partials/categories/item.tpl',
'/assets/templates/partials/categories/lastpost.tpl',
'/assets/templates/partials/categories/link.tpl',
'/assets/templates/partials/account/header.tpl',
'/assets/templates/partials/account/menu.tpl',
'/assets/templates/account/edit/email.tpl',
'/assets/templates/account/edit/password.tpl',
'/assets/templates/account/edit/username.tpl',
'/assets/templates/partials/acceptTos.tpl',
'/assets/templates/partials/breadcrumbs.tpl',
'/assets/templates/partials/category_list.tpl',
'/assets/templates/partials/cookie-consent.tpl',
'/assets/templates/partials/delete_posts_modal.tpl',
'/assets/templates/partials/fork_thread_modal.tpl',
'/assets/templates/partials/menu.tpl',
'/assets/templates/partials/move_post_modal.tpl',
'/assets/templates/partials/move_thread_modal.tpl',
'/assets/templates/partials/notifications_list.tpl',
'/assets/templates/partials/paginator.tpl',
'/assets/templates/partials/post_bar.tpl',
'/assets/templates/partials/posts_list.tpl',
'/assets/templates/partials/sos_button.tpl',
'/assets/templates/partials/subscribe.tpl',
'/assets/templates/partials/tags_list.tpl',
'/assets/templates/partials/thread_tools.tpl',
'/assets/templates/partials/topics_list.tpl',
'/assets/templates/partials/users_list_menu.tpl',
'/assets/templates/partials/users_list.tpl',
'/assets/templates/modules/taskbar.tpl',
'/assets/templates/modules/usercard.tpl',
'/assets/templates/groups/details.tpl',
'/assets/templates/groups/list.tpl',
'/assets/templates/groups/members.tpl',
'/assets/templates/account/best.tpl',
'/assets/templates/account/bookmarks.tpl',
'/assets/templates/account/downvoted.tpl',
'/assets/templates/account/edit.tpl',
'/assets/templates/account/followers.tpl',
'/assets/templates/account/following.tpl',
'/assets/templates/account/groups.tpl',
'/assets/templates/account/info.tpl',
'/assets/templates/account/posts.tpl',
'/assets/templates/account/profile.tpl',
'/assets/templates/account/settings.tpl',
'/assets/templates/account/topics.tpl',
'/assets/templates/account/upvoted.tpl',
'/assets/templates/account/watched.tpl',
'/assets/uploads/system/favicon.ico',
'/assets/uploads/system/site-logo.png',
'/assets/uploads/system/touchicon-36.png',
'/assets/uploads/system/touchicon-72.png',
'/assets/uploads/system/touchicon-96.png',
'/assets/uploads/system/touchicon-144.png',
'/assets/uploads/system/touchicon-192.png',
'/assets/uploads/system/touchicon-orig.png',
];
// Install the Service Worker
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME).then(function(cache) {
console.log('[ServiceWorker] Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// Listen to activation event
self.addEventListener('activate', function(e) {
//console.log('[ServiceWorker] Activate');
e.waitUntil(
// Get all cache containers
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
// Check and remove invalid cache containers
if (key !== CACHE_NAME) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
// Enforce immediate scope control
return self.clients.claim();
});
// Fetch from Cache
/**
* Good caching example to follow
* https://github.com/14islands/vecka.14islands.com/blob/master/server/service-worker.js
**/
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
// Cache hit - return response
if (response) {
//console.log('Fetched a cached response: ' + event.request.url);
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response.
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have two streams.
var responseToCache = response.clone();
var requestedURL = event.request.url;
// If; the request is something that causes an error in nodeBB to cache or an error in general; forward to server
if(event.request.method=='POST' || requestedURL.includes('socket') || requestedURL.includes('widgets/render') || requestedURL.includes('admin') ){
return response;
}
// Else; open put the request into the cache and serve from there
caches.open(CACHE_NAME).then(function(cache) {
//console.log('Fetching a response: ' + event.request.url);
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
/**
* Listen to pushsubscriptionchange event which is fired when subscription expires.
* Subscribe again and register the new subscription in the server by sending a POST request with endpoint.
* Real world application would probably use also user identification.
**/
self.addEventListener('pushsubscriptionchange', function(event) {
console.log('[ServiceWorker] Subscription expired');
event.waitUntil(
self.registration.pushManager.subscribe({ userVisibleOnly: true })
.then(function(subscription) {
console.log('[ServiceWorker] Subscribed after expiration', subscription.endpoint);
return fetch('register', {
method: 'post',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
endpoint: subscription.endpoint
})
});
})
);
});
/**
* Push notifications
* Register event listener for the ‘push’ event.
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
**/
self.addEventListener('push', function(event) {
// Retrieve the textual payload from event.data (a PushMessageData object).
var payload = event.data ? event.data.text() : 'no payload';
// Show the Notification
// Keep the service worker alive until the notification is created.
// By checking the payload (string compare), different notifications can be displayed!
event.waitUntil(
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
// Show a notification with title ‘Title’ and use the payload as the body.
// Vibration is on: [Vibrate] [Delay] ... scheme
/**
* This decision to ignore if app is already open, is to send a notification regardless of app state
**/
self.registration.showNotification('SOS', {
body: payload,
icon: '/assets/uploads/system/touchicon-orig.png', // icon: app.user.picture, // !!! make SW cache all user avatars
vibrate: [200, 100, 200, 100], // (...---...) V_200,P_100,V_200,P_100 ...
//tag: 'only_one', // Having a tag here will replace other notifications sent, instead of having different ones appear
actions: [
{action: 'postpone', title: 'Automate Response'},
{action: 'respond', title: 'Open Chat'}
],
})
);
});
/**
* List for when the notification is clicked
* https://developers.google.com/web/fundamentals/engage-and-retain/push-notifications/
**/
self.addEventListener('notificationclick', event => {
var appUrl='';
if (event.action === 'respond') {
// Goto the webchat directly
console.log('[SW] -respond- clicked');
// Grab a certain thing from the server based on action
appUrl = '/';
} else if (event.action === 'postpone') {
// don't open the app, send a premade response to the chatroom
console.log('[SW] -postponed- clicked');
// Grab a certain thing from the server based on action
appUrl = '/';
} else {
// Just open the app.
console.log('[SW] -notification- clicked');
// Grab a certain thing from the server based on action
appUrl = '/';
}
// gather a window, open the appUrl.
event.waitUntil(clients.matchAll({
includeUncontrolled: true,
type: 'window'
}).then( activeClients => {
if (activeClients.length > 0) {
activeClients[0].navigate(appUrl);
activeClients[0].focus();
} else {
clients.openWindow(appUrl);
}
})
);
// Do something with the event
event.notification.close();
});
self.addEventListener('notificationclose', event => {
// Do something with the event
console.log('[SW] notification closed');
});
The tpl is from another theme plugin but the rest is all on the same plugin. And I didn't really do much with plugin.json although I will now. Any other simplification pointers would be great as well.
Other thing in case you didn't already know for your app to be recognized for sw registration first it has to use https, says google. iOS doesn't have much support, safari on mac barely makes an effort to utilize service workers.
Hope I've been helpful