I can't store data from setBackgroundMessageHandler - javascript

I'm using firebase for pushing notification. When I get the notification in the background, I get data. I want to save this data in the local storage, but I get this error
TypeError: Cannot read property 'setItem' of undefined
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');
firebase.initializeApp({
messagingSenderId: '628214501041'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
var notificationTitle = 'Background Message Title';
var notificationOptions = {
body: 'Background Message body.',
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
self.addEventListener('push', function (event) {
var pushData = event.data.json();
try {
if(self.window.localStorage){
self.localStorage.setItem('notificationData' , JSON.stringify(pushData) ) ;
// }
}
catch (err) {
console.log('Push error happened:', err);
}
});

The self variable you're using in your code is not something that is defined by default.
But from your code, you're probably looking for:
if(self.window.localStorage) {
self.window.localStorage.setItem('notificationData' , JSON.stringify(pushData) ) ;
}
So the change there is using self.window.localStorage, instead of self.localStorage in the second line.

Well, you can't access the local storage or the session storage from your service worker because it have no access to the DOM.
You should use CacheAPI
Or for persisting data and access it from your service worker and window instance use:
IndexedDB
This is in the lifecyle of your service worker and can also be accessed by the window object.

Related

Twilio Conversations push notifications with Firebase Cloud Messaging

I'm attempting to set up push notifications using Twilio Conversations and Firebase Cloud Messaging on a Next.js 12 app. The documentation is written with the assumption of using Firebase 8 syntax, but I'm using Firebase 9 in this scenario. I've been struggling to get push notifications to work while the page is open. I have the service worker set up (per Firebase docs) but it doesn't seem to be recognizing that a new message is being received from Twilio in order to actually show the notification.
Docs I've followed:
https://www.twilio.com/docs/conversations/javascript/push-notifications-web
https://firebase.google.com/docs/cloud-messaging/js/client
What I've tried
On my backend, I pass the Push Credential SID when I construct a new ChatGrant:
const chatGrant = new ChatGrant({
pushCredentialSid: process.env.TWILIO_PUSH_CREDENTIAL_SID,
serviceSid: CONVERSATIONS_SID
});
In the frontend, I followed the Twilio documentation to set up Firebase:
init.ts
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import { initializeApp } from "firebase/app";
import { Client } from "#twilio/conversations";
// Omitted
const firebaseConfig = {};
export function getPermission(client: Client) {
const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);
getToken(messaging, { vapidKey:"KEY" })
.then((data) => {
console.log({ data });
client.setPushRegistrationId("fcm", data).catch((error) => {
console.error({ error });
});
onMessage(messaging, (payload) => {
console.log({ payload });
client.handlePushNotification(payload).catch((error) => {
console.error(error);
// test
});
});
})
.catch((error) => {
console.error(error);
// test
});
}
I call getPermission from this file once when the conversation app loads.
// chatClient is stored in a ref so it doesn't recalculate/refetch/reauthorize all the time
const chatClient = useRef(null);
// [Other code]
chatClient.current = new ConversationClient(data.chatAccessToken);
chatClient.current.on("connectionStateChanged", async (state) => {
switch (state) {
case "connected": {
// Only get permission once the chat client is fully set up
getPermission(chatClient.current);
// ..........
And my service worker firebase-messaging-sw.js:
importScripts('https://www.gstatic.com/firebasejs/9.14.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.14.0/firebase-messaging-compat.js');
if (!firebase.apps.length) {
firebase.initializeApp({
// CONFIG GOES HERE
});
}
const messaging = firebase.messaging();
//background notifications will be received here
messaging.onBackgroundMessage(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: '/android-chrome-192x192.png'
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
What's happening
In the service worker, messaging.onBackgroundMessage never appears to be invoked. I don't know where this issue is derived from - is Twilio not passing message info to Firebase? Or is Firebase not listening to when Twilio sends it the information? Has that changed from v8 to v9?
In init.ts, onMessage is never invoked. Same deal here, is Twilio not passing the right information to Firebase, or did I misconfigure something?
I'm not getting any console errors or warnings, and the network tab is not pointing out anything super helpful.
I got this to work by using the example code (from docs) and configuring my Next.js application to compile the TypeScript into JavaScript. This helped a lot: https://github.com/vercel/next.js/issues/33863#issuecomment-1140518693

Accessing Dexie(Indexed db) inside FCM onBackgroundMessageHandler

I am using FCM for push notifications in my web app and everything is working fine foreground and background except the fact that when my browser is closed, I am receiving FCM notifications but unable to process them. I am storing the data from FCM in an Indexed db wrapped by Dexie and my db is not updated with the FCM data values when the browser is itself closed.
However, if my app's tab is closed but the browser is open, things work fine,
I am using the below code to do my task in my firebase service worker :
messaging.onBackgroundMessage(async function (payload) {
console.log('Received background message ', payload);
const notificationTitle = payload.data.title;
const notificationOptions = {
body: payload.data.body,
tag: "notification-1",
};
if(!db){
await initDb()
}
if (payload.data.type == 'DATA') {
await addDataToDb(payload.data.item)
}
self.registration.showNotification(notificationTitle,
notificationOptions);
});
async function initDb() {
console.log("Initializing Dexie")
db = await new Dexie('AppDB').open()
Dexie.getDatabaseNames().then((names) => {
console.log('Db names ' + names)
});
}
DB updating code is all fine as it works as expected when the browser is still running.
Can someone please help me here?

FCM Push notifications arrive twice if the browser is in background

I've set up a simple push notification site, the notifications arrive okay if the browser is in foreground.
The problem begins if the browser is in background: the notification arrives twice, one styled with image and other settings set and the other has only title and body message.
Content of the service worker:
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': '...'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ',
return null;
});
self.addEventListener('install', function (event) {
event.waitUntil(skipWaiting());
});
self.addEventListener('activate', function (event) {
event.waitUntil(clients.claim());
});
self.addEventListener('push', function (event) {
var pushData = event.data.json();
try {
var notificationData = pushData.data;
notificationData.data = JSON.parse(notificationData.data);
console.log(notificationData);
self.registration.showNotification(pushData.notification.title, notificationData);
}
catch (err) {
console.log('Push error happened: ', err);
}
});
Client side js:
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.onMessage(function (payload) {
console.log("notification recieved");
return null;
});
self.addEventListener('push', function (event) {
console.log("window push stuff");
return null;
});
Thanks!
Simplest way to 100% avoid multiple notifications is adding "tag", eg.:
var options = {
body: "text",
tag: "notification-1"
};
self.registration.showNotification("title", options)
The problem can be solved with adding this line to the messaging.setBackgroundMessageHandler event:
self.registration.hideNotification();
This way, the default notification won't show and you have to show your notification in the self.addEventListener event.
It took for me around two weeks for me to understand and solve this issue. Hope that will save time for other people:
Firebase supports two types of push notifications:
Display notifications that FCM SDK handles automatically Data
Data messages, which are handled by the client app
Firebase cloud messaging UI has many advantages like advanced targeting by countries, devices, languages, and more. But it allows sending URLs only inside the data custom field.
Message sent from Firebase Console UI looks this way:
{
notification: {
title: "New Test",
body: "This is test",
},
data: {
url: 'someurl.com',
},
};
The notification comes twice when the service worker handles it and a second time when FCM SDK automatically does it.
I didn't found a way to disable auto handling of notification and in the frontend and used Firebase Functions to send it only as data message:
{
data: {
title: "New Test",
body: "This is test",
url: 'someurl.com',
},
};
So, if you want to pass custom URL to push notification, you will need to send it from your own server or using firebase functions.
This is how I get Firebase Cloud Messaging Notifications to work in Progresse Web Apps on Android Devices with those requirements:
Push notifications should only appear once in the status bar as well
The number of notifications should be highlighted at on the PWA app icon
The notification in the status bar should contain:
A small badge with the app icon
The actual app icon
A noticiation title
A notification body
Open the PWA on tap
firebase-messaging.sw.js
Don't add any cdoe or onBackgroundMessage() handler to this file. My file is nothing more but
//Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here. Other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/8.3.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.3.0/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({
apiKey: 'api-key',
authDomain: 'project-id.firebaseapp.com',
databaseURL: 'https://project-id.firebaseio.com',
projectId: 'project-id',
storageBucket: 'project-id.appspot.com',
messagingSenderId: 'sender-id',
appId: 'app-id',
measurementId: 'G-measurement-id',
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
if (firebase.messaging.isSupported()) {
const messaging = !firebase.apps.length
? firebase.initializeApp(firebaseConfig).messaging()
: firebase.app().messaging();
Server side job to send notifications
The content and structure of the messaging payload is key. Put your notification object into a webpush object. Do not add a data object in order to avoid dubplicate notifications with incomplete content.
Make sure your badge is 24x24px png that contains only white content on a transparent background.
var message = {
webpush: {
notification: {
title: "Let's Join App", // Your message title
body: messageBody, // Your message body
icon: "./img/icons/android-chrome-192x192.png", // Your App icon, up to 512x512px, any color
badge: "./img/icons/badge.png", // Your app badge, 24x24px, white content on transparent background
},
fcmOptions: {
link: "https://www.letsjoin.app", // Your Link
},
},
token,
};
// Send a message to the device corresponding to the provided
// registration token.
await admin
.messaging()
.send(message)
.then((response) => {
// Response is a message ID string.
console.log("Successfully sent message");
})
.catch((error) => {
console.log("Error sending message:", error.errorInfo.message);
});
};

How to pass data to angular app from setBackgroundMessageHandler() when using Firebase in Javascript?

I am using firebase in my angular app to send push notification in web.I implemented messaging.onMessage() for receiving notification when app is in foreground and messaging.setBackgroundMessageHandler() when app is in background.I receive notification without any problem but i need to update values based on the notification is received.I could do update when using messaging.onMessage() but how could we do that when using messaging.setBackgroundMessageHandler().
Even it will be ok if i can store the received value locally and get it in my app.
I did research on this is many places but i couldn't find any solution.
Can some one help me with this?
Thanks!
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-messaging.js');
var config = {
apiKey: "MY_API_KEY",
authDomain: "MY_AUTH_DOMAIN",
databaseURL: "MY_DB_URL",
storageBucket: "URL",
messagingSenderId: "VALID_ID"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
return self.registration.showNotification("Title", {body: 'New notification.',})
});
After searching for days found a Github page that contains a solution for it.
What you can do is get a list of window clients which will return a list of the tabs for your origin and then post a message to each window client. (This code would be in the setBackgroundMessageHandler()).
To get list of pages is:
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true
})
.then((windowClients) => {
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
windowClient.postMessage(data);
}
})
.then(() => {
return registration.showNotification('my notification title');
});
return promiseChain;
Then to receive the message in the page, add a listener like so:
navigator.serviceWorker.addEventListener('message', function(event) {
console.log('Received a message from service worker: ', event.data);
});
Thanks alot guys who contributed in Github.
For more reference go to
https://web-push-book.firebaseapp.com/chapter-05/04-common-notification-patterns/#message-page-from-a-push-event
Hope it will be helpful for you guys.Ping or comment me if you get any doubts in implementing.

Token Metadata in Tokbox

I am trying to create a video chatting service that has multiple user types. Based on these "roles" a user may simply be an observer, or an actual participant in a call. I know via the Tokbox Api (https://tokbox.com/developer/guides/create-token/node/index.html) that you can attach metadata to tokens, but I can't for the life of me decipher how to access them on the client side. See my token creation code:
app.get("/gettoken", function(req, res) {
var sessionId = req.query.sessionid;
var userrole = req.query.role;
var tokenOptions = {};
tokenOptions.role = "publisher";
tokenOptions.data = "role="+userrole;
var token = opentok.generateToken(sessionId, tokenOptions);
res.send({token: token});
})
And my session subscription code that is called when a new subscriber joins the stream:
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'theirCamDiv', {
insertMode: 'append'
});
console.log(??Subscriber Metadata??);
}
});
Thanks in advance for the help!
You can access your token metadata from event.stream.connection.data property, so in your case
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'theirCamDiv', {
insertMode: 'append'
});
console.log(event.stream.connection.data);
}
});

Categories