Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setBackgroundMessageHandler never fired #71

Closed
wizzar opened this issue Oct 23, 2016 · 28 comments
Closed

setBackgroundMessageHandler never fired #71

wizzar opened this issue Oct 23, 2016 · 28 comments

Comments

@wizzar
Copy link

wizzar commented Oct 23, 2016

The background handler is not working: no matter what I do, the sw always use the regular onMessage, displaying the actual notification I sent instead of the custom one set on the handler.

My code is the exact same as the one in the repo:

messaging.setBackgroundMessageHandler(function(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: '/Content/Img/logo-amezze-120.png'
    };

    return self.registration.showNotification(notificationTitle,
        notificationOptions);
});
@wizzar wizzar changed the title setBackgroundMessageHandler setBackgroundMessageHandler never fired Oct 23, 2016
@wizzar wizzar closed this as completed Oct 23, 2016
@mllrmat
Copy link

mllrmat commented Nov 7, 2016

@wizzar how did you solve this? I have the same problem...

@wizzar
Copy link
Author

wizzar commented Nov 7, 2016

Turns out the problem was in my json. There's a footnote on the documentation that says if your json defines notification property, then the setBackgroundMessageHandler is sort of ignored.

"Note: If you set notification fields in your HTTP or XMPP send request, those values take precedence over any values specified in the service worker."
https://firebase.google.com/docs/cloud-messaging/js/receive

So, this won't trigger the handler:

{
    "to": "abc",
        "notification": {
        "title": "Hello",
        "body": "world"
    }
}

but this will:

{
    "to":  "abc",

    "data": {
        "notification": {
        "title": "Hello",
        "body": "world"
    }
    }
}

This way you can send the notification in the data.

@mllrmat
Copy link

mllrmat commented Nov 7, 2016

Thank you so much!

@alexisvapillon
Copy link

I had the same problem, but not due to the "notification" field.
The problem was the "content_available" field.

This was NOT working (despite the lack of "notification"):

{
   "content_available": true,
   "data": {
      "aaa": "bla bla"
      "bbb": "bli bli"
   }
}

This is working:

{
   "content_available": false,
   "data": {
      "aaa": "bla bla"
      "bbb": "bli bli"
   }
}

Sounds like a bug, since according to the FCM doc this "content_available" flag is only for iOS...
https://firebase.google.com/docs/cloud-messaging/concept-options

@nemphys
Copy link

nemphys commented Feb 21, 2017

I am wondering why the included notification should take precedence over the service worker...
It is not some kind of hard limitation, since the firebase-messaging.js code clearly handles the notification part (it is even easy to understand from the minified code).

The problem is that this "feature" ruins my efforts to use the same messages for different platforms (eg. Web and Android), since I do not want to populate the "click_action" field in the sent message (as it would break the click handling in Android), but I still want to send the user to a page when the notification is clicked on the web.

It would be easy to achieve this if I could eg. include the desired target url in a special field of the data payload and use the service worker to inject it as a click_action in the generated notification...

@UniBrowse
Copy link

@nemphys I have been having the exact same problem. It seems like a catch 22. You must include the "notification" object for IOS to display a notification when the app is not in the foreground. If you include the "notification" object then you cannot customize the "click_action" in a Web app. If you specify a "click_action" as a URL then Android will not open your app when it is clicked (IOS opens your app fine surprisingly). If you are not worried about IOS then you could implement the FirebaseMessagingService in your Android app and remove the "notification" object. You could then use custom data keys and control the code to show the notification yourself in both the Web app and the Android app. Doing this would cause IOS not to show any notifications when your app is not in the foreground though.

I completely agree with you, they should allow you to override the notification code (to set a custom "click_action") even when a "notification"object is specified. Or they should allow for an "android_click_action" which overrides the "click_action" for Android apps.

@nemphys
Copy link

nemphys commented Feb 22, 2017

@UniBrowse Nice to know that I am not alone here :-)
One can either go with the method you propose, or decide to separate the logic for the web and handle the apps (Android & iOS) differently (which is what I think I will eventually do, since it makes more sense to me).
It is a shame though, since it would be really easy to implement something like this in firebase-messaging.js.
People from Google: anybody listening to this (and care to explain why you made this decision?)

PS. I have a feeling that I will go through all the trouble to create a separate message logic server-side for the web app and after a few days they will finally implement it...

@ivanff
Copy link

ivanff commented Mar 15, 2017

+1

@kaziller
Copy link

How can you cutomize the click_action? My messages are sent without the notification property. I tried to add the click_action property to the notificationOptions in my BackgroundMessageHandler like this:

