Flutter: How can i send push notification programmatically with fcm - android

I'm creating a chat application and i want to use fcm to send notification if the person has a new message, but i don't know how to proceed. All the tutorials i found use to send the message from firebase. But i want to send it automatically when there is a new message for the person

A possible workaround if you use firebase should be like this:
You need to store each firebase FCM token for a specific user (need to take in account here that a user can log in at the same time on his account from multiple devices) so you can store the userId and his deviceUniqueId on flutter you can get it from device_info https://pub.dev/packages/device_info:
String identifier;
final DeviceInfoPlugin deviceInfoPlugin = new DeviceInfoPlugin();
try {
if (Platform.isAndroid) {
var build = await deviceInfoPlugin.androidInfo;
identifier = build.id.toString();
} else if (Platform.isIOS) {
var data = await deviceInfoPlugin.iosInfo;
identifier = data.identifierForVendor;//UUID for iOS
}
} on PlatformException {
print('Failed to get platform version');
}
and after that to get the unique CFM token that Firebase provide for each device, you can get it using Firebase firebase_messaging plugin (https://pub.dev/packages/firebase_messaging) getToken() and insert the token to firestore (or an other database you want to store it)
FirebaseMessaging firebaseMessaging = new FirebaseMessaging();
firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true));
firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
firebaseMessaging.getToken().then((token){
print('--- Firebase toke here ---');
Firestore.instance.collection(constant.userID).document(identifier).setData({ 'token': token});
print(token);
});
After that you can insert one or more FCM token connected to multiple device for one user. 1 user ... n devices , 1 device ... 1 unique token to get push notifications from Firebase.
send it automatically when there is a new message for the person : now you need to call the Firestore API(is very fast indeed but need to be careful about the plan limit that you are using here) or another API call if you store the token to another db, in order to get the token/tokens for each user and send the push notifications.
To send the push notification from flutter you can use a Future async function.
P.s: I'm passing as argument a List here in order to use "registration_ids" instead of "to" and send the push notification to multiple tokens if the user has been logged in on multiple devices.
Future<bool> callOnFcmApiSendPushNotifications(List <String> userToken) async {
final postUrl = 'https://fcm.googleapis.com/fcm/send';
final data = {
"registration_ids" : userToken,
"collapse_key" : "type_a",
"notification" : {
"title": 'NewTextTitle',
"body" : 'NewTextBody',
}
};
final headers = {
'content-type': 'application/json',
'Authorization': constant.firebaseTokenAPIFCM // 'key=YOUR_SERVER_KEY'
};
final response = await http.post(postUrl,
body: json.encode(data),
encoding: Encoding.getByName('utf-8'),
headers: headers);
if (response.statusCode == 200) {
// on success do sth
print('test ok push CFM');
return true;
} else {
print(' CFM error');
// on failure do sth
return false;
}
}
You can also check the post call from postman in order to make some tests. POST request
On Headers add the:
key Authorization with value key=AAAAO........ // Project Overview -> Cloud Messaging -> Server Key
key Content-Type with value application/json
And on the body add
{
"registration_ids" :[ "userUniqueToken1", "userUniqueToken2",... ],
"collapse_key" : "type_a",
"notification" : {
"body" : "Test post",
"title": "Push notifications E"
}
}
"registration_ids" to send it to multiple tokens (same user logged in to more than on device at the same time)
"to" in order to send it to a single token (one device per user / or update always the user token that is connected with his device and have 1 token ... 1 user)
I'm making an edit to the response, in order to add that is very important to add the FCM Server Key on a trusted environment or server!

I'll list here a few related questions which I have participated with answers. I guess you'll find a lot of relevant info on using firebase cloud messaging (FCM) in a chat app.
Is FCM the only way to build a chat app ?
Suggested approach to use FCM in a chat app
Is using topics a better solution then using the fcmToken in a chat app?
Problems with FCM onMessage while app is in background
Problem: after logoff, user continues to receive notifications
Good luck!

