Handling multiple push notifications when restarting chrome browser - push-notification

I implemented web push notifications using service worker. Now I am facing an issue. Suppose if I push 5 notifications to an endpoint(chrome browser) and assume the chrome process was not active during the push, then when I start the browser again, it is showing all the queued up notifications at once. How to prevent this scenario?
I tried with getNotifications function. But it is returning me empty notifications array. Following is my code.
self.addEventListener('push', function(event) {
if (!(self.Notification && self.Notification.permission === 'granted')) {
return;
}
var data = event.data.json();
var options = {
body: data.notificationText,
icon: 'files/assets/staff.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
onClickUrl: data.onClickUrl,
event_id: data.event_id,
productName: data.product_name
}
};
event.waitUntil(
self.registration.getNotifications().then(function(notifications) {
console.log(notifications);
if (notifications && notifications.length > 0) {
notifications.forEach(function(notification) {
notification.close();
});
}
showNotification(data.title, options);
})
);
});

Related

Service worker show same notification 2 times

I have implemented web psuh notification using FCM. Its working fine at backend. At frontend angular5, I have used service worker to fetch that push notifications. At that function its enter only one time but sometime shows 2 notifications one with icon and other without it.
function showNotification(title, options) {
var notificationOptions = {
'tag': options.tag,
'badge': 'assets/icons/icon-128x128.png',
'icon': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/images/logos/logo-icon.png',
'vibrate': [300, 100, 200, 400, 500],
'data': options.data,
'body': options.body,
'sound': 'default'
};
if(options.data.type === 'media') {
notificationOptions.image = options.data.media;
} else {
notificationOptions.body = options.body;
}
return self.registration.showNotification(title, notificationOptions);
}
I have searched for this bit not get any solution.
Advance thanks. Any help will be appreciated.

Firebase sw.js not respnding after closing browser

I have created firebase project for using Cloud Messaging . until now I have sent a message from backend to client successfully when the browser (chrome) is open but when I close the browser notification not sending until reopening of the browser in below code you can see my firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/5.0.4/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/5.0.4/firebase-messaging.js');
firebase.initializeApp({
messagingSenderId: "My-Sender-Id"
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
var notificationTitle = 'Background Message Title';
var notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
self.addEventListener('push', function(e) {
var data;
if (e.data) {
data = JSON.parse(e.data.text()).data;
} else {
data = {};
}
var title = data.title;
var options = {
body: data.body,
icon: 'assets/custom/img/logo.png',
vibrate: [100, 50, 100],
requireInteraction: true,
data: data.data ? data.data : null,
dir: "rtl",
actions: [{
action: data.action,
title: 'open',
icon: 'images/checkmark.png'
},
{
action: 'close',
title: 'close',
icon: 'images/xmark.png'
},
]
};
e.waitUntil(
self.registration.showNotification(title, options)
);
});
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event.notification.tag);
event.notification.close();
if (event.action != "" && event.action != "close") {
return clients.openWindow(event.action);
}
});
how can I have notification even browser is closed?
I believe you are using service worker for FCM, and the service worker only works when the browser is open ,as service worker is responsible for handling the push notification, when the browser is closed you won't receive any push notification, Actually the push notification will appear only when the browser is opened ,or the application for which you have configured FCM is open on chrome tabs but not focused, there is one work around or you say solution for it , that is make sure the chrome browser is active in the background even after closing chrome browser.
There is option in chrome settings
Goto Settings -> Advanced -> Continue running background apps when Google Chrome is closed make sure you enable it
i have tested the code of FCM , after enabling the option you will get the notification popup.
What you are trying to achieve should be accomplished using a database like firestore. When a notification is received at the time when browser is closed, in the service worker, you should add the code to save the notification message in the firestore so that when user opens the website again, you can retrieve the messages from the firestore and can display them to user. As soon as the browser is opened, any pending notification would be saved to the database by service worker.
I believe this should be the preferred way to achieve this.

