Expo Push Notifications Generating Duplicate Tokens - push-notification

SDK Version: 37
Platforms(Android/iOS/web/all): all
Every time I run await Notifications.getExpoPushTokenAsync(); the same token is generated. The token looks like: ExponentPushToken[NgTr99YO5fy0EQM4R]. I am able to get push notifications to this token so I know it is formatted correctly, I am just confused as to why it keeps pushing the same token to me when it should generate a new one. What am I missing?
const registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const {status: existingStatus} = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const {status} = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
if (finalStatus !== "granted") {
return;
}
token = await Notifications.getExpoPushTokenAsync();
} else {
alert("Must use physical device for Push Notifications");
}
if (Platform.OS === "android") {
Notifications.createChannelAndroidAsync("default", {
name: "default",
sound: true,
priority: "max",
vibrate: [0, 250, 250, 250],
});
}
return token;
};
followed this documentation:
https://docs.expo.io/guides/push-notifications/

My mistake. The duplicate tokens were being created based off of what device i was using to create a new token. In my case I was making multiple profiles on the same device so I was receiving the same token for each profile which was throwing me off.
All is working as it should be.

Related

Flutter / Firebase delayed push notification

Hello I am Jr Flutter developer.
I made chat application with Flutter and Firebase, but I having some notification problem.
Below code is how I am sending notification, when message is created in Firebase database, I am pushing notification through the firebase function.
The problem is , it's is sending notification successfully , but Sometimes It is delayed few hours or few days!!
If which is not sending notification, I do understand , there are any bugs on my code,
But sometimes it delayed... mostly working fine.
How could I understand this situation ? Is there any way to manage notification speed ?
Thanks for reading , I will wait for your help.
exports.onCreateMessage = functions.firestore//Notification
.document('ChatRoom/{chatRoomID}/Messages/{message}')
.onCreate(async (snap, context) => {
const chatRoomID = context.params.chatRoomID;
const message = snap.data();
const chatRoomRef = await admin.firestore().collection('ChatRoom')
.doc(chatRoomID).get();
//setDate to Chatroom
chatRoomRef.ref.update({
latestMessageID: message.messageType === CHAT_MESSAGE_TYPE_EMOJI ? '[STICKER]' : message.message,
latestMessageTime: new Date()
});
const senderUserRef = await admin.firestore().collection('User').doc(message.senderID).get();
//getUserList add then number;
const joinedUserList = Object.entries(chatRoomRef.data().joinedUserList);//convert obejct to map.
joinedUserList.forEach(async (value, key, map) => {
if (value[0] !== message.senderID) {
const joinedChatRoomRef = await admin.firestore()
.collection('UserJoinedChatRooms').doc(value[0]).collection('JoinedChatRoomList')
.doc(chatRoomID).get();
await admin.firestore()
.collection('UserJoinedChatRooms').doc(value[0]).collection('JoinedChatRoomList')
.doc(chatRoomID).update({
unReadMessageCount: joinedChatRoomRef.data().unReadMessageCount + 1,
latestMessageTime: new Date(),
isInTheChatRoom: true,
});
return admin.messaging().sendToTopic(`${value[0]}`, {
notification: {
title: senderUserRef.data().name,
body: message.messageType === CHAT_MESSAGE_TYPE_EMOJI ? '[STICKER]' : message.message,
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
sound: 'default'
}
, data: {
notificationType: message.messageType.toString()
}
});
}
else {
await admin.firestore()
.collection('UserJoinedChatRooms').doc(value[0]).collection('JoinedChatRoomList')
.doc(chatRoomID).update({
latestMessageTime: new Date(),
});
}
});
});
there is no speed in firebase notification as long as you submit the data to firebase and the user is online it will be displayed.

How do you get to the values of ALL the tokens on your server?

