Firebase web push notification, on click not working - javascript

From days since I am trying to get it done, but I am totally stuck at this point.
Here is the code from my service worker file
importScripts('https://www.gstatic.com/firebasejs/6.0.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.0.2/firebase-messaging.js');
firebase.initializeApp({
messagingSenderId: "xxxxxxxxxxxx"
});
var messaging = firebase.messaging();
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,
image: payload.data.image,
data: { url:payload.data.openURL }, //the url which we gonna use later
actions: [{action: "open_url", title: "View"}]
};
return event.waitUntil(self.registration.showNotification(notificationTitle,
notificationOptions));
});
self.addEventListener('notificationclick', function(event) {
console.log('event = ',event);
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
switch(event.action){
case 'open_url':
window.open(event.notification.data.url);
break;
case 'any_other_action':
window.open(event.notification.data.url);
break;
}
}, false);
And data is in this format
$data=[
'title' => 'message title',
'body' => 'description body',
'icon' => 'https://i.ytimg.com/vi/gXSyP9ga-ag/hqdefault.jpg',
'image'=>'https://i.ytimg.com/vi/gXSyP9ga-ag/mqdefault.jpg',
'openURL'=>'https://google.com'
];
Now there are many issue.
when push notification body is clicked on mobile, it does not open the url, but only dismisses it (only clicking action button opens link
I did some reading online and found that
event.waitUntil(clients.openWindow(event.notification.data.url));
Does not work with safari and safari iPhone, can someone help me find
out how to implement a click even listener that will work with apple
devices?
Any help would be appreciated

After searching through many solutions I figured out myself. Here's full working example:
// firebase-messaging-sw.js (client side)
importScripts('https://www.gstatic.com/firebasejs/8.1.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.1.2/firebase-messaging.js');
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = '<get your url from event>'
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);
}
})
);
})
firebase.initializeApp({
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: "",
})
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
})
Here is the code for NodeJS side:
var admin = require("firebase-admin");
// This is a specific account key file generated through the firebase UI
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const payload = {
"token": "FCM TOKEN HERE",
"notification": {
"title":"James",
body: '14100000'
},
"webpush": {
"fcm_options": {
"link": "https://google.com"
},
},
};
admin.messaging().send(payload).then(res => {
console.log('SUCCESS ', res);
}).catch(err => {
console.log(err);
}).finally(() => {
process.exit(0);
});
The problem is if I put notificationclick on the bottom, it doesn't fire, really don't know why, then I move it to the top and it works. I can send push notification from server (using firebase-admin), and push notification will be shown (when app is in background), click the notification open the link I want.

You are using data messages, but you need to use notification messages.
See: https://firebase.google.com/docs/cloud-messaging/js/receive
Because data messages don't support fcm_options.link, you are recommended to add a notification payload to all data messages. Alternatively, you can handle notifications using the service worker.
For an explanation of the difference between notification and data messages, see Message types.
This is the JSON payload of a working notification.
The click_action is for handling clicks.
{
"data": {
"badge": "23",
"b": "xxxx",
"t": "yyyy",
"android_channel_id": "com.example.fcm"
},
"from": "11111111111",
"notification": {
"title": "Title",
"body": "Body",
"icon": "https://example.com/icon.png",
"click_action": "https://example.com"
},
"collapse_key": "do_not_collapse"
}

Related

fcm onmessage not triggered

