153 lines
4.1 KiB
Dart
153 lines
4.1 KiB
Dart
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});
|
|
}
|