How to count number of messages in firebasefirestore collection in flutter - 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;

Related

How to return a Stream using an async* function in Flutter

I am working on a chat app using Flutter and Firebase. I am new to Dart and so got stuck when I wanted to create a function which fetches (using await) a particular document from one collection (forums) and use an array property of the forum document to query and return a Stream from another collection (openMessages). The problem with my current solution is that it always returns an empty array. I am sure I am using the keywords or logic incorrectly. Can you please help me refactor my method.
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSent', descending: true)
.snapshots()
.map((event) {
for (var document in event.docs) {
messages.add(ChatMessage.fromMap(document.data()));
}
});
//print('LENGTH:'+messages.length.toString());
yield messages;}
You can use the following method.
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
final firestore = FirebaseFirestore.instance;
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
final result = firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSent', descending: true)
.snapshots();
await for (final r in result) {
final docs = r.docs;
for (final document in docs) {
messages.add(ChatMessage.fromMap(document.data()));
yield messages;
}
}
}
Or
Stream<List<ChatMessage>> getForumChatStream(String forumId) async* {
final firestore = FirebaseFirestore.instance;
List<ChatMessage> messages = [];
var docSnap = await firestore.collection('forums').doc(forumId).get();
Forum forum = Forum.fromMap(docSnap.data()!);
yield* firestore
.collection('openMessages')
.where('messageId', whereIn: forum.messageIds)
.orderBy('timeSend', descending: true)
.snapshots()
.map((event) =>
event.docs.map((e) => ChatMessage.fromMap(e.data())).toList());
}

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

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

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?

How to filter Firestore documents using Flutter?

I follow this tutorial to create a chat. The code to insert message into Firestore is the follow
var _firebaseRef = Firestore.instance.collection("chats");
void sendMessage(String message, String idFrom, ProductModel productModel,
String fromName) {
String groupChatId;
String idTo = productModel.userId;
if (idFrom.hashCode <= productModel.userId.hashCode) {
groupChatId = '$idFrom-$idTo';
} else {
groupChatId = '$idTo-$idFrom';
}
var documentReference = _firebaseRef
.document(groupChatId)
.collection(groupChatId)
.document(DateTime.now().millisecondsSinceEpoch.toString());
Firestore.instance.runTransaction((transaction) async {
await transaction.set(
documentReference,
{
'message': message,
'idFrom': idFrom,
"productId": productModel.id,
"createdAt": utils.currentTime(),
'timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
"fromName": fromName,
"idTo": productModel.userId
},
);
});
}
As you can see the groupChatId to be used on DocumentId and CollectionId is a composition of 2 ids (sender and receiver).
This is what its looks like:
The code to displey each message works fine
String groupChatId;
if (idFrom.hashCode <= idTo.hashCode) {
groupChatId = '$idFrom-$idTo';
} else {
groupChatId = '$idTo-$idFrom';
}
return _firebaseRef
.document(groupChatId)
.collection(groupChatId)
.orderBy('timestamp', descending: true)
.snapshots();
In that picture you can see 1 chat with 1 message, I am trying to display each chat of my current user, to do that I 'd to filter all document inside of chat collection, so the user can click it and get list message on chatd detail page.
I don't know how to do that, if you have a better approach to do the chat I will appreciate it
You can call getDocuments and then methods, inside of it you can make your validations, on this case you can if the documentIDcontains your ID. Like this
Firestore.instance.collection("chats").getDocuments().then((value) {
print(value);
value.documents.forEach((element) {
if (element.documentID.contains(currentUserId)) {
DocumentSnapshot document = element;
print(document);
}
});
});
It should works
you can work with something like this querying for where currentUser Id s equal to IdFrom or IdTo
StreamBuilder(
stream: Firestore.instance
.collection("users")
.orderBy('createAt', descending: true)
.where('idFrom', isEqualTo: currentuserId)
.where('idTo', isEqualTo: currentuserId)
.snapshots(),....
if you run this for the first time check your console for a link to create index

Flutter- show loading screen till firestore document changes

I am building a ridesharing app with flutter. So far i am stuck on how to communicate between the rider and driver app.
After rider submits pickup request to firestore db, i want the loading screen to show until a driver accepts the request(possibly by updating firestore db) then move to screen with driver info.
if (event is PaymentMadeEvent) {
yield TaxiBookingLoadingState(
state:
PaymentNotInitializedState(booking: null, methodsAvaiable: null));
TaxiBooking booking = await TaxiBookingStorage.addDetails(TaxiBooking.named(paymentMethod: event.paymentMethod));
String docID = await TaxiBookingController.submitRequest(
booking.source.areaDetails,
booking.destination.areaDetails,
[booking.source.position.latitude,booking.source.position.longitude],
[booking.destination.position.latitude,booking.destination.position.longitude],
booking.estimatedPrice,
booking.estimatedDuration,
booking.estimatedDistance
);
booking = await TaxiBookingStorage.addDetails(TaxiBooking.named(dbID: docID));
await TaxiBookingController.generateToken();
TaxiDriver taxiDriver = await TaxiBookingController.getTaxiDriver(booking);
// Timer.periodic(Duration(seconds: 5), (Timer t) async* {
// } );
taxiDriver = await TaxiBookingController.getTaxiDriver(booking);
yield TaxiNotConfirmedState(booking: booking, driver: taxiDriver);
}
static Future<TaxiDriver> getTaxiDriver(TaxiBooking booking) async {
TaxiDriver taxis2;
var driver = await Firestore.instance.collection("rider_pickup_pairing")
// .where(DocumentReference,isEqualTo: booking.dbID)
.where("driver_id",isEqualTo: 'jk')
.getDocuments()
.then((QuerySnapshot snapshot) {
if (snapshot.documents == []) {
taxis2 = null;
} else {
snapshot.documents.forEach((f) =>
taxis2 = TaxiDriver.named(
driverPic:
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/profilepic.jpg",
driverName: "John Doe",
driverRating: 4.5,
taxiDetails: "Toyota Corolla(ABJ823KU)")
);
TaxiBookingStorage.addDetails(TaxiBooking.named(driver: taxis2.driverName));
}
return taxis2;
});
return driver;
}
You should be using .onSnapshot() instead of .getDocument() in order to achieve this.
The difference between these two methods is that getDocument() will only retrieve the document once while onSnapshot() will keep listening to any event on Firestore. This is covered in these documents: get realtime updates and get data once.
Hope you find this useful.