Firebase functions: send multiple notifications based on array elements - javascript

Its possible for me to send a notification to the reciever: idTo which is a string in the database. However, is it possible to use the array-field instead?: participants and send a notification to everyone in the array?
I store my users with their respective tokens at this path in firebase: Users->{userId}:
I've tried changing:
const idTo = doc.idTo
admin.firestore().collection('users').where('uid', '==', idTo).get().then(querySnapshot => {
to:
const participants = doc.participants
admin.firestore().collection('users').where('uid', 'arrayContains', participants).get().then(querySnapshot => {
Full code:
exports.sendNotification = functions.firestore
.document('messages/{roomId1}/room/{message}/message/{messageId}')
.onCreate((snap, context) => {
console.log('----------------start function--------------------')
const doc = snap.data()
console.log(doc)
const idFrom = doc.idFrom
const idTo = doc.idTo
const contentMessage = doc.message
// Get push token user to (receive)
admin.firestore().collection('users').where('uid', '==', idTo).get().then(querySnapshot => {
querySnapshot.forEach(userTo => {
console.log(`Found user to: ${userTo.data().uid}`)
if (userTo.data().pushToken) {
// Get info user from (sent)
admin.firestore().collection('users').where('uid', '==', idFrom).get().then(querySnapshot2 => {
querySnapshot2.forEach(userFrom => {
console.log(`Found user from: ${userFrom.data().uid}`)
const payload = {
notification: {
title: `${userFrom.data().name}`,
body: contentMessage,
badge: '1',
sound: 'default',
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
// badge: '1'
},
data: {
title: '',
content: '',
image: '',
uploader: '',
type: 'chat',
},
}
// Let push to the target device
admin.messaging().sendToDevice(userTo.data().pushToken, payload).then(response => {
return console.log('Successfully sent message:', response)
}).catch(error => {
console.log('Error sending message:', error)
})
})
return console.log('failed')
}).catch(error => {
console.log('Error sending message:', error)
})
} else {
console.log('Can not find pushToken target user')
}
})
return console.log('error: invalid path')
}).catch(error => {
console.log('Error sending message:', error)
})
return null
})
I'm thinking maybe I need to loop over the array for each of the users and somehow execute the push notification. Any ideas are welcome

var messageToSend=req.body.messageToSend;
var message = { //this may vary according to the message type (single recipient, multicast, topic, et cetera)
registration_ids:regTokens , // Id List if more then one recipent
//to : regTokens, //use if One recipient
data: { //This is only optional, you can send any data
score: '',
time: ''
},
notification:{
body : messageToSend
}
};
console.log(message);
fcm.send(message, function(err, response){
if (err) {
console.log("Something has gone wrong!",err);
} else {
console.log("Successfully sent with response: ", response);
}
});

Related

Firebase Signout Triggers Before Other Firebase Actions

Is there any reason why I'm being signed out before my firebase actions are done finishing?
What Should Happen:
I first make a post request to my server to update values in my db.
Then I update the users firebase email if its changed.
Then I update the email if its changed as well.
Then finally I want to sign the user out.
What Happens:
I instantly gets signed out and then get errors in my console because the other actions couldn't be completed.
I have tried to trail the .then() after my axios post as well but I still had the same issue of being instantly signed out.
export const updateUserData = (userData) => {
return (dispatch, getState, {getFirebase}) => {
const state = getState();
const firebase = getFirebase()
let user = firebase.auth().currentUser;
let cred = firebase.auth.EmailAuthProvider.credential(user.email, userData.oldPassword);
user.reauthenticateWithCredential(cred).then(() => {
axios.post('/updateUserData', {
uid: state.firebase.auth.uid,
email: userData.email,
firstName: userData.firstName,
lastName: userData.lastName,
devices: userData.devices,
}, {
headers: {
"Authorization": `${state.firebase.auth.stsTokenManager.accessToken}`,
'Content-Type': 'application/json',
},
withCredentials: true
}).catch(err => {
console.log("Failed Email Change: " + err)
});
}).then(() => {
if (state.firebase.auth.email !== userData.email) {
firebase.auth().currentUser.updateEmail(userData.email).then(() => {
console.log("Email Changed")
}).catch(err => {
console.log("Failed Email Change: " + err)
});
}
}).then(() => {
if (userData.newPassword.length !== 0) {
firebase.auth().currentUser.updatePassword(userData.newPassword).then(() => {
console.log("Successful Password Change")
}).catch(err => {
console.log("Failed Password Change: " + err)
});
}
}).then(() => {
firebase.auth().signOut().then(null)
})
}
}
You aren't returning values from your promise chains. If you want an async action to take place after another one when using Promises, you need to return them:
// don't do this
doThing().then(() => {
doSomethingElse().then(() => { /* ... */ });
}).then(() => {
// this will happen before doSomethingElse is finished
finallyDoThing();
});
// instead do this
doThing().then(() => {
return doSomethingElse();
}).then(() => {
return finallyDoThing();
});

How do I write the try-catch properly in my google-cloud-function (javascript)

I'm trying to firebase deploy this function from Flutter chat app extended — push notification messages Guide on medium.com. I've copy/pasted it on but get these errors and warnings in return. I've been trying to add the try-catch statements, but it stills fails. Can anyone help me?
7:29 warning Expected to return a value at the end of arrow function consistent-return
19:9 error Expected catch() or return promise/catch-or-return
24:17 error Each then() should return a value or throw promise/always-return
29:17 error Expected catch() or return promise/catch-or-return
29:17 warning Avoid nesting promises promise/no-nesting
34:25 error Each then() should return a value or throw promise/always-return
46:23 warning Avoid nesting promises promise/no-nesting
46:23 warning Avoid nesting promises promise/no-nesting
49:31 error Each then() should return a value or throw promise/always-return
The function.
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
exports.sendNotification = functions.firestore
.document('messages/{groupId1}/{groupId2}/{message}')
.onCreate((snap, context) => {
console.log('----------------start function--------------------')
const doc = snap.data()
console.log(doc)
const idFrom = doc.idFrom
const idTo = doc.idTo
const contentMessage = doc.content
// Get push token user to (receive)
admin
.firestore()
.collection('users')
.where('id', '==', idTo)
.get()
.then(querySnapshot => {
querySnapshot.forEach(userTo => {
console.log(`Found user to: ${userTo.data().nickname}`)
if (userTo.data().pushToken && userTo.data().chattingWith !== idFrom) {
// Get info user from (sent)
admin
.firestore()
.collection('users')
.where('id', '==', idFrom)
.get()
.then(querySnapshot2 => {
querySnapshot2.forEach(userFrom => {
console.log(`Found user from: ${userFrom.data().nickname}`)
const payload = {
notification: {
title: `You have a message from "${userFrom.data().nickname}"`,
body: contentMessage,
badge: '1',
sound: 'default'
}
}
// Let push to the target device
admin
.messaging()
.sendToDevice(userTo.data().pushToken, payload)
.then(response => {
console.log('Successfully sent message:', response)
})
.catch(error => {
console.log('Error sending message:', error)
})
})
})
} else {
console.log('Can not find pushToken target user')
}
})
})
return null
})
Got everything working:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
exports.sendNotification = functions.firestore
.document('messages/{groupId1}/{groupId2}/{message}')
.onCreate((snap, context) => {
console.log('----------------start function--------------------')
const doc = snap.data()
console.log(doc)
const idFrom = doc.idFrom
const idTo = doc.idTo
const contentMessage = doc.message
// Get push token user to (receive)
admin.firestore().collection('users').where('uid', '==', idTo).get().then(querySnapshot => {
querySnapshot.forEach(userTo => {
console.log(`Found user to: ${userTo.data().uid}`)
if (userTo.data().pushToken) {
// Get info user from (sent)
admin.firestore().collection('users').where('uid', '==', idFrom).get().then(querySnapshot2 => {
querySnapshot2.forEach(userFrom => {
console.log(`Found user from: ${userFrom.data().uid}`)
const payload = {
notification: {
title: `You have a message from "${userFrom.data().uid}"`,
body: contentMessage,
badge: '1',
sound: 'default'
}
}
// Let push to the target device
admin.messaging().sendToDevice(userTo.data().pushToken, payload).then(response => {
return console.log('Successfully sent message:', response)
}).catch(error => {
console.log('Error sending message:', error)
})
})
return console.log('failed')
}).catch(error => {
console.log('Error sending message:', error)
})
} else {
console.log('Can not find pushToken target user')
}
})
return console.log('error: invalid path')
}).catch(error => {
console.log('Error sending message:', error)
})
return null
})

Javascript if conditional variable problem

I have two receiver type. 0 for guests 1 for users. I'm trying to push notification from firebase cloud functions. I can send notification user to guest succesfully. But can not send notification guest to user. Is there any problem with my if condition?
var usersRef = null;
if (receiverType === 0) {
usersRef = admin.firestore().collection('Guests').doc(receiverId);
} else {
usersRef = admin.firestore().collection('Users').doc(receiverId);
}
I want to change var usersRef variable based on receiverType. I managed to change it to Guest path but on the else part its path must change again to Users. But not changing. There is a problem in my if else statement.
var getDoc = usersRef.get()
.then(doc => {
if (!doc.exists) {
return null;
} else {
const token = doc.data().token;
const payload = {
notification: {
title: "New Message",
body: chatMessage,
}
};
admin.messaging().sendToDevice(token, payload)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
return null;
})
.catch((error) => {
console.log('Error sending message:', error);
return null;
});
}
return null;
})
.catch(err => {
console.log('Error getting document', err);
return null;
});
return null;
});

