To communicate service-worker with Angular.js controller - javascript

I'm implementing Push Notifications in my app. I made a service-worker to show the notification in my browser (Chrome).
Now, I need to call a function that it's inside an Angular Controller. I was trying to make an event like this in my service worker.
self.addEventListener('push', function(event) {
event.waitUntil(
fetch(self.CONTENT_URL, {headers: headers})
.then(function(response) {
if (response.status !== 200) {
}
return response.json().then(function(data) {
/* some stuff*/
document.dispatchEvent('myEvent');
return notification;
});
})
);
});
In this event I handle the notification and I'm trying to use an event.
In the controller I wrote the code below
document.addEventListener('myEvent', function(){
console.log("im here");
});
But the browser doesn't show the console.log()
Any ideas to complete this task? Thanks a lot!

Here is what I did for communications between angular (or anything at the window/document side) with Service Worker
Somewhere in your angular app.
if ('serviceWorker' in navigator) {
// ensure service worker is ready
navigator.serviceWorker.ready.then(function (reg) {
// PING to service worker, later we will use this ping to identifies our client.
navigator.serviceWorker.controller.postMessage("ping");
// listening for messages from service worker
navigator.serviceWorker.addEventListener('message', function (event) {
var messageFromSW = event.data;
console.log("message from SW: " + messageFromSW);
// you can also send a stringified JSON and then do a JSON.parse() here.
});
}
}
At the start of your service worker
let angularClient;
self.addEventListener('message', event => {
// if message is a "ping" string,
// we store the client sent the message into angularClient variable
if (event.data == "ping") {
angularClient = event.source;
}
});
When you receive a push
// In your push stuff
self.addEventListener('push', function(event) {
event.waitUntil(
fetch(self.CONTENT_URL, {headers: headers})
.then(function(response) {
if (response.status !== 200) {
}
return response.json().then(function(data) {
/* some stuff*/
angularClient.postMessage('{"data": "you can send a stringified JSON here then parse it on the client"}');
return notification;
});
})
);
});

Related

Service worker activate and push event don't get triggered even though the registration is successful and it's activated

I have built a React progressive web application that makes use of service workers.
The service worker gets registered and is activated:
I have been trying to detect the "activate" event using this:
service-worker.js
navigator.serviceWorker.addEventListener("activate", function (event) {
console.log("service worker activated");
});
I added that at the end of the service-worker file. But, this event never gets triggered and I have no idea why.
I also tried to implement push notifications and trigger the from the backend. For this, I needed a "push" event listener that would listen to these events from the server:
navigator.serviceWorker.addEventListener("push", async function (event) {
const message = await event.data.json();
let { title, description, image } = message;
await event.waitUntil(showPushNotification(title, description, image));
});
This is how showPushNotification is defined:
export function showPushNotification(title, description, image) {
if (!("serviceWorker" in navigator)) {
console.log("Service Worker is not supported in this browser");
return;
}
navigator.serviceWorker.ready.then(function (registration) {
registration.showNotification(title, {
body: description,
icon: image,
actions: [
{
title: "Say hi",
action: "Say hi",
},
],
});
});
}
I tested calling that function manually and it successfully triggerss a push notification.
This is the server code that triggers the push notification:
const sendPushNotification = async (user_id, title, description, image) => {
const search_option = { user: user_id };
const users_subscriptions = await PushNotificationSubscription.find(
search_option
);
const number_of_users_subscriptions = users_subscriptions.length;
const options = {
vapidDetails: {
subject: "mailto:xxxx#xxxx.com",
publicKey: VAPID_PUBLIC_KEY,
privateKey: VAPID_PRIVATE_KEY,
},
};
let push_notif_sending_results = {};
for (let i = 0; i < number_of_users_subscriptions; i++) {
const user_subscription = users_subscriptions[i];
await webPush
.sendNotification(
user_subscription,
JSON.stringify({
title,
description,
image,
}),
options
)
.then((notif_send_result) => {
push_notif_sending_results[i] = { success: notif_send_result };
})
.catch((error) => {
push_notif_sending_results[i] = { error: error };
});
}
return push_notif_sending_results;
};
This is the part responsible for sending the push notification:
webPush
.sendNotification(
user_subscription,
JSON.stringify({
title,
description,
image,
}),
options
)
And it's successfully executed as it returns a 201 HTTP response.
So the "push" event listener is supposed to detect it and trigger a push notification.
I think everything regarding the push notification has been successfully implementing and the problem is how the "push" event listener is added since the "activate" event listener also doesn't work.
So I tried moving the two event listeners here right after the registration of the service worker is successful:
function registerValidSW(swUrl, config) {
navigator.serviceWorker.register(swUrl).then((registration) => {
registration.addEventListener("activate", (event) => {
console.log(
"🚀 ~ file: serviceWorker.js:159 ~ navigator.serviceWorker.register ~ event",
event
);
});
registration.addEventListener("push", async function (event) {
const message = await event.data.json();
let { title, description, image } = message;
await event.waitUntil(
showPushNotification(title, description, image)
);
});
});
}
But, it's still the same result.
Neither the "push" nor the "activate" event listeners get triggered.
Any idea what's going on?
Here's the whole service-worker file:
service-worker.js
import axios from "axios";
const isLocalhost = Boolean(
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets;
return;
}
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
} else {
// Is not localhost. Just register service worker
console.log(
"Is not localhost. Just register a service worker, by calling registerValidSW"
);
registerValidSW(swUrl, config);
}
});
}
}
async function subscribeToPushNotifications(serviceWorkerReg) {
let subscription = await serviceWorkerReg.pushManager.getSubscription();
if (subscription === null) {
const dev_public_vapid_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const prod_public_vapid_key =
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const public_vapid_key = isLocalhost
? dev_public_vapid_key
: prod_public_vapid_key;
subscription = await serviceWorkerReg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: public_vapid_key,
});
axios
.post("/api/push_notif_subscription/subscribe", subscription)
.then((response) => {})
.catch((error) => {});
}
}
export function showPushNotification(title, description, image) {
if (!("serviceWorker" in navigator)) {
console.log("Service Worker is not supported in this browser");
return;
}
navigator.serviceWorker.ready.then(function (registration) {
registration.showNotification(title, {
body: description,
icon: image,
actions: [
{
title: "Say hi",
action: "Say hi",
},
],
});
});
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker.register(swUrl).then((registration) => {
subscribeToPushNotifications(registration);
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (!installingWorker) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been preached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { "Service-Worker": "script" },
}).then((response) => {
// Ensure the service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get("content-type");
if (
response.status === 404 ||
(!!contentType && contentType.indexOf("javascript") === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
console.log("Service worker found, calling registerValidSW");
registerValidSW(swUrl, config);
}
});
}
export function unregister() {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister();
});
}
}
navigator.serviceWorker.addEventListener("activate", function (event) {
console.log("service worker activated");
});
navigator.serviceWorker.addEventListener("push", async function (event) {
const message = await event.data.json();
let { title, description, image } = message;
await event.waitUntil(showPushNotification(title, description, image));
});
The events "push" and "activate" are part of the ServiceWorkerGlobalScope as within the Service Worker API.
Push notifications must be handled within the service worker itself.
Therefore only the service worker can register an "activate" event listener.
The same applies for a "push" listener.
Specially in terms of the "push" listener this makes sense.
The idea of push events is to receive them, even if the main app (in this case the website) has been closed.
The service worker is an exception, as it even runs without the page being loaded.
Therefore move the "push" event into your service worker.
Your code (within the service worker) may look like this:
this.addEventListener("push", async function (event) {
const message = await event.data.json();
let { title, description, image } = message;
await event.waitUntil(showPushNotification(title, description, image));
});
function showPushNotification(title, description, image) {
registration.showNotification(title, {
body: description,
icon: image,
actions: [
{
title: "Say hi",
action: "Say hi",
},
],
});
}
The rest seems fine to me.
Update (Some more explanation)
I took a more careful look at your service-worker.js and it seems it contains general methods for registering the service worker.
As mentioned above the main app and the service worker are two completely separate chunks of code, running in different spaces. So this means everything which is not supposed to run in the service worker itself must be put outside of the service-worker.js. The service worker (in your case) should only contain the code for handling push notifications. It's important that you do not include the "service-worker.js" within your application.
In your case, you may seperate these functions into service-worker-register.js which contain all functions which are for managing the service worker registration but should not be executed within the service worker itself (isLocalhost, register, subscribeToPushNotifications, registerValidSW, checkValidServiceWorker, and unregister). Please note the code snippet from above and make changes accordingly to the code left within the service worker.
MDN has a pretty in depth tutorial on service workers (and there are a lot more) I recommend having a look at:
developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers

