react native firebase How to check if chatroom already exist - javascript

Hi I would like to check if chatroom already exist. and I don't want to make another chatroom with the same person. I searched it a lot and I found this code but it didn't work for me. I can make a lot of chatroom with the same person.
same chatrooms
like that
firebase data
if you help me I'd really appreciate it.
const userChatRef = db
.collection('chats')
.where('users', 'array-contains', user.email);
const [chatsSnapshot] = useCollection(userChatRef);
const makeRoom = () => {
if (user.email !== email && !chatAlreadyExists(email)) {
db.collection('chats')
.add({
users: [user.email, email],
})
.then(doc => {
navigate(MESSAGEROOM, {id: doc.id, recipientEmail: email});
});
} else {
alert('Already exist!');
}
};
const chatAlreadyExists = recipientEmail => {
!!chatsSnapshot?.docs.find(
chat =>
chat.data().users.find(userEmail => userEmail === recipientEmail)?.length > 0,
);

Unfortunately you currently can't have more than one 'array-contains' statements in a firebase query. I would suggest to simply do the following:
const checkForDuplicateChat = (userid1, userid2) => {
const db = firebase.firestore()
// fetching all chats of bb#naver.com
// userid1 = e.g. bb#naver.com
let chats = db.collection('chats').where('users', 'array-contains', userid1).get()
// mapping the parsed json data in the array
data = data.docs.map(el => el.data())
// el.users.length === 2 to check if it is a chat existing only
// between user1 and user2 (excluding groupchats etc.)
// userid2 = f.e. dd#naver.com
data = data.filter(el => el.users.length === 2 && el.users.includes(userid2))
if(data.length > 0) return // what ever happens when there already is a chat
createChat(user1, user2)
}
I hope this helps at first, but keep in mind you will have a ton of reads when all users have a lot of chats, because you always have to fetch all the chats of the user before filtering out.
EDIT:
You also could add an Array to the document of each user, which lists all other users he has a chat with like:
bb#naver.com:{
mail: "bb#naver.com",
hasChatWith: [
"dd#naver.com",
"cc#naver.com",
"ee#naver.com"
]
}
So in this case you just have to check the array hasChatWith of bb#naver.com before he opens a chat. If you open a new chat make sure you add both users to eachothers list.

Related

How do I query a collection of firebase documents by a properties value?

I am a building a Firebase React social media app with a search bar, I want my searchbar to show suggestions of users based on the value of input. If I type "F" I expect Foo and then underneath Bar, pretty much like any social media search bar that filters the users and returns the relevant ones. . I am having trouble understanding the Firebase queries and what would be appropriate for this.
The Layout of the DB is
users collections
documents that represent a user
a username property on the document
const searchUser = async (text) => {
const queryUsers = [];
if (text !== '') {
try {
const usersRef = collection(firestore, "users");
const q = query(usersRef, orderBy("username"),startAt(text.toLowerCase()),limit(5))
const querySnapshot = await getDocs(q)
querySnapshot.forEach((doc) => {
queryUsers.push(doc.data())
})
} catch (error) {
console.error(error);
}
}
console.log(queryUsers);
return queryUsers;
};
I tried all sorts of queries but none of them worked, I am expecting to get all the users ordered by the value of the string that was sent to the query
Found the issue, the username was saved with a Capital letter. Not sure if this query is also the best one.

Firebase Funtions push notifications

I'm new to firebase and there is something I can't do. I want to send a notification to the phone with firebase functions. I want to receive notifications on the phone when someone follows me. My Firebase collection is as in the photo. I want to access the Followers array and send its information with notification. The codes I could write are as follows. What do I need to add?
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.sendPushNotification = functions.firestore.document('/users/{uid}').onCreate((snap, context) => {
var values = snap.data();
var token = values.fcmTokens;
var payload = {
notification: {
title: values.title,
body: values.message
}
}
return admin.messaging().sendToDevice(token, payload);
});
First, onCreate() function is triggered when a document is created. I assume followers array will be updated everytime someone follows a user? In that case you should be using onUpdate() that'll trigger the function when the document is updated. You can just check if length of followers array has changed in the update, if yes then send the notification as shown below:
exports.sendPushNotification = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const newValue = change.after.data();
const previousValue = change.before.data();
if (newValue.followers.length > previousValue.followers.length) {
// followers count increased, send notification
const token = newValue.fcmTokens;
const payload = {
notification: {
title: "New Follower",
body: "Someone followed you"
}
}
await admin.messaging().sendToDevice(token, payload);
}
return null;
});
Here, we send notification only if the followers field has changed since this function will trigger whenever any field in this user document is updated.
If you want to specify who followed the user, then you'll have to find the new UID added in followers array and query that user's data.
Firestore documents have a max size limit of 1 MB so if a user can have many followers then I'll recommend creating a followers sub-collection. Then you'll be able to use onCreate() on the sub-document path /users/{userId}/followers/{followerId}

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 get documents with user information (firestore)

