So I am attempting to use Firebase cloud messaging to send push notifications to my app. However, it seems to fail continuously for some odd reason. I am using the onCreate method to listen for the creation of a node under a certain path. It is getting the UID from that info gathered in the onCreate method but then when I try to get the user info from a different node to grab the fcmToken so I can send the notification. It always says undefined.
My index.js looks like this:
//listen for reply notification and then trigger a push notification
exports.observeNotification = functions.database.ref('/notifications/{uid}/{commentId}/')
.onCreate(event => {
// Grab the current value of what was written to the Realtime Database.
//const notification = event.data.val();
console.log('User ID is: ', event.params.uid);
console.log('Comment ID Is: ', event.params.commentId);
return admin.database().ref('/users/' + event.params.uid).once('value', snapshot => {
var userWeAreSendingTo = snapshot
console.log('User ID is: ', userWeAreSendingTo);
console.log('FCM Token of that user is: ', userWeAreSendingTo.fcmToken);
var payload = {
notification: {
title: "",
body: snapshot.content
}
};
admin.messaging().sendToDevice(userWeAreSendingTo.fcmToken, payload)
.then((res) => {
// Response is a message ID string.
console.log('Successfully sent message:', res);
return true
})
.catch((error) => {
console.log('Error sending message:', error);
return true
})
})
})
This is a snapshot of the child node under the user node in my database.
"9mzAHeX3lcdzriPdC4TfbRTkaUm2" : {
"fcmToken" : "coC8uorosgc:APA91bGur2vvH4fwIProh87pUzVw0jYTOOFW3KfqWRVk4WdX0x8M1iBFwg28wM3tFyB5iRrowTWCZ_45oGwo0_7BFD6YvULE30NNZXxvE2O2XLjlLd_fqYwMfkndOqSUem2HqO-qvNcZ",
"profilePic" : "https://firebasestorage.googleapis.com/v0/b/eventful-3d558.appspot.com/o/profile_images%2F832F8156-C14A-4EF3-86EB-D8F4CFC784E5.PNG?alt=media&token=b9d11ea1-8cb3-4d0f-b111-b0baf567ac7b",
"username" : "lol123"
}
This screenshot of the console log
enter image description here
Related
I've been trying to figure this out for hours and I just can't. I'm still a beginner with Node.js and Firebase. I need your help to be able to retrieve the tokens array in my "userdata" collection to Node.js and be able to use it to send notifications in the Cloud Function. So far this is what I've been working on. Here is what my database looks like:
The receiverId is gathered from when I have an onCreate function whenever a user sends a new message. Then I used it to access the userdata of a specific user which uses the receiverId as their uid.
In the cloud function, I was able to start the function and retrieve the receiverId and print the userToken[key]. However, when I try to push the token it doesnt go through and it results in an error that says that the token is empty. See the image:
Your help would mean a lot. Thank you!
newData = snapshot.data();
console.log("Retrieving Receiver Id");
console.log(newData.receiverId); //uid of the user
const tokens = [];
const docRef = db.collection('userdata').doc(newData.receiverId);
docRef.get().then((doc) => {
if (doc.exists) {
console.log("DocRef exist");
const userToken = doc.data().tokens;
for(var key in userToken){
console.log(userToken[key]);
tokens.push(userToken[key]);
}
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch((error) => {
console.log("Error getting document:", error);
});
//Notification Payload
var payload = {
notification: {
title: newData.sendBy,
body: 'Sent you a message',
sound: 'default',
},
data: {
click_action : 'FLUTTER_NOTIFICATION_CLICK',
route: '/telconsultinbox',
}
};
console.log("Sending Notification now.");
console.log(tokens);
try{
//send to device
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
console.log(newData.sendBy);
}catch(err){
console.log(err);
}
I think you should avoid using for..in to iterate through an array (you can read more about it in this answer). Try one of these 2 options:
You could use forEach(), which is more elegant:
userToken.forEach((token) => {
console.log(token);
tokens.push(token);
});
for-of statement:
for(const token of userToken){
console.log(token);
tokens.push(token);
}
Also, I would consider renaming userToken to userTokens, since it should contain multiple values. Makes the code a bit more readable.
I'm developing an authentication system for my React app with Firebase Auth. When the user signs up, firebase auth registers the user (createUserWithEmailAndPassword) and returns a promise with auth.uid and auth.emailVerified set to "false". This is fine.
I then use sendEmailVerification() method so the email address can be verified. I've tested the code and it works ok for a "valid" and "existing" email address. "catch" does give error for duplicate email address as expected. The problem is, it does not send the NON-EXISTING email (which I suppose is the correct bahaviour) but its should then give an error (so I can display to the user) which it does not.
Can someone explain why I'm not getting error for NON EXISTENT email address?
export const unpSignUp = (newUser) => {
console.log("newUser", newUser);
return (dispatch, getState, { getFirebase, getFirestore }) => {
const firebase = getFirebase();
const firestore = getFirestore();
firebase.auth().createUserWithEmailAndPassword(
newUser.unp_a05_email,
newUser.unp_a10_password
)
.then((response) => {
return firestore
.collection("unps")
.doc(response.user.uid)
.set({
unp_a02_surname: newUser.unp_a02_surname,
unp_a03_name: newUser.unp_a03_name,
unp_a06_verify_email_sent_datetime: null,
unp_a07_email_verified_on_datetime: null,
unp_a18_acc_created_on_datetime: moment().format("YYYY MM DD HH:mm:ss"),
});
})
.then((response) => {
console.log("SIGNUP SUCCESS ", response);
// user has been signed up, next step is to send verification email
dispatch({ type: SIGNUP_SUCCESS })
})
.then(() => {
// user has been signed up, next step is to send verification email
console.log('send email adr verification')
return firebase.auth().currentUser.sendEmailVerification()
})
.then( (response) => {
console.log("Verification email sent", response);
const user = firebase.auth().currentUser
console.log('current user', user)
const unp = firestore.collection("unps").doc(user.uid);
return unp.update({
unp_a06_verify_email_sent_datetime: moment().format("YYYY MM DD HH:mm:ss"),
})
})
.then( () => {
console.log(`unp_a06_verify_email_sent_datetime update to ${moment().format("YYYY MM DD HH:mm:ss")} `)
})
.catch((error) => {
console.log("SIGNUP ERROR", error);
console.log("SIGNUP ERROR CODE", error.code);
console.log("SIGNUP ERROR MESAGE", error.message);
dispatch({ type: SIGNUP_ERROR, error });
});
};
};
From firebase reference,
the return type of sendEmailVerification is Promise<void> - and it does not tell about any error codes that are sent in case of wrong email or failed/bounce mails. The error code it mentions is with respect to actionCodeSettings
If the actionCodeSettings is not specified, no URL is appended to the action URL. The state URL provided must belong to a domain that is whitelisted by the developer in the console. Otherwise an error will be thrown.
Thus it can not be checked if it is a valid email id. (This is expected behaviour as receiving mail servers may be down at times and hence there is a concept of retrying bounce mails)
I'm building an iOS messenger app using Swift, Firebase and Nodejs.
My Goal:
Whenever a user sends a message and writes message data (such as senderId, receiverId, messageText) into a Firebase database inside node (/messages/{pushId}/), I want to make a message count increment by 1 using a transaction method that Firebase provides and display a notification to a receiver user.
Progress I've made so far and Problem I'm facing:
I've successfully increment message count (totalCount) using transaction method but I can't get value inside transaction result (Here's image of functions log )
I want to get "value_: 1"( this is the incremented message count) inside snapshot and put it to a badge parameter.
exports.observeMessages = functions.database.ref('/messages/{pushId}/')
.onCreate((snapshot, context) => {
const fromId = snapshot.val().fromId;
const toId = snapshot.val().toId;
const messageText = snapshot.val().messageText;
console.log('User: ', fromId, 'is sending to', toId);
return admin.database().ref('/users/' + toId).once('value').then((snap) => {
return snap.val();
}).then((recipientId) => {
return admin.database().ref('/users/' + fromId).once('value').then((snaps) => {
return snaps.val();
}).then((senderId) => {
return admin.database().ref('/user-messages/' + toId + '/totalCount').transaction((current) => {
return (current || 0) + 1
}).then((readCount) => {
console.log('check readCount:', readCount);
var message = {
data: {
fromId: fromId,
badge: //I want to display the message count here
},
apns: {
payload: {
aps: {
alert: {
title: 'You got a message from ' + senderId.username,
body: messageText
},
"content-available": 1
}
}
},
token: recipientId.fcmToken
};
return admin.messaging().send(message)
}).then((response) => {
console.log('Successfully sent message:', response);
return response;
})
.catch((error) => {
console.log('Error sending message:', error);
//throw new error('Error sending message:', error);
})
})
})
})
Does anyone know how to do this?
Thanks in advance.
The API documentation for transaction() suggests that the promise from the transaction will receive an object with a property snapshot with the snapshot of the data that was written at the location of the transaction. So:
admin.database.ref("path/to/count")
.transaction(current => {
// do what you want with the value
})
.then(result => {
const count = result.snapshot.val(); // the value of the count written
})
In my Firestore database I store DocumentReferences to users so that I am always using up-to-date user data such as username, profile pictures, and auth tokens.
I am also implementing Cloud Functions to listen for database triggers so that I can send notifications to those specific users about activity related to their posts.
This is where I run into trouble, because I do not know how to use the stored reference object properly inside the Node.js function when I access it like all other database information.
The following is my function code:
exports.countNameChanges = functions.firestore
.document('posts/{postId}')
.onUpdate((change, context) => {
// Retrieve the current and previous value
const data = change.after.data();
const previousData = change.before.data();
var registrationToken = '';
var notification = '';
var postTitle = data.statement;
var userRef = data.userRef; //This is my `DocumentReference` object
if (data.interactionCount > previousData.interactionCount && data.postTypeId == 2131165321) notification = 'You recieved a new comment!';
if (data.interactionCount > previousData.interactionCount && data.postTypeId == 2131165335) notification = 'You recieved a new vote!';
if (data.likes > previousData.likes) notification = 'You have a new post like!' ;
if (data.dislikes > previousData.dislikes) notification = 'You have a new post dislike!' ;
admin.firestore()
.doc(userRef) //This is my `DocumentReference` object
.get()
.then(doc => {
registrationToken = doc.data().token;
var payload = {
data: {
title: postTitle,
body: notification
},
token: registrationToken
};
admin.messaging().send(payload)
.then((response) => {
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
})
});
});
});
My Function Log
I would assume that the DocumentReference object would be easy to work with
inside a Cloud Function since the object is supported for direct storage into Firestore, but I can't figure it out.
If userRef is a DocumentReference type object, then just call get() on it directly. Don't pass it to doc(). You're only supposed to pass string type objects to doc().
userRef.get().then(...)
I tried sending a specific message to a client device using node js and firebase functions. But when I tried executing the function, it came back with an error saying:
Error. Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
The image is shown below.
I was guessing it's from my JS code. So I am posting that too. What I am actually do is retrieving a data from a specific node to be used when a totally different node is being written. So I am gonna post the JS code before the database screenshots.
exports.sendNotification8 = functions.database.ref('/Users/{user_id}/Notifications/')
.onWrite(( change,context) =>{
var user_id = context.params.user_id;
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var device_token = admin.database().ref('/Users/{user_id}/device_token').once('value');
return device_token.then(result => {
var token_id = result.val();
var str = eventSnapshot.from + " : " + eventSnapshot.message;
console.log(eventSnapshot.from);
var payload = {
data: {
name: str,
title: eventSnapshot.from,
click_action: "Chats"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToDevice(token_id, payload).then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});
And below is my database screenshots...
So that's how I am retrieving the device_token node. From the user that had the newest data written to his/her notifications node. Please help. What am I doing wrong?
Wow. This has been torture. But it finally worked. I got something like this.
exports.sendNotification8 = functions.database.ref('/Users/{user_id}/Notifications/{notifications_id}')
.onWrite((change,context) =>{
var user_id = context.params.user_id;
console.log(user_id);
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var device_token = admin.database().ref('/Users/'+user_id+'/device_token').once('value');
return device_token.then(result => {
var token_id = result.val();
console.log(token_id);
var str = eventSnapshot.message;
console.log(eventSnapshot.from);
var payload = {
data: {
name: str,
title: eventSnapshot.from,
click_action: "Chats"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToDevice(token_id, payload).then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});