Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase web Push notifications is triggered twice when using onBackgroundMessage()

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)

notifications

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.

like image 913
Walter Gandarella Avatar asked Mar 18 '21 18:03

Walter Gandarella


4 Answers

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

like image 57
Cornelius Avatar answered Oct 18 '22 08:10

Cornelius


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();

like image 31
Binh Ho Avatar answered Oct 18 '22 06:10

Binh Ho


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

like image 2
Riski Putra Awali Avatar answered Oct 18 '22 07:10

Riski Putra Awali


If you using Firebase Admin to send the message, you can console.log in onBackgroundMessage the FCM will send some data like below.

Payload from firead

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

enter image description here

With that, FCM will not show the notification, because the payload doesn't has property notification.

like image 2
Muhammad Al Faris Avatar answered Oct 18 '22 08:10

Muhammad Al Faris