-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into hello/master
- Loading branch information
Showing
26 changed files
with
1,083 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// FLUTTER / DART / THIRD-PARTIES | ||
import 'dart:convert'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:logger/logger.dart'; | ||
|
||
// SERVICES | ||
import 'package:notredame/core/services/networking_service.dart'; | ||
import 'package:notredame/core/managers/cache_manager.dart'; | ||
|
||
// MODELS | ||
import 'package:notredame/core/models/news.dart'; | ||
|
||
// UTILS | ||
import 'package:notredame/core/utils/cache_exception.dart'; | ||
|
||
// OTHER | ||
import 'package:notredame/locator.dart'; | ||
|
||
/// Repository to access all the news | ||
class NewsRepository { | ||
static const String tag = "NewsRepository"; | ||
|
||
@visibleForTesting | ||
static const String newsCacheKey = "newsCache"; | ||
|
||
final Logger _logger = locator<Logger>(); | ||
|
||
/// Cache manager to access and update the cache. | ||
final CacheManager _cacheManager = locator<CacheManager>(); | ||
|
||
/// Used to verify if the user has connectivity | ||
final NetworkingService _networkingService = locator<NetworkingService>(); | ||
|
||
/// List of the news with 3 test news. | ||
List<News> _news = <News>[ | ||
News( | ||
id: 1, | ||
title: | ||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec tempus arcu sed quam tincidunt, non venenatis orci mollis.", | ||
description: "Test 1 description", | ||
date: DateTime.now(), | ||
image: "https://picsum.photos/400/200", | ||
tags: ["tag1", "tag2"], | ||
), | ||
News( | ||
id: 2, | ||
title: "Test 2", | ||
description: "Test 2 description", | ||
date: DateTime.now(), | ||
image: "https://picsum.photos/400/200", | ||
tags: ["tag1", "tag2"], | ||
), | ||
News( | ||
id: 3, | ||
title: "Test 3", | ||
description: "Test 3 description", | ||
date: DateTime.now(), | ||
image: "https://picsum.photos/400/200", | ||
tags: ["tag1", "tag2"], | ||
), | ||
]; | ||
|
||
List<News> get news => _news; | ||
|
||
/// Get and update the list of news. | ||
/// After fetching the news from the [?] the [CacheManager] | ||
/// is updated with the latest version of the news. | ||
Future<List<News>> getNews({bool fromCacheOnly = false}) async { | ||
// Force fromCacheOnly mode when user has no connectivity | ||
if (!(await _networkingService.hasConnectivity())) { | ||
// ignore: parameter_assignments | ||
fromCacheOnly = true; | ||
} | ||
|
||
// Load the news from the cache if the list doesn't exist | ||
if (_news == null) { | ||
await getNewsFromCache(); | ||
} | ||
|
||
if (fromCacheOnly) { | ||
return _news; | ||
} | ||
|
||
final List<News> fetchedNews = fetchNewsFromAPI(); | ||
|
||
// Update the list of news to avoid duplicate news | ||
for (final News news in fetchedNews) { | ||
if (!_news.contains(news)) { | ||
_news.add(news); | ||
} | ||
} | ||
|
||
try { | ||
// Update cache | ||
_cacheManager.update(newsCacheKey, jsonEncode(_news)); | ||
} on CacheException catch (_) { | ||
// Do nothing, the caching will retry later and the error has been logged by the [CacheManager] | ||
_logger.e( | ||
"$tag - getNews: exception raised will trying to update the cache."); | ||
} | ||
|
||
return _news; | ||
} | ||
|
||
Future<void> getNewsFromCache() async { | ||
_news = []; | ||
try { | ||
final List responseCache = | ||
jsonDecode(await _cacheManager.get(newsCacheKey)) as List<dynamic>; | ||
|
||
// Build list of news loaded from the cache. | ||
_news = responseCache | ||
.map((e) => News.fromJson(e as Map<String, dynamic>)) | ||
.toList(); | ||
|
||
_logger | ||
.d("$tag - getNewsFromCache: ${_news.length} news loaded from cache"); | ||
} on CacheException catch (_) { | ||
_logger.e( | ||
"$tag - getNewsFromCache: exception raised will trying to load news from cache."); | ||
} | ||
} | ||
|
||
// TODO : Fetch news from the API | ||
List<News> fetchNewsFromAPI() { | ||
final List<News> fetchedNews = []; | ||
|
||
_logger.d("$tag - getNews: fetched ${fetchedNews.length} news."); | ||
|
||
return fetchedNews; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
class News { | ||
final int id; | ||
final String title; | ||
final String description; | ||
final String image; | ||
final List<String> tags; | ||
final DateTime date; | ||
|
||
News({ | ||
required this.id, | ||
required this.title, | ||
required this.description, | ||
required this.image, | ||
required this.tags, | ||
required this.date, | ||
}); | ||
|
||
/// Used to create [News] instance from a JSON file | ||
factory News.fromJson(Map<String, dynamic> map) { | ||
return News( | ||
id: map['id'] as int, | ||
title: map['title'] as String, | ||
description: map['description'] as String, | ||
image: map['image'] as String, | ||
tags: map['tags'] as List<String>, | ||
date: DateTime.parse(map['date'] as String), | ||
); | ||
} | ||
|
||
Map<String, dynamic> toJson() { | ||
return { | ||
'id': id, | ||
'title': title, | ||
'description': description, | ||
'image': image, | ||
'tags': tags.toList(), | ||
'date': date.toString(), | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// FLUTTER / DART / THIRD-PARTIES | ||
import 'package:fluttertoast/fluttertoast.dart'; | ||
import 'package:stacked/stacked.dart'; | ||
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||
|
||
import 'package:notredame/core/managers/news_repository.dart'; | ||
import 'package:notredame/core/models/news.dart'; | ||
|
||
import 'package:notredame/locator.dart'; | ||
|
||
class NewsViewModel extends FutureViewModel<List<News>> { | ||
/// Load the events | ||
final NewsRepository _newsRepository = locator<NewsRepository>(); | ||
|
||
/// Localization class of the application. | ||
final AppIntl _appIntl; | ||
|
||
/// News list | ||
List<News> _news = []; | ||
|
||
/// Return the list of all the news. | ||
List<News> get news { | ||
_news = []; | ||
|
||
// Build the list of news | ||
for (final n in _newsRepository.news) { | ||
_news.add(n); | ||
} | ||
|
||
return _news; | ||
} | ||
|
||
NewsViewModel({required AppIntl intl}) : _appIntl = intl; | ||
|
||
bool isLoadingEvents = false; | ||
|
||
@override | ||
Future<List<News>> futureToRun() async { | ||
try { | ||
setBusyForObject(isLoadingEvents, true); | ||
_news = await _newsRepository.getNews(fromCacheOnly: true); | ||
notifyListeners(); | ||
} catch (e) { | ||
onError(e); | ||
} finally { | ||
setBusyForObject(isLoadingEvents, false); | ||
} | ||
|
||
return news; | ||
} | ||
|
||
@override | ||
// ignore: type_annotate_public_apis | ||
void onError(error) { | ||
Fluttertoast.showToast(msg: _appIntl.error); | ||
} | ||
|
||
Future refresh() async { | ||
try { | ||
setBusyForObject(isLoadingEvents, true); | ||
_newsRepository.getNews(); | ||
notifyListeners(); | ||
} on Exception catch (error) { | ||
onError(error); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.