Raise ServiceWorker Push Notification Event - javascript

I'm developing a webApp, and want to send notifications using a ServiceWorker.
I have my sw.js and my main.js. I'm able to register my Service Worker and I'm able to send notifications using a command like:
navigator.serviceWorker.getRegistration().then(function(reg) {
reg.showNotification('Title', {
body: 'body'
});
});
But this is not calling my push event defined in the 'sw.js'.
For reference here is my "sw.js":
self.addEventListener('push', function(event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
const title = 'title test from push';
const options = {
body: 'body test from push.',
icon: 'images/icon.png'
};
event.waitUntil(self.registration.showNotification(title, options));
});
const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
self.registration.showNotification(title, options);
As additional information, in Chrome if I go in the Developer tool => Application Tab => Service Workers, and click on "push" button, the correct push event is raised. But, again, I'm not able to trigger it from my main page.

Related

FCM / How to handle foreground & background notifications?

I use the REST API from Firebase to send notifications to all browsers. I use PHP to send the message and handle the result with the service worker.
The problem I'm facing is that each time I send a notification, I get two notifications in the browser. One corresponds to the notification content send from PHP (foreground notification) and the other one is coming from the service worker (background notification).
While I understand the need for both, I would like to show only one depending on the context (context = browser open or not).
For the sending, I'm using the following array:
$additional["url1"]="https://www.orange.be";
$additional["url2"]="https://www.proximus.be";
$additional["url3"]="https://www.base.be";
$additional = json_encode($additional);
$fields = array(
'to'=>$to,
'notification' => [
'title' => 'new title',
'body' => 'aaa' ,
'color' => "#FF33CC",
'data'=>$additional
],
"priority" => "high",
);
And in the service worker, I have this:
messaging.onBackgroundMessage((payload) => {
//console.log('Messaging:');
//console.log(messaging);
console.log('Payload:');
console.log(payload);
additional = payload.data["gcm.notification.data"];
console.log(additional)
additional = JSON.parse(additional);
console.log(additional);
const notificationTitle = payload.notification["title"]+'(bg)';
const notificationOptions = {
body: payload.notification["body"],
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle,notificationOptions);
});
If I remove the last line (self.registration), I only receive the foreground notification coming from PHP.
How can I detect the context to use either the foreground or the background notification instead of both at the same time?
Thanks
Messages received in background are handled using the onBackgroundMessage and for foreground onMessage.
Background Handler
messaging.onBackgroundMessage((payload) => {
console.log('Background Message received. ', payload);
// ...
});
Foreground Handler
messaging.onMessage((payload) => {
console.log('Message received. ', payload);
// ...
});
I found a solution based on another post here. I removed the notification from the json and passed everything through variables:
$fields = array(
'to'=> $to,
'data'=> [
"title"=> "My title",
"description"=>"My description ",
"link"=>"destination link",
"badge"=>"image url",
"image"=>"image url"],
"priority" => "high",
);
And inside the service worker, I capture the data with payload.data and associate it to the notification options
messaging.onBackgroundMessage((payload) => {
console.log('Messaging / bg');
console.log(messaging);
console.log('Payload:');
console.log(payload);
additional = payload.data;
console.log(additional)
timing = Date.now();
if (typeof additional["timing"] !=="undefined")
{
timing = additional["timing"];
}
const notificationTitle =additional["title"]+'(bg)';
const notificationOptions = {
body: additional["description"],
icon: additional["image"],
image: additional["image"],
badge: additional["badge"],
eventTime: timing,
};
// DISPLAY NOTIFICATION
self.registration.showNotification(notificationTitle,notificationOptions);});

Firebase messaging for web. Multiple tabs notifications

I'm using Firebase Messaging for browser notifications. Problem is when multiple tabs are open. Notification for every tab is shown. Adding tag to options didn't help
firebase.initializeApp(constants.CONFIG);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(({ data }) => {
const options = {
body: data.body,
data: { link: data.link, },
icon: data.icon,
};
const backgroundNotification = self.registration.showNotification(data.title, options);
return backgroundNotification;
});
I need only one notification to be displayed even when many tabs with same app are open
Comment this line of code
const backgroundNotification = self.registration.showNotification(data.title, options);
and use instead
new Notification(notification.title, {
body : notification.body,
tag : notification.title,
icon : notification.icon
});
Note that - Tag is unique, If you have 2 tabs and you are sending a push
with name title: title1 and title1, it will show the push only ones.
Example 3.5:
https://notifications.spec.whatwg.org/

Listening for button click in PWA service worker