Cloud Functions for Firebase - Send FCM message to multiple tokens

I have to send a message to many token when a node is created in my realtime database.
I use that's code, but any notification are lost (people not receive its).
exports.sendMessage = functions.database.ref('/messages/{messageId}')
.onCreate((snapshot, context) => {
const original = snapshot.val();
let msg = {
message: {
data: {
title: 'title2 test',
body: 'body2 test',
notify_type: 'chat_message',
notify_id: ((new Date()).getTime()).toString(),
},
apns: {
headers: {
'apns-priority': '10',
'apns-expiration': '0'
},
payload: {
aps: { contentAvailable: true, sound:'' },
'acme1': 'bar',
title: 'title test',
body: 'body test',
notify_type: 'chat_message',
notify_id: ((new Date()).getTime()).toString()
}
},
token: token
}
};
var query = firebase.database().ref("users");
return query.once("value")
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var user = childSnapshot.val();
var token = user.token;
var username = user.username;
msg.message.token = token;
admin.messaging().send(msg.message).then((response) => {
console.log('message sent to '+username);
}).catch((error) => {
console.log(error);
});
});
});
});
Is the "return" Promise right ? I think I have to wait all "admin.messagging() Promise, but I don't know how can I do this.
Thank you so much.
This is how you send a FCM to an array of tokens:
return Promise.all([admin.database().ref(`/users/${user}/account/tokensArray`).once('value')]).then(results => {
const tokens = results[0];
if (!tokens.hasChildren()) return null;
let payload = {
notification: {
title: 'title',
body: 'message',
icon: 'icon-192x192.png'
}
};
const tokensList = Object.keys(tokens.val());
return admin.messaging().sendToDevice(tokensList, payload);
});
You can send notifications to an array of tokens. I am using this code to send notifications
return admin.messaging().sendToDevice(deviceTokenArray, payload, options).then(response => {
console.log("Message successfully sent : " + response.successCount)
console.log("Message unsuccessfully sent : " + response.failureCount)
});
I think you can find more information here
https://firebase.google.com/docs/cloud-messaging/admin/legacy-fcm
To return a Promise for all the send actions, modify your code to this:
return query.once("value")
.then(function(snapshot) {
var allPromises = [];
snapshot.forEach(function(childSnapshot) {
var user = childSnapshot.val();
var token = user.token;
var username = user.username;
msg.message.token = token;
const promise = admin.messaging().send(msg.message).then((response) => {
console.log('message sent to '+username);
}).catch((error) => {
console.log(error);
});
allPromises.push(promise);
});
return Promise.all(allPromises);
});

