Files
fcs/lib/helpers/pagination_model.dart

153 lines
4.1 KiB
Dart
Raw Normal View History

2020-09-20 05:34:49 +06:30
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
/*
* PaginationModel load data in page
* and listen to document change based on 'update_time' and 'delete_time' fields
* of the document
*/
class PaginationModel<T> {
final log = Logger('PaginationModel');
List<String> ids = [];
DocumentSnapshot prev;
int rowPerLoad = 10;
bool ended = false;
StreamSubscription<QuerySnapshot> listener;
CollectionReference listeningCol;
Query pageQuery;
PaginationModel(CollectionReference listeningCol, Query pageQuery,
{this.rowPerLoad = 10}) {
this.listeningCol = listeningCol;
this.pageQuery = pageQuery;
initData();
}
void initData() async {
_clearState();
_initListener();
load();
}
void _clearState() {
prev = null;
ids = [];
ended = false;
if (listener != null) listener.cancel();
listener = null;
if (controller != null) controller.close();
}
StreamController<Result> controller;
Stream<Result> listen() {
if (controller != null) {
controller.close();
}
controller = StreamController<Result>(onCancel: _clearState);
return controller.stream;
}
void close() {
_clearState();
}
final String updateTimeField = 'update_time';
final String deleteTimeField = 'delete_time';
void _initListener() {
Query _query =
listeningCol.orderBy(updateTimeField, descending: true).limit(1);
_query.getDocuments(source: Source.server).then((QuerySnapshot snapshot) {
int count = snapshot.documents.length;
int updateTime = 0;
if (count == 1) {
updateTime = snapshot.documents[0].data[updateTimeField];
}
Query _queryListener = listeningCol
.where(updateTimeField, isGreaterThan: updateTime)
.orderBy(updateTimeField, descending: true);
listener =
_queryListener.snapshots(includeMetadataChanges: true).listen((qs) {
qs.documentChanges.forEach((c) {
switch (c.type) {
case DocumentChangeType.added:
log.info("added!! $c");
_update(c.document.documentID, c.document.data);
break;
case DocumentChangeType.modified:
log.info("modified!! $c");
_update(c.document.documentID, c.document.data);
break;
default:
}
});
});
});
}
void _update(String id, Map<String, dynamic> data) {
if (ids.contains(id)) {
var deleted = data[deleteTimeField];
if (deleted > 0) {
ids.remove(id);
controller.add(Result(
id: id,
data: data,
documentChangeType: DocumentChangeType.removed));
} else {
controller.add(Result(
id: id,
data: data,
documentChangeType: DocumentChangeType.modified));
}
} else {
ids.add(id);
controller.add(Result(
id: id, data: data, documentChangeType: DocumentChangeType.added));
}
}
Future<bool> load() async {
Query _query =
prev != null ? pageQuery.startAfterDocument(prev) : pageQuery;
try {
await _query
.where(deleteTimeField, isEqualTo: 0)
.limit(rowPerLoad)
.getDocuments(source: Source.server)
.then((QuerySnapshot snapshot) {
int count = snapshot.documents.length;
ended = count < rowPerLoad;
prev = count > 0 ? snapshot.documents[count - 1] : prev;
snapshot.documents.forEach((e) {
if (!ids.contains(e.documentID)) log.shout("load!! $e");
ids.add(e.documentID);
controller.add(Result(
id: e.documentID,
data: e.data,
documentChangeType: DocumentChangeType.added));
});
if (ended) {
controller.add(Result(isEnded: true));
}
});
} catch (e) {
log.warning("Error!! $e");
}
return ended;
}
}
class Result {
String id;
Map<String, dynamic> data;
DocumentChangeType documentChangeType;
bool isEnded;
Result({this.id, this.data, this.documentChangeType, this.isEnded = false});
}