I see this question is repeatedly asked, I went through many posts yet failed to achieve it with 9.6.1
I get a notification on my windows notification bar. What I need is to log the message in the console and later do some action on it.
Requirement: When a message is pushed to the client using the token
from the server. That particular client shall receive the data and
using that data I was thinking to show a custom popup/modal. Is this
achievable using FCM?
below is what I configured on a page.
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
import {getToken,onMessage} from "https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging.js";
import {getMessaging,onBackgroundMessage } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging-sw.js";
console.log(Notification.permission);
if (Notification.permission == 'granted') {
loadFCM();
}
function loadFCM(p){
console.log(p);
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "esdfdsgdsg",
authDomain: "sdgsdgsdg",
projectId: "sgsdgsdgds",
storageBucket: "sgdsgdsgds",
messagingSenderId: "sdgdsgsdg",
appId: "1:sgdgs:web:sgdgdsg",
measurementId: "G-sgdgdsdg"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);
console.log(messaging);
getToken(messaging, { vapidKey: 'sdgdsgsd-sdgdgsdggds' }).then((currentToken) => {
if (currentToken) {
console.log('token to send...'+currentToken);
} else {
// Show permission request UI
console.log('No registration token available. Request permission to generate one.');
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
messaging.setBackgroundMessageHandler(payload => {
console.log(payload);
const title = payload.data.title;
const options = {
body: payload.data.score
};
return self.registration.showNotification(title, options);
});
});
onBackgroundMessage(messaging, (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: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
}
</script>
Using postman I send below request. Please tell me what am I doing wrong here.
URL POST : https://fcm.googleapis.com/fcm/send
{
"data": {
"title": "test",
"body": "test",
"click_action": "http://localhost:8001/"
},
"webpush": {
"fcm_options": {
"link": "https://domain.in"
}
},
"notification":{
"title":"mytitle",
"body":"mybody",
"content_available": true },
"to": "dMNS-tergdrgd-PPMcqiCseoE3fLIQYAL9KbCxlnKcN2yQ1VV" }
So the problem here is when ever the your notification is properly arranged / designed i.e. it looks some thing like :
{
"notification": {
"title": "mytitle",
"body": "mybody",
"content_available": true
}
}
It considers it as a notification and onMessage is never called.
So instead send your data in data key only.
In postman try sending this instead :
URL POST : https://fcm.googleapis.com/fcm/send
{
"data": {
"title": "test",
"body": "test",
"click_action": "http://localhost:8001/"
},
"webpush": {
"fcm_options": {
"link": "https://domain.in"
}
},
"to": "dMNS-tergdrgd-PPMcqiCseoE3fLIQYAL9KbCxlnKcN2yQ1VV"
}
Related
I'm trying to send notifications based on business logic that runs (on nodejs) on my server via a cron.
Issue
Notifications aren't appearing on the device.
Description
I'm using the firebase admin node package.
My code looks something like this
import admin from "firebase-admin";
import serviceAccount from "../../firebase-admin.json" assert { type: 'json' };
import { getMessaging } from 'firebase-admin/messaging';
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
...
console.log(message);
await getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
My log output is something like this
{
notification: {
title: 'This is a string',
body: 'This is another string'
},
token: 'aLphaNumeric:reallyLongAlphaNumericWithDashesAndUnderscores'
}
Successfully sent message: projects/<project-name>/messages/<id>
Everything I'm seeing suggests this should be sent!
sendMulticast and the Admin FCM APIs allow you to multicast a message to a list of device registration tokens. You can specify up to 500 device registration tokens per invocation.
sendMulticast take 2 arguments as input, 1st one is notification which contains the title and body of the message.
The other argument is fcmTokens with type array, so you must pass that argument as array even though there is only one fcmToken
//Import the file where you have imported the service file.
const adminApp = require("../firebase/firebaseConfig");
const notificationToAll = (title, body, tokens) => {
var notibody = {
notification: {
title: title,
body: body,
},
tokens: tokens,
};
return new Promise((resolve, reject) => {
adminApp
.messaging()
.sendMulticast(notibody)
.then((response) => {
console.log(response.responses);
if (response.responses[0].error != undefined) {
console.log(JSON.stringify(response.responses[0].error));
}
resolve(response);
})
.catch((error) => {
console.log(JSON.stringify(error));
reject(error);
});
});
};
module.exports = notificationToAll;
app.js
const notificationToAll = require("./helper/notification");
notificationToAll(
"This is a string",
`This is another string`,
["aLphaNumeric:reallyLongAlphaNumericWithDashesAndUnderscores"]
)
This is tested code and working in a live environment.
As the title suggests, in my Nuxt webapp using Firebase auth in SSR mode, I want to have the following behaviour :
As a client, by clicking on logout button from my profile space, I want to keep this state even if I reload the page.
But for the moment it seems to be that I'm only log-out from the client side (by clearing cookies, vuex store) but on server side User data/session looks still alive, then on page refresh with F5 the vuex store is set-up again, even if we add 'gates' to prevent it ! (check cookies from req object, get local user from res object).
I think something is missing in my config, so can someone tell me what is it ?
=> Here the nuxt.config.js file :
// ...
firebase: {
config: {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: `${process.env.FIREBASE_PROJECT_ID}.firebaseapp.com`,
databaseURL: process.env.FIREBASE_DATABASE_URL,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: `${process.env.FIREBASE_PROJECT_ID}.appspot.com`,
messagingSenderId: process.env.FIREBASE_SENDER_ID,
appId: process.env.FIREBASE_APP_ID,
},
services: {
auth:
{
ssr: true
},
firestore: true
}
},
// ...
pwa: {
manifest: {
// ...
},
workbox: {
importScripts: ['firebase-auth-sw.js'],
dev: process.env.ENV === 'development', // Must be FALSE for prod env.
},
},
// ...
=> The index.js store :
import { vuexfireMutations } from 'vuexfire';
import cookieparser from 'cookieparser';
export const mutations = {
// Plugin used for Firestore operations (read, write)
...vuexfireMutations,
};
export const actions = {
async nuxtServerInit({ dispatch }, { req, res }) {
if (process.server && process.static) return;
if (!req?.headers?.cookie) return;
const { token: currToken } = cookieparser.parse(req.headers.cookie);
if (!currToken) return;
if (!res?.locals?.user) return;
const { allClaims: claims, idToken: token, ...authUser } = res.locals.user
console.info('Auth User verified on server-side. User: ', authUser, 'Claims:', claims)
dispatch('onAuthStateChanged', { authUser, claims, token })
},
onAuthStateChanged({ commit }, { authUser, claims, token }) {
console.log('authUser :', authUser);
if (!authUser) {
commit('user/resetState');
return;
}
// Set existing user into vuex store
const existingUser = {
uid: authUser.uid,
email: authUser.email,
token: token,
}
commit('user/setUser', existingUser);
},
};
=> The log-out method from store/user.js :
async logOut({ commit }) {
try {
await this.$fire.auth.signOut();
commit('resetState');
this.$cookies.removeAll()
} catch (error) {
console.error('Error on logout process from client side :', error);
throw error;
}
},
To be more precise, on page refresh, the user data is set again without calling the commit('user/setUser', existingUser); line ! Don't know how and why...
Thanks in advance for any help ! :)
I am working on a Next.js project and I included the twilio chat. The next step I want to do is to set up the push notifications. I tried to follow this guide and it seems all to be fine. I actually get the FCM token and pass it to the twilio chat SDK. I expect that when I write a message something happens even errors, but I see nothing.
I have the following code:
messaging_get_token.js
import { initializeApp } from 'firebase/app';
import { getAnalytics } from 'firebase/analytics';
import { getMessaging, getToken, onMessage } from 'firebase/messaging';
export const firebaseConfiguration = () => {
const firebaseConfig = {
apiKey: 'my api key',
authDomain: 'my auth domani',
projectId: 'my project id',
storageBucket: 'my storage bucket',
messagingSenderId: 'my messaging sender id',
appId: 'my app id',
measurementId: 'measurement id',
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
};
export const handleNotifications = async (chatClientInstance) => {
firebaseConfiguration();
const messaging = getMessaging();
getToken(messaging, {
vapidKey: 'my vapid key'
})
.then((currentToken) => {
if (currentToken) {
// the flow arrives here and I get no errors
chatClientInstance?.client.setPushRegistrationId('fcm', currentToken);
onMessage((payload) => {
chatClientInstance?.client.handlePushNotification(payload);
});
} else {
console.log(
'No registration token available. Request permission to generate one.'
);
}
})
.catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
};
After this I call the function handleNotifications(client) in a useEffect when I render the chat component and pass to it the twilio Client.
In order to get the twilio client and channel I did this:
import toast from 'react-hot-toast';
const Chat = require('twilio-chat');
export const getTwilioClient = async (token: string, room?: string) => {
const client = await Chat.Client.create(token || 'no_token');
let channel;
const joinChannel = async (channel) => {
if (channel.channelState.status !== 'joined') {
await channel.join();
}
};
if (room) {
try {
channel = await client.getChannelByUniqueName(room);
await joinChannel(channel);
} catch {
try {
channel = await client.createChannel({
uniqueName: room,
friendlyName: room,
});
await joinChannel(channel);
} catch {
toast.error('Unable to create channel, please reload this page');
throw new Error('unable to create channel, please reload this page');
}
}
}
return { client, channel };
};
I followed all the steps, but I can't figure out why I don't see anything. Is there something I missed? Unfortunately I don't find any sample about doing this..
p.s.
I use twilio 3.71.3, twilio-chat 6.0.0 and firebase 9.6.8
From days since I am trying to get it done, but I am totally stuck at this point.
Here is the code from my service worker file
importScripts('https://www.gstatic.com/firebasejs/6.0.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.0.2/firebase-messaging.js');
firebase.initializeApp({
messagingSenderId: "xxxxxxxxxxxx"
});
var messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
var notificationTitle = payload.data.title; //or payload.notification or whatever your payload is
var notificationOptions = {
body: payload.data.body,
icon: payload.data.icon,
image: payload.data.image,
data: { url:payload.data.openURL }, //the url which we gonna use later
actions: [{action: "open_url", title: "View"}]
};
return event.waitUntil(self.registration.showNotification(notificationTitle,
notificationOptions));
});
self.addEventListener('notificationclick', function(event) {
console.log('event = ',event);
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
switch(event.action){
case 'open_url':
window.open(event.notification.data.url);
break;
case 'any_other_action':
window.open(event.notification.data.url);
break;
}
}, false);
And data is in this format
$data=[
'title' => 'message title',
'body' => 'description body',
'icon' => 'https://i.ytimg.com/vi/gXSyP9ga-ag/hqdefault.jpg',
'image'=>'https://i.ytimg.com/vi/gXSyP9ga-ag/mqdefault.jpg',
'openURL'=>'https://google.com'
];
Now there are many issue.
when push notification body is clicked on mobile, it does not open the url, but only dismisses it (only clicking action button opens link
I did some reading online and found that
event.waitUntil(clients.openWindow(event.notification.data.url));
Does not work with safari and safari iPhone, can someone help me find
out how to implement a click even listener that will work with apple
devices?
Any help would be appreciated
After searching through many solutions I figured out myself. Here's full working example:
// firebase-messaging-sw.js (client side)
importScripts('https://www.gstatic.com/firebasejs/8.1.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.1.2/firebase-messaging.js');
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = '<get your url from event>'
event.waitUntil(
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 (clients.openWindow) {
return clients.openWindow(url);
}
})
);
})
firebase.initializeApp({
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: "",
})
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
})
Here is the code for NodeJS side:
var admin = require("firebase-admin");
// This is a specific account key file generated through the firebase UI
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const payload = {
"token": "FCM TOKEN HERE",
"notification": {
"title":"James",
body: '14100000'
},
"webpush": {
"fcm_options": {
"link": "https://google.com"
},
},
};
admin.messaging().send(payload).then(res => {
console.log('SUCCESS ', res);
}).catch(err => {
console.log(err);
}).finally(() => {
process.exit(0);
});
The problem is if I put notificationclick on the bottom, it doesn't fire, really don't know why, then I move it to the top and it works. I can send push notification from server (using firebase-admin), and push notification will be shown (when app is in background), click the notification open the link I want.
You are using data messages, but you need to use notification messages.
See: https://firebase.google.com/docs/cloud-messaging/js/receive
Because data messages don't support fcm_options.link, you are recommended to add a notification payload to all data messages. Alternatively, you can handle notifications using the service worker.
For an explanation of the difference between notification and data messages, see Message types.
This is the JSON payload of a working notification.
The click_action is for handling clicks.
{
"data": {
"badge": "23",
"b": "xxxx",
"t": "yyyy",
"android_channel_id": "com.example.fcm"
},
"from": "11111111111",
"notification": {
"title": "Title",
"body": "Body",
"icon": "https://example.com/icon.png",
"click_action": "https://example.com"
},
"collapse_key": "do_not_collapse"
}
I'm trying to implement push notification with React Native and Firebase through this documentation.
I set up the settings I need by the tutorial.
import React, { Component } from 'react'
import { View } from 'react-native'
import { Input, Text, Button } from '../Components'
import type { RemoteMessage } from 'react-native-firebase'
import firebase from 'react-native-firebase'
import type { Notification, NotificationOpen } from 'react-native-firebase';
export default class TestComponent extends Component {
async componentDidMount() {
await this.SetUpAuth();
await this.SetUpMessaging();
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen: NotificationOpen) => {
// Get the action triggered by the notification being opened
const action = notificationOpen.action;
// Get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
});
const notificationOpen: NotificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
console.log(notificationOpen)
// App was opened by a notification
// Get the action triggered by the notification being opened
const action = notificationOpen.action;
// Get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
}
}
componentWillUnmount() {
}
async SetUpAuth() {
const credential = await firebase.auth().signInAnonymouslyAndRetrieveData();
if (credential) {
console.log('default app user ->', credential.user.toJSON());
} else {
console.error('no credential');
}
}
async SetUpMessaging() {
this.notification2 = new firebase.notifications.Notification()
.setNotificationId('notificationId')
.setTitle('My notification title')
.setBody('My notification body')
.android.setChannelId('test')
.android.setClickAction('action')
.setData({
key1: 'value1',
key2: 'value2',
});
this.notification2
.android.setChannelId('channelId')
.android.setSmallIcon('ic_launcher');
console.log('assa')
onTokenRefreshListener = firebase.messaging().onTokenRefresh(fcmToken => {
console.log('token generated ->', fcmToken);
// store.dispatch(DeviceActions.SetFCMToken(fcmToken));
});
const fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
// user has a device token
console.log('has token ->', fcmToken);
console.log(firebase.auth().currentUser._user)
firebase.database().ref(`/users/${firebase.auth().currentUser._user.uid}`).set({ pushToken: fcmToken })
// store.dispatch(DeviceActions.SetFCMToken(fcmToken));
} else {
// user doesn't have a device token yet
console.error('no messaging token');
}
const messagingEnabled = await firebase.messaging().hasPermission();
if (messagingEnabled) {
// user has permissions
console.log('User has FCM permissions');
} else {
// user doesn't have permission
console.log('User does not have FCM permissions');
await this.RequestMessagePermissions();
}
messageListener = firebase.messaging().onMessage((message: RemoteMessage) => {
console.log(`Recieved message - ${JSON.stringify(message)}`);
});
notificationDisplayedListener = firebase
.notifications()
.onNotificationDisplayed(notification => {
// Process your notification as required
// ANDROID: Remote notifications do not contain the channel ID. You will have to specify this manually if you'd like to re-display the notification.
console.log(`Recieved notification 1`);
});
notificationListener = firebase
.notifications()
.onNotification(notification => {
console.log(notification)
firebase.notifications().displayNotification(this.notification2)
// Process your notification as required
console.log(`Recieved notification 2`);
});
}
async RequestMessagePermissions() {
console.log('request')
console.log('Requesting FCM permission');
await firebase
.messaging()
.requestPermission()
.catch(err => console.err(err));
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
</View>
)
}
When I try to use it in postman I get success:
{
"success": {
"results": [
{
"messageId": "0:1525013439417985%a0cec506a0cec506"
}
],
"canonicalRegistrationTokenCount": 0,
"failureCount": 0,
"successCount": 1,
"multicastId": 6840884736220792000
}
}
But in my debugger (by console.log) I don't see any new incoming message or something else. I sent a message to my device with the token I added to this post but nothing happened.
it works only when app is in foreground, But I want to make it work also when app in background/closed the app
As mentioned in the docs you need onNotificationOpened listener for android, when the app is in background
Android Background: onNotificationOpened triggered if the notification is tapped.
onNotificationDisplayed is for IOS app in background and triggered if content_available set to true
notificationBackgroundListener = firebase
.notifications()
.onNotificationOpened(notification => {
// Process your notification as required
console.log(`Recieved notification 2`);
});
If your notification is working fine when your app is in foreground then the problem is with your service. There are two possible reasons it's not working.
Either you are sending the payload as
{
time_to_live: 86400,
collapse_key: "xxxxxx",
delay_while_idle: false,
registration_ids: registration_ids,
notification: payload
}
instead of
{
time_to_live: 86400,
collapse_key: "xxxxxx",
delay_while_idle: false,
registration_ids: registration_ids,
data: payload
}
which is because the key in notification is recieved only when app is in foreground.
Another possible reason could be the service worker is getting killed for some reason. ex: I was using a one plus which auto kills the service when I force close the app. So you can try debugging your service in native code by adding log or attaching a debugger
also make sure to add google-service.json file in your android/app folder
Edit:
{
"registration_ids" : ["reg_id"],
"time_to_live": 86400,
"collapse_key": "test_type_b",
"delay_while_idle": false,
"notification": {},
"data": {
"subText":"sub title R",
"title":"Notification Heading R",
"message":"Short big text that will be shown when notification is expanded R",
"color":"red",
"actions": ["hello", "welcome"],
"vibrate": true,
"vibration": 1000,
"ticker": "My Notification Ticker",
"imageUrl": "https://cdn-images-1.medium.com/max/712/1*c3cQvYJrVezv_Az0CoDcbA.jpeg" ,
"bigText": "blalMy big text that will be shown when notification is expanded"
}
}
Here is my headers
Authorization: key=mykey:myKey
Content-Type: application/json
Request:
https://fcm.googleapis.com/fcm/send
which is of post and params are of raw type
you can use Headless JS to run task in background and you should write some native code to handle task , unfortunately is only available in android
https://facebook.github.io/react-native/docs/headless-js-android.html
second way(i don't test it) is to use this library
https://github.com/jamesisaac/react-native-background-task#installation
ps:firebase and other push notification service like onesignal handle push notification easily and you should not be concerned unless you want unique notification for each user