I really don't know what is going on with these things. I am using FCM web push normally for my website. I can receive the message on the website if I am in foreground or I receive the notification if I am in the background. So far so good.
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-messaging.js');
firebase.initializeApp({
...
});
const messaging = firebase.messaging();
The problem is that the default configuration of firebase-messaging-sw.js displays a notification in the background showing the chrome icon. I want to be able to customize this notification and display my application icon. Then reading on the web I found that I need to intercept the message with onBackgroundMessage().
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.5/firebase-messaging.js');
firebase.initializeApp({
...
});
const messaging = firebase.messaging();
if (messaging) {
messaging.onBackgroundMessage(payload => {
const notificationTitle = payload.notification.title || payload.data.title;
const notificationOptions = {
body: payload.notification.body || payload.data.subtitle || '',
icon: 'https://firebasestorage.googleapis.com/v0/b/yes-4-web.appspot.com/o/pontonos%2Ficons%2Fandroid-chrome-192x192.png?alt=media&token=35616a6b-5e70-43a0-9284-d780793fa076',
data: payload.data
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(clients.matchAll({ type: "window" }).then(function(clientList) {
for (let i = 0; i < clientList.length; i++) {
const client = clientList[i];
if (client.url === '/' && 'focus' in client) {
if (event.notification.data.route) client.href(event.notification.data.route);
return client.focus();
}
}
if (clients.openWindow)
return clients.openWindow(event.notification.data.route || '/');
}));
});
}
The problem is that now, when using onBackgroundMessage(), two notifications appear to me, the original with the chrome icon and my personalized message with my application icon (see image)
Another problem is that if I click on the original notification, the tab with my website comes to the main focus, but if I click on the personalized notification, a new tab opens with my website.
Probably too late.
I had this issue and the problem, as I eventually figured out, was that I was sending 'notification' and 'data' objects in my payload.
Removing 'notification' and leaving just 'data' solved the problem.
From FCM docs:
Use notification messages when you want FCM to handle displaying a notification on your client app's behalf. Use data messages when you want to process the messages on your client app.
FCM can send a notification message including an optional data payload. In such cases, FCM handles displaying the notification payload, and the client app handles the data payload.
Here's what my payload now looks like:
$payload = [
'message' => [
'token' => $token,
'data' => $msg,
//'notification' => $msg, (this caused the notification to deliver twice)
'webpush'=> [
'fcm_options'=> [
'link' => $link,
'analytics_label' => 'notification_label'
]
]
],
];
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
If you have a custom notification, do not use notification
on POST payload. Change to data
or other keyword instead.
{
"to": "fH7CtR77FgxZsGiwbBwNT5:APA91bGSOeIW7o34lppBErHuCiapYOs5xrj1WOw1IR6gvn2TW3HsEGdyV5yfcyYksauhKCKXTWnbaULukPyJrH34Ht0GsonBt6_gYH9UdN_S3wR6w0ZDLSDo7iPbYO6Wbcyvw_9RTvi3",
"priority": "high",
"data": {
"title": "Title",
"body" : "First Notification",
"text": "Text"
}
}
function onBackgroundMessage() {
const messaging = firebase.messaging();
// [START messaging_on_background_message]
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
const notification = payload.data; -----> here is custom
if (!notification) {
console.warn('[firebase-messaging-sw.js] Unknown notification on message ', payload);
return
}
// Customize notification here
const notificationOptions = {
...notification,
icon: '/img/icons/favicon-32x32.png'
};
self.registration.showNotification(
notification.title,
notificationOptions);
});
}
onBackgroundMessage();
I had the same problem with twice notification. I prefer not to use the Firebase console to test your notification. Use postman/insomnia instead. Your notification was triggered twice because, in the firebase console, they send data notification and only notification. This is my JSON data:
{
"to": "epasJ3fYJG3pjXC8UHCg37:APA91bHEbtPBtRzgEOKGR9Kg9DkZbHLdnMT2uEseQ8AzCSPKv8PkLsm_dkfN_AagFRcVhmOOqBlF9mNXtl7KqelG2g9tbeKTH1_Ey9KAGaNRGKwrIyttH58dP-jAWiGYKxLHu3mgdUMN",
/* here the problem. If you use both of them, you'll get notification twice. Just pick one. Notification key or data key */
"notification": {
"title": "Test title send from insomnia",
"body": "Test body send from insomnia",
"image": "https://mostrans.co.id/CompanyProfile/static/media/logo-mostrans.ff215158.png"
},
"data": {
"title": "Test title send from insomnia",
"body": "Test body send from insomnia",
"image": "https://mostrans.co.id/CompanyProfile/static/media/logo-mostrans.ff215158.png"
}
}
and for click on your customize notification, you can do this
const urlToOpen = new URL("/", self.location.origin).href;
const promiseChain = clients
.matchAll({
type: "window",
includeUncontrolled: true,
})
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
event.waitUntil(promiseChain);
Read more here
If you using Firebase Admin to send the message, you can console.log in onBackgroundMessage the FCM will send some data like below.
The payload include property notification
, from the FCM Documentation
With FCM, you can send two types of messages to clients:
Notification messages, sometimes thought of as "display messages." These are handled by the FCM SDK automatically.
It means, if your data has property notification
it will handle automatically by FCM, you don't need to add a method to show notification in onBackgroundMessage.
if you add method to show the notification in onBackgroundMessage, it is expected to show twice, because the first notif handle automatically by FCM, and the second handle by onBackgroundMessage
.
If you don't want FCM automatically to show the notification, you just need to remove property notification
from the payload.
You can try this :
//Insert to firebase-messaging-sw.js
firebase.initializeApp(firebaseConfig);
class CustomPushEvent extends Event {
constructor(data) {
super('push');
Object.assign(this, data);
this.custom = true;
}
}
/*
* Overrides push notification data, to avoid having 'notification' key and firebase blocking
* the message handler from being called
*/
self.addEventListener('push', (e) => {
// Skip if event is our own custom event
if (e.custom) return;
// Kep old event data to override
const oldData = e.data;
// Create a new event to dispatch, pull values from notification key and put it in data key,
// and then remove notification key
const newEvent = new CustomPushEvent({
data: {
ehheh: oldData.json(),
json() {
const newData = oldData.json();
newData.data = {
...newData.data,
...newData.notification,
};
delete newData.notification;
return newData;
},
},
waitUntil: e.waitUntil.bind(e),
});
// Stop event propagation
e.stopImmediatePropagation();
// Dispatch the new wrapped event
dispatchEvent(newEvent);
});
Source Code Github
The code above will move key in property notification
to data
, so the expected result will be like below
With that, FCM will not show the notification, because the payload doesn't has property notification
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With