What would be the better way to write this dart iteration and return a list - flutter

The purpose is to do some sort of join. Get each user doc from the user document in firestore and attach it to some collection by uid. Then return the collection with the user document full details rather than just the uid.
Future<List<ChatRoom>> getChatsrooms(uid) async {
ChatRoom chatroom = new ChatRoom();
try {
List<ChatRoom> chatrooms = new List();
var snapshots = await _db
.collection('my-chats-chats')
.where('users', arrayContains: uid)
.orderBy('updated_at', descending: true)
.get();
for (var room in snapshots.docs) {
chatroom.chatroomid = room.data()['chatroomid'];
chatroom.users = room.data()['users'];
// get user profile that isn'nt u.
List<dynamic> userids = await room.data()['users']
..remove(uid);
var doc = await _db.collection('my-chats-users').doc(userids[0]).get();
UserModel user = UserModel.fromMap(doc.data());
chatroom.users.add(user);
// Remove the users string UID from users list
chatroom.users.remove(user.uid);
chatroom.users.remove(uid);
chatrooms.add(chatroom);
}
return chatrooms.toList();
} catch (e) {
print("Couldn't get user\'s chatrooms exception: " + e.toString());
return null;
}
}
The above seems to do what I want, except the chatrooms list returned, only contains duplicates of one chat room. So even there are 10 different chat rooms, I am only getting duplicates of one chatroom 10 times.
What I am thinking of is the chatrooms.add(chatroom); only adds one item. But the chatrooms list is supposed to be a growable list. So why is it ending up with just one duplicated item?
What would be the better way to write this?

Related

How to use Array contains in the same array fields in firebase for flutter

I have a chat collection.
each document has an array with two user id's.
my goal is to get the chat that has both user sys id's
I tried running the following but I got an error because we cant use two 'arrayContains' in one query.
Is there any way to perform such query?
here is an image of the data structure
Future getChat({required List userIdsArr}) async {
var docId = '';
userIdsArr.sort((a, b) {
return a.compareTo(b);
});
var filter1 = userIdsArr[0];
var filter2 = userIdsArr[1];
await chat
.where(userIdsArrayColumn, arrayContains: userIdsArr[0])
.where(userIdsArrayColumn, arrayContains: userIdsArr[1])
.get()
.then((value) {
value.docs.forEach((element) {
docId = element.id;
});
});
return docId;
}
the goal is to get the chat that pertains to the users being passed in userIdsArr
this seems to work, is there a better way of doing this?
Future getChat({required List userIdsArr}) async {
var docId = '';
userIdsArr.sort((a, b) {
return a.compareTo(b);
});
await chat
.where(userIdsArrayColumn, arrayContains: userIdsArr[0])
// .where(userIdsArrayColumn, arrayContains: userIdsArr[1])
.get()
.then((value) {
value.docs.forEach((element) {
if (element[userIdsArrayColumn].contains(userIdsArr[1])) {
log('match found!');
docId = element.id;
}
});
});
return docId;
}
A query can only contain a single array-contains query.
To allow your query, you'll want to add an additional field to the document where you keep the pair of UIDs in a predictable (e.g. alphabetical) order. With such a field you can then use a query such as:
where("concatenated_uids", isEqualTo: userIdsArr[0]+"_"+ userIdsArr[1])

How to count number of messages in firebasefirestore collection in flutter