Where to put eventlisteners for serviceworker

Basicly I'm trying to accomplish to show a dialog when a new serviceworker version has been detected, and then to have user decide to reload to fetch it. To accomplish this we need to actively set skipWaiting before we reload window.
Here's my action:
onClickHandler = () => {
console.log('on click', 'posting skipWaiting');
navigator.serviceWorker.controller.postMessage('skipWaiting');
};
Here's my attempt to create the eventListener:
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
console.log('worker state', installingWorker.state);
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed.'
);
navigator.serviceWorker.addEventListener('message', event => {
console.log('skip waiting');
if (event.data === 'skipWaiting') {
self.skipWaiting();
}
});
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
The issue is that navigator.serviceWorker.addEventListener('message', event => does not get triggered. Am I declaring the listener wrong?
You are close. In your installed block you can make a check for
navigator.serviceWorker.controller
If this exists it means that the old content will have been purged and the fresh content will have been added to the cache. Its a perfect time to display a message or to force a refresh.
navigator.serviceWorker.register('service-worker.js').then(function (registration) {
$log.debug('The service worker has been registered ', registration);
if(navigator.online) {
toastr.warning('Offline Mode', 'Application Status');
}
// updatefound is fired if service-worker.js changes.
registration.onupdatefound = function () {
// The updatefound event implies that reg.installing is set; see
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
var installingWorker = registration.installing;
installingWorker.onstatechange = function () {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and the fresh content will
// have been added to the cache.
// It's the perfect time to display a "New content is available; please refresh."
// message in the page's interface.
toastr.info('Application has been updated, please refresh this page for the most recent version');
window.location.reload();
});
caches.delete('scope-dynamic').then(function () {
$log.debug('Killed the dynamic cache!');
})
$log.debug('New or updated content is available.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a "Content is cached for offline use." message.
toastr.success('Content is cached for offline use.', 'Application Status');
$log.debug('Content is now available offline!');
}
break;
case 'redundant':
$log.error('The installing service worker became redundant.');
break;
}
};
};
}).catch(function (e) {
$log.error('Error during service worker registration:', e);
});
There is some angular stuff sprinkled in there but that should help you get to where you wanna be.
The problem here is that the code you provided has only defined receiving & sending messages from your client to the service worker.
The below method has defined a message to be sent to your controller (active) service worker.
onClickHandler = () => {
console.log('on click', 'posting skipWaiting');
navigator.serviceWorker.controller.postMessage('skipWaiting');
};
The definition below has added an event listener on your ServiceWorker container to receive any messages sent from the service worker.
navigator.serviceWorker.addEventListener('message', event => {
console.log('skip waiting');
if (event.data === 'skipWaiting') {
self.skipWaiting();
}
});
You now need to define the event handlers from the service worker file to receive & send messages.
In the service worker file, to receive messages from the client:
self.addEventListener('message', function handler (event) {
console.log('skip waiting');
if (event.data === 'skipWaiting') {
self.skipWaiting();
}
});
To send messages from serviceworker to your client:
self.addEventListener('fetch', function(event) {
self.clients.matchAll().then(all => all.map(client => client.postMessage('data from webworker')));
});
You could also send data back from the serviceworker using a MessageChannel. On your client you would have to define something like below:
navigator.serviceWorker
.register(swUrl)
.then(registration => {
var messageChannel = new MessageChannel();
// listener for messages from the ServiceWorker
messageChannel.port1.addEventListener('message', (event) => console.log(event.data));
function sendMessage (message) {
//send message to the active service worker
navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
}
onClickHandler = () => {
sendMessage('skipWaiting');
};
});
I found the below article to be quite helpful.
ServiceWorker, MessageChannel & postMessage by Nicolás Bevacqua

