diff --git a/lib/news/models/article.dart b/lib/news/models/article.dart index ac7ea054f..88e4abbb7 100644 --- a/lib/news/models/article.dart +++ b/lib/news/models/article.dart @@ -1,7 +1,7 @@ /// News-article object class Article { /// Primary key of the article - final int _id; + final int id; /// Body of the article final String text; @@ -16,17 +16,15 @@ class Article { final int? categoryId; /// MD5 hash of the article - final String _md5; + final String md5; const Article( - {required id, + {required this.id, required this.text, required this.title, required this.pubDate, required this.categoryId, - required md5}) - : _id = id, - _md5 = md5; + required this.md5}); /// Returns an article given a [json] representation of an article. factory Article.fromJson(Map json) { @@ -42,13 +40,13 @@ class Article { /// Returns a json representation of the article object calling this method. Map toJson() => - {'id': _id, 'title': title, 'text': text, 'pub_date': pubDate.toString(), 'category_id': categoryId, 'md5': _md5}; + {'id': id, 'title': title, 'text': text, 'pub_date': pubDate.toString(), 'category_id': categoryId, 'md5': md5}; @override - int get hashCode => _md5.hashCode; + int get hashCode => md5.hashCode; @override bool operator ==(Object other) { - return other is Article && other._md5 == _md5; + return other is Article && other.md5 == md5; } } diff --git a/lib/news/services/news.dart b/lib/news/services/news.dart index 8f9aa4bdb..b4079e0c7 100644 --- a/lib/news/services/news.dart +++ b/lib/news/services/news.dart @@ -1,9 +1,9 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:convert'; import 'package:flutter/foundation.dart' hide Category; import 'package:http/http.dart' as http; -import 'package:intl/intl.dart'; import 'package:priobike/http.dart'; import 'package:priobike/logging/logger.dart'; import 'package:priobike/main.dart'; @@ -27,7 +27,7 @@ class News with ChangeNotifier { List
articles = []; /// List with all articles that have been read by the user - Set
readArticles = {}; + HashSet
readArticles = HashSet(); /// Map with all categories Map categories = {}; @@ -35,35 +35,22 @@ class News with ChangeNotifier { /// Reset the service to its initial state. Future reset() async { hasLoaded = false; - articles = []; - readArticles = {}; - categories = {}; + articles.clear(); + readArticles.clear(); + categories.clear(); notifyListeners(); } /// Returns all available articles from the shared preferences or if not stored locally from the backend server. Future getArticles() async { - // Get articles that are already saved in the shared preferences on the device. - List
localSavedArticles = await _getStoredArticles(); - - // If there are articles saved already in the shared preferences on the device - // get the lastSyncDate for later usage eg when deciding whether the "Neu"-tag - // should be shown on the article items in the list. - DateTime? newLastSyncDate; - if (localSavedArticles.isNotEmpty) { - newLastSyncDate = localSavedArticles[0].pubDate; - } - final settings = getIt(); String baseUrl = settings.city.selectedBackend(false).path; - final newsArticlesUrl = newLastSyncDate == null - ? "https://$baseUrl/news-service/news/articles" - : "https://$baseUrl/news-service/news/articles?from=${DateFormat('yyyy-MM-ddTH:mm:ss').format(newLastSyncDate)}Z"; + final newsArticlesUrl = "https://$baseUrl/news-service/news/articles"; final newsArticlesEndpoint = Uri.parse(newsArticlesUrl); - List
articlesFromServer = []; + List
newArticles = []; // Catch the error if there is no connection to the internet. try { @@ -77,7 +64,7 @@ class News with ChangeNotifier { await json.decode(response.body).forEach( (element) { final Article article = Article.fromJson(element); - articlesFromServer.add(article); + newArticles.add(article); }, ); hadError = false; @@ -87,12 +74,10 @@ class News with ChangeNotifier { hadError = true; } - articles = [...articlesFromServer, ...localSavedArticles]; + articles = newArticles; await _getCategories(); - await _storeArticles(); - readArticles = await _getStoredReadArticles(); hasLoaded = true; @@ -103,22 +88,13 @@ class News with ChangeNotifier { Future _getCategories() async { for (final article in articles) { if (article.categoryId != null && !categories.containsKey(article.categoryId)) { - await _getCategory(article.categoryId!); + await _fetchCategory(article.categoryId!); } } } - /// Gets single category given the [categoryId] from the shared preferences or if not stored locally from the backend server - Future _getCategory(int categoryId) async { - Category? category = await _getStoredCategory(categoryId); - if (category != null) { - if (!categories.containsKey(categoryId)) { - categories[categoryId] = category; - } - return; - } - - // If the category doesn't exist already in the shared preferences get it from backend server. + /// Fetches single category given the [categoryId] from the backend server + Future _fetchCategory(int categoryId) async { final settings = getIt(); final baseUrl = settings.city.selectedBackend(false).path; final newsCategoryUrl = "https://$baseUrl/news-service/news/category/${categoryId.toString()}"; @@ -133,76 +109,17 @@ class News with ChangeNotifier { throw Exception(err); } - category = Category.fromJson(json.decode(response.body)); + final category = Category.fromJson(json.decode(response.body)); if (!categories.containsKey(categoryId)) { categories[categoryId] = category; } - - await _storeCategory(category); } catch (e) { final hint = "Failed to load category: $e"; log.e(hint); } } - /// Store all articles in shared preferences. - Future _storeArticles() async { - if (articles.isEmpty) return; - final storage = await SharedPreferences.getInstance(); - - final backend = getIt().city.selectedBackend(false); - - final jsonStr = jsonEncode(articles.map((e) => e.toJson()).toList()); - await storage.setString("priobike.news.articles.${backend.name}", jsonStr); - } - - /// Store category in shared preferences. - Future _storeCategory(Category category) async { - if (articles.isEmpty) return; - final storage = await SharedPreferences.getInstance(); - - final backend = getIt().city.selectedBackend(false); - - final String jsonStr = jsonEncode(category.toJson()); - await storage.setString("priobike.news.categories.${backend.name}.${category.id}", jsonStr); - } - - /// Get all stored articles - Future> _getStoredArticles() async { - final storage = await SharedPreferences.getInstance(); - - final backend = getIt().city.selectedBackend(false); - - final storedArticlesStr = storage.getString("priobike.news.articles.${backend.name}"); - - if (storedArticlesStr == null) { - return []; - } - - List
storedArticles = []; - for (final articleMap in jsonDecode(storedArticlesStr)) { - storedArticles.add(Article.fromJson(articleMap)); - } - - return storedArticles; - } - - /// Get stored category for given [categoryId] - Future _getStoredCategory(int categoryId) async { - final storage = await SharedPreferences.getInstance(); - - final backend = getIt().city.selectedBackend(false); - - final storedCategoryStr = storage.getString("priobike.news.categories.${backend.name}.$categoryId"); - - if (storedCategoryStr == null) { - return null; - } - - return Category.fromJson(jsonDecode(storedCategoryStr)); - } - /// Store all read articles in shared preferences. Future _storeReadArticles() async { if (readArticles.isEmpty) return; @@ -216,7 +133,7 @@ class News with ChangeNotifier { } /// Get stored articles that were already read by the user. - Future> _getStoredReadArticles() async { + Future> _getStoredReadArticles() async { final storage = await SharedPreferences.getInstance(); final backend = getIt().city.selectedBackend(false); @@ -224,10 +141,10 @@ class News with ChangeNotifier { final storedReadArticlesStr = storage.getString("priobike.news.read_articles.${backend.name}"); if (storedReadArticlesStr == null) { - return {}; + return HashSet(); } - Set
storedReadArticles = {}; + HashSet
storedReadArticles = HashSet(); for (final articleMap in jsonDecode(storedReadArticlesStr)) { storedReadArticles.add(Article.fromJson(articleMap)); }