I'm using the following code to send a notification from one device to another using FCM. Everything works fine until before return admin.messaging().sendToDevice(...). The 'Token ID: ' log displays token ID of the receiver, but when I set the variable token_id to the sendToDevice function, the notification is not called, therefore the notification is not sent.
Can someone tell me what's wrong?
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/notifications/{user_id}/{notification_id}').onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
console.log('We have a notification to send to ', user_id);
if(!event.data.val()){
return console.log('Notification has been deleted from the database: ', notification_id);
}
const fromUser = admin.database().ref(`/notifications/${user_id}/${notification_id}`).once('value');
return fromUser.then(fromUserResult => {
const from_user_id = fromUserResult.val().from;
console.log('You have a new notification from ', from_user_id);
const user_query = admin.database().ref(`/usuarios/${from_user_id}/nome`).once('value');
return user_query.then(userResult => {
const user_name = userResult.val();
const device_token = admin.database().ref(`/usuarios/${user_id}/device_token`).once('value');
return device_token.then(result => {
const token_id = result.val();
console.log('Token ID: ', token_id);
const payload = {
notification: {
title: "New Request",
body: `${user_name} has sent you a request.`,
icon: "default",
},
data: {
p_tab: "2"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response =>{
console.log('Notification sent to ', user_id);
});
});
});
});
});
EDIT Code I use to save Firebase Instance ID to the current user's database.
String deviceToken = FirebaseInstanceId.getInstance().getToken();
usersRef.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("device_token").setValue(deviceToken);
Related
I recently purchased a Sunmi V2 device for a client, and I am trying to print something when I receive a notification from expo-notification using react-native. I have successfully implemented this in another application, but I am having difficulty getting it to work on the Sunmi V2 device.
Here is the approach I took: On my StackNavigator.js component, I have...
const { user } = useAuth();
const { registerForPushNotificationsAsync, handleNotifications, handleNotificationsResponse } = useNotifications();
useEffect(() => {
registerForPushNotificationsAsync();
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
const responseListener =
Notifications.addNotificationResponseReceivedListener(
handleNotificationsResponse
);
return () => {
if (responseListener)
Notifications.removeNotificationSubscription(responseListener)
}
}, [user])
Here is the useNotification component:
export const useNotifications = () => {
const { user } = useAuth();
const registerForPushNotificationsAsync = async () => {
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
// console.log('Failed to get push token for push notification!')
return;
}
try {
const token = (await Notifications.getExpoPushTokenAsync()).data;
Alert.alert("TOKEN: ", token);
if (user) {
const useRef = doc(db, "users", user.uid);
if (token !== undefined) {
updateDoc(useRef, {
pushTokenExpo: token
});
}
}
} catch (error) {
Alert.alert(error)
}
} else {
if (user) {
alert('Must use physical device for Push Notifications', user.uid)
}
alert('Must use physical device for Push Notifications');
console.log('Must use physical device for Push Notifications')
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
};
const handleNotifications = (notification: Notifications.Notification) => {
console.log('NEW NOTIFICACTION')
};
const handleNotificationsResponse = (
response: Notifications.NotificationResponse
) => {
console.log(response.notification.request.content.data)
// HERE DO WHAT YOU WHAT AFTER CLICK ON NOTIF
}
return { registerForPushNotificationsAsync, handleNotifications, handleNotificationsResponse }
}
When I run this code on my device, it either crashes the app or does not update the Firebase collection 'user'. How can I fix this issue?
I have set up the Ionic/Capacitor app to receive push notifications following the capacitor tutorial,
I went through all the tutorial successfully and I receive test notifications sent from FCM both in foreground and background
I can successfully register to a topic
I can receive notifications sent to the topic
I can receive notifications sent to the token (test mode)
Now I'm trying to send notifications from a different device,
I just created a two test button to send notification in multicast to an array of tokens and another button to send notification to a given topic
In both cases from my device I receive the notifications in foreground, but not in background
I believe is something wrong with the format I'm using to send the notifications that is not correct in case of background (I can receive those from the FCM test tool)
Client
const send = functions.httpsCallable('sendPushNotificationToUsers');
const sendToTopic = functions.httpsCallable('sendPushNotificationToTopic');
const sendNotification = useCallback(
({
to,
title,
body,
onClick
}) => {
setLoading(true);
send({
to,
title,
body,
onClick
})
.then(() => setSuccess(true))
.catch(() => setError(true))
.finally(() => setLoading(false));
}, [send],
);
const sendNotificationToTopic = useCallback(
({
topic,
title,
body,
onClick
}) => {
setLoading(true);
sendToTopic({
topic,
title,
body,
onClick
})
.then(() => setSuccess(true))
.catch(() => setError(true))
.finally(() => setLoading(false));
}, [sendToTopic],
);
Server / Functions
exports.sendPushNotificationToUsers = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
db.collection('users_meta')
.doc(data.to)
.collection('messagingTokens')
.get()
.then(messagingTokens => {
if (messagingTokens && messagingTokens.size) {
const to = messagingTokens.docs.map(i => i.data().token);
console.log(to); // I get to this console log and the tokens are printed correctly in an array
admin.messaging().sendMulticast({
title: data.title,
body: data.body,
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
tokens: to,
});
}
});
},
);
exports.sendPushNotificationToTopic = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
admin.messaging().send({
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
topic: data.topic,
});
},
);
Notifications handler
const initNative = useCallback(() => {
PushNotifications.register();
PushNotifications.requestPermission().then(result => {
if (result.granted) {
// Register with Apple / Google to receive push via APNS/FCM
PushNotifications.register();
} else {
// Show some error
}
});
PushNotifications.addListener(
'registration',
(token: PushNotificationToken) => {
const device = new DeviceUUID().get();
registerTokenToUser(device, token.value);
alert('Push registration success, token: ' + token.value);
},
);
PushNotifications.addListener('registrationError', (error: any) => {
alert('Error on registration: ' + JSON.stringify(error));
});
PushNotifications.addListener(
'pushNotificationReceived',
(notification: PushNotification) => {
alert(JSON.stringify(notification));
// this array fires correctly with the app in foreground, but nothing on the notifications tray with the app in background if sent from my send functions, works correctly if sent from FCM
},
);
// Method called when tapping on a notification
PushNotifications.addListener(
'pushNotificationActionPerformed',
(notification: PushNotificationActionPerformed) => {
alert(JSON.stringify(notification));
},
);
}, [PushNotifications, history, registerTokenToUser]);
Any suggestion?
Thanks
I've found the error myself,
for the notifications be visible in background mode the notification object needs to have "notification" key populated, that was missing in my case,
correct send function should be
exports.sendPushNotificationToUsers = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
db.collection('users_meta')
.doc(data.to)
.collection('messagingTokens')
.get()
.then(messagingTokens => {
if (messagingTokens && messagingTokens.size) {
const to = messagingTokens.docs.map(i => i.data().token);
console.log(to); // I get to this console log and the tokens are printed correctly in an array
admin.messaging().sendMulticast({
title: data.title,
body: data.body,
notification: {
title: data.title,
body: data.body,
},
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
tokens: to,
});
}
});
},
);
exports.sendPushNotificationToTopic = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
admin.messaging().send({
notification: {
title: data.title,
body: data.body,
},
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
topic: data.topic,
});
},
);
I am using react-native-notifee and react-native-firebase v11.2.0
The problem is, when I press the notification during the app is in foreground, it is not navigating to the required screen. It is working fine while my app is in background.
notification.js
import notifee, {
AndroidCategory,
AndroidImportance,
AndroidStyle,
EventType
} from '#notifee/react-native';
import messaging from '#react-native-firebase/messaging';
import { requestNotificationPermission } from '../helpers/permissions';
import * as RootNavigator from '../navigator/NavigationRef';
const createChannel = async () => {
try {
const channel = await notifee.createChannel({
id: 'default_channel',
name: 'Default Channel',
description: 'Default notification channel',
sound: 'default',
importance: AndroidImportance.HIGH,
vibration: false
});
return channel;
} catch (error) {
console.log('Error in creating notification channel', error);
return 'default_channel';
}
};
export const requestPermissionForNotifications = async () => {
const hasPermission = await messaging().hasPermission();
if (!hasPermission) {
// checking for ios notification permission;
const isGranted = await requestNotificationPermission();
if (isGranted < 1) return false;
}
// always request permisssion even if it is granted
return messaging()
.requestPermission()
.then(() => console.log('granted'))
.catch(err => console.log('error in granted', err));
};
export const displayNotification = async (
title,
body,
notificationId,
imageUrl = null
) => {
try {
// always check permisssion even if it has access to register the app to
// receive the push notification as stated in documentation
await requestPermissionForNotifications();
const channel = await createChannel();
let notificationObj = {
title,
body,
id: notificationId,
android: {
channelId: channel,
smallIcon: 'ic_launcher',
sound: 'default',
category: AndroidCategory.ALARM,
importance: AndroidImportance.HIGH,
pressAction: {
id: 'default'
},
timestamp: Date.now(),
showTimestamp: true
},
ios: {
sound: 'default',
attachments: [
{
url: imageUrl
}
]
}
};
await notifee.displayNotification(notificationObj);
} catch (error) {
console.log('Error in displaying notification', error);
}
};
export const onMessageReceived = async message => {
const { messageId, notification } = message;
return displayNotification(
notification.title,
notification.body,
messageId,
notification.image
);
};
export const notificationPressActionEventListener = async ({
type,
detail
}) => {
try {
const { navigate } = RootNavigator.navigationRef.current;
switch (type) {
case EventType.PRESS:
navigate('SampleStackScreen');
break;
default:
break;
}
} catch (error) {
console.log('Error in navigation', error);
}
return null;
};
export const getDeviceToken = async () => {
const token = await messaging().getToken();
if (token) return token;
return null;
};
NavigationRef.js
import * as React from 'react';
export const navigationRef = React.createRef();
Listeners in Home.js
componentDidMount() {
this.notificationListener = messaging().onMessage(onMessageReceived);
this.notificationActionEventListener = notifee.onForegroundEvent(
notificationPressActionEventListener
);
}
Listeners in index.js
/** #format */
import messaging from '#react-native-firebase/messaging';
import notifee from '#notifee/react-native';
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import App from './src/App';
import {
notificationPressActionEventListener,
onMessageReceived
} from './src/firebase/notification';
notifee.onBackgroundEvent(notificationPressActionEventListener);
messaging().setBackgroundMessageHandler(onMessageReceived);
AppRegistry.registerComponent(appName, () => App);
I'm trying to send a notification based on the user FCM token, I have used the firebase function to successfully send a notification if there are any changes to the firebase database for that particular user. But currently, the node.JS function does not provide any logs messages/notification sent to the user. Please do help me in resolving these issues.
//import firebase functions modules
const functions = require('firebase-functions');
//import admin module
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Listens for new messages added to messages/:pushId
exports.pushNotification = functions.database.ref('/Notification/{receiver_id}/push_id/{job_id}').onWrite((data, context) => {
const receiver_id = context.params.receiver_id;
const job_id = context.params.job_id;
console.log('Start');
console.log('receiverID : ' + receiver_id);
console.log('jobID : ' + job_id);
const DeviceToken = admin.database().ref(`/User/${receiver_id}/fcmtoken`).once('value');
return DeviceToken.then(result =>
{
const token_id = result.val();
console.log(token_id);
const payload =
{
notification:
{
title: "New Job Request",
body: `JobID ` + job_id,
tag: collapseKey,
icon: "default",
color: '#18d821',
sound: 'default',
}
};
return admin.messaging().sendToDevice(token_id, payload)
.then(response =>
{
console.log('This was a notification feature.');
return null;
})
.catch(function(error) {
console.log('Error sending message:', error);
});
});
});
It's not displaying any log messages or any notification.
You are using the incorrect promise. The sendToDevice may be abort when the triggger function finish because it isn't waiting for that promise.
exports.pushNotification = functions.database.ref('/Notification/{receiver_id}/push_id/{job_id}').onWrite((data, context) => {
const receiver_id = context.params.receiver_id;
const job_id = context.params.job_id;
console.log('Start');
console.log('receiverID : ' + receiver_id);
console.log('jobID : ' + job_id);
const DeviceToken = admin.database().ref(`/User/${receiver_id}/fcmtoken`).once('value');
return DeviceToken.then(result =>
{
const token_id = result.val();
console.log(token_id);
const payload =
{
notification:
{
title: "New Job Request",
body: `JobID ` + job_id,
tag: collapseKey,
icon: "default",
color: '#18d821',
sound: 'default',
}
};
return admin.messaging().sendToDevice(token_id, payload)
})
.then(response => {
console.log('This was a notification feature.');
return null;
})
.catch(function(error) {
console.log('Error sending message:', error);
});
});
Can someone help me with this code? I'm trying to get the user id but it shows something like
TypeError: Cannot read property 'uid' of undefined
at exports.sendNotification.functions.database.ref.onWrite.event (/user_code/index.js:9:30)
at Object. (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112:27)
at next (native)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:28:71
at __awaiter (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:82:36)
at /var/tmp/worker/worker.js:710:26
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
here is my code (index.js):
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification =
functions.database.ref('/Notifications/{uid}/{notification_id}').onWrite(event => {
const user_id = event.params.uid;
const notification = event.params.notification;
console.log('We have notification to sent to : ', user_id);
if(!event.data.val()){
return console.log("A notification has been deleted from the database : ", notification_id);
}
const deviceToken = admin.database().ref(`/Users/${uid}/device_token`).once('value');
return deviceToken.then(result => {
const token_id = result.val();
const payload = {
notification: {
title: "Friend Request",
body: "You have received a new friend request",
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response => {
return console.log("This was the notification feature");
});
});
});