Firebase notification showing twice if on different tab or browser minimised

Viewed 2000

I am using Google's firebase for notifications in browser which is working absolutely fine. I am only facing issues if I subscribe for notifications in particular tab and then I change the tab or minimize the browser I get to see two instances of notifications shown if any is received.

Am not able to figure out any idea about the issue. I am using angular 1.5 and firebase web sdk.

Below is the code for reference.

Firebase service worker:

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

 // Initialize the Firebase app in the service worker by passing in the
 // messagingSenderId.
 if(!firebase.apps.length) {
     firebase.initializeApp({
       'messagingSenderId': 'XXXXXXXXXXXXX'
     });
 }

 // Retrieve an instance of Firebase Messaging so that it can handle background
 // messages.
 const messaging = firebase.messaging();
 function getUniqueId(suffix) {
        var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : r & 0x3 | 0x8;
            return v.toString(16);
        });
        if (typeof(suffix) == "string" || typeof(suffix) == "number") {
            return uuid + ":" + suffix;
        } else {
            return uuid + "";
        }
    }

    function paramterize(url) {
        var data = {};
        data.type = +url.match(/type\=(\d+)/)[1];
        if (data.type === 0 || data.type === 1) {
            data.accept_reject = true;
        }
        if (data.type === 3) {
            data.popup = true;
        }
        if (/conference_id=(\b([a-f0-9]*)\b)/.test(url)) {
            data.conference_id = url.match(/conference_id=(\b([a-f0-9]*)\b)/)[1];
        }
        if (/idp_privilege\=(\b([Y|N])\b)/.test(url) ) {
            data.idp_privilege = url.match(/idp_privilege\=(\b([Y|N])\b)/)[1];
        }
        if (/country_dialer\=(\+\d+)/.test(url) ) {
            data.country_dialer = url.match(/country_dialer\=(\+\d+)/)[1];
        }
        if (/group_id\=(\b([a-f0-9]*)\b)/.test(url)) {
            data.group_id = url.match(/group_id\=(\b([a-f0-9]*)\b)/)[1];
        }
        return data
    }

var setupDone = false;
var landing_url= 'https://pro.nowconfer.com'; 
var storage = {
        setup: function() {
            return new Promise(function(resolve, reject) {                         
                var request = indexedDB.open('nowconferdb',18);
                request.onsuccess = function (e) {
                    setupDone = true;
                    var db = e.target.result;
                    storage.db = db;
                    resolve();
                };
                request.onupgradeneeded = function (e) {
                    storage.db = e.target.result;
                    if (storage.db.objectStoreNames.contains('notifications')) {
                        storage.db.deleteObjectStore('notifications');
                    }
                    storage.db.createObjectStore('notifications', {
                        keyPath: '_id'
                    });
                };
            });
        },

        transaction: function (mode) {
            var trans = storage.db.transaction('notifications', mode);
            return trans.objectStore('notifications');
        },

        add: function (notification) {
            return new Promise(function(resolve, reject) {
                var request = storage.transaction('readwrite').put(notification);
                request.onsuccess = function () {
                     console.log("Successfully stored in database");
                    resolve(notification);
                };
                request.onerror = function() {
                    console.log("Error in storing database");
                    reject();
                };
            });
        }
    };

// If you would like to customize notifications that are received in the
// background (Web app is closed or not in browser focus) then you should
// implement this optional method.
// [START background_handler]
messaging.setBackgroundMessageHandler(function(payload) {


  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  const notificationTitle = 'Background Message Title';
  const notificationOptions = {
    body: 'Background Message body.',
    icon: '/firebase-logo.png'
  };
  return self.registration.showNotification(notificationTitle, {
                            body: 'Background Message body',
                            icon: 'nowconfer_icon_enterprise.png' 
                         });
});

function createDB(){
 if (!setupDone) {
        storage.setup().then(function() {
        console.log("created");
    });
  }
}
self.addEventListener('install', function(event) { event.waitUntil(self.skipWaiting())});

self.addEventListener('push', function(event) {
    console.log("Inside push event");
    if(event.data) {
            console.info('SERVICE-WORKER');
            var data = JSON.parse(event.data.text());
            console.log("data is :"+data);
            data = data.notification;
            if(data.title && (data.title == "Refresh Contacts" || data.title == "You have a group message")) return;
            console.log("notification data is :"+data);
            var notif_title = "Missed notification";
            var notif_body = "This website has been updated.";
            //var notif_icon = "https://www.pushchamp.com/static/images/notif_icon.png";
            var notif_icon = "https://pro.nowconfer.com/images/Nowconfer_Logo.png";
            var promises = [];
            landing_url = data.click_action || landing_url;
            console.log('landing_url :'+landing_url);
            data._id = data.click_action.match(/_id=(\b([a-f0-9]*)\b)/)[1];
            data.time = Date.now();
            if(data.title.indexOf("You have a new message")==-1){
                promises.push(self.registration.showNotification(data.title || notif_title, {
                    body: data.body || notif_body,
                    icon: data.icon || notif_icon,
                    click_action: landing_url               
                }));
            }

            Object.assign(data, paramterize(landing_url));
            self.clients.matchAll().then(function(clients) {
                Array.prototype.slice.call(clients).map(function(client) {
                    return client.postMessage(data);
                });
            });
            if (!setupDone) {
                storage.setup().then(function() {
                    storage.add(data);
                });
            }
            else {
                storage.add(data);
            }

            landing_url = data.landing_url || landing_url;
            return Promise.all(promises);
        }
}); 


   self.addEventListener('install', function(event) { event.waitUntil(self.skipWaiting())});

    self.addEventListener('activate', function(event) { event.waitUntil(self.clients.claim())});

    self.onactivate = function(evt) {
        var result;
        self.clients.claim().then(function(_result) {
          result = _result;
          return self.clients.matchAll();
        });
    };

 self.addEventListener('notificationclick', function(event) {
 console.log('Inside notificationclick landing_url:'+landing_url);
 event.notification.close();
        event.waitUntil(
            clients.matchAll({
                type: "window"
            }).then(function(clientList) {
                if (landing_url!=''){
                    return clients.openWindow(landing_url);
                }
                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(landing_url);
                }
            })
        );

});