...
const notificationOptions = {
    body: 'Background Message body.',
    icon: '/Content/Img/logo-amezze-120.png'
    click_action: 'http://localhost:30321/'
};
...

But nothing happens when I click on the notification. I read somewhere, that this is not supported. So created an Event Listener, that reacts to a click on the push notification, something like this:

self.addEventListener('notificationclick', 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 == '/' && 'focus' in client)
                return client.focus();
        }
        if (clients.openWindow)
            return clients.openWindow('http://localhost:30321/');
    }));
});

(I got this from here: https://developer.mozilla.org/en-US/docs/Web/API/WindowClient)

By trying this, the clientList is empty. So by clicking on the notification it opens a new window even if the tab, where my application is running, is still open. Anyone have an idea how to fix this?

@gauntface
Copy link

@nemphys this is a known limitation at the moment and something that I've raised as an issue with the FCM team.

@Birowsky
Copy link

@gauntface where do we track progress on this "catch 22" api?

@UniBrowse
Copy link

@Birowsky, This is what I was told when I asked the Firebase team for a solution for the meantime and how I would know if they had changed anything in the future.

_

You can make the web application subscribe to a different topic from the mobile apps instead of subscribing them all in the same topic. By doing this, you can separate the web app from the mobile apps since there is no way to filter the notification messages to just the web apps for now.

We value your feedback and we will keep it in consideration moving forward. Also, you can join our Firebase Google Group mailing list to get notified on any Firebase releases, announcements and community discussions or you can keep an eye out on our release notes for any further updates.

_

@pinturic
Copy link

pinturic commented Sep 13, 2017

@kaziller you should accept clients in a service worker like this: https://developer.mozilla.org/en-US/docs/Web/API/Clients

@ivanseidel
Copy link

ivanseidel commented Mar 7, 2018

Okay, this is what we call 'GAMBIARRA' in Brazil, (or, workaround for fancy people). It works in Chrome, and Firefox, didn't tested in other browsers...

Make sure to call this BEFORE setBackgroundMessageHandler. It will basically bypass firebase code at this line (https://github.com/firebase/firebase-js-sdk/blob/master/packages/messaging/src/controllers/sw-controller.ts#L212)

Then, you can access the 'notification' data as '_notification' inside the handler

/*
 * We need a generic class to hold data and methods, that inherit from Event
 */
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
  let oldData = e.data

  // Create a new event to dispatch
  let newEvent = new CustomPushEvent({
    data: {
      json() {
        let newData = oldData.json()
        newData._notification = newData.notification
        delete newData.notification
        return newData
      },
    },

    waitUntil: e.waitUntil.bind(e),
  })

  // Stop event propagation
  e.stopImmediatePropagation()

  // Dispatch the new wrapped event
  dispatchEvent(newEvent)
})

@danrasmuson
Copy link

This should be better documented. It's not intuitive and definitely a gotcha.

@marcosbrigante
Copy link

I converted @ivanseidel solution (btw thank you) to old good javascript, and I'm posting here to help someone who needs it:

/*
* Overrides push notification data, to avoid having 'notification' key and firebase blocking
* the message handler from being called
*/
self.addEventListener('push', function (e) {
  // Skip if event is our own custom event
  if (e.custom) return;

  // Create a new event to dispatch
  var newEvent = new Event('push');
  newEvent.waitUntil = e.waitUntil.bind(e);
  newEvent.data = {
     json: function() {
         var newData = e.data.json();
         newData._notification = newData.notification;
         delete newData.notification;
         return newData;
     },
  };     
  newEvent.custom = true;          

  // Stop event propagation
  e.stopImmediatePropagation();

  // Dispatch the new wrapped event
  dispatchEvent(newEvent);
});

@ee92
Copy link

ee92 commented Jul 5, 2018

@marcosbrigante your solution works but causes the notification to display 4 times (original, new, original, new) any idea why that is?

@mpinkard
Copy link

mpinkard commented Nov 7, 2018

@gauntface Is there any progress on resolving this issue? Specifically, on resolving the fact that setBackgroundMessageHandler is not called when the notification object is specified and notificationclick events are bypassed when the notification object click_action is specified?

@tanzeelrana
Copy link

To open an existing tab this worked for me :

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(clients.matchAll({
    type: "window"
  }).then(function (clientList) {
    
    for (var i = 0; i < clientList.length; i++) {
        var client = clientList[i];
        if (client.url == event.notification.data && 'focus' in client)
            return client.focus();
    }
    if (clients.openWindow)
        return clients.openWindow(event.notification.data);
  }));
});