I am trying to follow the following example for PWA Push Notifications but have a general question regarding service workers
https://developers.google.com/web/fundamentals/codelabs/push-notifications/
Within my Service Worker, I want to add a listener to a button/anchor tag
My Service Worker is initialised as follows:
if ("serviceWorker" in navigator && "PushManager" in window) {
navigator.serviceWorker
.register("./scripts/my.serviceworker.js", { scope: "/" })
.then(function (swReg) {
console.log("Service Worker Registered: ", swReg);
swRegistration = swReg;
initializeUI();
});
};
And the function initializeUI() needs to contain a listener for the click event so that the user can subscribe/unsubscribe
function initializeUI() {
var pushButton = document.querySelector(".pwa-pushbutton");
pushButton.addEventListener("click", function () {
console.log("Button pushed");
if (isSubscribed) {
// TODO: Unsubscribe user
} else {
subscribeUser();
}
});
But the pushButton variable is not defined.
How can I add a listener event to an element on the DOM within my service worker JS file?
Service Workers cannot listen to nor interact with DOM directly. All DOM activity (like clicks) would need to be sent to Service Workers with postMessage()
App:
function onClick(event => {
navigator.serviceWorker.controller.postMessage({
value1: 'hello',
value2: 'there'
});
})
Service Worker:
self.addEventListener('message', event => {
const val1 = event.data.value1,
val2 = event.data.value2;
});

Firefox TypeError: ServiceWorker script encountered an error during installation

I'm developing web push notification on my website. I follow the Web Push Notifications of Google and The Service Worker Cookbook of Mozilla.
I have tested on the Google Chrome v50+ and everything is working but I will get the error below on Firefox 44, 45, 46, 52, latest Firefox (version 57.0.4 64 bit) when calling navigator.serviceWorker.register('./push-service-worker.js') function.
TypeError: ServiceWorker script at http://localhost:9600/push-service-worker.js for scope http://localhost:9600/ encountered an error during installation.
This is my code:
Register ServiceWorker in controller.js
navigator.serviceWorker.register('push-service-worker.js')
.then((registration) => {
return registration.pushManager.getSubscription()
.then((subscription) => {
if (subscription) {
return subscription;
}
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: buildApplicationServerKey(),
};
return registration.pushManager.subscribe(subscribeOptions);
});
})
.then((subscription) => {
sendSubscriptionToServer(subscription);
})
.catch((err) => {
console.log('Unable to subscribe to push: ', err);
});
push-service-worker.js
'use strict';
self.addEventListener('push', (event) => {
var payload = event.data.json();
var title = payload.title || 'Title';
event.waitUntil(
self.registration.showNotification(title, {
body: payload.body,
icon: './common/images/notification-icon-192x192.png',
image: payload.image || '',
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
var urlToOpen = new URL('/', self.location.origin).href;
event.waitUntil(
clients.matchAll({
type: 'window',
includeUncontrolled: true,
})
.then((windowClients) => {
var matchingClient = null;
for (var i = 0; i < windowClients.length; i++) {
var windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
})
);
});
Directory structure
./root
---- manifest.json
---- push-service-worker.js
---- src
---- controller.js
Thank for helping!
As wanderview said at here:
FWIW, you should always use a separate profile for each channel (release/beta/dev-edition/nightly). We're working on making it work like that out-of-the-box, but its not ready yet.
This problem is encountered when I use one profile for multiple Firefox version. To fix this issue go to about:support and click Refresh Firefox. If it doesn't work, you can go to about:profiles, click Create new profile, and then Launch profile in new browser.
In my case, this was caused by Firefox not being able to access the serviceworker.js file.
The server I was hosting it on had a permissions check based on cookies, and in this case Firefox was not sending the cookie as I believe it was considered a cross-site script.
On the server, I made the serviceworker.js file accessible publicly, and then Firefox could register the service worker.
It was particularly difficult to notice this, because the Firefox Developer Tools did not show the Forbidden response in the Console, nor did it even show any request for serviceworker.js in the Network tab.
Therefore, presumably the TypeError is in fact a generic error and should be read as 'something went wrong registering the Service Worker'.

Chrome push notification - how to open URL adress after click?

I am new to Google Chrome Push notifications and I was just reading some questions and answers here, on stackoverflow and I have ended with this easy push notification javascript.
navigator.serviceWorker.register('sw.js');
function notify() {
Notification.requestPermission(function(result) {
if (result === 'granted') {
navigator.serviceWorker.ready.then(function(registration) {
registration.showNotification('test notification', {
body: 'Hey I am test!',
icon: 'image.png',
});
});
}
});
}
Its just simple notification, but I need open a new window with other webpage after click on notification.
I know it is possible, but I cant find examples using "serviceWorker" syntax.
Please help. Thanks.
I am guessing you are in a Service Worker context, because that's where Push Notifications are received. So you have the self object to add a event listener to, that will react to a click on the notification.
(Place this code in your sw.js file, which is your Service Worker script.)
self.addEventListener('notificationclick', function(event) {
let url = 'https://example.com/some-path/';
event.notification.close(); // Android needs explicit close.
event.waitUntil(
clients.matchAll({type: 'window'}).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
If you want to open website with dynamic URL received from FCM push notification or any other web push notification then
BELOW IS AN EXAMPLE OF SERVICE WORKER USED FOR FCM PUSH NOTIFICATION
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
var notificationTitle = payload.data.title; //or payload.notification or whatever your payload is
var notificationOptions = {
body: payload.data.body,
icon: payload.data.icon,
data: { url:payload.data.click_action }, //the url which we gonna use later
actions: [{action: "open_url", title: "Read Now"}]
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
and handle click event with below code
self.addEventListener('notificationclick', function(event) {
switch(event.action){
case 'open_url':
clients.openWindow(event.notification.data.url); //which we got from above
break;
case 'any_other_action':
clients.openWindow("https://www.example.com");
break;
}
}
, false);
Hope it helps!
(This code refers to firebase messaging) I was also searching for a soluting and the answer was very easy, but there was no doc saying it clearly. You need to put "click_action" = "your url" inside the notification json. Here is an example:
notification: {
title: "Come",
icon: '../../../../assets/logo.png',
vibrate: [300,100,400,100,400,100,400],
body: "some text",
click_action : "your link"
}
Hope it helps.
{
"notification": {
"title": "Hey there",
"body": "Subscribe to might ghost hack youtube channel",
"click_action" : "http://localhost:4200"
},
"to":"YOUR_TOKEN"
}
This worked for me
"#angular/fire": "^6.1.5",
"firebase": "^7.0 || ^8.0"

Categories