Notification controller in Angular: Providing the block where event is heared about notification being pushed

$scope.$on('push:notification', function(event, notifObj) {
            var notif = {};
            //console.log('notification received:'+JSON.stringify(notif["firebase-messaging-msg-data"].notification));
            if(notifObj && notifObj["firebase-messaging-msg-data"] && notifObj["firebase-messaging-msg-data"].notification) {
                notif = notifObj["firebase-messaging-msg-data"].notification;
                notif.type = notifObj.type?notifObj.type:notif.click_action.match(/type\=(\d+)/)[1];
                notif._id = notifObj._id;
                notif.conference_id = notifObj.conference_id;
            }else{
                notif = notifObj;
            }
            switch (notif.type) {
                case 0:
                    console.log("schedule Notify:"+JSON.stringify(notif));
                    save(notif);
                    break;
                case 1:
                    save(notif);
                    break;
                case 2:
                    save(notif);
                    break;
                case 3:
                    save(notif);
                    break;
                /*case 4:
                    me.fetchProfile().then(function() {
                        $rootScope.$broadcast('refresh:profile');
                    });
                    me.getContacts('refresh').then(function() {
                        $rootScope.$broadcast('refresh:contacts');
                    });
                    break;*/
                case 5: // leave grp by others
                     save(notif);
                     me.getGroups('refresh').then(function() {
                         $rootScope.$broadcast('groups-refreshed');
                     });

                     break;
                case 6:
                     //disable group chat for group_id
                     save(notif);
                     me.getGroups('refresh').then(function() {
                         $rootScope.$broadcast('groups-refreshed');
                     });
                    break;
                case 7:
                    //enable only chat
                    me.removeConfPrivilege();
                    $rootScope.$broadcast('remove:conf:privilege');
                    console.log('case 7 is called');
                    break;

                case 11:
                    save(notif);
                    break;
                case 12:
                    save(notif);
                    console.log('Number verified notif:'+JSON.stringify(notif["firebase-messaging-msg-data"].notification));
                    var my_profile = me.getMe();

                    my_profile.mobile_number = notif.newMobileNo;
                    my_profile.num_verified = true;

                    me.setMe(my_profile).then(function(){
                        // console.log("set verified no to true for profile:"+JSON.stringify(my_profile));
                        if($state.current.title == 'profile') {
                            console.log("Reloading as currently in profile");
                            $state.go('root.profile', {}, { reload: true });
                        }
                    });

                    break;
                case 13:
                    save(notif);
                    console.log('Number unverified notif:'+JSON.stringify(notif["firebase-messaging-msg-data"].notification));
                    if(notif.message){
                        ModalService.showModal({
                            templateUrl: absoluteURL('app/layout/alert.html'),
                            controller: 'AlertController',
                            inputs: {
                                text: notif.message
                            }
                        });
                    }


                    var my_profile = me.getMe();
                    console.log("got my profile:"+JSON.stringify(my_profile));

                    my_profile.mobile_number = notif.conference_id;
                    my_profile.num_verified = false;

                    me.setMe(my_profile).then(function(){
                        console.log("set verified no to false for profile:"+JSON.stringify(my_profile));

                        if($state.current.title == "dashboard"){
                            $rootScope.$broadcast('reload:dashboard');
                        }
                    });

                    break;
                case 14:
                    save(notif);
                    // console.log('suggestion notif:'+JSON.stringify(notif));

                    break;
                case 17:
                    save(notif);
                    // console.log('suggestion notif:'+JSON.stringify(notif));

                    break;
                case 404:
                    console.log('404:'+JSON.stringify(notif));
                    sessionservice.destroy();
                    $window.location.reload();
                    break;

                case 'offer':
                    save(notif);
                    break;

                case 'missedVideoCall':
                    save(notif);
                    break;

                case 'missedVideoConf':
                    save(notif);
                    break;

                default:
                    break;
            }
        });

I do not want the complete solution but just if anyone can give me some idea of what might be happening so that i can try fixing myself. Thanks in advance

2 Answers

Do you login your page same id?

I think your page receive same time foreground(foucused window) and background(no focused window).

If i were you, I show small toast message when page receive message at foreground.

And, This will help you. https://firebase.google.com/docs/cloud-messaging/js

The real issue is you will get x no. of notification if x no. of tabs are open.

You can try this document.hidden

document.hidden will be true if the tab is active tab. So in this case the notification will only come on the active tab and rest of the tab can be ignored. You can do something like this:

if (document.hidden) {
    return false;
}
Related