I see this question is repeatedly asked, I went through many posts yet failed to achieve it with 9.6.1
I get a notification on my windows notification bar. What I need is to log the message in the console and later do some action on it.
Requirement: When a message is pushed to the client using the token
from the server. That particular client shall receive the data and
using that data I was thinking to show a custom popup/modal. Is this
achievable using FCM?
below is what I configured on a page.
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
import {getToken,onMessage} from "https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging.js";
import {getMessaging,onBackgroundMessage } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging-sw.js";
console.log(Notification.permission);
if (Notification.permission == 'granted') {
loadFCM();
}
function loadFCM(p){
console.log(p);
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "esdfdsgdsg",
authDomain: "sdgsdgsdg",
projectId: "sgsdgsdgds",
storageBucket: "sgdsgdsgds",
messagingSenderId: "sdgdsgsdg",
appId: "1:sgdgs:web:sgdgdsg",
measurementId: "G-sgdgdsdg"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);
console.log(messaging);
getToken(messaging, { vapidKey: 'sdgdsgsd-sdgdgsdggds' }).then((currentToken) => {
if (currentToken) {
console.log('token to send...'+currentToken);
} else {
// Show permission request UI
console.log('No registration token available. Request permission to generate one.');
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
messaging.setBackgroundMessageHandler(payload => {
console.log(payload);
const title = payload.data.title;
const options = {
body: payload.data.score
};
return self.registration.showNotification(title, options);
});
});
onBackgroundMessage(messaging, (payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
}
</script>
Using postman I send below request. Please tell me what am I doing wrong here.
URL POST : https://fcm.googleapis.com/fcm/send
{
"data": {
"title": "test",
"body": "test",
"click_action": "http://localhost:8001/"
},
"webpush": {
"fcm_options": {
"link": "https://domain.in"
}
},
"notification":{
"title":"mytitle",
"body":"mybody",
"content_available": true },
"to": "dMNS-tergdrgd-PPMcqiCseoE3fLIQYAL9KbCxlnKcN2yQ1VV" }
So the problem here is when ever the your notification is properly arranged / designed i.e. it looks some thing like :
{
"notification": {
"title": "mytitle",
"body": "mybody",
"content_available": true
}
}
It considers it as a notification and onMessage is never called.
So instead send your data in data key only.
In postman try sending this instead :
URL POST : https://fcm.googleapis.com/fcm/send
{
"data": {
"title": "test",
"body": "test",
"click_action": "http://localhost:8001/"
},
"webpush": {
"fcm_options": {
"link": "https://domain.in"
}
},
"to": "dMNS-tergdrgd-PPMcqiCseoE3fLIQYAL9KbCxlnKcN2yQ1VV"
}

FCM Push notifications arrive twice if the browser is in background

I've set up a simple push notification site, the notifications arrive okay if the browser is in foreground.
The problem begins if the browser is in background: the notification arrives twice, one styled with image and other settings set and the other has only title and body message.
Content of the service worker:
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': '...'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ',
return null;
});
self.addEventListener('install', function (event) {
event.waitUntil(skipWaiting());
});
self.addEventListener('activate', function (event) {
event.waitUntil(clients.claim());
});
self.addEventListener('push', function (event) {
var pushData = event.data.json();
try {
var notificationData = pushData.data;
notificationData.data = JSON.parse(notificationData.data);
console.log(notificationData);
self.registration.showNotification(pushData.notification.title, notificationData);
}
catch (err) {
console.log('Push error happened: ', err);
}
});
Client side js:
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.onMessage(function (payload) {
console.log("notification recieved");
return null;
});
self.addEventListener('push', function (event) {
console.log("window push stuff");
return null;
});
Thanks!
Simplest way to 100% avoid multiple notifications is adding "tag", eg.:
var options = {
body: "text",
tag: "notification-1"
};
self.registration.showNotification("title", options)
The problem can be solved with adding this line to the messaging.setBackgroundMessageHandler event:
self.registration.hideNotification();
This way, the default notification won't show and you have to show your notification in the self.addEventListener event.
It took for me around two weeks for me to understand and solve this issue. Hope that will save time for other people:
Firebase supports two types of push notifications:
Display notifications that FCM SDK handles automatically Data
Data messages, which are handled by the client app
Firebase cloud messaging UI has many advantages like advanced targeting by countries, devices, languages, and more. But it allows sending URLs only inside the data custom field.
Message sent from Firebase Console UI looks this way:
{
notification: {
title: "New Test",
body: "This is test",
},
data: {
url: 'someurl.com',
},
};
The notification comes twice when the service worker handles it and a second time when FCM SDK automatically does it.
I didn't found a way to disable auto handling of notification and in the frontend and used Firebase Functions to send it only as data message:
{
data: {
title: "New Test",
body: "This is test",
url: 'someurl.com',
},
};
So, if you want to pass custom URL to push notification, you will need to send it from your own server or using firebase functions.
This is how I get Firebase Cloud Messaging Notifications to work in Progresse Web Apps on Android Devices with those requirements:
Push notifications should only appear once in the status bar as well
The number of notifications should be highlighted at on the PWA app icon
The notification in the status bar should contain:
A small badge with the app icon
The actual app icon
A noticiation title
A notification body
Open the PWA on tap
firebase-messaging.sw.js
Don't add any cdoe or onBackgroundMessage() handler to this file. My file is nothing more but
//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/8.3.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.3.0/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
apiKey: 'api-key',
authDomain: 'project-id.firebaseapp.com',
databaseURL: 'https://project-id.firebaseio.com',
projectId: 'project-id',
storageBucket: 'project-id.appspot.com',
messagingSenderId: 'sender-id',
appId: 'app-id',
measurementId: 'G-measurement-id',
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
if (firebase.messaging.isSupported()) {
const messaging = !firebase.apps.length
? firebase.initializeApp(firebaseConfig).messaging()
: firebase.app().messaging();
Server side job to send notifications
The content and structure of the messaging payload is key. Put your notification object into a webpush object. Do not add a data object in order to avoid dubplicate notifications with incomplete content.
Make sure your badge is 24x24px png that contains only white content on a transparent background.
var message = {
webpush: {
notification: {
title: "Let's Join App", // Your message title
body: messageBody, // Your message body
icon: "./img/icons/android-chrome-192x192.png", // Your App icon, up to 512x512px, any color
badge: "./img/icons/badge.png", // Your app badge, 24x24px, white content on transparent background
},
fcmOptions: {
link: "https://www.letsjoin.app", // Your Link
},
},
token,
};
// Send a message to the device corresponding to the provided
// registration token.
await admin
.messaging()
.send(message)
.then((response) => {
// Response is a message ID string.
console.log("Successfully sent message");
})
.catch((error) => {
console.log("Error sending message:", error.errorInfo.message);
});
};

