How to use react-native-gifted-chat with cloud firestore? - javascript

I am having problem of react-native-gifted-chat by using cloud firestore. I am unable to fetch previous messages and append to the gifted chat. please show me the code that how it is used with the cloud firestore.
thanks

I have been able to get this to work on my app using a similar method to that found at GitHib repo
My code calls a loadMessages function in componentDidMount which uses onSnapshot to keep track of any changes in my Message or Chats collections. If a change occurs it uses a callback function to append the new messages to GiftedChat.
Here is my code:
async componentDidMount() {
this.loadMessages(message => {
this.setState(previousState => {
return {
messages: GiftedChat.append(previousState.messages, message)
};
});
});
}
async loadMessages(callback) {
var that = this;
var recipientId = this.props.navigation.getParam("recipientId");
var chatId = this.generateChatID(recipientId);
this.setState({ chatId });
firebase
.firestore()
.collection("Message")
.doc(chatId)
.collection("Chats")
.orderBy("createdAt", "asc")
.onSnapshot(function(doc) {
doc.docChanges().forEach(chat => {
var id = chat.doc.id;
chat = chat.doc.data();
const newMessage = {
_id: id,
text: chat.text,
createdAt: chat.createdAt.toDate(),
user: {
_id: chat.user._id,
name: chat.user.name,
avatar: chat.avatar
}
};
callback(newMessage);
});
});
}
Lmk if you have any questions!

Related

Trouble batch setting a document field by docId in Firestore

I have been using firebase (firestore) for a while but I'm a little stuck and was wondering if anyone can think of a solution.
On the firestore DB I have a single collection of users, each user has an email address and several other fields. In this instance I am checking if a user email exists and if it does, I want to create a list field for that particular user with a listUid. I am referencing the users by email, grabbing the docId for those users and then trying to set a list field for each of them.
I am not getting any error's from firestore, it's simply not updating in the DB for some reason and I can't figure out where I am going wrong. Thanks in advance
export const addListUidToExistingUserList = (
{ firestore },
emailArray,
listUid
) => {
return async () => {
let docIds = [];
emailArray.forEach((emailAddress) => {
//find users by email (works)
const query = db
.collection("users")
.where("email", "==", emailAddress);
//get docId's for user with matching email (works)
query.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
docIds.push(doc.id);
});
});
//add a new list with corresponding listUid (does not work)
docIds.forEach((id) => {
let userRef = db.collection("users").doc(id);
batch.set(userRef, { lists: [{ listUid }] });
});
});
return await batch.commit();
};
};
You are running into this issue because your docIds array is always empty at the time you call docIds.forEach.
That's because query.get().then runs asynchronously, and so docIds.forEach is not waiting for it to complete.
You could either:
await query.get().then; or
Add the docIds.forEach function INSIDE the then callback of query.get.
Here are your possible fixes:
await query.get().then
//get docId's for user with matching email (works)
await query.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
docIds.push(doc.id);
});
});
OR:
docIds.forEach inside then
//get docId's for user with matching email (works)
query.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
docIds.push(doc.id);
});
docIds.forEach((id) => {
let userRef = db.collection("users").doc(id);
batch.set(userRef, { lists: [{ listUid }] });
});
});
Note: Of course, you could also add batch.set directly into your first iteration of querySnapshot.docs.forEach to prevent an unnecessary iteration.

How to push data into subcollections in firebase

I'm creating a messaging app with react-native & firebase. Its working quite alright.
Now i want to take it a step further by creating private chats. To my understanding, i need to populate the data into subcollections. e.g : chats>chatroom1>data, chats>chatroom2>data e.t.c
I'm currently working with this code
import firebase from 'firebase';
class Fire {
constructor (props) {
this.init()
this.checkAuth()
}
init = () => {
if (!firebase.apps.length) {
}
};
checkAuth = () => {
firebase.auth().onAuthStateChanged(user => {
if (!user) {
firebase.auth().signInAnonymously();
}
})
}
send = messages => {
messages.forEach(item => {
const message = {
text: item.text,
timestamp: firebase.database.ServerValue.TIMESTAMP,
user: item.name
}
this.db.push(message)
})
}
parse = message => {
const {user, text, timestamp} = message.val();
const {key, _id} = message
const createdAt = new Date(timestamp)
return {
_id,
createdAt,
text,
user
}
}
get = callback => {
this.db.on('child_added', snapshot => callback(this.parse(snapshot)))
}
off() {
this.db.off()
}
get db() {
return firebase.database().ref("messages");
}
get uid(){
return(firebase.auth().currentUser || {}).uid
}
}
How can I populate the subcollections from this code?
I believe for calling out of specific subcollections, firebase.database().ref("messages/chatroomid"); will do the trick, right?
what i mean by sub collection is this
Currently my JSON tree looks like:
database
- messages
- mvhhsjsfurhcb
- text: "hi"
timestamp: 9942313949
user: "David"
firebase.database().ref("messages"); calls out the data under messages
This is what i want
database
- messages
-chatroom1
- mvhhsjsfurhcb
- text: "hi"
timestamp: 9942313949
user: "David"
-chatroom2
- mvhhsjsfurhcb
- text: "hey, this i room2"
timestamp: 9942313949
user: "Sam"
Then for firebase.database().ref("messages/chatroom1"); to call out only messages in chatroom1.
What I intend to achieve with this is to create a private chat for users
To give you more insight, if I was to do this with PHP, I would be doing SELECT * WHERE chatroom = :chatroom;
i believe the answer will be related to this.db.push(message). maybe adding another '.' indicating that there's another branch before pushing the message
If you want to push the chat message to a specific chat room, you can do:
this.db.child("chatroomid1").push(message)

