Can't get click_action to work on FCM notifications with web app / PWA
Asked Answered
J

3

7

I'm trying to get my "click_action" to take users to specific URLs on notifications that I'm sending to clients, but whatever I do it either does nothing (desktop) or just opens the PWA (android). The messages are coming through fine (checked in Chrome console) but clicking just doesn't seem to work.

I have the following in my service worker, cribbed from various places including other answers provided on this site:

importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-messaging.js');
// importScripts('/__/firebase/init.js');

/* An empty service worker!  */
self.addEventListener('fetch', function(event) {
  /* An empty fetch handler! */
});

   var firebaseConfig = {
//REDACTED
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);

 const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  // Customize notification here


  notificationTitle = payload.notification.title;
  notificationOptions = {
    body: payload.notification.body,
    icon: payload.notification.icon,
    click_action: payload.notification.click_action
  };

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

self.addEventListener('notificationclick', function(event) {
    let url = event.notification.click_action;
// I've also added a data.click_action field in my JSON notification, and have tried using that
// instead, but that didn't work either

    console.log('On notification click: ', event.notification.tag); 
    event.notification.close(); // Android needs explicit close.
    event.waitUntil(
        clients.matchAll({ includeUncontrolled: true, 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);
            }
        })
    );
});


self.onnotificationclick = function(event) {
  let url = event.notification.click_action;
  console.log('On notification click: ', event.notification.tag);
  event.notification.close();

  // This looks to see if the current is already open and
  // focuses if it is
  event.waitUntil(clients.matchAll({ includeUncontrolled: true, type: 'window' }).then(function(clientList) {
    for (var i = 0; i < clientList.length; i++) {
      var client = clientList[i];
      if (client.url == url && 'focus' in client)
        return client.focus();
    }
    if (clients.openWindow)
      return clients.openWindow(url);
  }));
};

The notifications come through fine on both android (installed PWA) and chrome, and the message payload in the developer console is well formatted and received fine. In the message I'm sending from the server I have a URL with a custom parameter on the end (e.g. https://[domain]/list.php?userid=123) but, as above, clicking on the notification doesn't do anything on windows/chrome, and on the android it opens the PWA successfully but then doesn't go to the URL in the payload, it just goes to wherever the PWA was when last open. The "userid" changes depending on the message trigger.

Sample JSON of message payload:

{data: {…}, from: "xxx", priority: "high", notification: {…}, collapse_key: "do_not_collapse"}
collapse_key: "do_not_collapse"
data: {gcm.notification.badge: "[logo URL]", click_action: "https://[URL]/list.php?userid=33"}
from: "xxx"
notification:
body: "'5' has just been added"
click_action: "https://[URL]/list.php?userid=33"
icon: "https://[logo URL]"
title: "alert "

I also saw something about "webpush": { "fcm_options": { "link": "https://dummypage.com"}} on https://firebase.google.com/docs/cloud-messaging/js/receive but couldn't figure out if that was relevant or needed also.

Am very surprised just providing a URL in the click_action doesn't seem to just do that action when you click the notificaiton! Is anything needed in the service worker at all?!?!

Could one of the problems be that the PWA doesn't update the SW regularly, and so if my code above should work (a big if!) then i just need to wait for the SW to update on the installed android app? If so, is there a way to speed up its updating?!?

Thanks so much in advance for any assistance. Am tying myself in knots here!

Jorgejorgensen answered 4/6, 2020 at 22:8 Comment(0)
E
7

I spent a lot of time looking for a solution for the same problem. Maybe this can help :

if you send notification with firebase messaging, you can use webpush field. firebase messaging client library execute self.registration.showNotification() ... No more need messaging.onBackgroundMessage in your service worker.

// firebabse-coud-function.js

app.messaging().send({
    webpush: {
        notification: {
            title: notification?.title || "Default title",
            icon: notification?.icon || "/icon.png",
            badge: notification?.icon || "/icon.png",
        },
        fcmOptions: {
            link: `${BASE_URL || ""}${notification?.clickAction || "/"}`,
        }
    },
    data: {
        userID: notification.userID,
        link: notification?.clickAction || "/",
    },
    topic
});

