am devloping a chat app with flutter and want to know which is best practice when creating a new room should i create a new topic and subscribe to this topic from the client app , or to use message trigger with devices token using sendtodevice method and sendmulticast
exports.messageTrigger = functions.firestore.document('/Messages/{messageID}').onCreate(
async (snapshot, context) => {
var currentRoomUsers = snapshot.data().members;
currentRoomUsers.forEach( (userID) => {
db.collection('Users').doc(userID).get().then(async(doc)=>{
if(doc.exists){
const message = {
notification: {
title: `New message from ${snapshot.data().room}`,
body: snapshot.data().body
},
data: {
click_action: 'FLUTTER_NOTIFICATION_CLICK'
},
tokens: doc.data()['Device_token'],
android: {
priority: "high"
},
priority: 10
}
const response2 = await admin.messaging().sendMulticast(message);
}else {
console.log("No such document!");
}
}).catch((error)=>{
console.log("Error getting document:", error);
});
}
);
i think there is a better way rather than what am actually doing maybe subscribing to topics but how to create a new topic and subscribe users when creating a new room
Related
I'm trying to send Expo push notifications to multiple devices. I'm retrieving the Expo tokens from Firestore. When I enter the tokens manually, it works! It sends the notification to both devices I'm using, but when I retrieve the data from Firestore, it only sends the notification to one device.
async function sendPushNotification(readx) {
const message = {
to: readx,
sound: "default",
title: "Original Title",
body: "And here is the body!",
data: { someData: "goes here" },
};
const retrieveNetwork = async () => {
try {
//const querySnapshot = await getDocs(collection(db, "cities"));
const q = query(collection(db, "users"));
const querySnapshot = await getDocs(q);
setRead(querySnapshot.docs.map((doc) => doc.data().expoUser));
setReadx(JSON.stringify(read));
} catch (e) {
alert(e);
}
};
The retrieving of data from the firestore seems to be an issue , as your code is using the Snapshot for querying the data ,it should get the token id for both the devices in the loop and then return to the await sync to call the notification function.As per the Firebase documentation on reading multiple documents, you'll see that it uses the data() function on each DocumentSnapshot to get at the fields of that document.
So try to modify accordingly,like use doc.role and doc.token instead of doc.data().role and doc.data().token.
Check this example code below:
let tokenList = []; const userNotificationTokenDocs = await db.collection("userToken").doc(userId).get() .then(querySnapshot => { querySnapshot.forEach((doc) => { console.log(doc.data().Tokens); tokenList.push(doc.data().Tokens); }); return null; });
Also you may try adding the below to your code:
userToken.forEach((token) => { console.log(token); tokens.push(token); });
Checkout these following with similar implementation:
Push notification firestore
Triggering expo sdk to push notification to users
Notification to a collection of token
Array token sending notification
Just solved. Need to change
<Button
title="Press to Send Notification"
onPress={async () => {
await sendPushNotification(expoPushToken);
}}
/>
to
<Button
title="Press to Send Notification"
onPress={async () => {
await sendPushNotification(readx);
}}
/>
I need to fetch the user token from the firestore in a cloud function.
the user token was stored as follows:
void saveToken(String token) async {
await FirebaseFirestore.instance
.collection("User tokens")
.doc(userId)
.set({'token': token});
}
here is the goal.
When a message is created on the collection 'chat messages',
grab the "Chat id" value and the user who sends the message "User id".
query the collection "chat" using the "Chat id" value,
grab the "Job users data" value (this is an array with two objects, each object contains the users involved in the chat (userName,userId) ).
from the "Job users data", I need to grab the userId of the member who should be receiving the message.
query "User tokens" collection to grab the "token" value.
use the "token" value, to send a notification to
here is my cloud function:
as you see, I have hardcoded the token to see if I could send that device a notification.... that works perfect. now I need to to make this dynamic...
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { database } = require("firebase-admin");
// eslint-disable-next-line max-len
const tokens = ["JNKDNASNDAUIU324234....."];
admin.initializeApp();
// exports.onCreate = functions.firestore
// .document("chat/{docId}")
// .onCreate((snapshot, context) => {
// console.log(snapshot.data());
// console.log("fake data");
// });
exports.onChatMessageCreate = functions.firestore
.document("chat messages/{docId}")
.onCreate( (snapshot, context) => {
console.log(snapshot.data());
// fetch user to send message to
// admin.database().ref("/")
const payload = {
// eslint-disable-next-line max-len
notification: {title: snapshot.data()["userName"], body: snapshot.data()["Chat message"], sound: "default"},
// eslint-disable-next-line max-len
data: {click_action: "FLUTTER_NOTIFICATION_CLICK", message: "Sample Push Message"},
};
try {
admin.messaging().sendToDevice(tokens, payload);
console.log("NOTIFICATION SEND SUCCESSFULLY");
} catch (e) {
console.log("ERROR SENDING NOTIFICATION");
console.log(e);
}
});
So all i need to know is how to query collections from a cloud function
There are 2 ways to query a collection in Node.js. either through then() or async/await.
to query using promise:
const collectionRef = admin.firestore().collection("yourCollection");
return collectionRef.get().then((collections) => {
//you can now use your collections here
});
using async/await:
const collectionRef = admin.firestore().collection("yourCollection");
const collections = await collectionRef.get();
I'm displaying a foreground notification using overlay_support but I wanted to change it to flutter_local_notification since the design is sexier than the former. However, I got stuck in this AndroidNotificationSettings that accepts channel_id and channel_name. Now the problem is where do I get these? I'm also sending a notification using cloud functions, how will I add that channel_id and channel_name from my payload.?
///my showNotification method where i need to fill the channel_id and channel_name
static void showNotificationOnForeground(RemoteMessage message) {
final notification = message.notification;
if (notification != null) {
const notificationDetails = NotificationDetails(
android: AndroidNotificationDetails('channel_id', 'channel_name',
importance: Importance.max, priority: Priority.high));
_notificationsPlugin.show(DateTime.now().microsecond, notification.title,
notification.body, notificationDetails);
}
}
//my cloud function used to send notification when message received
export const onPersonChatCreate = functions.firestore
.document("personChats/{personChatId}/chats/{chatId}")
.onCreate(async (snap, context)=> {
// message senderId
functions.logger.log(context);
const chat = snap.data();
const receiverId : string = chat.receiverId;
const senderName : string = chat.senderName;
try {
return await getUserToken(receiverId).then(async (tokens)=>{
//Where and how to add that channel_id and channel_name here?
const notificationBody = (chat.type === "text") ?
`${senderName} sent you: ${chat.content}` :
`${senderName} sent you: Media Attachment`;
const payload = {
"notification": {
title: `${senderName} sent you a message.`,
body: `${notificationBody}`,
sound: "default",
},
"data": {click_action:
// eslint-disable-next-line max-len
"FLUTTER_NOTIFICATION_CLICK"},
};
return await admin.messaging()
.sendToDevice(tokens, payload).then((response)=> {
return handleResponse(receiverId, response, tokens);
});
});
} catch (e) {
functions.logger.log(e);
throw e;
}
});
I am trying to create a mobile app that can connect to mobile wallets (ex: Metamask, TrustWalet,...) via WalletConnect, but i can't find anything.
Is there any way to implement walletconnect on flutter app yet?
You need walletconnect_dart and url_launcher
import 'package:url_launcher/url_launcher_string.dart';
import 'package:walletconnect_dart/walletconnect_dart.dart';
// Create a connector
final connector = WalletConnect(
bridge: 'https://bridge.walletconnect.org',
clientMeta: PeerMeta(
name: 'WalletConnect',
description: 'WalletConnect Developer App',
url: 'https://walletconnect.org',
icons: [
'https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media'
],
),
);
// Subscribe to events
connector.on('connect', (session) => print(session));
connector.on('session_update', (payload) => print(payload));
connector.on('disconnect', (session) => print(session));
// Create a new session
if (!connector.connected) {
final session = await connector.createSession(
onDisplayUri: (uri) async {
_uri = uri;
await launchUrlString(uri, mode: LaunchMode.externalApplication);
}
);
}
For more information please visit walletconnect_dart
dependencies:
wallet_connect: ^1.0.2
final wcClient = WCClient(
onConnect: () {
// Respond to connect callback
},
onDisconnect: (code, reason) {
// Respond to disconnect callback
},
onFailure: (error) {
// Respond to connection failure callback
},
onSessionRequest: (id, peerMeta) {
// Respond to connection request callback
},
onEthSign: (id, message) {
// Respond to personal_sign or eth_sign or eth_signTypedData request callback
},
onEthSendTransaction: (id, tx) {
// Respond to eth_sendTransaction request callback
},
onEthSignTransaction: (id, tx) {
// Respond to eth_signTransaction request callback
},
);
For More Check : Link
As in title, what is the current workaround in order to receive custom data when the user click on a notification when the app is terminated?
Seems like on Android is not possible to receive a data message in the onLaunch (which is the ideal way)
On IOS i hadn't tried yet since i'm facing this issue first.
Any clue?
Additional infos: The notification that i'm sending through a firebase cloud function are in this form:
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
},
"data" : {
"Nick" : "Mario",
"Room" : "PortugalVSDenmark"
}
}
}
https://firebase.google.com/docs/cloud-messaging/concept-options
on the onResume i'm able to perform an action, but the onLaunch seems to not be called.
This is the format that your payload should take with data included. It does work as this is how I have gotten it to work in my project. Additionally to get everything to work correctly, there is an answer here that might be helpful for general project setup:
{
notification: {
title: 'A title',
body: 'The body.'
},
data: {
// your data here
},
android: {
ttl: 3600 * 1000,
notification: {
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
},
// The below is for iOS specific properties
// apns: {
// payload: {
// aps: {
// badge: 42
// }
// }
// },
tokens: [] // or a single 'token'
}
I think, you already getting data onLaunch, just need to wait a little bit. I wrote it because I came across the same thing.
Can you try this,
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async { .. },
onResume: (Map<String, dynamic> message) async { .. },
onLaunch: (Map<String, dynamic> message) async {
WidgetsBinding.instance.addPostFrameCallback((_) {
Timer(const Duration(seconds: 7), () {
// your process...
});
});
},
);
Also in my situation, i used it like that, and solved.
onLaunch: (Map<String, dynamic> message) async {
WidgetsBinding.instance.addPostFrameCallback((ignored) {
Timer.periodic(const Duration(seconds: 1), (timer) {
if (CurrentUser.currentUser != null) {
timer.cancel(); // important **
// redirect process with post details inside data
if (message['data']['type'] == NotifTypes.NEW_POST)
goGroupAndPost(Map.from(message['data']));
}
});
});
},
Telling if your Activity is in the foreground (and reacting differently) can be a little tricky. My favorite way is to register a local BroadcastReceiver in the Activity and then do a sendBroadcast from the service. You will get true from that method if your receiver is running (meaning your app is in the foreground, and false otherwise.
You can see an example of this (using GCM, but the logic is the same for FCM) Source
Also this is a great example MyFirebaseMessagingService