Communicate notification data from messaging.setBackgroundMessageHandler to webpage

I am trying to communicate data received by the service worker back to webpage.
On the webpage 'navigator.serviceWorker.controller' is null. The sevice worker has self.client as empty.
Any samples or directions will help
What you can do is get a list of window clients which will return a list of the tabs for your origin and then post a message to each window client. (This code would be in the setBackgroundMessageHandler() ):
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true
})
.then((windowClients) => {
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
windowClient.postMessage(data);
}
})
.then(() => {
return registration.showNotification('my notification title');
});
return promiseChain;
Then to receive the message in the page, add a listener like so:
navigator.serviceWorker.addEventListener('message', function(event) {
console.log('Received a message from service worker: ', event.data);
});

ServiceWorker WindowClient.navigate promise rejected

I'm using Firebase Cloud Messaging + Service worker to handle background push notifications.
When the notification (which contains some data + a URL) is clicked, I want to either:
Focus the window if it's already on the desired URL
Navigate to the URL and focus it if there is already an active tab open
Open a new window to the URL if neither of the above conditions are met
Points 1 and 3 work with the below SW code.
For some reason point #2 isn't working. The client.navigate() promise is being rejected with:
Uncaught (in promise) TypeError: Cannot navigate to URL: http://localhost:4200/tasks/-KMcCHZdQ2YKCgTA4ddd
I thought it might be due to a lack of https, but from my reading it appears as though localhost is whitelisted while developing with SW.
firebase-messaging-sw.js:
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'XXXX'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(payload => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
let notificationData = JSON.parse(payload.data.notification);
const notificationOptions = {
body: notificationData.body,
data: {
clickUrl: notificationData.clickUrl
}
};
return self.registration.showNotification(notificationData.title,
notificationOptions);
});
self.addEventListener('notificationclick', event => {
console.log('[firebase-messaging-sw.js] Notification OnClick: ', event);
// Android doesn’t close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.notification.close();
let validUrls = /localhost:4200/;
let newUrl = event.notification.data.clickUrl || '';
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
event.waitUntil(
clients.matchAll({
includeUncontrolled: true,
type: 'window'
})
.then(windowClients => {
for (let i = 0; i < windowClients.length; i++) {
let client = windowClients[i];
if (validUrls.test(client.url) && 'focus' in client) {
if (endsWith(client.url, newUrl)) {
console.log('URL already open, focusing.');
return client.focus();
} else {
console.log('Navigate to URL and focus', client.url, newUrl);
return client.navigate(newUrl).then(client => client.focus());
}
}
}
if (clients.openWindow) {
console.log('Opening new window', newUrl);
return clients.openWindow(newUrl);
}
})
);
});
The vast majority of my SW code is taken from:
https://gist.github.com/vibgy/0c5f51a8c5756a5c408da214da5aa7b0
I'd recommend leaving out includeUncontrolled: true from your clients.matchAll().
The WindowClient that you're acting on might not have the current service worker as its active service worker. As per item 4 in the specification for WindowClient.navigate():
If the context object’s associated service worker client’s active
service worker is not the context object’s relevant global object’s
service worker, return a promise rejected with a TypeError.
If you can reproduce the issue when you're sure the client is currently controlled by the service worker, then there might be something else going on, but that's what I'd try as a first step.
This worked for me:
1- create an observable and make sure not to call the messaging API before it resolves.
2- register the service worker yourself, and check first if its already registered
3- call event.waitUntil(clients.claim()); in your service worker
private isMessagingInitialized$: Subject<void>;
constructor(private firebaseApp: firebase.app.App) {
navigator.serviceWorker.getRegistration('/').then(registration => {
if (registration) {
// optionally update your service worker to the latest firebase-messaging-sw.js
registration.update().then(() => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
});
}
else {
navigator.serviceWorker.register('firebase-messaging-sw.js', { scope:'/'}).then(
registration => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
}
);
}
});
this.isMessagingInitialized$.subscribe(
() => {
firebase.messaging(this.firebaseApp).usePublicVapidKey('Your public api key');
firebase.messaging(this.firebaseApp).onTokenRefresh(() => {
this.getToken().subscribe((token: string) => {
})
});
firebase.messaging(this.firebaseApp).onMessage((payload: any) => {
});
}
);
}
firebase-messaging-sw.js
self.addEventListener('notificationclick', function (event) {
event.notification.close();
switch (event.action) {
case 'close': {
break;
}
default: {
event.waitUntil(clients.claim());// this
event.waitUntil(clients.matchAll({
includeUncontrolled: true,
type: "window"
}).then(function (clientList) {
...
clientList[i].navigate('you url');
...
}
}
}
}

Is there any way for sending push notifications without appcelaretor cloud services?

I already know how to send push notifications in Titanium with Alloy, the way I do is:
// Require the module
var CloudPush = require('ti.cloudpush');
var deviceToken = null;
// Initialize the module
CloudPush.retrieveDeviceToken({
success: deviceTokenSuccess,
error: deviceTokenError
});
// Enable push notifications for this device
// Save the device token for subsequent API calls
function deviceTokenSuccess(e) {
deviceToken = e.deviceToken;
// alert("--->" + deviceToken);
subscribeToChannel();
}
function deviceTokenError(e) {
alert('Failed to register for push notifications! ' + e.error);
}
// Process incoming push notifications
CloudPush.addEventListener('callback', function (evt) {
alert("Notification received: " + evt.payload);
});
// For this example to work, you need to get the device token. See the previous section.
// You also need an ACS user account.
// Require in the Cloud module
var Cloud = require("ti.cloud");
function loginUser(){
// Log in to ACS
Cloud.Users.login({
login: 'example',
password: 'example'
}, function (e) {
if (e.success) {
alert('Login successful');
} else {
alert('Error:\n' +
((e.error && e.message) || JSON.stringify(e)));
}
});
}
function subscribeToChannel(){
// Subscribe the user and device to the 'test' channel
// Specify the push type as either 'android' for Android or 'ios' for iOS
// Check if logged in:
Cloud.PushNotifications.subscribe({
channel: 'test',
//device_token: 'APA91bHRjGoZLCYKwn-XcCtNLETuf-KRKfT4sMgVE4KgXQgInYfZuYTNrZC7FUMugLs0idzzqtLytrvVJjVzYBzQoc7Q81hEerq0O2vww_tV8mACuUfAi0JRvs7LoufnQZpYLZrb_1rlUsIOEMsPxDs9b_pIRJF5rw',
device_token:deviceToken,
type: Ti.Platform.name == 'android' ? 'android' : 'ios'
}, function (e) {
if (e.success) {
alert('Subscribed');
} else {
alert('Error:\n' +
((e.error && e.message) || JSON.stringify(e)));
}
});
}
function unsubscribeToChannel (){
// Unsubscribes the user and device from the 'test' channel
Cloud.PushNotifications.unsubscribe({
channel: 'test',
device_token: deviceToken
}, function (e) {
if (e.success) {
alert('Unsubscribed');
} else {
alert('Error:\n' +
((e.error && e.message) || JSON.stringify(e)));
}
});
}
loginUser();
However this way is only for sending push notification through https://cloud.appcelerator.com/ such a manually because it is needed you write the alert and push the button in that backend site.
So my question: Is there any way for sending push notification in Titanium from an own server in an "automatically" way?
Thanks in advance for any help.
Yes, it is possible.
How to obtain a device token for push notifications is described in the Titanium docs, here.
To send notifications you have to send the token to your server. The server then sends your notification to Apple Push Notification Services (APNS). See Apple docs. That's not "automatic" but it's a simple task for PHP or any other language - you can find a lot of scripts.
You can also schedule local notifications which might come in handy depending on your case.

Resources