Most importantly, in your service worker add a 'notificationclick' event listener before calling firebase.messaging()

so my service worker looks like:

// firebase-messaging-sw.js

// ...

self.addEventListener('notificationclick', function (event) {
    console.debug('SW notification click event', event)
    const url = event.notification?.data?.FCM_MSG?.data?.link;
    // ...
})

const messaging = firebase.messaging();

messaging.onBackgroundMessage(function (payload) {
    // received others messages
})

For me, clicking on the event does not go to the correct url. So i add this:

// background client - service worker
const channel = new BroadcastChannel('sw-messages');

self.addEventListener('notificationclick', function (event) {
  console.debug('SW notification click event', event)
  const url = event.notification?.data?.FCM_MSG?.data?.link;

  channel.postMessage({
    type: 'notification_clicked',
    data: {
      title: event.notification.title,
      clickAction: url
    }
  });

})
// foreground client
const channel = new BroadcastChannel('sw-messages');
channel.addEventListener("message", function (event) {
    // go the page
})

I hope this helps someone.

Expendable answered 18/11, 2020 at 23:27 Comment(3)
const url = event.notification?.data?.FCM_MSG?.data?.link; was just what I needed to get mine working. ThanksCardenas
excuse me, what is the purpose of of the question marks? I've never seen that before. Is it checking for the key before evaluating it? Is this native JS or a package?Armington
@Armington see developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… and for compatibility medium.com/@danwild.io/…Expendable
R
7

This question and other answers seems to be related to the legacy FCM API, not the v1. In those case, I needed the SW to open any url sent by FCM, which is by default not possible because host differs (see here). Also, the notification object as changed, and the url for the webpush config is there now: event.notification.data.FCM_MSG.notification.click_action

So adapting others answers to get the correct field and open the url by only editing the firebase-messaging-sw.js:

importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.10/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({
   ...
})

self.addEventListener('notificationclick', function(event) {
    event.notification.close();
    // fcp_options.link field from the FCM backend service goes there, but as the host differ, it not handled by Firebase JS Client sdk, so custom handling
    if (event.notification && event.notification.data && event.notification.data.FCM_MSG && event.notification.data.FCM_MSG.notification) {
        const url = event.notification.data.FCM_MSG.notification.click_action;
        event.waitUntil(
            self.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 (self.clients.openWindow) {
                    console.log("open window")
                    return self.clients.openWindow(url);
                }
            })
        )
    }
}, false);

const messaging = firebase.messaging();

(register the addEventListener before initializing messaging)

Raucous answered 13/3, 2021 at 12:9 Comment(2)
You can change: if (event.notification && event.notification.data && event.notification.data.FCM_MSG && event.notification.data.FCM_MSG.notification) to: if (event.notification?.data?.FCM_MSG?.notification) for brevityMauney
This doesn't seem to work on firefox on android.. just opens up about:blank, works in android chrome.Mylonite
A
1

Just add addeventlistner notification click event before calling firebase.messaging() Everything will work fine.

importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-messaging.js');


self.onnotificationclick = function(event) {
  console.log('On notification click: ', event.notification.tag);
  event.notification.close();

  // This looks to see if the current is already open and
  // focuses if it is
  event.waitUntil(clients.matchAll({
      type: "window"
  }).then(function(clientList) {
      for (var i = 0; i < clientList.length; i++) {
          var client = clientList[i];
          if (client.url == '/index' && 'focus' in client)
              return client.focus();
      }
      if (clients.openWindow)
          return clients.openWindow('/index');
  }));
};


var firebaseConfig = {
    apiKey: "xcxcxcxcxcxc",
    authDomain: "xcxcxc.firebaseapp.com",
    projectId: "fdsfdsdfdf",
    storageBucket: "dfsdfs",
    messagingSenderId: "sdfsdfsdf",
    appId: "sdfsdfsdfsdfsdfsdf"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);

  const messaging = firebase.messaging();

Antechoir answered 18/4, 2021 at 16:54 Comment(1)
I tried it like this, but still on click the notification on android, it still does not open my PWA :(.Cardoon

© 2022 - 2024 — McMap. All rights reserved.