Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use notification actions with Firebase Messaging Web SDK

How do I use notification actions with the Firebase Messaging SDK on the web?

like image 955
Matt Gaunt Avatar asked Jul 10 '17 07:07

Matt Gaunt


People also ask

How do you handle notifications when an app is in the background in Firebase web?

There are two types of messages in FCM (Firebase Cloud Messaging): Display Messages: These messages trigger the onMessageReceived() callback only when your app is in foreground. Data Messages: Theses messages trigger the onMessageReceived() callback even if your app is in foreground/background/killed.

How do I get notifications from Firebase?

To get notification data when your app in the background, you should add click_action inside notification payload. "Put that intent-filter on your manifest, inside application tag." you can't put intent-filter inside application tag.


Video Answer


2 Answers

There are a few common pitfalls people hit when attempting this.

  1. Firebase Notifications - There is a feature of the Firebase Messaging SD K's none as "Firebase Notifications". When you send a push message to a Firebase Instance-ID (IID) token, you can use a "notification" key which the SDK's will look for and if found, construct a notification for you. The benefit of this is that you have to write no code to show a notification. The downside is that it can be restrictive if you want to do anything complex or perform work on the device once the notification is received. So to use actions, you MUST NOT USE THIS. Instead call the FCM API with the IID token and a "data" payload.
  2. Data Payload - The data payload has a restriction where it can only be key value pairs, where the value must be a string, i.e. no arrays. What this means is that you can't just send an array of actions and construct a notification with that. The way around this is to create a JSON string, send that to the FCM API and then parse and use the JSON on the device.

Time for an example.

Calling the FCM API

The format of your payload should be something like this:

{
  "data": {
    "some-data": "Im a string",
    "some-other-data": "Im also a string",
    "json-data": "{\"actions\": [{\"action\":\"yes\", \"title\":\"Yes\"},{\"action\":\"no\",\"title\":\"No\"}]}"
  },
  "to": "YOUR-IID-TOKEN"
}

You can send this with curl like so:

curl -X POST -H "Authorization: key=YOUR-SERVER-KEY" -H "Content-Type: application/json" -d '{
  "data": {
    "some-data": "Im a string",
    "some-other-data": "Im also a string",
    "json-data": "{\"actions\": [{\"action\":\"yes\", \"title\":\"Yes\"},{\"action\":\"no\",\"title\":\"No\"}]}"
  },
  "to": "YOUR-IID-TOKEN"
}' "https://fcm.googleapis.com/fcm/send"

With that you'll be able to get the data in the onBackgroundMessage callback in your service worker.

Receiving the Payload on the Device

In a service worker we could have the following code:

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('Message received: ', payload);
});

Which would print out the following in the console:

DevTools Printing Payload

Notice the JSON data is still just a string, not an object.

Next up we can parse the JSON data and check its the right format to use as our notification actions.

We can change our code to the following:

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('Message received: ', payload);
  const parsedJSON = JSON.parse(payload.data['json-data']);
  console.log('Actions:', parsedJSON);
});

This will give the following log:

enter image description here

With this, we can finally create our notification with the following code:

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('Message received: ', payload);
  const parsedJSON = JSON.parse(payload.data['json-data']);
  console.log('Actions:', parsedJSON);

  // Customize notification here
  const notificationTitle = 'Actions Title';
  const notificationOptions = {
    body: 'Actions body.',
    actions: parsedJSON.actions,
  };

  return self.registration.showNotification(notificationTitle,
      notificationOptions);
});

Now you should have a notification with actions:

enter image description here

Testing

As Meggin as pointed out in the comments, it's not obvious how to test it, so a few guiding principles.

The biggest pain point is that if your web server sets a cache header for you service worker file, it won't update between refreshes, one way to fix this it to open your service worker file in a new tab and refresh that page until your service worker is up to date (This is viewing the actual source code of your service worker). Then when you refresh your web page your service worker will be the latest one and you can tell it's updated by the number next to the service worker incrementing.

Alternatively, just unregister the service worker the service worker and refresh the page - this should give you the latest service worker.

To test your notification, you'll need to click a tab that is for a different web page before sending a push message.

The reason for this is that if the user is currently on one of your pages, the push message is sent to the pages onMessage() callback instead of the onBackgroundMessage() callback.

like image 146
Matt Gaunt Avatar answered Oct 09 '22 03:10

Matt Gaunt


Following Matt's advice, I was able to get a proper notification with content from my firebase function passed into my service worker (including actions), but I had to pass all of my data through the one json object, otherwise it wouldn't work for me.

Here's what my firebase functions code looks like:

function sendPayload(tokenArray) {

  const payload = {
    "data": {
      "jsondata": "{\"body\":\"Meggin needs help\", \"title\":\"Can you help her make the code work?\",\"actions\": [{\"action\":\"yes\", \"title\":\"Yes\"},{\"action\":\"no\",\"title\":\"No\"}]}"
    }
  };

  admin.messaging().sendToDevice(tokenArray, payload)
    .then(function(response) {
      // See the MessagingDevicesResponse reference documentation for
      // the contents of response.
      console.log("Successfully sent message:", response);
    })
    .catch(function(error) {
      console.log("Error sending message:", error);
    });
}

And here's what my code looks like in my service worker:

messaging.setBackgroundMessageHandler(function(payload) {

  console.log('Payload received: ', payload);

  const parsedJSON = JSON.parse(payload.data.jsondata);

  console.log("What does actions look like? " + parsedJSON.actions);
  console.log("What does title look like? " + parsedJSON.title);

  const notificationTitle = parsedJSON.title;
  const parsedBody = parsedJSON.body;
  const parsedActions = parsedJSON.actions;

  // Customize notification here
  const notificationOptions = {
    body: parsedBody,
    actions: parsedActions,
  };

  return self.registration.showNotification(notificationTitle, notificationOptions);
});

It's worth noting that one major hurdle that helped me get passed this is understanding how to test push notifications and service workers!

You actually can't see my notification unless the browser is closed, so obviously, you can't watch the console.

But then once you've pushed the notification, you go into the console, and change the file at the top of console to be the service worker file specifically.

And then you can see the console logs!

I realize this might seem obvious to many people, but it wasn't to me, and it's crucial to understanding how to parse the payload and get it to do what you want!

like image 42
Meggin Kearney Avatar answered Oct 09 '22 04:10

Meggin Kearney