import 'dart:async'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:faker/faker.dart'; import 'package:logging/logging.dart'; import 'base_model.dart'; class TestModel extends BaseModel { final log = Logger('TestModel'); List tests = []; DocumentSnapshot prev; static const int row_count = 10; bool ended = false; StreamSubscription listener; final Query queryBase = Firestore.instance.collection("/tests"); final Query query = Firestore.instance .collection("/tests") // .orderBy("deleted") .orderBy("age", descending: false); void initData() async { _clearState(); _initListener(); load(); } void _clearState() { prev = null; tests = []; ended = false; if (listener != null) listener.cancel(); listener = null; } void _initListener() { Query _query = queryBase.orderBy("update_time", descending: true).limit(1); _query.getDocuments(source: Source.server).then((QuerySnapshot snapshot) { int count = snapshot.documents.length; if (count == 1) { var test = Test.fromMap( snapshot.documents[0].data, snapshot.documents[0].documentID); Query _queryListener = queryBase .where("update_time", isGreaterThan: test.updateTime) .orderBy("update_time", descending: true); listener = _queryListener.snapshots(includeMetadataChanges: true).listen((qs) { qs.documentChanges.forEach((c) { switch (c.type) { case DocumentChangeType.added: var test = Test.fromMap(c.document.data, c.document.documentID); if (tests.contains(test)) { tests[tests.indexOf(test)].name = test.name; notifyListeners(); } if (!tests.contains(test)) { tests.add(test); notifyListeners(); } break; case DocumentChangeType.modified: var test = Test.fromMap(c.document.data, c.document.documentID); if (tests.contains(test)) { bool deleted = c.document.data["deleted"]; if (deleted != null && deleted) { tests.remove(test); } else { tests[tests.indexOf(test)].name = test.name; } notifyListeners(); } break; default: } }); }); notifyListeners(); } }); } Future load() async { Query _query = prev != null ? query.startAfterDocument(prev) : query; try { _query // .where("deleted", isNull: null) .limit(row_count) .getDocuments(source: Source.server) .then((QuerySnapshot snapshot) { int count = snapshot.documents.length; ended = count < row_count; prev = count > 0 ? snapshot.documents[count - 1] : prev; snapshot.documents.forEach((e) { var test = Test.fromMap(e.data, e.documentID); if (!tests.contains(test)) tests.add(test); }); notifyListeners(); }); } catch (e) { log.warning("Error!! $e"); } } void populate() { for (var i = 0; i < 30; i++) { Firestore.instance .collection('tests') .document(faker.person.name()) .setData({ 'name': faker.person.name(), 'age': random.decimal(), 'update_time': DateTime.now().microsecondsSinceEpoch }); } } void add() { Firestore.instance .collection('tests') .document(faker.person.name()) .setData({ 'name': faker.person.name(), 'age': random.decimal(), 'update_time': DateTime.now().microsecondsSinceEpoch }); } void update() { Firestore.instance.collection('tests').document(tests[0].id).setData({ 'name': faker.person.name(), 'update_time': DateTime.now().microsecondsSinceEpoch }, merge: true); } void remove() { Firestore.instance.collection('tests').document(tests[0].id).setData( {'deleted': 1, 'update_time': DateTime.now().microsecondsSinceEpoch}, merge: true); } @override void logout() { _clearState(); } } class Test { String id; String name; double age; int updateTime; Test(this.id, {this.name, this.age, this.updateTime}); factory Test.fromMap(Map map, String id) { return Test(id, name: map['name'], age: map['age'], updateTime: map['update_time']); } @override bool operator ==(other) { if (identical(this, other)) { return true; } return other.id == this.id; } @override int get hashCode { int result = 17; result = 37 * result + id.hashCode; return result; } }