//Notification Sending Side Using Dio flutter Library to make http post request
static Future<void> sendNotification(receiver,msg)async{
var token = await getToken(receiver);
print('token : $token');
final data = {
"notification": {"body": "Accept Ride Request", "title": "This is Ride Request"},
"priority": "high",
"data": {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"id": "1",
"status": "done"
},
"to": "$token"
};
final headers = {
'content-type': 'application/json',
'Authorization': 'key=AAAAY2mZqb4:APA91bH38d3b4mgc4YpVJ0eBgDez1jxEzCNTq1Re6sJQNZ2OJvyvlZJYx7ZASIrAj1DnSfVJL-29qsyPX6u8MyakmzlY-MRZeXOodkIdYoWgwvPVhNhJmfrTC6ZC2wG7lcmgXElA6E09'
};
BaseOptions options = new BaseOptions(
connectTimeout: 5000,
receiveTimeout: 3000,
headers: headers,
);
try {
final response = await Dio(options).post(postUrl,
data: data);
if (response.statusCode == 200) {
Fluttertoast.showToast(msg: 'Request Sent To Driver');
} else {
print('notification sending failed');
// on failure do sth
}
}
catch(e){
print('exception $e');
}
}
static Future<String> getToken(userId)async{
final Firestore _db = Firestore.instance;
var token;
await _db.collection('users')
.document(userId)
.collection('tokens').getDocuments().then((snapshot){
snapshot.documents.forEach((doc){
token = doc.documentID;
});
});
return token;
}
//Now Receiving End
class _LoginPageState extends State<LoginPage>
with SingleTickerProviderStateMixin {
final Firestore _db = Firestore.instance;
final FirebaseMessaging _fcm = FirebaseMessaging();
StreamSubscription iosSubscription;
//this code will go inside intiState function
if (Platform.isIOS) {
iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
// save the token OR subscribe to a topic here
});
_fcm.requestNotificationPermissions(IosNotificationSettings());
}
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
// TODO optional
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
// TODO optional
},
);
//saving token while signing in or signing up
_saveDeviceToken(uid) async {
// FirebaseUser user = await _auth.currentUser();
// Get the token for this device
String fcmToken = await _fcm.getToken();
// Save it to Firestore
if (fcmToken != null) {
var tokens = _db
.collection('users')
.document(uid)
.collection('tokens')
.document(fcmToken);
await tokens.setData({
'token': fcmToken,
'createdAt': FieldValue.serverTimestamp(), // optional
'platform': Platform.operatingSystem // optional
});
}
}

Related

Firebase Messaging - Validate incoming background messages based on user logged in

I'm creating a chat app (kind of WhatsApp-like messaging) using Flutter.
First, the notifications mechanism is working as intended, whenever I send a message from 1 device to another device, the notification would pop up.
I created a local_notification_service.dart to handle the foreground notification & sending a not
import 'dart:math';
import 'package:get/get.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService extends GetConnect {
String serverKey ='xxxxxxxxxxxxxxxxxxxx'
static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
static void initialize() {
const InitializationSettings initializationSettings = InitializationSettings(android: AndroidInitializationSettings("#mipmap/ic_launcher"));
_flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
static void display(RemoteMessage message) async {
try {
print("Display notification");
// int id = DateTime.now().microsecondsSinceEpoch ~/1000000;
Random random = Random();
int id = random.nextInt(1000);
const NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
"mychanel",
"my chanel",
importance: Importance.max,
priority: Priority.high,
));
print("my id is ${id.toString()}");
await _flutterLocalNotificationsPlugin.show(
id,
message.notification!.title,
message.notification!.body,
notificationDetails,
);
} on Exception catch (e) {
print('Error>>>$e');
}
}
Future<void> sendNotification({
String? title,
String? message,
String? token,
String? uniqueId,
String? action,
String? channelId,
String? channelName,
String? channelDesc,
}) async {
final data = {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"action": action,
"uniqueId": uniqueId,
"message": message,
"channelId": channelId ?? 'my channel id',
"channelName": channelName ?? 'my channel Name',
"channelDesc": channelDesc ?? 'my channel description',
};
try {
final response = await post(
'https://fcm.googleapis.com/fcm/send',
{
'notification': {'title': title, 'body': message},
'priority': 'high',
'data': data,
'to': '$token',
'direct_boot_ok': true,
},
headers: {
'Content-Type': 'application/json',
'Authorization': 'key=$serverKey',
},
);
print('response body : ${response.body}');
} catch (e) {}
}
}
Then, I'm trying to validate the users in my flutter application whenever they receive FCM notification, here's the logic that I want to create:
If the user is not logged in, then the device could not receive the notification
If the user is logged in, but the specific user is not eligible to receive the message (in case there are some users with the same FCM token / device registered ) then the device could not receive the notification. I would want to solve this after the point number 1 is succeeded
Here's my main.dart file
void main() async {
await GetStorage.init();
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
GlobalController globalC = Get.put(GlobalController());
AuthController authC = Get.put(AuthController());
ErrorController errorC = Get.put(ErrorController());
ConnectivityResult connectivityResult = ConnectivityResult.none;
final Connectivity connectivity = Connectivity();
connectivityResult = await connectivity.checkConnectivity();
if (connectivityResult == ConnectivityResult.wifi || connectivityResult == ConnectivityResult.mobile) {
// Start FCM
final fcmToken = await FirebaseMessaging.instance.getToken();
globalC.fcmToken.value = fcmToken ?? ''; //set global fcm Token
final FirebaseMessaging fcmInstance = FirebaseMessaging.instance;
NotificationSettings settings = await fcmInstance.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
/* Handle message when in foreground */
FirebaseMessaging.onMessage.listen((event) {
if (globalC.isAuthenticated.isTrue) {
LocalNotificationService.display(event); //display notification
}
});
/* Handle message when in background */
if (globalC.isAuthenticated.isTrue) {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
fcmInstance.onTokenRefresh.listen((fcmToken) {
// Note: This callback is fired at each app startup and whenever a new
// token is generated.
}).onError((err) {
// Error getting token.
});
// End FCM
}
FirebaseAnalytics analytics = FirebaseAnalytics.instance;
runApp(MyApp());
}
as you can see, I'm trying to filter the non logged in user when in the foreground using the FirebaseMessaging.onMessage.listen with the globalC.isAuthenticated.isTrue validation. And it works (because the default of globalC.isAuthenticated is false whenever user is not logged in)
But for the FirebaseMessaging.onBackgroundMessage function does not seems to work with the validation. I've tried to search for the solution in the documentations, youtube but i couldn't find it till this question is made.
How can I make this kind of validation for background message?
Sorry for this newbie question, any help would be greatly appreciated.
Thank you .

Send notification to specific user firebase in flutter

How do I send a notification to another user when one user presses a button? Can someone show me a code snippet?
I realize that this question was asked before, however, it was closed since there were "several answers." The links that were provided that were similar did not explain sending notifications in flutter.
The below solution works, however, my solution is much simpler, and avoids adding new technologies
I have figured out how send a notification to another device using an in app feature.
First, you will need to import the necessary packages:
firebase_messaging
flutter_local_notifications
Note: you will also use the http package
Also note: to send notifications to another device, you must know the device token of that device. I prefer getting the token and saving it in Firestore or Realtime Database. Here is the code to get the device token.
String? mtoken = " ";
void getToken() async {
await FirebaseMessaging.instance.getToken().then((token) {
setState(() {
mtoken = token;
});
});
}
The token will be saved in mtoken, you can now use this as the token for the coming steps.
The next step is to request permission to send push notifications to your app.
void requestPermission() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
} else if (settings.authorizationStatus ==
AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
}
(If you get "User declined or has not accepted permission" in your console, try going out of your app, finding the icon in the homescreen, pressing and holding on the app icon, tapping "App Info", tapping "Notifications" and turn on "All [app name] notifications."
You will also need two functions to load a Firebase Cloud Messaging notification and one to listen for a notification.
Code to load a Firebase Cloud Messaging notification:
void loadFCM() async {
if (!kIsWeb) {
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
importance: Importance.high,
enableVibration: true,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
}
And this function to listen for a Firebase Cloud Messaging notifcation.
void listenFCM() async {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
// TODO add a proper drawable resource to android, for now using
// one that already exists in example app.
icon: 'launch_background',
),
),
);
}
});
}
You will want to run loadFCM, listenFCM, and requestPermission when the page is initialized.
void initState() {
super.initState();
requestPermission();
loadFCM();
listenFCM();
}
The next step is to find your Firebase Cloud Messaging API key. This can simply be done by heading to your Firebase project > Project Settings > Cloud Messaging then copy the API key under Cloud Messaging API (Legacy).
When you have your Firebase Cloud Messaging API key, this is the code to display a notification given the notification title, body, and device token to send it to.
void sendPushMessage(String body, String title, String token) async {
try {
await http.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization':
'key=REPLACETHISWITHYOURAPIKEY',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
'body': body,
'title': title,
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done'
},
"to": token,
},
),
);
print('done');
} catch (e) {
print("error push notification");
}
}
Now you can call this function like this:
sendPushMessage('Notification Body', 'Notification Title', 'REPLACEWITHDEVICETOKEN');
I hope this helps.
You will need Firebase Cloud Messaging for that.
The way I've done it is using a Cloud Function that you can trigger via HTTP or even via a Firestore trigger, like this:
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
/**
* Triggered by a change to a Firestore document.
*
* #param {!Object} event Event payload.
* #param {!Object} context Metadata for the event.
*/
exports.messageNotificationTrigger = (change, context) => {
db.collection('users').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
const userData = doc.data();
if (userData.id == '<YOUR_USER_ID>') {
admin.messaging().sendToDevice(userData.deviceToken, {
notification: {
title: 'Notification title', body: 'Notification Body'}
});
}
});
});
};
Every user you have registered in your users collection must have a device token, sent from their device they access the app.
From Flutter, using the FCM package, this is how you send the device token to Firebase:
// fetch the device token from the Firebase Messaging instance
// and store it securely on Firebase associated with this user uid
FirebaseMessaging.instance.getToken().then((token) {
FirebaseFirestore.instance.collection('users').doc(userCreds.user!.uid).set({
'deviceToken': token
});
});
Where userCredentials.user!.uid is the user you use to log in to your application using Firebase Authentication like this:
UserCredential userCreds = await FirebaseAuth.instance.signInWithCredential(credential);
Hope that helps.

Script for making firebase user to user notifications

(Sorry for bad English)
I'm looking for a way to make user to user notifications in Android.
The system have to catch a new child event in the Database, read the data and send the notification to destination.
The struct of the DB is this:
notificationRequests
$pushid
message: "You have a new request! Open the app"
userId: "sadbijasuobru112u4124u21b" //user destination id
By doing some researches in the web I've found the possibility to use topic messages.
So, i've added this in my LoginActivity befor calling the MainActivity:
FirebaseMessaging.getInstance().subscribeToTopic("users_topic$uid")
.addOnCompleteListener {
Log.d("LoginActivity", "User registered")
}
The code works. I can send notifications from the console
Like I said before, i need automatic messages.
I've found this code on the web, but it doesn't work.
var firebase = require('firebase-admin');
var request = require('request');
var API_KEY = "Firebase Cloud Messaging Server API key"
// Fetch the service account key JSON file contents
var serviceAccount = require("./serviceAccountKey.json");
// Initialize the app with a service account, granting admin privileges
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: "https://appname.firebaseio.com/"
});
ref = firebase.database().ref();
function listenForNotificationRequests() {
var requests = ref.child('notificationRequests');
requests.on('child_added', (requestSnapshot) => {
var request = requestSnapshot.val();
sendNotificationToUser(
request.userId,
request.message,
() => {
requestSnapshot.ref.remove();
}
);
}, (error) => {
console.error(error);
});
}
function sendNotificationToUser(userID, message, onSuccess) {
request({
url: 'https://fcm.googleapis.com/fcm/send',
method: 'POST',
headers: {
'Content-Type' :' application/json',
'Authorization': 'key='+API_KEY
},
body: JSON.stringify({
notification: {
title: message
},
to : '/topics/users_topic'+userID
})
}, (error, response, body) => {
if (error) { console.error(error); }
else if (response.statusCode >= 400) {
console.error('HTTP Error: '+response.statusCode+' - '+response.statusMessage);
}
else {
onSuccess();
}
});
}
// start listening
listenForNotificationRequests();
I've implemented this code in the index.js for deployng. I've also write the entire file instead of requiring it, but it's always the same.
const functions = require('firebase-functions');
exports.sendNotifications = functions.https.onRequest((request, response) => {
require('./sendNotifications.js')
})
Any suggestions?
Thanks for the kelp!
EDIT
Does this need a billing account configurated? If yes, how can i make it with the free plan?
Does this need a billing account configurated? If yes, how can i make it with the free plan?
See https://firebase.google.com/support/faq#functions-runtime, but in short: from Node 10 you will need to enter billing information to use Cloud Functions.
You can use Node 8 until early next year without entering billing information.
Note that entering billing information does not necessarily mean you'll have to pay, as Cloud Functions has a pretty significant free tier.
The problem was that I was calling an external site. I solved it doing this:
const functions = require('firebase-functions');
var firebase = require('firebase-admin');
// Fetch the service account key JSON file contents
var serviceAccount = require("./serviceAccountKey.json");
// Initialize the app with a service account, granting admin privileges
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: "https://appname.firebaseio.com/"
});
exports.sendNotifications = functions.database.ref('/notificationRequest/{pushId}')
.onCreate( (change, context) => {
var uid = change.child('userId').val()
var notificationMessage = change.child('message').val()
var userTopic = 'users_topic'+uid
var payload = {
data: {
message: notificationMessage
}
};
var options = {
priority: 'high',
timeToLive: 60 * 60 * 24,
collapseKey: 'notification'
};
firebase.messaging().sendToTopic(userTopic, payload, options)
// eslint-disable-next-line promise/always-return
.then((response) => {
console.log('Done', response);
})
.catch((error) => {
console.log('Error: ', error);
});
return change.ref.remove();
});

Firebase Functions receive Object instead of String

Bear with me. I've spent a month just PHRASING this question: I've been using Firebase Database and Firebase functions for about a year. I've gotten it to work... but only if I sent the text of the message as a STRING. The problem is that now I wish to receive an OBJECT instead but I'm unsure of how to do this in FireBaseMessage.
My previous structure:
messages
T9Vh5cvUcbqC8IEZowBpJC3
ZWfn7876876ZGJeSNBbCpPmkm1
message
"messages": {
".read": true,
"$receiverUid": {
"$senderUid": {
"$message": {
".read": true,
".write": "auth.uid === $senderUid"
And my function for the listener was this:
exports.sendMessage = functions.database.ref('/messages/{receiverUid}/{senderUid}/{message}')
This is problematic... for a variety of reasons. Namely if the old message was "Hey" and then that same person just writes "Hey" again... then the original gets overwritten.
So my NEW structure is more like this:
messages
-LkVcYqJoEroWpkXZnqr
body: "genius grant"
createdat: 1563915599253
name: "hatemustdie"
receiverUid: "TW8289372984KJjkhdsjkhad"
senderUid: "yBNbs9823789KJkjahsdjkas"
Which is written as:
mDatabase.child("messages").push().setValue(message);
...and I'm just unsure about how to write out that function.
I mean... IDEALLY... it would be something like:
exports.sendMessage = functions.database.ref('/messages/{receiverUid}/{senderUid}/{msgID}/{msgOBJECT}')
...but I'm just not sure how Firebase functions is reading this new structure.
Now I'm pushing to the database like so:
mDatabase.child("messages").child(guid).child(user_Id).push().setValue(msgObject).addOnSuccessListener(this, new OnSuccessListener<Void>() {
#Override
public void onSuccess(#NonNull Void T) {
Log.d("MessageActivity", "Message Sent");
Basically I would just like to receive the message object... with everything in it... when it arrives from the notification... and be able to easily parse the body, date, userids, etc.
Can someone explain the correct way to go about this?
UPATE By request here's the complete cloud function:
exports.sendMessage = functions.database.ref('/messages/{receiverUid}/{senderUid}/{msgId}/{message}')
.onWrite(async (change, context) => {
const message = context.params.message;
// const messageId = context.params.messageId;
const receiverUid = context.params.receiverUid;
const senderUid = context.params.senderUid;
// If un-follow we exit the function.
if (!change.after.val()) {
return console.log('Sender ', senderUid, 'receiver ', receiverUid, 'message ', message);
}
console.log('We have a new message: ', message, 'for: ', receiverUid);
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database()
.ref(`/users/${receiverUid}/notificationTokens`).once('value');
// Get the follower profile.
const getSenderProfilePromise = admin.auth().getUser(senderUid);
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
const results = await Promise.all([getDeviceTokensPromise, getSenderProfilePromise]);
tokensSnapshot = results[0];
const sender = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
console.log('Fetched sender profile', sender);
// console.log('David you're looking for the following UID:', followerUid);
// Notification details.
const payload = {
notification: {
title: `${sender.displayName} sent you a message.`,
body: message,
tag: senderUid
},
// 'data': { 'fuid': followerUid }
data: {
type: 'message',
name: sender.displayName
}
};
console.log('David you are looking for the following message:', message);
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
Since you now store the sender and receiver's UIDs inside the message, the declaration of your Cloud Function will need to change.
Instead of this:
exports.sendMessage = functions.database.ref('/messages/{receiverUid}/{senderUid}/{msgId}/{message}').onWrite(async (change, context) => {
You'll need to trigger on:
exports.sendMessage = functions.database.ref('/messages/{messageId}').onWrite(async (change, context) => {
So with this change your code will trigger on each message that is written /messages.
Now you "just" need to get the sender and receiver's UID. And since you no longer can get them from the context, you will instead get them from the change. Specifically change.after contains the data snapshot as it exists in the database after the write has completed. So (as long as you're not deleting the data), you can get the UIDs with:
const receiverUid = change.after.val().receiverUid;
const senderUid = change.after.val().senderUid;
And you'll also get the actual message from there of course:
const message = change.after.val().message;
And just in case you need the message ID (the -L... key that it was written under in the database):
const messageId = change.after.val().messageId;
You need a trigger on just the messageId:
exports.sendMessage = functions.database.ref('/messages/{messageId}').onWrite((change, context) => {
const changedData = change.after.val(); // This will have the complete changed data
const message = change.after.val().message; // This will contain the message value
......
});
Elaborating on Frank's answer:
You can't get the data from context like const message = context.params.message;because those parameters don't exists anymore on the context.

Send push notifications using Cloud Functions for Firebase

I am trying to make a cloud function that sends a push notification to a given user.
The user makes some changes and the data is added/updated under a node in firebase database (The node represents an user id). Here i want to trigger a function that sends a push notification to the user.
I have the following structure for the users in DB.
Users
- UID
- - email
- - token
- UID
- - email
- - token
Until now i have this function:
exports.sendNewTripNotification = functions.database.ref('/{uid}/shared_trips/').onWrite(event=>{
const uuid = event.params.uid;
console.log('User to send notification', uuid);
var ref = admin.database().ref('Users/{uuid}');
ref.on("value", function(snapshot){
console.log("Val = " + snapshot.val());
},
function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
When i get the callback, the snapshot.val() returns null. Any idea how to solve this? And maybe how to send the push notification afterwards?
I managed to make this work. Here is the code that sends a notification using Cloud Functions that worked for me.
exports.sendNewTripNotification = functions.database.ref('/{uid}/shared_trips/').onWrite(event=>{
const uuid = event.params.uid;
console.log('User to send notification', uuid);
var ref = admin.database().ref(`Users/${uuid}/token`);
return ref.once("value", function(snapshot){
const payload = {
notification: {
title: 'You have been invited to a trip.',
body: 'Tap here to check it out!'
}
};
admin.messaging().sendToDevice(snapshot.val(), payload)
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
})
Just answering the question from Jerin A Mathews...
Send message using Topics:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//Now we're going to create a function that listens to when a 'Notifications' node changes and send a notificcation
//to all devices subscribed to a topic
exports.sendNotification = functions.database.ref("Notifications/{uid}")
.onWrite(event => {
//This will be the notification model that we push to firebase
var request = event.data.val();
var payload = {
data:{
username: request.username,
imageUrl: request.imageUrl,
email: request.email,
uid: request.uid,
text: request.text
}
};
//The topic variable can be anything from a username, to a uid
//I find this approach much better than using the refresh token
//as you can subscribe to someone's phone number, username, or some other unique identifier
//to communicate between
//Now let's move onto the code, but before that, let's push this to firebase
admin.messaging().sendToTopic(request.topic, payload)
.then((response) => {
console.log("Successfully sent message: ", response);
return true;
})
.catch((error) => {
console.log("Error sending message: ", error);
return false;
})
})
//And this is it for building notifications to multiple devices from or to one.
Return this function call.
return ref.on("value", function(snapshot){
console.log("Val = " + snapshot.val());
},
function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
This will keep the cloud function alive until the request is complete. Learn more about returning promises form the link give by Doug in the comment.
Send Notification for a Topic In Cloud function
Topics a basically groups you can send notification for the selected group
var topic = 'NOTIFICATION_TOPIC';
const payload = {
notification: {
title: 'Send through Topic',
body: 'Tap here to check it out!'
}
};
admin.messaging().sendToTopic(topic,payload);
You can register the device for any new or existing topic from mobile side
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotificationToTopic =
functions.firestore.document('Users/{uuid}').onWrite(async (event) => {
//let title = event.after.get('item_name');
//let content = event.after.get('cust_name');
var message = {
notification: {
title: "TGC - New Order Recieved",
body: "A New Order Recieved on TGC App",
},
topic: 'orders_comming',
};
let response = await admin.messaging().send(message);
console.log(response);
});
For sending notifications to a topic, The above code works well for me, if you have any doubt, let me know.

Categories

Resources