I'd like to get the collection of ALL my FCM user iOS device tokens from this path in my Firebase Database:
BootCamp/Notifications/iOS
At this location, an autoIDChild is created to store the users' device tokens as "deviceToken".
I've been trying to follow the cloud_functions example at this link, but as my use-case is different it's been a little tough to figure out. Here's my cloud-function code in JS:
exports.iOSPush = functions.database.ref('/BootCamp/Bulletins/date').onWrite((snapShot, context) =>{
let tokensSnapShot
let tokens
//here, I attempt to get access to all iOS tokens on my server
const getTokens = admin.database().ref('/BootCamp/Notifications/iOS/{key}').once('value');
return Promise.all([getTokens]).then( (results) => {
tokensSnapShot = results[0]
tokens = Object.keys(tokensSnapShot)
const payload = {
notification:{
title: 'congrats it works',
body: 'Cloud function noti for ios',
sound: 'default',
badge: '1'
}
};
//tokens value in the console log is: "node_,ref_,index_". I was expecting an array of tokens:/
return admin.messaging().sendToDevice(tokens, payload)
})
});
How do I get to these iOS tokens on my server?
It finally occurred to me that I had to name the childPath the same as the device token instead of a randomly generated childID.
Please check this example:
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);
});

How do I properly grab the users profile from Realtime Database to get their username before the cloud function returns?