Inconsistent results from Firestore Cloud Functions

I have a Cloud Function setup on Firebase that involved checking different parts of the Firestore Database and then sending a message via Cloud Messaging
Below is the JavaScript for the function in question:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().Firebase);
var db = admin.firestore();
exports.newMemberNotification = functions.firestore
.document('Teams/{teamId}/Waitlist/{userId}').onDelete((snap, context) => {
// get the user we want to send the message to
const newValue = snap.data();
const teamidno = context.params.teamId;
const useridno = newValue.userID;
//start retrieving Waitlist user's messaging token to send them a message
var tokenRef = db.collection('Users').doc(useridno);
tokenRef.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
} else {
const data = doc.data();
//get the messaging token
var token = data.messaging_token;
console.log("token: ", token);
//reference for the members collection
var memberRef = db.collection('Teams/'+teamidno+' /Members').doc(useridno);
memberRef.get()
.then(doc => {
if (!doc.exists){
console.log('user was not added to team. Informing them');
const negPayload = {
data: {
data_type:"team_rejection",
title:"Request denied",
message: "Your request to join the team has been denied",
}
};
return admin.messaging().sendToDevice(token, negPayload)
.then(function(response){
console.log("Successfully sent rejection message:", response);
return 0;
})
.catch(function(error){
console.log("Error sending rejection message: ", error);
});
} else {
console.log('user was added to the team. Informing them')
const payload = {
data: {
data_type: "team_accept",
title: "Request approved",
message: "You have been added to the team",
}
};
return admin.messaging().sendToDevice(token, payload)
.then(function(response){
console.log("Successfully sent accept message:", response);
return 0;
})
.catch(function(error){
console.log("Error sending accept message: ", error);
});
}
})
.catch(err => {
console.log('Error getting member', err);
});
}
return 0;
})
.catch(err => {
console.log('Error getting token', err);
});
return 0;
});
The issues I have with this are:
The code runs and only sometimes actually checks for the token or sends a message.
the logs show this error when the function runs: "Function returned undefined, expected Promise or value " but as per another Stack Oveflow posts, I have added return 0; everywhere a .then ends.
I am VERY new to node.js, javascript and Cloud Functions so I am unsure what is going wrong or if this is an issue on Firebase's end. Any help you can give will be greatly appreciated
As Doug said, you have to return a promise at each "step" and chain the steps:
the following code should work:
exports.newMemberNotification = functions.firestore
.document('Teams/{teamId}/Waitlist/{userId}').onDelete((snap, context) => {
// get the user we want to send the message to
const newValue = snap.data();
const teamidno = context.params.teamId;
const useridno = newValue.userID;
//start retrieving Waitlist user's messaging token to send them a message
var tokenRef = db.collection('Users').doc(useridno);
tokenRef.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
throw 'No such document!';
} else {
const data = doc.data();
//get the messaging token
var token = data.messaging_token;
console.log("token: ", token);
//reference for the members collection
var memberRef = db.collection('Teams/' + teamidno + '/Members').doc(useridno);
return memberRef.get()
}
})
.then(doc => {
let payload;
if (!doc.exists) {
console.log('user was not added to team. Informing them');
payload = {
data: {
data_type: "team_rejection",
title: "Request denied",
message: "Your request to join the team has been denied",
}
};
} else {
console.log('user was added to the team. Informing them')
payload = {
data: {
data_type: "team_accept",
title: "Request approved",
message: "You have been added to the team",
}
};
}
return admin.messaging().sendToDevice(token, payload);
})
.catch(err => {
console.log(err);
});
});

Categories

Resources