@BharadhanVM
Copy link

How can you cutomize the click_action? My messages are sent without the notification property. I tried to add the click_action property to the notificationOptions in my BackgroundMessageHandler like this:

...
const notificationOptions = {
    body: 'Background Message body.',
    icon: '/Content/Img/logo-amezze-120.png'
    click_action: 'http://localhost:30321/'
};
...

But nothing happens when I click on the notification. I read somewhere, that this is not supported. So created an Event Listener, that reacts to a click on the push notification, something like this:

self.addEventListener('notificationclick', 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 == '/' && 'focus' in client)
                return client.focus();
        }
        if (clients.openWindow)
            return clients.openWindow('http://localhost:30321/');
    }));
});

(I got this from here: https://developer.mozilla.org/en-US/docs/Web/API/WindowClient)

By trying this, the clientList is empty. So by clicking on the notification it opens a new window even if the tab, where my application is running, is still open. Anyone have an idea how to fix this?

can you help me on get this working please

@simonh1000
Copy link

@marcosbrigante I am seeing that your solution works but causes the notification to display 2 times: original & new

@YeetOrBeYate
Copy link

@kaziller you should accept clients in a service worker like this: https://developer.mozilla.org/en-US/docs/Web/API/Clients

This was very helpful thank you

@Suketu-Patel
Copy link

Suketu-Patel commented Dec 24, 2020

I used ivanseidel's code, and I was facing the issue of duplicating notifications. I did some tinkering with the code and now it works as expected. Hope this helps everyone.
A life-saving GAMBIARRA!!


firebase.initializeApp({
...
});

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
    let 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
    let newEvent = new CustomPushEvent({
        data: {
            ehheh: oldData.json(),
            json() {
                let 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)
})
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
    const notificationTitle = payload.data.title;
    const notificationOptions = {
        body: payload.data.body,
        icon: payload.data.icon
    };

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

self.addEventListener('notificationclick', function (event) {
    self.clients.openWindow('https://github.com/Suketu-Patel')
    //close notification after click
    event.notification.close()
})

@brunos3d
Copy link

brunos3d commented May 11, 2021

I used ivanseidel's code, and I was facing the issue of duplicating notifications. I did some tinkering with the code and now it works as expected. Hope this helps everyone.
A life-saving GAMBIARRA!!

Wonderful, I had the audacity to modify a little and create a repository to collaborate with the evolution of this gambiarra!

https://github.com/BrunoS3D/firebase-messaging-sw.js

https://github.com/BrunoS3D/firebase-messaging-sw.js/blob/main/firebase-messaging-sw.js

@MuhammadAhmedHassan
Copy link

If you are using react native and @react-native-firebase/messaging.
In index.js file

import messaging from '@react-native-firebase/messaging';
/* 
Set a message handler function which is called when
the app is in the  background or terminated.
In Android, a headless task is created, allowing 
you to access the React Native environment to 
perform tasks such as updating local storage,
or sending a network request.

This method must be called outside of your application
lifecycle, e.g.  alongside your AppRegistry.registerComponent()
method call at the the entry point of your application code.
*/
messaging().setBackgroundMessageHandler(async remoteMessage => {
  // you can update localstorage or send an api request here
});

@TitanArmy
Copy link

TitanArmy commented Jan 20, 2023

could you provide me your index.js (hide your necessary part after that send me here it will very helpfull for me) because i am using RNVoip call with fcm so in my backgroundhandler it will not working properly (like ringtone are not playing and some point to navigate to anthor screen when app is in kill mode)

@MuhammadAhmedHassan
Copy link

@TitanArmy Right now, I'm not using this callback in my code it's there so that I don't get any error.
There is another package @notifee/react-native that is used for local notifications and other stuff, I'm handling background events/messages using that.

Currently the implementation is something like this:

image
image

@hakimov-dev
Copy link

hakimov-dev commented Apr 10, 2023

Turns out the problem was in my json. There's a footnote on the documentation that says if your json defines notification property, then the setBackgroundMessageHandler is sort of ignored.

"Note: If you set notification fields in your HTTP or XMPP send request, those values take precedence over any values specified in the service worker." https://firebase.google.com/docs/cloud-messaging/js/receive

So, this won't trigger the handler:

{
    "to": "abc",
        "notification": {
        "title": "Hello",
        "body": "world"
    }
}

but this will:

{
    "to":  "abc",

    "data": {
        "notification": {
        "title": "Hello",
        "body": "world"
    }
    }
}

This way you can send the notification in the data.

Which json ? And how did u disable caching in js ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests