My app has chatting page. And I want to stream total number of unread messages. (sum of all unread messages in chatting rooms for specific user)
First I have to look up 'userChat' to get the list of chatKey the user is involved, then I should iterate through each 'messages/{chatKey}' to count number of unread. I have to get the number in real time, so both queries are written in .onValue.map((event) {...}).
My code is below.
Stream<int> numberOfTotalUnread() {
int unread = 0;
return _userChatDB.child(_uid).onValue.map((event) {
unread = 0;
if(event.snapshot.value != null) {
for (var element in event.snapshot.children) {
print('level one');
print(element.key);
_messageDB.child(element.key!).orderByChild('read').equalTo(false).onValue.map((ev) {
if(ev.snapshot.value != null) {
for (var el in ev.snapshot.children) {
print('level two');
print(el.key);
if(Map<String, dynamic>.from(el.value as dynamic)['senderID'] != _uid) unread += 1;
}
}
});
}
}
print(unread);
return unread;
});
}
But when I run it, I only see
I/flutter (31470): level one
I/flutter (31470): (some chat room key)
I/flutter (31470): 0
It never gets to level two and total unread is always zero. Is it possible to nest .onValue.map in flutter firebase? If so, what am I doing wrong?
EDITED
This is userChat/{userID}. It holds all the chat rooms info that user is participating.
{
"{chatKey}" : {
"id" : "ltRJxFIXzVPHhiCd4bYOTft5aKo2",
"isMentor" : false,
"lastMessage" : "99",
"lastTime" : "2022-02-27 15:29:30.803526Z",
"name" : "mento",
"photoURL" : "https://firebasestorage.googleapis.com/~~",
}
}
This is messages/{chatKey}. When message is first sent it has "read" : false
field. And when the receiver sees it, that field is deleted.
My initial thought was since some messages don't have '''read''' field that might be the reason of error. But according to doc(https://firebase.google.com/docs/database/rest/retrieve-data#section-rest-ordered-data) there should be no problem. So I must be doing something wrong.
{
"0VqSLAvcsCcx68H4" : {
"message" : "6",
"receiverID" : "68bCkh4p8FcM34VhQto4yJqNyNj2",
"senderID" : "ltRJxFIXzVPHhiCd4bYOTft5aKo2",
"time" : "2022-02-19 13:55:10.348445Z"
},
"0x7rp72Xgemq2T30" : {
"message" : "2",
"receiverID" : "68bCkh4p8FcM34VhQto4yJqNyNj2",
"senderID" : "ltRJxFIXzVPHhiCd4bYOTft5aKo2",
"time" : "2022-02-19 13:49:03.956939Z"
},
"19map5hhJTlqJwoN" : {
"message" : "9",
"receiverID" : "68bCkh4p8FcM34VhQto4yJqNyNj2",
"senderID" : "ltRJxFIXzVPHhiCd4bYOTft5aKo2",
"time" : "2022-02-12 13:42:23.180828Z"
},
"1LpD0qIW5dClPd4p" : {
"message" : "7",
"receiverID" : "68bCkh4p8FcM34VhQto4yJqNyNj2",
"senderID" : "ltRJxFIXzVPHhiCd4bYOTft5aKo2",
"time" : "2022-02-19 14:11:47.681230Z"
},
}
EDITED2
I have another function that returns number of unread messages in single chat room. This one looks exactly same as the onValue.map statement in the inner side of numberOfTotalUnread()
. This function works just fine even if only some (or no) messages has "read" property.
Stream<int> numberOfUnread({required ChatModel chatModel}) {
int unread = 0;
return _messageDB.child(chatModel.key).orderByChild('read').equalTo(false).onValue.map((event) {
unread = 0;
if(event.snapshot.value != null) {
event.snapshot.children.forEach((element) {
if(Map<String, dynamic>.from(element.value as dynamic)['senderID'] != _uid) unread += 1;
});
}
return unread;
});
}
EDITED 3
I figured out what I need is getting sum of Stream. This is the latest version(still not working).
Stream<int> numberOfTotalUnread() {
int unread, unread2;
List<Stream<int>> streams;
return _userChatDB.child(_uid).onValue.map((event) {
unread = 0;
streams = [];
for (var element in event.snapshot.children) {
if(element.key != null) {
print('level one');
streams.add(_messageDB.child(element.key!).orderByChild('read').equalTo(false).onValue.map((ev) {
print('level two');
unread2 = 0;
if(ev.snapshot.value != null) {
for (var el in ev.snapshot.children) {
print('level three');
if(Map<String, dynamic>.from(el.value as dynamic)['senderID'] != _uid) unread2 += 1;
}
}
return unread2;
}));
}
}
print(streams);
for (var stream in streams) {
stream.listen((content) {unread += content;});
}
return unread;
});
}
}
This is what I get.
I/flutter ( 5199): level one
I/flutter ( 5199): [Instance of '_MapStream<DatabaseEvent, int>']
I/flutter ( 5199): 0
I/flutter ( 5199): level two
I/flutter ( 5199): level three
So I got to the part of getting List of Stream, but my function returns 0 before getting the sum of stream contents. Any ideas?