I am implementing Cloud Functions to send my users notifications for when interesting things happen like following, liking, commenting. I have copied & adapted the Firebase tutorial for sending a notification when a change at the followers node is detected, but I need to also query the database to get the follower's account data including their username. I think I am close, but the function doesn't finish in time and I'm having trouble understanding promises. Here is the function:
exports.sendFollowerNotification = functions.database.ref(`/userFollowers/{followedUid}/{followerUid}`)
.onWrite((change, context) => {
const followerUid = context.params.followerUid;
const followedUid = context.params.followedUid;
// If un-follow we exit the function
if (!change.after.val()) {
return console.log('User ', followerUid, 'un-followed user', followedUid);
}
console.log('We have a new follower UID:', followerUid, 'for user:', followedUid);
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database()
.ref(`/users/${followedUid}/notificationTokens`).once('value');
console.log('Found the followed user\'s token')
const userInfo = admin.database().ref(`/users/${followedUid}`).once('value');
console.log(userInfo)
const username = userInfo['username'];
console.log(username);
////////////////// ABOVE is where I'm trying to get the username by reading their account data ///////////////////
// Get the follower profile.
const getFollowerProfilePromise = admin.auth().getUser(followerUid);
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
return Promise.all([getDeviceTokensPromise, getFollowerProfilePromise]).then(results => {
tokensSnapshot = results[0];
const follower = 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 follower profile', follower);
// Notification details.
const payload = {
notification: {
title: 'You have a new follower!',
body: `{username} is now following you.`,
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload);
}).then((response) => {
// 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);
});
});
How can I ensure that username will have been made available before it returns? Thanks.
Ok, I think I get what you are saying...
These lines of code don't do what you think. All DB reads are done asynchronous, so...
const userInfo = admin.database().ref(`/users/${followedUid}`).once('value');
console.log(userInfo)
const username = userInfo['username'];
console.log(username);
once returns a promise, so userInfo is actually a promise to return the data. You won't get the data until you do a then.
More chaining promises I'm afraid... just rename userInfo to userInfoPromise and add it to your Promise.All array.

fetch api not working after Network change - react native android

I have one application in which user can login and see some info.
Issue:
user able to login and see info and logout any number of times before changing network.but once user change network(from wifi to mobile or vice versa) and try to login and fetch information. it's throwing error:Network request failed at XMLHttpRequest.xhr.onerror (fetch.js:441)
Note: i am using fetch api for network call.
Fetch Api call:
export const request = async function request(path, body = null, method = 'GET') {
try {
const headers = {
'Content-Type': body instanceof FormData ? 'multipart/form-data' : 'application/json',
};
const token = await AsyncStorage.getItem('authToken');
if (token) {
headers.Authorization = token;
}
const config = {
method,
url: Config.API_URL + path,
headers,
};
if (!['HEAD', 'GET'].includes(method.toUpperCase())) {
config.body = body instanceof FormData ? body : JSON.stringify(body);
}
const response = await fetch(Config.API_URL + path, config);
const data = await response.json();
if (response.status >= 400) {
throw data.error;
}
return data;
} catch (e) {
console.log('Error', path, e);
return Promise.reject(e);
}
Error
Network request failed
at XMLHttpRequest.xhr.onerror (fetch.js:441)
at XMLHttpRequest.dispatchEvent (event-target.js:172)
at XMLHttpRequest.setReadyState (XMLHttpRequest.js:567)
at XMLHttpRequest.__didCompleteResponse (XMLHttpRequest.js:397)
at XMLHttpRequest.js:503
at RCTDeviceEventEmitter.emit (EventEmitter.js:179)
at MessageQueue.__callFunction (MessageQueue.js:351)
at MessageQueue.js:116
at MessageQueue.__guardSafe (MessageQueue.js:314)
SDK Version:
compileSdkVersion: 25
buildToolsVersion: "25.0.2"
minSdkVersion: 16
targetSdkVersion: 25
I am beginner in react native. not able to identify the problem. any help will be appreciate.
Thanks

Notifications not showing up on database trigger

I am using firebase cloud functions for sending notifications to users when database trigger is called.The registration token is saved in firebase database. The problem is that inspite of registration tokens getting saved, the notifications are not showing up in these devices.
This is index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/Blog').onWrite(event => {
const title = event.data.child('title').val();
const token_no = event.data.child('token_no').val();
const getDeviceTokensPromise = admin.database().ref(`/Token/${token_no}`).once('value');
const getBody=admin.database().ref(`/Blog`).once('value');
return Promise.all([getDeviceTokensPromise,getBody]).then(results => {
const tokensSnapshot = results[0];
const notify=results[1];
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.');
// Notification details.
const payload = {
notification: {
title: 'You have a new Alert!',
body: `${notify.child('title').val()}`,
}
};
// Listing all tokens.
const tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// 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);
});
});
});
Database snapshot:
"Token" : {
"token_no" : {
"ctxjePemYZE:APA91bFJXXyTkyvXOlSY...4VbWu7Vbf7itwbwZu9umSvg_tdB1lKD1d8g" : "true",
"dgVszInjIW0:APA91bFZE3Av5unZTgp...RUeYb-CUhWjO1otqguhE9NTQZ8XgK6nRIW5" : "true"
}
}
Update 2:
Your code uses Object.keys(tokensSnapshot.val()) to get the tokens. That means the tokens must be the keys under token_no, not the values. Like this:
"Token" : {
"-KoWsMn9rCIitQeNvixr" : {
"dK1FjGbNr6k:APA91b...S8JK2d69JpO" : "123" // token is the key, value is not significant; could be "123", true, or 0.
},
...
}
Update:
You should review the documentation for the Event parameter of a database trigger to get a better understanding of the params and data properties. params provide acces to the wildcards in the trigger reference, data is the snapshot. In your code, you want to get values from the snapshot.
Add these changes:
const title = event.data.child('title').val();
const desp = event.data.child('desp').val();
const token_no = event.data.child('token_no').val()
const payload = {
notification: {
title: 'You have a new Alert!',
body: `${Post.child('title').val()}`, // <= CHANGED
//icon: follower.photoURL
}
};
Running the code you posted, with these changes, I am able to send and receive a notification.
There are two problems. The first is this statement:
const getDeviceTokensPromise = admin.database().ref(`/Token/{token_no}`).once('value');
token_no is not defined. And when you do define it and want to substitute its value, you will need to add a $:
`/Token/${token_no}`
The second problem is that Post is not defined, which causes function execution to fail. Check your function logs:
const payload = {
notification: {
title: 'You have a new Alert!',
body: `${Post.title}`,
//icon: follower.photoURL
}
};

Resources