I have collection with documents.
Structure (fields):
questions
- question
- userID
and my project uses Firebase Authentication. How to get questions with data about author of each question (avatar and name) in javascript.
You need to store every user in collection (ex. 'users' ) after register, and then make leftJoin with both questions and users collections.
something like this:
async function getQuestionsWithAuthors() {
const result = [];
const questionsSnap = await firebase.firestore().collection('questions').get();
for(const doc of questionsSnap.docs) {
const questionData = doc.data();
const userData = await firebase.firestore().collection('users').doc(questionData.userID).get();
result.push({...questionData, user: userData});
}
return result
}

How to scale push notifications in Firebase Cloud functions for this use case?

In my app, I am sending push notifications to the followers of a user when that user creates a new post. As you can see in the code below, I have some additional settings that I need to query from each follower's profile to get their push token and check for some additional notification settings. I am afraid that this query of each user's profile might become a bottleneck if a user has a large number of followers i.e. 1000.
What is the best way to approach this?
// The cloud function to trigger when a post is created
exports.newPost = functions.database.ref('/posts/{postId}').onCreate(event => {
const postId = event.params.postId;
const post = event.data.val();
const userId = post.author;
let tokens = [];
let promises = [];
return admin.database().ref(`/followers/${userId}`).once('value', (followers) => {
followers.forEach((f) => {
let follower = f.key;
promises.push(
admin.database().ref(`users/${follower}`).once('value')
);
});
})
.then(() => {
return Promise.all(promises).then((users) => {
users.forEach((user) => {
const userDetails = user.val();
if (userDetails.post_notifications) {
if(userDetails.push_id != null) {
tokens.push(userDetails.push_id);
}
}
})
})
})
.then(() => {
if (tokens.length > 0) {
const payload = {
notification: {
title: 'New Post!',
body: 'A new post has been created'
}
};
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload);
}
});
})
EDIT:
We have thought about using topics. But we are not sure how we can still have our customized notifications settings working with topics. Here's our dilemma.
We have multiple actions that can create notifications and we provide individual switches for each type of notification in the app so a user can select which type of notifications they want to switch off.
Let's say when User A follows User B. We can subscribe User A to "User B's topic" so whenever User B performs an action which sends out notifications to his/her followers, I can send send out notifications to users subscribed to "User B topic".
Because we have multiple notification switches in the app and when user A changes his/her settings that they do not want notifications for new posts but still want other types of notifications from the users he/she follows, we have not been able to figure out how we can use topics in this case.
Instead of using tokens, you can use topics for this. So lets say a user started following someone, then he will register to that topic.
Lets say he followed someone called "Peter", then you can execute this:
FirebaseMessaging.getInstance().subscribeToTopic("Peter");
Now if you have this database:
posts
postid
postdetails: detailshere
author: Peter
then using onCreate():
exports.newPost = functions.database.ref('/posts/{postId}').onCreate(event => {
const postId = event.params.postId;
const post = event.data.val();
const authorname = post.author;
const details=post.postdetails;
const payload = {
data: {
title:userId,
body: details,
sound: "default"
},
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
return admin.messaging().sendToTopic(authorname, payload, options);
});
You can use this, everytime the author creates a new post, onCreate() is triggered then you can add the details of the post and the author name in the notification (if you want) and sendToTopic() will send it to all users subscribed to the topic that is the authorname (ex: Peter)
After your edit, I think you want the user to be unsubscribed from a topic, but stay subscribed to other topics, then you have to use the admin sdk for this:
https://firebase.google.com/docs/cloud-messaging/admin/manage-topic-subscriptions
Using the admin sdk you can unsubscribe the user also from a topic, a simple example:
// These registration tokens come from the client FCM SDKs.
var registrationTokens = [
'YOUR_REGISTRATION_TOKEN_1',
// ...
'YOUR_REGISTRATION_TOKEN_n'
];
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
admin.messaging().unsubscribeFromTopic(registrationTokens, topic)
.then(function(response) {
// See the MessagingTopicManagementResponse reference documentation
// for the contents of response.
console.log('Successfully unsubscribed from topic:', response);
})
.catch(function(error) {
console.log('Error unsubscribing from topic:', error);
});

Categories

Resources