Transfer data between client and serviceWorker

I want to try run websockets in serviceWorker.
I write code for register serviceWorker:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(
GetDataForUser.settings.service_worker + "?version=" + GetDataForUser.settings.service_worker_version,
{scope: './'}
)
.then(() => navigator.serviceWorker
.ready
.then((worker) => {
console.log(worker);
worker.sync.register('syncdata');
})
)
.catch((err) => console.log(err));
navigator.serviceWorker.addEventListener('message', event => {
console.log('++++++++++++++++++++', event.data.msg, event.data.url);
});
}
And write serviceWorker code:
var ws = null;
self.addEventListener('install', (event) => {
console.log('Install');
});
self.addEventListener('activate', (event) => {
console.log('Activate');
ws = new WebSocket('ws://local.ll:8880');
ws.onopen = function() {
console.log("Open");
};
// On message receive
ws.onmessage = function (event) {
console.log("Message receive " + event.data);
console.log(event);
event.ports[0].postMessage({'test': 'This is my response.'});
};
// On error connection
ws.onerror = function (error) {
console.log("Error " + error.message);
};
// On close connection
ws.onclose = function (event) {
if (event.wasClean) {
console.log('clean closed');
} else {
console.log('broken connection');
}
console.log('Code: ' + event.code + ' reason: ' + event.reason);
};
});
self.addEventListener('fetch', (event) => {});
self.addEventListener('message', function (evt) {
console.log('postMessage received', evt.data);
ws.send('121212');
});
I try to send data to serviceWorker next:
navigator.serviceWorker.controller.postMessage({'hello': 'world'});
But receive an error:
Uncaught TypeError: Cannot read property 'postMessage' of null
console.log(navigator.serviceWorker);
ServiceWorkerContainer {controller: null, ready: Promise, oncontrollerchange: null, onmessage: null}
Trying to send message from serviceWorker to client next:
event.ports[0].postMessage({'test': 'This is my response.'});
But event.ports is empty.
Whats wrong?
How to transfer data between client and serviceWorker.
To fix your error of controller being null, you need to add the activate event listener
self.clients.claim()
Without that, when the service worker is activated, the page is not controlled by it until the next navigation. This allows the service worker to take control of the pages as soon as it's active.
But I don't think it's your only problem. Looking at your code I think you're confusing WebSocket and MessageChannel. You seem to try to use WebSocket to send messages between client and service worker where you should be using MessageChannel for that.
For exemple if we want to send messages from the client to the SW and allow the SW to respond to those messages, we create a MessageChannel, and we send a port of this channel allong with the message for the SW to use in his response.
in the client :
var msg = new MessageChannel();
msg.port1.onmessage = function(event){
//Response received from SW
console.log(event.data);
};
// Send message to SW
navigator.serviceWorker.controller.postMessage("hello", [msg_chan.port2]);
in the SW :
self.addEventListener('message', function(event){
//Message received from client
console.log(event.data);
//Send response to client using the port that was sent with the message
event.ports[0].postMessage("world");
});
Correct and worked example, only for understanding, not for production:
https://github.com/Shkarbatov/WebSocketInServiceWorkerJS

