Open mibrah42 opened 3 years ago
Hi @mibrah42. Right now we are working on animations and pagination in the chat UI, but as of right now we don't have plans to add pagination to Firebase's messages stream. We are always open to PRs though :)
So the pagination was added to the chat UI package, if there are volunteers to add pagination to Firebase we will be more than happy to accept a PR :)
So the pagination was added to the chat UI package, if there are volunteers to add pagination to Firebase we will be more than happy to accept a PR :)
Does the documentation includes this new feature?
@SalahAdDin Chat UI - yes, Firebase - no, feel free to add.
I found this one! https://pub.dev/packages/paginate_firestore I think it could help.
@demchenkoalex any news to support pagination to Firebase ?
@azazadev this is the lowest priority we have, unless someone else do it, don't expect it this year. This package was designed as an option to quickly build an MVP and not a full-blown fully-featured chat backend.
@demchenkoalex First thank you for the wonderful work! I try to add this feature in my project, reference official document and video. Here is my solution , you have done the most part of work in this, the rest is is easy piece in your package, big difference for the reads number which relate to the cost in real online project. Thank you again, excuse my bluntness and poor drawing skill.
Stream part
Stream<List<types.Message>> messages(types.Room room, [doc]) {
return FirebaseFirestore.instance
.collection('${config.roomsCollectionName}/${room.id}/messages')
.orderBy('createdAt', descending: true)
.endBeforeDocument(doc)
.snapshots()
page part
Future fetchMessages(types.Room room,
{var docWhere, int pageSize = 5}) async {
List<QueryDocumentSnapshot<Map<String, dynamic>>>? quary;
if (docWhere == null) {
//first bunch of messages, page1
await FirebaseFirestore.instance
.collection('${config.roomsCollectionName}/${room.id}/messages')
.orderBy('createdAt', descending: true)
.limit(pageSize)
.get();
} else {
//next page messages
await FirebaseFirestore.instance
.collection('${config.roomsCollectionName}/${room.id}/messages')
.orderBy('createdAt', descending: true)
.startAfterDocument(docWhere)
.limit(pageSize)
.get();
}
return quary;
}
@demchenkoalex First thank you for the wonderful work! I try to add this feature in my project, reference official document and video. Here is my solution , you have done the most part of work in this, the rest is is easy piece in your package, big difference for the reads number which relate to the cost in real online project. Thank you again, excuse my bluntness and poor drawing skill.
Stream part
Stream<List<types.Message>> messages(types.Room room, [doc]) { return FirebaseFirestore.instance .collection('${config.roomsCollectionName}/${room.id}/messages') .orderBy('createdAt', descending: true) .endBeforeDocument(doc) .snapshots()
page part
Future fetchMessages(types.Room room, {var docWhere, int pageSize = 5}) async { List<QueryDocumentSnapshot<Map<String, dynamic>>>? quary; if (docWhere == null) { //first bunch of messages, page1 await FirebaseFirestore.instance .collection('${config.roomsCollectionName}/${room.id}/messages') .orderBy('createdAt', descending: true) .limit(pageSize) .get(); } else { //next page messages await FirebaseFirestore.instance .collection('${config.roomsCollectionName}/${room.id}/messages') .orderBy('createdAt', descending: true) .startAfterDocument(docWhere) .limit(pageSize) .get(); } return quary; }
What is docWhere
for?
docWhere is just a name I made for my project, it is a doc which located at the last of List docs in page1, use it to tell firestore where next bunch of docs start.
@demchenkoalex First thank you for the wonderful work! I try to add this feature in my project, reference official document and video. Here is my solution , you have done the most part of work in this, the rest is is easy piece in your package, big difference for the reads number which relate to the cost in real online project. Thank you again, excuse my bluntness and poor drawing skill.
Stream part
Stream<List<types.Message>> messages(types.Room room, [doc]) { return FirebaseFirestore.instance .collection('${config.roomsCollectionName}/${room.id}/messages') .orderBy('createdAt', descending: true) .endBeforeDocument(doc) .snapshots()
page part
Future fetchMessages(types.Room room, {var docWhere, int pageSize = 5}) async { List<QueryDocumentSnapshot<Map<String, dynamic>>>? quary; if (docWhere == null) { //first bunch of messages, page1 await FirebaseFirestore.instance .collection('${config.roomsCollectionName}/${room.id}/messages') .orderBy('createdAt', descending: true) .limit(pageSize) .get(); } else { //next page messages await FirebaseFirestore.instance .collection('${config.roomsCollectionName}/${room.id}/messages') .orderBy('createdAt', descending: true) .startAfterDocument(docWhere) .limit(pageSize) .get(); } return quary; }
Thanks @SalahAdDin , can you please give complete example ?
Thanks @SalahAdDin , can you please give complete example ?
@azazadev You have to ask to @what2003, he did it.
Thanks @SalahAdDin , can you please give complete example ?
@azazadev You have to ask to @what2003, he did it.
sorry, question to @what2003 any chance to have complete example ?
My project with a lot custom feather is too complex to show, maybe later when I finish my project and organize my code to set an example. Of course the best way is waiting for @demchenkoalex to update. Again I shall express my gratitude for the brilliant work @demchenkoalex team did!
Hey @what2003 thanks for the kind words and an update that this is actually not that hard as I assume it would :D Definitely share an example if you will have a chance, in the meantime I will try to follow you leads to see if I could make it work too.
It is my pleasure to contribute a little, example, made some changes base on @demchenkoalex flutter_firebase_chat_core example, hope it helps. My google-services.json is in it, so it can be directly built on android(not IOS), please ignore some Chinese characters in chat room and custom messages which appears as blank in this example. @azazadev
I tried to create pagination with firestore while taking inspiration from FilledStack's video about Flutter and Firestore real-time Pagination. The approach is not perfect but it's working fine for me.
"I tried it only with single-page chat room."
class MessagesService with ChangeNotifier {
late int currentIndex;
MessagesService();
final databaseService = sl.get<DatabaseService>();
///constants
static const _serverTime = 'metadata.createdAt';
// static const _createdAt = 'createdAt';
static const _resultLimit = 10;
bool _hasMoreItems = true;
bool _isLoading = false;
bool get hasMoreItems => _hasMoreItems;
bool get isLoading => _isLoading;
void _setLoading(bool value) {
_isLoading = value;
// notifyListeners();
}
///
DocumentSnapshot? _lastDocument;
final List<List<types.Message>> _allMessage = [];
final _streamController = StreamController<List<types.Message>>.broadcast();
Stream<List<types.Message>> messageStream() {
requestData();
return _streamController.stream;
}
void requestData() {
if (_isLoading) return;
_setLoading(true);
Query<types.Message> query;
/// First Query to get `_resultLimit` messages
query = databaseService
.messages()
// .where(_serverTime, isGreaterThanOrEqualTo: timeStamp)
.orderBy(_serverTime, descending: true)
.limit(_resultLimit);
currentIndex = _allMessage.length;
/// If User scrolls to upwards
if (_lastDocument != null) {
if (_hasMoreItems) {
query = query.startAfterDocument(_lastDocument!);
query.snapshots().listen((snapshot) => _createList(snapshot, false));
return;
}
}
query.snapshots().listen((snapshot) => _createList(snapshot, true));
}
void _createList(QuerySnapshot<types.Message> snapshot, bool isNewMessage) {
_setLoading(true);
if (isNewMessage) {
// I'm using a hack here, this hack fixes a problem when user scroll upwards and a new mesasge came, the message doesn't appear.
currentIndex = 0;
}
print('Current Index $currentIndex');
print('All Message Length ${_allMessage.length}');
final tempList = snapshot.docs.map((e) => e.data()).toList();
final pageExists = currentIndex < _allMessage.length;
if (pageExists) {
_allMessage[currentIndex] = tempList;
} else {
_allMessage.add(tempList);
}
final foldedList = _allMessage.fold<List<types.Message>>(<types.Message>[],
(initialValue, element) => initialValue..addAll(element));
_streamController.add(foldedList);
if (currentIndex == _allMessage.length - 1) {
_lastDocument = snapshot.docs.last;
}
// Determine if there's more Messages to request
_hasMoreItems = tempList.length == _resultLimit;
_setLoading(false);
}
@override
void dispose() {
super.dispose();
_streamController.close();
}
}
I got pagination to work on my end by using FirestoreQueryBuilder from flutter fire ui package.
So I have this:
FirestoreQueryBuilder(
query: FirebaseFirestore.instance
.collection('rooms/${widget.room.id}/messages')
.orderBy('createdAt', descending: true),
builder: (context, snapshot, c) {
if (snapshot.isFetching) {
return const CircularProgressIndicator();
}
...
return Chat(
onEndReached: () {
snapshot.fetchMore();
return Future.value();
},
messages: snapshot.docs.map((e) {
final data = e.data();
if (data != null) {
var map = data as Map<dynamic, dynamic>;
var castedMap = map.cast<String, dynamic>();
final author = widget.room.users.firstWhere(
(u) => u.id == castedMap['authorId'],
orElse: () =>
types.User(id: castedMap['authorId'] as String),
);
data['author'] = author.toJson();
data['createdAt'] =
data['createdAt']?.millisecondsSinceEpoch;
data['id'] = e.id;
data['updatedAt'] =
data['updatedAt']?.millisecondsSinceEpoch;
return types.Message.fromJson(castedMap);
}
return types.Message.fromJson({});
}).toList(),
....
);
}
It is my pleasure to contribute a little, example, made some changes base on @demchenkoalex flutter_firebase_chat_core example, hope it helps. My google-services.json is in it, so it can be directly built on android(not IOS), please ignore some Chinese characters in chat room and custom messages which appears as blank in this example. @azazadev
hey. your example is private I think
It is my pleasure to contribute a little, example, made some changes base on @demchenkoalex flutter_firebase_chat_core example, hope it helps. My google-services.json is in it, so it can be directly built on android(not IOS), please ignore some Chinese characters in chat room and custom messages which appears as blank in this example. @azazadev
hey. your example is private I think
Or it was deleted.
@akshays-repo @SalahAdDin My example included my firebase account info which is no longer in use, so please refer to my code snippet, else in my example just insignificant.
The library does not seem to have pagination on the messages stream. Is this something that will be added?