I'm using FCM API to receive push notifications from browser. The firebase-messaging-sw.js
works as expected and messaging.setBackgroundMessageHandler
fires only once when the web app is in background. However, when the app is in foreground, I'm receiving one notification for each browser tab (if I have the app opened in 3 tabs, I receive 3 notifications). I wonder how should I handle this, since I can't find any reference to this issue. This is the code for FCM messages in the foreground:
import NotificationActionCreators from '../actions/NotificationActionCreators';
import NotificationService from './NotificationService';
import LocalStorageService from './LocalStorageService';
import { FIREBASE_SCRIPT, FCM_URL, FCM_API_KEY, FCM_AUTH_DOMAIN, FCM_PROJECT_ID, FCM_SENDER_ID, PUSH_PUBLIC_KEY } from '../constants/Constants';
class ServiceWorkerService {
constructor() {
this._messaging = null;
this._subscriptionData = null;
}
// This function is called once
init() {
this.loadScript(FIREBASE_SCRIPT, () => this.onFirebaseLoaded());
}
onFirebaseLoaded() {
// Initialize Firebase
let config = {
apiKey: FCM_API_KEY,
authDomain: FCM_AUTH_DOMAIN,
projectId: FCM_PROJECT_ID,
messagingSenderId: FCM_SENDER_ID
};
firebase.initializeApp(config);
this._messaging = firebase.messaging();
this.requestPermission();
// Callback fired if Instance ID token is updated.
this._messaging.onTokenRefresh(() => {
this._messaging.getToken()
.then((refreshedToken) => {
console.log('Token refreshed.');
NotificationActionCreators.unSubscribe(this._subscriptionData).then(() => {
// Indicate that the new Instance ID token has not yet been sent to the
// app server.
this.setTokenSentToServer(false);
// Send Instance ID token to app server.
this.sendTokenToServer(refreshedToken);
}, () => console.log('Error unsubscribing user'));
})
.catch(function(err) {
console.log('Unable to retrieve refreshed token ', err);
});
});
// Handle incoming messages.
// *** THIS IS FIRED ONCE PER TAB ***
this._messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
const data = payload.data;
NotificationActionCreators.notify(data);
});
}
requestPermission() {
console.log('Requesting permission...');
return this._messaging.requestPermission()
.then(() => {
console.log('Notification permission granted.');
this.getToken();
})
.catch(function(err) {
console.log('Unable to get permission to notify.', err);
});
}
getToken() {
// Get Instance ID token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
return this._messaging.getToken()
.then((currentToken) => {
if (currentToken) {
this.sendTokenToServer(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
this.setTokenSentToServer(false);
}
})
.catch(function(err) {
console.log('An error occurred while retrieving token. ', err);
this.setTokenSentToServer(false);
});
}
sendTokenToServer(currentToken) {
const subscriptionData = {
endpoint: FCM_URL + currentToken,
platform: 'Web'
};
if (!this.isTokenSentToServer()) {
console.log('Sending token to server...');
this.updateSubscriptionOnServer(subscriptionData);
} else {
console.log('Token already sent to server so won\'t send it again ' +
'unless it changes');
}
this._subscriptionData = subscriptionData;
}
isTokenSentToServer() {
return LocalStorageService.get('sentToServer') == 1;
}
setTokenSentToServer(sent) {
LocalStorageService.set('sentToServer', sent ? 1 : 0);
}
updateSubscriptionOnServer(subscriptionData) {
if (subscriptionData) {
NotificationActionCreators.subscribe(subscriptionData);
this.setTokenSentToServer(true);
this._subscriptionData = subscriptionData;
} else {
console.log('Not subscribed');
}
}
unSubscribe() {
this.removeSetTokenSentToServer();
return this._messaging.getToken()
.then((currentToken) => {
return this._messaging.deleteToken(currentToken)
.then(() => {
console.log('Token deleted.');
return NotificationActionCreators.unSubscribe(this._subscriptionData);
})
.catch(function(err) {
console.log('Unable to delete token. ', err);
return new Promise(function(resolve, reject) {
reject(error)
});
});
})
.catch(function(err) {
console.log('Error retrieving Instance ID token. ', err);
return new Promise(function(resolve, reject) {
reject(error)
});
});
}
}
removeSetTokenSentToServer() {
LocalStorageService.remove('sentToServer');
}
loadScript = function (url, callback) {
let head = document.getElementsByTagName('head')[0];
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = callback;
// Fire the loading
head.appendChild(script);
}
}
Is there any way to show the notification just for the first tab found?
The only way I've found to achieve this is to check and set a "notification id" variable in the local storage with a setTimeout
with a random time:
this._messaging.onMessage(function(payload) {
const data = payload.data;
// This prevents to show one notification for each tab
setTimeout(() => {
if (localStorage.getItem('lastNotificationId')) != parseInt(data.notId)) {
localStorage.setItem('lastNotificationId', parseInt(data.notId))
NotificationActionCreators.notify(data);
}
}, Math.random() * 1000);
});
The notId
is sent within the push notification and is an identifier for the notification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With