import 'package:fluent_reader_lite/models/item.dart'; import 'package:fluent_reader_lite/utils/global.dart'; import 'package:flutter/cupertino.dart'; import 'package:sqflite/sqflite.dart'; class ItemsModel with ChangeNotifier { Map _items = Map(); bool has(String id) => _items.containsKey(id); RSSItem getItem(String id) => _items[id]; Iterable getItems() => _items.values; void loadItems(Iterable items) { for (var item in items) { _items[item.id] = item; } } Future updateItem(String iid, {Batch batch, bool read, bool starred, local: false}) async { Map updateMap = Map(); if (_items.containsKey(iid)) { final item = _items[iid].clone(); if (read != null) { item.hasRead = read; if (!local) { if (read) Global.service.markRead(item); else Global.service.markUnread(item); } Global.sourcesModel.updateUnreadCount(item.source, read ? -1 : 1); } if (starred != null) { item.starred = starred; if (!local) { if (starred) Global.service.star(item); else Global.service.unstar(item); } } _items[iid] = item; } if (read != null) updateMap["hasRead"] = read ? 1 : 0; if (starred != null) updateMap["starred"] = starred ? 1 : 0; if (batch != null) { batch.update("items", updateMap, where: "iid = ?", whereArgs: [iid]); } else { notifyListeners(); await Global.db.update("items", updateMap, where: "iid = ?", whereArgs: [iid]); } } Future markAllRead(Set sids, {DateTime date, before = true}) async { Global.service.markAllRead(sids, date, before); List predicates = ["hasRead = 0"]; if (sids.length > 0) { predicates.add("source IN (${List.filled(sids.length, "?").join(" , ")})"); } if (date != null) { predicates.add("date ${before ? "<=" : ">="} ${date.millisecondsSinceEpoch}"); } await Global.db.update( "items", { "hasRead": 1 }, where: predicates.join(" AND "), whereArgs: sids.toList(), ); for (var item in _items.values.toList()) { if (sids.length > 0 && !sids.contains(item.source)) continue; if (date != null && (before ? item.date.compareTo(date) > 0 : item.date.compareTo(date) < 0)) continue; item.hasRead = true; } notifyListeners(); Global.sourcesModel.updateUnreadCounts(); } Future fetchItems() async { final items = await Global.service.fetchItems(); final batch = Global.db.batch(); for (var item in items) { _items[item.id] = item; batch.insert( "items", item.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } await batch.commit(noResult: true); // notifyListeners(); Global.sourcesModel.updateWithFetchedItems(items); Global.feedsModel.addFetchedItems(items); } Future syncItems() async { final tuple = await Global.service.syncItems(); final unreadIds = tuple.item1; final starredIds = tuple.item2; final rows = await Global.db.query( "items", columns: ["iid", "hasRead", "starred"], where: "hasRead = 0 OR starred = 1", ); final batch = Global.db.batch(); for (var row in rows) { final id = row["iid"]; if (row["hasRead"] == 0 && !unreadIds.remove(id)) { await updateItem(id, read: true, batch: batch, local: true); } if (row["starred"] == 1 && !starredIds.remove(id)) { await updateItem(id, starred: false, batch: batch, local: true); } } for (var unread in unreadIds) { await updateItem(unread, read: false, batch: batch, local: true); } for (var starred in starredIds) { await updateItem(starred, starred: true, batch: batch, local: true); } notifyListeners(); await batch.commit(noResult: true); await Global.sourcesModel.updateUnreadCounts(); } }