Service worker basic setup

I have the following code for a service worker:
'use strict';
// Incrementing CACHE_VERSION will kick off the install event and force previously cached
// resources to be cached again.
const CACHE_VERSION = 1;
let CURRENT_CACHES = {
offline: 'offline-v' + CACHE_VERSION
};
const OFFLINE_URL = 'offline.php';
function createCacheBustedRequest(url) {
let request = new Request(url, {cache: 'reload'});
// See https://fetch.spec.whatwg.org/#concept-request-mode
// This is not yet supported in Chrome as of M48, so we need to explicitly check to see
// if the cache: 'reload' option had any effect.
if ('cache' in request) {
return request;
}
// If {cache: 'reload'} didn't have any effect, append a cache-busting URL parameter instead.
let bustedUrl = new URL(url, self.location.href);
bustedUrl.search += (bustedUrl.search ? '&' : '') + 'cachebust=' + Date.now();
return new Request(bustedUrl);
}
self.addEventListener('install', event => {
event.waitUntil(
// We can't use cache.add() here, since we want OFFLINE_URL to be the cache key, but
// the actual URL we end up requesting might include a cache-busting parameter.
fetch(createCacheBustedRequest(OFFLINE_URL)).then(function(response) {
return caches.open(CURRENT_CACHES.offline).then(function(cache) {
return cache.put(OFFLINE_URL, response);
});
})
);
});
self.addEventListener('activate', event => {
// Delete all caches that aren't named in CURRENT_CACHES.
// While there is only one cache in this example, the same logic will handle the case where
// there are multiple versioned caches.
let expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
return CURRENT_CACHES[key];
});
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (expectedCacheNames.indexOf(cacheName) === -1) {
// If this cache name isn't present in the array of "expected" cache names,
// then delete it.
console.log('Deleting out of date cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
self.addEventListener('fetch', event => {
// We only want to call event.respondWith() if this is a navigation request
// for an HTML page.
// request.mode of 'navigate' is unfortunately not supported in Chrome
// versions older than 49, so we need to include a less precise fallback,
// which checks for a GET request with an Accept: text/html header.
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' &&
event.request.headers.get('accept').includes('text/html'))) {
console.log('Handling fetch event for', event.request.url);
event.respondWith(
fetch(createCacheBustedRequest(event.request.url)).catch(error => {
// The catch is only triggered if fetch() throws an exception, which will most likely
// happen due to the server being unreachable.
// If fetch() returns a valid HTTP response with an response code in the 4xx or 5xx
// range, the catch() will NOT be called. If you need custom handling for 4xx or 5xx
// errors, see https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker/fallback-response
console.log('Fetch failed; returning offline page instead.', error);
return caches.match(OFFLINE_URL);
})
);
}
// If our if() condition is false, then this fetch handler won't intercept the request.
// If there are any other fetch handlers registered, they will get a chance to call
// event.respondWith(). If no fetch handlers call event.respondWith(), the request will be
// handled by the browser as if there were no service worker involvement.
});
How could it be possible to make it in a way so that it does not have to save anything on cache? The webapp in question needs connection at all time. Therefore, the main purpose of this service worker is to qualify for phone installation and to have later on push notifications capabilities.
After some online research, here is the best solution:
sw_install.js
console.log('Started', self);
self.addEventListener('install', function(event) {
self.skipWaiting();
console.log('Installed', event);
});
self.addEventListener('activate', function(event) {
console.log('Activated', event);
});
self.addEventListener('push', function(event) {
console.log('Push message received', event);
// TODO
});
main.js
if ('serviceWorker' in navigator) {
console.log('Service Worker is supported');
navigator.serviceWorker.register('sw_install.js').then(function(reg){
console.log(':^)', reg);
// TODO
}).catch(function(err) {
console.log(':^(', err);
});
}
You can use npm sw-toolbox library (https://www.npmjs.com/package/sw-toolbox).
main.js
if (navigator.serviceWorker){
navigator.serviceWorker.register('/service-worker.js', {scope: './'})
.then(function (registration) {
console.log("sw registered",registration);
})
.catch(function (e) {
console.error("error",e);
})
} else {
console.log('Service Worker is not supported in this browser.')
}
}
service-worker.js
(global => {
'use strict';
// Load the sw-tookbox library.
importScripts('/js/sw-toolbox.js');
var precache_urls = [
'/index.html',
'/img/logo.png',
'/img/main.png'
];
//for debugging only
global.toolbox.options.debug = true;
global.toolbox.router.get('/img/(.*)', self.toolbox.cacheFirst, {
cache: {
name: "Images",
maxEntries: 10
}
});
global.toolbox.router.default = global.toolbox.networkFirst;
global.addEventListener('install', event => event.waitUntil(global.skipWaiting()));
global.addEventListener('activate', event => event.waitUntil(global.clients.claim()));
global.addEventListener('push', event => {
var pushObj = event.data.json();
var pushData = pushObj.data;
// push payload if there, if not make an ajax get call to get it (can use fetch)
var title = pushData && pushData.title;
var body = pushData && pushData.body;
var icon = '/img/logo.png';
event.waitUntil(global.registration.showNotification(title, {
body: body,
icon: icon,
badge: icon
data:pushData
}));
});
global.addEventListener('notificationclick', event => {
event.notification.close();
var url = event.notification.data.url|| '/';
event.waitUntil(
clients.matchAll({
type: 'window'
}).then(windowClients => {
console.log('WindowClients', windowClients);
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
console.log('WindowClient', client);
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow(url);
}
}));
});
})(self);

