Skip to content

Commit

Permalink
Merge branch 'master' into hello/master
Browse files Browse the repository at this point in the history
  • Loading branch information
Udito3 committed Jan 31, 2024
1 parent 9393dea commit c255aca
Show file tree
Hide file tree
Showing 26 changed files with 1,083 additions and 17 deletions.
3 changes: 3 additions & 0 deletions l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"profile_program_completion_not_available": "N/A",
"profile_other_programs": "Other Programs",

"useful_link_title": "Useful links",
"ets_security_title": "Security",
"ets_monets_title": "MonÉTS",
"ets_bibliotech_title": "Bibliotech",
Expand All @@ -169,6 +170,8 @@
"ets_gus": "GUS",
"ets_papercut_title":"PaperCut",

"news_title" : "News",

"more_about_applets_title": "About ApplETS",
"more_report_bug": "Report a bug or request a feature",
"more_report_bug_steps_title": "How does it work?\n\n",
Expand Down
3 changes: 3 additions & 0 deletions l10n/intl_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"profile_program_completion_not_available": "ND",
"profile_other_programs": "Autres Programmes",

"useful_link_title": "Liens utiles",
"ets_security_title": "Sécurité",
"ets_monets_title": "MonÉTS",
"ets_bibliotech_title": "Bibliotech",
Expand All @@ -169,6 +170,8 @@
"ets_gus": "GUS",
"ets_papercut_title":"PaperCut",

"news_title" : "Annonces",

"more_about_applets_title": "À propos d'ApplETS",
"more_report_bug": "Rapporter un bogue ou une amélioration",
"more_report_bug_steps_title": "Comment faire?\n\n",
Expand Down
2 changes: 2 additions & 0 deletions lib/core/constants/router_paths.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class RouterPaths {
static const String ets = "/ets";
static const String webView = "/ets/web-view";
static const String security = "/ets/security";
static const String usefulLinks = "/ets/useful-links";
static const String news = "/ets/news";
static const String more = "/more";
static const String settings = "/more/settings";
static const String contributors = "/more/contributors";
Expand Down
132 changes: 132 additions & 0 deletions lib/core/managers/news_repository.dart
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;
}
}
40 changes: 40 additions & 0 deletions lib/core/models/news.dart
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(),
};
}
}
4 changes: 4 additions & 0 deletions lib/core/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ mixin Utils {
: darkColor;
}

static bool isDarkTheme(BuildContext context) {
return Theme.of(context).brightness == Brightness.dark;
}

/// Get first day of the week depending on startingDay which corresponds to weekday
static DateTime getFirstDayOfCurrentWeek(
DateTime currentDate, StartingDayOfWeek startingDay) {
Expand Down
67 changes: 67 additions & 0 deletions lib/core/viewmodels/news_viewmodel.dart
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);
}
}
}
2 changes: 2 additions & 0 deletions lib/locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'package:notredame/core/services/internal_info_service.dart';
import 'package:notredame/core/services/launch_url_service.dart';
import 'package:notredame/core/services/navigation_service.dart';
import 'package:notredame/core/services/networking_service.dart';
import 'package:notredame/core/managers/news_repository.dart';
import 'package:notredame/core/services/preferences_service.dart';
import 'package:notredame/core/services/remote_config_service.dart';
import 'package:notredame/core/services/rive_animation_service.dart';
Expand Down Expand Up @@ -47,6 +48,7 @@ void setupLocator() {
locator.registerLazySingleton(() => CacheManager());
locator.registerLazySingleton(() => SettingsManager());
locator.registerLazySingleton(() => QuickLinkRepository());
locator.registerLazySingleton(() => NewsRepository());

// Other
locator.registerLazySingleton(() => SignetsAPIClient());
Expand Down
10 changes: 10 additions & 0 deletions lib/ui/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import 'package:notredame/ui/views/choose_language_view.dart';
import 'package:notredame/ui/views/contributors_view.dart';
import 'package:notredame/ui/views/dashboard_view.dart';
import 'package:notredame/ui/views/faq_view.dart';
import 'package:notredame/ui/views/ets_view.dart';
import 'package:notredame/ui/views/news_view.dart';
import 'package:notredame/ui/views/feedback_view.dart';
import 'package:notredame/ui/views/grade_details_view.dart';
import 'package:notredame/ui/views/login_view.dart';
Expand Down Expand Up @@ -80,9 +82,17 @@ Route<dynamic> generateRoute(RouteSettings routeSettings) {
pageBuilder: (_, __, ___) =>
GradesDetailsView(course: routeSettings.arguments! as Course));
case RouterPaths.ets:
return PageRouteBuilder(
settings: RouteSettings(name: routeSettings.name),
pageBuilder: (_, __, ___) => ETSView());
case RouterPaths.usefulLinks:
return PageRouteBuilder(
settings: RouteSettings(name: routeSettings.name),
pageBuilder: (_, __, ___) => QuickLinksView());
case RouterPaths.news:
return PageRouteBuilder(
settings: RouteSettings(name: routeSettings.name),
pageBuilder: (_, __, ___) => NewsView());
case RouterPaths.webView:
return PageRouteBuilder(
pageBuilder: (_, __, ___) =>
Expand Down
Loading

0 comments on commit c255aca

Please sign in to comment.