I am trying to make a chat app with the help of a channel,
there is a search page where I search user to chat,
If I tap on user, a new windows will be created if no previous chat found,
and if not chatting first time, will use existing chatroom
code is running well, but I want some implement ,
if I search a user and tap on him, and than I go back without chatting, created new room should be deleted.... so that I need number of message's logic...
how to implement to achieve it
Future<ChatRoomModel?> getchatroom(UserModel targetuser) async {
ChatRoomModel? chatroom;
//here i feel something wrong as even if blocked chatroom, window should be open
QuerySnapshot querysnapshot = await FirebaseFirestore.instance
.collection("chatrooms")
.where("participants.${targetuser.uid}", isEqualTo: true)
.where("participants.${widget.userModel.uid}", isEqualTo: true)
.get();
if (querysnapshot.docs.length > 0) {
var docdata = querysnapshot.docs[0].data();
ChatRoomModel existingchatroom =
ChatRoomModel.fromMap(docdata as Map<String, dynamic>);
chatroom = existingchatroom;
} else {
//creating new chat room
ChatRoomModel newchatroommodel = ChatRoomModel(
chatroomid: DateTime.now().toString(),
participants: {
widget.userModel.uid.toString(): true,
targetuser.uid.toString(): true,
},
lastMessage: "Say Hi");
await FirebaseFirestore.instance
.collection("chatrooms")
.doc(newchatroommodel.chatroomid)
.set(newchatroommodel.toMap());
chatroom = newchatroommodel;
print("Created success");
}
return chatroom;
}
Delete your whole chat via 'ChatRoomId'
FirebaseFirestore.instance
.collection("chatrooms/'your_chat_room_id'")
.delete()
.then((value_2) {
print('========> successfully deleted');
});
Count your messages by retrieving a list of documents from your "messages" collection:
QuerySnapshot messages = await querysnapshot.docs[0].reference.collection("messages").get();
int messageCount = messages.size;

Filtering with just one parameter in a list of firestore collection in flutter

I am creating a chat app where people join groups, I have been able to create the groups and allow people to join in, anytime a user joins I save the group object into the user collection, so as to fetch for some other time, now what I want to achieve is fetching all user where group ID is the same value. here is what I have tried.
getUsersInCircle(String circleID) async {
userList.clear();
try {
QuerySnapshot<Map<String, dynamic>> ref = await db
.collection(AppStrings.users)
.where("circles", arrayContains: circleID)
.get()
.then((QuerySnapshot<Map<String, dynamic>> value) {
if (value.docs.isNotEmpty) {
userList.assignAll(
value.docs.map((e) => UserModel.fromJson(e.data())).toList());
print(value.docs.first.data());
} else {
print("no data");
}
return value;
});
return ref;
} catch (e) {
print(e.toString());
}
}
This is what my firestore database looks like
enter image description here
What I want to filter by is the circle Id but it does not return any data
i think i found the problem, you are storing an map inside the circles array, not as a normal array.
this is the difference between an map and array in Firestore
map vs array
Query an array:
where("circles", arrayContains: circleID)
Query an map
where("circles.circleID", isEqualTo: circleID)

Does streambuilder from firebase rtdb will update list<User> user data?