Fcm serviceworker receive message in background but code not executed

var config = {
messagingSenderId: "18????????2"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
$.ajax({
type: "POST",
dataType: "json",
data: { notificationID: payload.data.notificationID },
url: "https://myapi/somerestservice",
success: function (data) {
console.log("remove notification status : " + data.Status + " - Message : " + data.Message);
}
});
const notificationTitle = payload.notification.title;
const notificationOptions = {
body:payload.notification.body,
};
return self.registration.showNotification(notificationTitle, notificationOptions);
I receive messages in background without any errors and message appear in browser perfectly
My problem is I need to execute some code to delete data from db, but any code I added in setBackgroundMessageHandler not fired when message received
is there any event fired when message received in background
(in foreground I use onMessage and its work nice)
If the notification key is set on the message when you send the HTTP/XMPP request (e.g. from your backend), the message handler registered with setBackgroundMessageHandler will not be called. Firebase will handle the message for you and display it as-is.
If you need to do something when the message is received while you app is in the background, you can instead use the data key on your message; if the notification key is omitted, the handler registered with setBackgroundMessageHandler will be called and you can handle the payload and customize the notification:
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Do you AJAX request here.
// Customize and show your notification
const notificationTitle = payload.data.title,
const notificationOptions = {
body: payload.data.body,
icon: payload.data.icon
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
NB: In addition to displaying the notification yourself, you will also have to manually handle clicking and closing events.
To send the message:
https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{ "data": {
"title": "Hello",
"body": "How are you?",
"icon": "https://..."
},
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}
This part of the official docs can be a bit confusing, but some clarifications about when the two message handlers are called can be found here: https://sentinelstand.com/article/handling-firebase-notification-messages-in-your-web-app

FCM onWrite() function firing twice and sending two notifications?

I am having issues sending multiple FCM notifications in my iOS app.
I am using Firebase and it's Realtime Database Trigger function, and am able to trigger the Firebase code when a new node is added to the database and when the node is updated.
The problem is that the first time the function runs on write, it runs twice and sends two notifications.
When a node is updated it only runs once and sends only 1 notification, which is the expected behavior.
Below is my javascript code and JSON structure of the database write.
Can anyone please shed any light onto why my function might be fired twice?
var functions = require('firebase-functions');
var admin = require('firebase-admin')
var userDeviceToken = ""
var sharedUserID = ""
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://userlocation-aba20.firebaseio.com/"
});
var payloadStart = {
notification: {
title: "Name of App",
body: "Someone has shared a journey with you."
},
};
var options = {
priority: "high"
}
var payloadEnd = {
notification: {
title: "Name of App",
body: "A shared journey has ended."
},
};
exports.newEntry = functions.database.ref('/StartedJourneys/{fireUserID}')
.onWrite(event => {
const original = event.data.val()
console.log(original.SharedWithUserID)
console.log(original.JourneyEnded)
console.log(event.data.changed())
console.log(event.data.exists())
console.log(event.data.previous)
console.log(event.params)
var payload = payloadStart
if (original.JourneyEnded) {
payload = payloadEnd
}
sharedUserID = original.SharedWithUserID
console.log(sharedUserID)
var db = admin.database()
var ref = db.ref('/UserTokens')
return ref.orderByKey().equalTo(sharedUserID).on("child_added", function(snapshot) {
const deviceToken = snapshot.val()
admin.messaging().sendToDevice(deviceToken, payload, options)
.then(function(response) {
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
})
})
"StartedJourneys" : {
"nFQhaMkjDeSHDAZCklzN7LoGGc22" : {
"CurrentLat" : 37.543821,
"CurrentLong" : -122.239187,
"DestinationLat" : 37.5232217,
"DestinationLong" : -122.2520166,
"DestinationName" : "Nob Hill",
"JourneyEnded" : false,
"SharedWithUser" : "Lisa",
"SharedWithUserID" : "mSJoMJPWWBZEnbq8X05BHwrSd2M2"
}
},
EDIT: I have reduced the scope of the question in hope of getting a response. Thanks in advance!
**EDIT: Added screenshot of the 2 console logs triggered by the function. I should only be seeing one of these.
Firebase logs

How to pass data to angular app from setBackgroundMessageHandler() when using Firebase in Javascript?

I am using firebase in my angular app to send push notification in web.I implemented messaging.onMessage() for receiving notification when app is in foreground and messaging.setBackgroundMessageHandler() when app is in background.I receive notification without any problem but i need to update values based on the notification is received.I could do update when using messaging.onMessage() but how could we do that when using messaging.setBackgroundMessageHandler().
Even it will be ok if i can store the received value locally and get it in my app.
I did research on this is many places but i couldn't find any solution.
Can some one help me with this?
Thanks!
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-messaging.js');
var config = {
apiKey: "MY_API_KEY",
authDomain: "MY_AUTH_DOMAIN",
databaseURL: "MY_DB_URL",
storageBucket: "URL",
messagingSenderId: "VALID_ID"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
return self.registration.showNotification("Title", {body: 'New notification.',})
});
After searching for days found a Github page that contains a solution for it.
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()).
To get list of pages is:
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);
});
Thanks alot guys who contributed in Github.
For more reference go to
https://web-push-book.firebaseapp.com/chapter-05/04-common-notification-patterns/#message-page-from-a-push-event
Hope it will be helpful for you guys.Ping or comment me if you get any doubts in implementing.

Categories