Google Chrome Push Notification

I am implementing chrome push notification for my website users. Which I am able to do successfully.
I have two question ?
1) how to get the previous subscription id whenever i block the notification from browser setting. I have to remove the subscription id from my backend server
2) whenever i reload the website pushManager.subscribe method is running every time in which i am sending subscription id to server due to which the API is hitting every time with same subscription id
push.js
'use strict';
if ('serviceWorker' in navigator) {
console.log('Service Worker is supported');
navigator.serviceWorker.register('service_worker.js').then(function() {
return navigator.serviceWorker.ready;
}).then(function(reg) {
console.log('Service Worker is ready :^)', reg);
reg.pushManager.subscribe({userVisibleOnly: true}).then(function(sub) {
console.log('endpoint:',JSON.stringify(sub.endpoint));
console.log(sub.endpoint.substring('https://android.googleapis.com/gcm/send/'.length));
});
}).catch(function(error) {
console.log('Service Worker error :^(', error);
});
}
service-worker.js
'use strict';
var myurl;
console.log('Started', self);
self.addEventListener('install', function(event) {
self.skipWaiting();
console.log('Installed', event);
});
self.addEventListener('activate', function(event) {
console.log('Activated', event);
});
self.addEventListener('push', function(event) {
console.log('Push message', event);
event.waitUntil(
fetch('/notify.json').then(function(response) {
return response.json().then(function(data) {
console.log(JSON.stringify(data));
var title = data.title;
var body = data.body;
myurl=data.myurl;
return self.registration.showNotification(title, {
body: body,
icon: 'profile.png',
tag: 'notificationTag'
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var body = 'We were unable to get the information for this push message';
return self.registration.showNotification(title, {
body: body,
icon: 'profile.png',
tag: 'notificationTag'
});
})
);
});
// var title = 'Vcona';
// event.waitUntil(
// self.registration.showNotification(title, {
// 'body': 'School Management',
// 'icon': 'profile.png'
// }));
self.addEventListener('notificationclick', function(event) {
console.log('Notification click: tag', event.notification.tag);
// Android doesn't close the notification when you click it
// See http://crbug.com/463146
event.notification.close();
var url = 'https://demo.innotical.com';
// Check if there's already a tab open with this URL.
// If yes: focus on the tab.
// If no: open a tab with the URL.
event.waitUntil(
clients.matchAll({
type: 'window'
})
.then(function(windowClients) {
console.log('WindowClients', windowClients);
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
console.log('WindowClient', client);
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow(myurl);
}
})
);
});
Best pieces of advice I can give:
Keep track of your subscription (especially what you send to your server) in indexDB. Why IndexDB?
You can update indexDB in the window and in the serviceworker. This is important as you'll first get a PushSubscription in the window, but serviceworker will dispatch pushsubscriptionchange events which you should listen for and attempt to get a new PushSubscription, if you can.
When the page loads, check indexDB for an old subscription, if it exists, compare it to getSubscription() (i.e. your current subscription). This check should include any values you need server side, for example, when browsers go from not supporting payloads, to supporting them, they go from having no keys, to suddenly having keys - so you should check if you server has these keys or not.
DO NOT USE any of the API's for GCM, this will NOT work on other browsers (Firefox, Opera, Samsung Browser + others in the future) and aren't needed.
1) You can't get previous reg id. There are to ways:
Every time you subscribe for notifications you can save it to a local chrome db(for example indexdb) and when you subscribe another time you just restore you previous reg id from this db.
When you send a notification to GCM it responds you with canonical ids and another information about correctness of reg ids, so you can remove invalid one
2) You have to check first if subscription id already exists, then subscribe if not:
if ('serviceWorker' in navigator) {
console.log('Service Worker is supported');
navigator.serviceWorker.register('service_worker.js').then(function() {
return navigator.serviceWorker.ready;
}).then(function(reg) {
console.log('Service Worker is ready :^)', reg);
reg.pushManager.getSubscription().then(function(subscription) {
if(!subscription) {
reg.pushManager.subscribe({userVisibleOnly: true}).then(function(sub) {
console.log('endpoint:',JSON.stringify(sub.endpoint));
console.log(sub.endpoint.substring('https://android.googleapis.com/gcm/send /'.length));
//send new subscription id to server
return;
});
}
});
}).catch(function(error) {
console.log('Service Worker error :^(', error);
});
}

Categories