currently I understadn with the method streamBuilder I can fetch updated data and add in the List<User> users.
But what if this user which is already added in the List<User> users has updated data, and then it could be double adding this user data in the List<User> users right?
Could you plz show me how to confirm whether for the new coming data List<User> users has already same userId, if yes, the new data / userId will replace this exisiting userId?
If the user is deleted from Firebase rtdb, the stream will be notified, and therefore remove this user from List<User> users?
here is example, my concern is since stream will always add data to the List users, but what if this user is removed from database or disconnect, how to remove this user from this list?
_streamSubscription = availableUserStream.onValue.listen((snap) {
if (snap.snapshot.exists && snap.snapshot.value != null) {
DataSnapshot snapshotData = snap.snapshot;
for (var userSnapshot in snapshotData.children) {
final data = Map<String, dynamic>.from(userSnapshot.value as Map);
List<User> users = [];
User newUser = User.fromJson(data);
users.add(newUser);
firebaseController.setUsers(users: users);
}
}
});
So I thought to do a double confirm here if this user is still exisitng in the database:
User getRandomSenderUser({User asReceiverUser}) {
if (availableSenderUsersList.isNotEmpty) {
final random = Random();
var i = random.nextInt(availableSenderUsersList.length);
User randomUser = availableSenderUsersList[i];
bool thisRandomUserIsAvailable; //TODO
I don't know how to do this check, e.g. if this randomerUser is unavailable, so I need to get next randomUser, so it should be a loop? But it will slow down the response speed.
updateSenderUserAvailableStatus(asReceiverUser:asReceiverUser,connectionUser: randomUser);
return randomUser;
} else {
return null;
}
}
thank you!
Update:
Here is the example code, so now I understand stream will pass user data to List<User> users, but in my way there will always be user who is added in this list before, but was already removed from database, my plan is using while loop for double confirming to remove unavailable user when getting the randomUser, but it sounds not smart and still waste time I guess....
#override
void initState() {
_listenAvailableUsers();
}
_listenAvailableUsers() {
var availableUserStream =
FirebaseDatabase.instance.ref().child('/waitingList');
_streamSubscription = availableUserStream.onValue.listen((snap) {
if (snap.snapshot.exists && snap.snapshot.value != null) {
DataSnapshot snapshotData = snap.snapshot;
for (var userSnapshot in snapshotData.children) {
final data = Map<String, dynamic>.from(userSnapshot.value as Map);
List<User> users = [];
User newUser = User.fromJson(data);
users.add(newUser);
firebaseController.setUsers(users: users);
}
}
});
}
Here is the method I though to confirm if the randomUser is still existing in the database:
Future<User> getRandomSenderUser({User asReceiverUser}) async {
if (availableSenderUsersList.isNotEmpty) {
User randomUser;
while (true) {
final random = Random();
var i = random.nextInt(availableSenderUsersList.length);
randomUser = availableSenderUsersList[i];
DatabaseEvent event = await databaseReference
.child('/waitingList/${randomUser.userId}')
.once();
print('randomUser is ${randomUser.toString()}');
if (event.snapshot.value != null) {
break;
}
}
await updateSenderUserAvailableStatus(
asReceiverUser: asReceiverUser, connectionUser: randomUser);
print('connectionUserId is $connectionUserId');
return randomUser;
} else {
return null;
}
}
Since you're listening to the onValue of a path in the database, the DataSnapshot you get will contain the entire data at that path. When there was only a small change in the data, the server will only send that update to the client, but the SDK will then merge that with the existing data and still fire an event with a snapshot of all the data at the path.
Since you're starting with an empty list (List<User> users = [];) each time you get an event from the stream, that means you're rebuilding the entire lit of users each time, which seems correct to me.

Flutter - Stream not returning data

I have a document collection called posts_favorites, which stores the reference to all the Posts that a user has bookmarked. The posts_favorites collection look as follows:
I have created a Stream to get all posts references that belong to a specific user, then I want to get the Post documents from the posts collection using the posts references.
I created a Stream to produce the data I need, but I am not getting any data returned from my Stream. Here is my code for the Stream:
Stream<List<PostsRecord>> getFavoritePostsStream() async* {
List<PostsRecord> myList = [];
await FirebaseFirestore.instance
.collection("posts_favorites")
.where("user", isEqualTo: currentUserReference)
.get()
.then((favoriteList) {
favoriteList.docs.forEach((element) async {
String documentPath = element['post'].toString();
var start = documentPath.indexOf("/");
var end = documentPath.indexOf(")");
var documentRef = documentPath.substring(start+1, end);
//DocumentReference docRef = FirebaseFirestore.instance.doc(documentPath);
DocumentReference docRef = FirebaseFirestore.instance.collection('posts').doc(documentRef);
await docRef.get().then((DocumentSnapshot documentSnapshot) async {
if (documentSnapshot.exists) {
print('Document exists on the database');
PostsRecord postsRecord = PostsRecord.getDocumentFromData(
documentSnapshot.data(), element['post']);
//return myList.add(postsRecord);
//print(postsRecord);
return postsRecord;
}
});
});
});
}
I know this function works because the commented code produces the Post Records that I expect to get from Firestore.
I am not sure how to get the Stream function to return data that I can use in the Stream.
Thank you