firebase cloud functions for push notifications are not working

in my flutter app, i have saved every devices token to a collection in firebase database and i wrote the code for firebase cloud functions so it sends a message(notification) for every user that subscribed to a topic and have their token is in the tokens collection but it doesnt send anything when i add something to the topic i subscribed them to, heres my cloud functions code using javascrpit in the index file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.myTrigger = functions.firestore.document('messages/{messageId}').onCreate(async (snapshot, context) => {
//
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = snapshot.data();
const deviceIdTokens = await admin
.firestore()
.collection('messages')
.get();
var tokens = [];
for (var tokened of deviceIdTokens.docs) {
tokens.push(tokened.data().token);
}
var payload = {
notification: {
title: 'Push Title',
body: 'Push Body',
sound: 'default',
},
data: {
message: newData.message,
click_action: 'FLUTTER_NOTIFICATION_CLICK',
},
};
try {
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
});
and heres my database structure
and the tokens collection :
what am i doing wrong?
trying his instead :
i should have written :
'''
const deviceIdTokens = await admin
.firestore()
.collection('tokens')
.get()
'''
also for sending a message through the database i should have written "message" in the field because i named the "data"'s key "message" :)

Query current user .once('value') in Firestore

I am transitioning a Firebase real-time database to a Firebase Firestore database but am having trouble finding the appropriate reference to query the current user.
onAuthUserListener = (next, fallback) =>
this.auth.onAuthStateChanged(authUser => {
if (authUser) {
this.user(authUser.uid)
.once('value')
.then(snapshot => {
const dbUser = snapshot.val();
// default empty roles
if (!dbUser.roles) {
dbUser.roles = [];
}
// merge auth and db user
authUser = {
uid: authUser.uid,
email: authUser.email,
emailVerified: authUser.emailVerified,
providerData: authUser.providerData,
...dbUser,
};
next(authUser);
});
} else {
fallback();
}
});
Most specifically, what would be the replacement for once('value') and snapshot.val();?
I had thought that
.onSnapshot(snapshot => {
const dbUser = snapshot.val();
...
The equivalent of once('value' in Firestore is called get(), and the equivalent of val() is data(). Calling get() returns a promise, so:
.get().then(snapshot => {
const dbUser = snapshot.data();
...
If you have a collection of users, where the profile of each user is stored within a document with their UID as its ID, you can load that with:
firebase.firestore().collection('users').doc(authUser.uid)
.get()
.then(snapshot => {
const dbUser = snapshot.val();
Note that this is pretty well covered in the documentation on getting data, so I'd recommend spending some time there and potentially taking the Cloud Firestore codelab.

Firebase Multi path atomic update with child values?

I am succesfully updating my user's profile picture on their profile and on all of their reviews posted with this function:
export const storeUserProfileImage = (url) => {
const { currentUser } = firebase.auth();
firebase.database().ref(`/users/${currentUser.uid}/profilePic`)
.update({ url });
firebase.database().ref('reviews')
.orderByChild('username')
.equalTo('User3')
.once('value', (snapshot) => {
snapshot.forEach((child) => {
child.ref.update({ profilePic: url });
});
});
};
I am aware that I should be using an atomic update to do this so the data updates at the same time (in case a user leaves the app or something else goes wrong). I am confused on how I can accomplish this when querying over child values.
Any help or guidance would be greatly appreciated!
Declare a variable to store all the updates. Add the updates as you read them on your listener's loop. When the loop is finished, run the atomic update.
export const storeUserProfileImage = (url) => {
const { currentUser } = firebase.auth();
firebase.database().ref('reviews')
.orderByChild('username')
.equalTo('User3')
.once('value', (snapshot) => {
var updates = {};
updates[`/users/${currentUser.uid}/profilePic`] = url;
snapshot.forEach((child) => {
updates[`/reviews/${child.key}/profilePic`] = url;
});
firebase.database().ref().update(updates);
});
};

Categories

Resources