diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b9a272696..80c95058f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,90 +4,84 @@ PODS: - ReachabilitySwift - device_info_plus (0.0.1): - Flutter - - Firebase/Analytics (9.2.0): + - Firebase/Analytics (10.3.0): - Firebase/Core - - Firebase/Core (9.2.0): + - Firebase/Core (10.3.0): - Firebase/CoreOnly - - FirebaseAnalytics (~> 9.2.0) - - Firebase/CoreOnly (9.2.0): - - FirebaseCore (= 9.2.0) - - Firebase/Crashlytics (9.2.0): + - FirebaseAnalytics (~> 10.3.0) + - Firebase/CoreOnly (10.3.0): + - FirebaseCore (= 10.3.0) + - Firebase/Crashlytics (10.3.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 9.2.0) - - Firebase/RemoteConfig (9.2.0): + - FirebaseCrashlytics (~> 10.3.0) + - Firebase/RemoteConfig (10.3.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 9.2.0) - - firebase_analytics (9.2.0): - - Firebase/Analytics (= 9.2.0) + - FirebaseRemoteConfig (~> 10.3.0) + - firebase_analytics (10.1.0): + - Firebase/Analytics (= 10.3.0) - firebase_core - Flutter - - firebase_core (1.19.2): - - Firebase/CoreOnly (= 9.2.0) + - firebase_core (2.4.1): + - Firebase/CoreOnly (= 10.3.0) - Flutter - - firebase_crashlytics (2.6.1): - - Firebase/Crashlytics (= 9.2.0) + - firebase_crashlytics (3.0.9): + - Firebase/Crashlytics (= 10.3.0) - firebase_core - Flutter - - firebase_remote_config (2.0.10): - - Firebase/RemoteConfig (= 9.2.0) + - firebase_remote_config (3.0.9): + - Firebase/RemoteConfig (= 10.3.0) - firebase_core - Flutter - - FirebaseABTesting (9.6.0): - - FirebaseCore (~> 9.0) - - FirebaseAnalytics (9.2.0): - - FirebaseAnalytics/AdIdSupport (= 9.2.0) - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" + - FirebaseABTesting (10.5.0): + - FirebaseCore (~> 10.0) + - FirebaseAnalytics (10.3.0): + - FirebaseAnalytics/AdIdSupport (= 10.3.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseAnalytics/AdIdSupport (9.2.0): - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleAppMeasurement (= 9.2.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" + - FirebaseAnalytics/AdIdSupport (10.3.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCore (9.2.0): - - FirebaseCoreDiagnostics (~> 9.0) - - FirebaseCoreInternal (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - FirebaseCoreDiagnostics (9.5.0): - - GoogleDataTransport (< 10.0.0, >= 9.1.4) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCoreInternal (9.5.0): - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - FirebaseCrashlytics (9.2.0): - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleDataTransport (< 10.0.0, >= 9.1.4) - - GoogleUtilities/Environment (~> 7.7) + - FirebaseCore (10.3.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Logger (~> 7.8) + - FirebaseCoreInternal (10.5.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseCrashlytics (10.3.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleDataTransport (~> 9.2) + - GoogleUtilities/Environment (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (~> 2.1) - - FirebaseInstallations (9.5.0): - - FirebaseCore (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/UserDefaults (~> 7.7) + - FirebaseInstallations (10.5.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) - - FirebaseRemoteConfig (9.2.0): - - FirebaseABTesting (~> 9.0) - - FirebaseCore (~> 9.0) - - FirebaseInstallations (~> 9.0) - - GoogleUtilities/Environment (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" + - FirebaseRemoteConfig (10.3.0): + - FirebaseABTesting (~> 10.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - Flutter (1.0.0) - flutter_config (0.0.1): - Flutter - flutter_custom_tabs (0.0.1): - Flutter - - flutter_secure_storage (3.3.1): + - flutter_secure_storage (6.0.0): - Flutter - fluttertoast (0.0.2): - Flutter @@ -95,30 +89,30 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - google_maps_flutter (0.0.1): + - google_maps_flutter_ios (0.0.1): - Flutter - GoogleMaps - - GoogleAppMeasurement (9.2.0): - - GoogleAppMeasurement/AdIdSupport (= 9.2.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" + - GoogleAppMeasurement (10.3.0): + - GoogleAppMeasurement/AdIdSupport (= 10.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (9.2.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 9.2.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" + - GoogleAppMeasurement/AdIdSupport (10.3.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (9.2.0): - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" + - GoogleAppMeasurement/WithoutAdIdSupport (10.3.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/MethodSwizzler (~> 7.8) + - GoogleUtilities/Network (~> 7.8) + - "GoogleUtilities/NSData+zlib (~> 7.8)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleDataTransport (9.2.0): + - GoogleDataTransport (9.2.1): - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) @@ -127,24 +121,24 @@ PODS: - GoogleMaps/Base (5.2.0) - GoogleMaps/Maps (5.2.0): - GoogleMaps/Base - - GoogleUtilities/AppDelegateSwizzler (7.7.0): + - GoogleUtilities/AppDelegateSwizzler (7.11.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.7.0): + - GoogleUtilities/Environment (7.11.0): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.7.0): + - GoogleUtilities/Logger (7.11.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.7.0): + - GoogleUtilities/MethodSwizzler (7.11.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (7.7.0): + - GoogleUtilities/Network (7.11.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.7.0)" - - GoogleUtilities/Reachability (7.7.0): + - "GoogleUtilities/NSData+zlib (7.11.0)" + - GoogleUtilities/Reachability (7.11.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.7.0): + - GoogleUtilities/UserDefaults (7.11.0): - GoogleUtilities/Logger - home_widget (0.0.1): - Flutter @@ -166,6 +160,7 @@ PODS: - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) + - SwiftyXMLParser (5.5.0) - Toast (4.0.0) - url_launcher_ios (0.0.1): - Flutter @@ -184,13 +179,14 @@ DEPENDENCIES: - flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - - google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`) + - google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`) - home_widget (from `.symlinks/plugins/home_widget/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) + - SwiftyXMLParser (from `https://github.com/yahoojapan/SwiftyXMLParser.git`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) @@ -200,7 +196,6 @@ SPEC REPOS: - FirebaseABTesting - FirebaseAnalytics - FirebaseCore - - FirebaseCoreDiagnostics - FirebaseCoreInternal - FirebaseCrashlytics - FirebaseInstallations @@ -238,8 +233,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_secure_storage/ios" fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" - google_maps_flutter: - :path: ".symlinks/plugins/google_maps_flutter/ios" + google_maps_flutter_ios: + :path: ".symlinks/plugins/google_maps_flutter_ios/ios" home_widget: :path: ".symlinks/plugins/home_widget/ios" in_app_review: @@ -252,40 +247,46 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_ios/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" + SwiftyXMLParser: + :git: https://github.com/yahoojapan/SwiftyXMLParser.git url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" webview_flutter_wkwebview: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" +CHECKOUT OPTIONS: + SwiftyXMLParser: + :commit: d7a1d23f04c86c1cd2e8f19247dd15d74e0ea8be + :git: https://github.com/yahoojapan/SwiftyXMLParser.git + SPEC CHECKSUMS: - connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e + connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed - Firebase: 4ba896cb8e5105d4b9e247e1c1b6222b548df55a - firebase_analytics: e6754d7dd82bd2006b002d88c0f44f8f50da26e6 - firebase_core: ada8be870601fe3c2684dae2356f634189bd598f - firebase_crashlytics: fed0cb9004bc3bb7005f04cf2e01765c441e941a - firebase_remote_config: a99557e31b31cb583d3ca70c66134099ad4b054b - FirebaseABTesting: 61826730ce9eee8781ba99a2b3420e9bce148dc9 - FirebaseAnalytics: af5a03a8dff7648c7b8486f6a78b1368e0268dd3 - FirebaseCore: 0e27f2a15d8f7b7ef11e7d93e23b1cbab55d748c - FirebaseCoreDiagnostics: 17cbf4e72b1dbd64bfdc33d4b1f07bce4f16f1d8 - FirebaseCoreInternal: 50a8e39cae8abf72d5145d07ea34c3244f70862b - FirebaseCrashlytics: 9fff819edb2bfc9d3eff612225b207d41945a935 - FirebaseInstallations: 41f811b530c41dd90973d0174381cdb3fcb5e839 - FirebaseRemoteConfig: 16e29297f0dd0c7d2415c4506d614fe0b54875d1 + Firebase: f92fc551ead69c94168d36c2b26188263860acd9 + firebase_analytics: 9f3a4cb560a59976b2c48707abae2d4cb94bcb3a + firebase_core: bf59c32d2e53814f558efa20840c1902fa2fe461 + firebase_crashlytics: a45cced3521640e1e389d8b3662936ea9afd6055 + firebase_remote_config: 5007603d4cec2dc1e5016077a7ec36ed93c5041b + FirebaseABTesting: 8cb5cc4e395c8dce8a2820a6a329020ead56fe2f + FirebaseAnalytics: 036232b6a1e2918e5f67572417be1173576245f3 + FirebaseCore: 988754646ab3bd4bdcb740f1bfe26b9f6c0d5f2a + FirebaseCoreInternal: e463f41bb935cd049505bf7e9a5bdd7dcea90df6 + FirebaseCrashlytics: f20d956f8229010b645e534693c39e0b7843c268 + FirebaseInstallations: 935bc4abb6f7a035cab7a0c31cb777b2be3dd254 + FirebaseRemoteConfig: c24f767c17b0440ee63c7e93380d599173556113 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_config: 2226c1df19c78fe34a05eb7f1363445f18e76fc1 flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 - flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - google_maps_flutter: c59fc576c0d0c7f4dc4bd63832c862d22d5a7c6d - GoogleAppMeasurement: 7a33224321f975d58c166657260526775d9c6b1a - GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f + google_maps_flutter_ios: 66201f392bf62d500f07670a30488a247b9bb5b9 + GoogleAppMeasurement: c7d6fff39bf2d829587d74088d582e32d75133c3 + GoogleDataTransport: ea169759df570f4e37bdee1623ec32a7e64e67c4 GoogleMaps: 025272d5876d3b32604e5c080dc25eaf68764693 - GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 + GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f home_widget: 2829415127ee92e876f816cbbe44c0b6601b8a37 - in_app_review: 4a97249f7a2f539a0f294c2d9196b7fe35e49541 + in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 @@ -293,10 +294,11 @@ SPEC CHECKSUMS: ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + SwiftyXMLParser: 027d9e6fb54a38d95dccec025bcea9693f699c47 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162 -PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 +PODFILE CHECKSUM: a6ab79567d5a527b85ace1f55bd19f140d2d619c COCOAPODS: 1.11.3 diff --git a/lib/core/constants/quick_links.dart b/lib/core/constants/quick_links.dart index 819979296..823195e6e 100644 --- a/lib/core/constants/quick_links.dart +++ b/lib/core/constants/quick_links.dart @@ -12,6 +12,7 @@ import 'package:notredame/ui/utils/app_theme.dart'; List quickLinks(AppIntl intl) => [ QuickLink( + id: 1, name: intl.ets_security_title, image: const FaIcon( FontAwesomeIcons.shieldHalved, @@ -20,6 +21,7 @@ List quickLinks(AppIntl intl) => [ ), link: 'security'), QuickLink( + id: 2, name: intl.ets_monets_title, image: Image.asset( 'assets/images/ic_monets_sans_nom_red.png', @@ -27,6 +29,7 @@ List quickLinks(AppIntl intl) => [ ), link: 'https://portail.etsmtl.ca/home'), QuickLink( + id: 3, name: intl.ets_bibliotech_title, image: const FaIcon( FontAwesomeIcons.book, @@ -35,6 +38,7 @@ List quickLinks(AppIntl intl) => [ ), link: 'https://www.etsmtl.ca/Bibliotheque/Accueil'), QuickLink( + id: 4, name: intl.ets_news_title, image: const FaIcon( FontAwesomeIcons.newspaper, @@ -43,6 +47,7 @@ List quickLinks(AppIntl intl) => [ ), link: 'https://www.etsmtl.ca/nouvelles'), QuickLink( + id: 5, name: intl.ets_directory_title, image: const FaIcon( FontAwesomeIcons.addressBook, @@ -51,6 +56,7 @@ List quickLinks(AppIntl intl) => [ ), link: 'https://www.etsmtl.ca/bottin'), QuickLink( + id: 6, name: intl.ets_moodle_title, image: Image.asset( 'assets/images/ic_moodle_red.png', @@ -58,6 +64,7 @@ List quickLinks(AppIntl intl) => [ ), link: 'https://ena.etsmtl.ca/'), QuickLink( + id: 7, name: intl.ets_schedule_generator, image: const FaIcon( FontAwesomeIcons.calendar, @@ -66,11 +73,13 @@ List quickLinks(AppIntl intl) => [ ), link: 'https://horairets.emmanuelcoulombe.dev/'), QuickLink( + id: 8, name: intl.ets_gus, image: SvgPicture.asset('assets/images/ic_gus_red.svg', color: AppTheme.etsLightRed), link: 'https://gus.etsmtl.ca/c2atom/mobile/login'), QuickLink( + id: 9, name: intl.ets_papercut_title, image: const FaIcon( FontAwesomeIcons.print, diff --git a/lib/core/managers/quick_link_repository.dart b/lib/core/managers/quick_link_repository.dart new file mode 100644 index 000000000..9250445b2 --- /dev/null +++ b/lib/core/managers/quick_link_repository.dart @@ -0,0 +1,46 @@ +// FLUTTER / DART / THIRD-PARTIES +import 'dart:convert'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +// SERVICES +import 'package:notredame/core/managers/cache_manager.dart'; + +// MODELS +import 'package:notredame/core/models/quick_link.dart'; +import 'package:notredame/core/models/quick_link_data.dart'; + +// CONSTANTS +import 'package:notredame/core/constants/quick_links.dart'; + +// OTHERS +import 'package:notredame/locator.dart'; + +class QuickLinkRepository { + final CacheManager _cacheManager = locator(); + + static const String quickLinksCacheKey = "quickLinksCache"; + + Future> getQuickLinkDataFromCache() async { + final cacheData = await _cacheManager.get(quickLinksCacheKey); + final responseCache = jsonDecode(cacheData) as List; + + return responseCache + .map((e) => QuickLinkData.fromJson(e as Map)) + .toList(); + } + + Future updateQuickLinkDataToCache(List quickLinkList) async { + final quickLinkDataList = quickLinkList + .asMap() + .entries + .map((e) => QuickLinkData(id: e.value.id, index: e.key)) + .toList(); + + await _cacheManager.update( + quickLinksCacheKey, jsonEncode(quickLinkDataList)); + } + + List getDefaultQuickLinks(AppIntl intl) { + return quickLinks(intl); + } +} diff --git a/lib/core/models/quick_link.dart b/lib/core/models/quick_link.dart index c2fe715b6..d2699d7e3 100644 --- a/lib/core/models/quick_link.dart +++ b/lib/core/models/quick_link.dart @@ -2,9 +2,14 @@ import 'package:flutter/material.dart'; class QuickLink { + final int id; final Widget image; final String name; final String link; - QuickLink({@required this.image, @required this.name, @required this.link}); + QuickLink( + {@required this.id, + @required this.image, + @required this.name, + @required this.link}); } diff --git a/lib/core/models/quick_link_data.dart b/lib/core/models/quick_link_data.dart new file mode 100644 index 000000000..274b89b29 --- /dev/null +++ b/lib/core/models/quick_link_data.dart @@ -0,0 +1,23 @@ +// FLUTTER / DART / THIRD-PARTIES +import 'package:flutter/material.dart'; + +class QuickLinkData { + final int id; + final int index; + + QuickLinkData({@required this.id, @required this.index}); + + factory QuickLinkData.fromJson(Map json) { + return QuickLinkData( + id: json['id'] as int, + index: json['index'] as int, + ); + } + + Map toJson() { + return { + 'id': id, + 'index': index, + }; + } +} diff --git a/lib/core/viewmodels/quick_links_viewmodel.dart b/lib/core/viewmodels/quick_links_viewmodel.dart index 7ceee69cd..ca59736b8 100644 --- a/lib/core/viewmodels/quick_links_viewmodel.dart +++ b/lib/core/viewmodels/quick_links_viewmodel.dart @@ -2,15 +2,88 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:stacked/stacked.dart'; -// CONSTANTS -import 'package:notredame/core/constants/quick_links.dart'; +// MANAGERS +import 'package:notredame/core/managers/quick_link_repository.dart'; // MODELS import 'package:notredame/core/models/quick_link.dart'; +import 'package:notredame/core/models/quick_link_data.dart'; + +// OTHERS +import 'package:notredame/locator.dart'; + +class QuickLinksViewModel extends FutureViewModel> { + /// Localization class of the application. + final AppIntl _appIntl; -class QuickLinksViewModel extends BaseViewModel { /// used to get all links for ETS page - final List quickLinkList; + List quickLinkList = []; + + List deletedQuickLinks = []; + + final QuickLinkRepository _quickLinkRepository = + locator(); + + QuickLinksViewModel(AppIntl intl) : _appIntl = intl; + + Future> getQuickLinks() async { + List quickLinkDataList = []; + try { + quickLinkDataList = + await _quickLinkRepository.getQuickLinkDataFromCache(); + } catch (e) { + // if cache is not initialized, return default list + return _quickLinkRepository.getDefaultQuickLinks(_appIntl); + } + + // otherwise, return quickLinks according to the cache + final defaultQuickLinks = + _quickLinkRepository.getDefaultQuickLinks(_appIntl); + quickLinkDataList.sort((a, b) => a.index.compareTo(b.index)); + return quickLinkDataList + .map((data) => defaultQuickLinks + .firstWhere((quickLink) => quickLink.id == data.id)) + .toList(); + } + + Future> getDeletedQuickLinks() async { + // Get ids from current quick links + final currentQuickLinkIds = quickLinkList.map((e) => e.id).toList(); + + // Return those not in current quick links but in default list + return _quickLinkRepository + .getDefaultQuickLinks(_appIntl) + .where((element) => !currentQuickLinkIds.contains(element.id)) + .toList(); + } + + Future deleteQuickLink(int index) async { + final deletedQuickLink = quickLinkList.removeAt(index); + deletedQuickLinks.add(deletedQuickLink); + await _quickLinkRepository.updateQuickLinkDataToCache(quickLinkList); + notifyListeners(); + } + + Future restoreQuickLink(int index) async { + final deletedQuickLink = deletedQuickLinks.removeAt(index); + quickLinkList.add(deletedQuickLink); + await _quickLinkRepository.updateQuickLinkDataToCache(quickLinkList); + notifyListeners(); + } + + Future reorderQuickLinks(int oldIndex, int newIndex) async { + final QuickLink item = quickLinkList.removeAt(oldIndex); + quickLinkList.insert(newIndex, item); + await _quickLinkRepository.updateQuickLinkDataToCache(quickLinkList); + notifyListeners(); + } - QuickLinksViewModel(AppIntl intl) : quickLinkList = quickLinks(intl); + @override + Future> futureToRun() async { + setBusyForObject(quickLinkList, true); + quickLinkList = await getQuickLinks(); + deletedQuickLinks = await getDeletedQuickLinks(); + setBusyForObject(quickLinkList, false); + return quickLinkList; + } } diff --git a/lib/locator.dart b/lib/locator.dart index 012a95d7e..abc371eab 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -22,6 +22,7 @@ import 'package:notredame/core/managers/user_repository.dart'; import 'package:notredame/core/managers/course_repository.dart'; import 'package:notredame/core/managers/cache_manager.dart'; import 'package:notredame/core/managers/settings_manager.dart'; +import 'package:notredame/core/managers/quick_link_repository.dart'; // OTHER import 'package:ets_api_clients/clients.dart'; @@ -49,6 +50,7 @@ void setupLocator() { locator.registerLazySingleton(() => CourseRepository()); locator.registerLazySingleton(() => CacheManager()); locator.registerLazySingleton(() => SettingsManager()); + locator.registerLazySingleton(() => QuickLinkRepository()); // Other locator.registerLazySingleton(() => SignetsAPIClient()); diff --git a/lib/ui/views/quick_links_view.dart b/lib/ui/views/quick_links_view.dart index 97ce90ef5..3c3fb8d8d 100644 --- a/lib/ui/views/quick_links_view.dart +++ b/lib/ui/views/quick_links_view.dart @@ -3,44 +3,215 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +// MODEL +import 'package:notredame/core/models/quick_link.dart'; + // VIEW-MODEL import 'package:notredame/core/viewmodels/quick_links_viewmodel.dart'; +// UTILS +import 'package:notredame/ui/utils/app_theme.dart'; + // WIDGETS import 'package:notredame/ui/widgets/base_scaffold.dart'; import 'package:notredame/ui/widgets/web_link_card.dart'; +import 'package:reorderable_grid_view/reorderable_grid_view.dart'; class QuickLinksView extends StatefulWidget { @override _QuickLinksViewState createState() => _QuickLinksViewState(); } -class _QuickLinksViewState extends State { +class _QuickLinksViewState extends State + with SingleTickerProviderStateMixin { + // Enable/Disable the edit state + bool _editMode = false; + + // Animation Controller for Shake Animation + AnimationController _controller; + Animation _animation; + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + duration: const Duration(milliseconds: 250), + vsync: this, + ); + + _animation = Tween(begin: -0.05, end: 0.05).animate(_controller); + } + @override Widget build(BuildContext context) => ViewModelBuilder.reactive( viewModelBuilder: () => QuickLinksViewModel(AppIntl.of(context)), builder: (context, model, child) => BaseScaffold( isLoading: model.isBusy, - appBar: AppBar( - title: Text(AppIntl.of(context).title_ets), - automaticallyImplyLeading: false, - ), - body: SafeArea( - child: Align( - alignment: Alignment.topCenter, - child: SingleChildScrollView( - padding: const EdgeInsets.only(top: 8.0), - child: Wrap( - alignment: WrapAlignment.center, - children: List.generate( - model.quickLinkList.length, - (index) => WebLinkCard(model.quickLinkList[index]), - ), - ), + appBar: _buildAppBar(context, model), + body: _buildBody(context, model), + ), + ); + + AppBar _buildAppBar(BuildContext context, QuickLinksViewModel model) { + return AppBar( + title: Text(AppIntl.of(context).title_ets), + automaticallyImplyLeading: false, + actions: const [], + ); + } + + Widget _buildBody(BuildContext context, QuickLinksViewModel model) { + return GestureDetector( + onTap: () { + if (_editMode) { + _controller.reset(); + setState(() { + _editMode = false; + }); + } + }, + child: SafeArea( + child: Column( + children: [ + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0), + child: _buildReorderableGridView( + model, model.quickLinkList, _buildDeleteButton), ), ), - ), + if (_editMode && model.deletedQuickLinks.isNotEmpty) ...[ + const Divider( + thickness: 2, + indent: 10, + endIndent: 10, + ), + Expanded( + child: Padding( + padding: + const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0), + child: _buildReorderableGridView( + model, model.deletedQuickLinks, _buildAddButton), + ), + ), + ], + ], ), - ); + ), + ); + } + + ReorderableGridView _buildReorderableGridView( + QuickLinksViewModel model, + List quickLinks, + Widget Function(QuickLinksViewModel, int) buildButtonFunction) { + return ReorderableGridView.count( + mainAxisSpacing: 2.0, + crossAxisSpacing: 2.0, + crossAxisCount: 3, + children: List.generate( + quickLinks.length, + (index) { + return KeyedSubtree( + key: ValueKey(quickLinks[index].id), + child: + _buildGridChild(model, index, quickLinks, buildButtonFunction), + ); + }, + ), + onReorder: (oldIndex, newIndex) { + setState(() { + model.reorderQuickLinks(oldIndex, newIndex); + }); + }, + ); + } + + Widget _buildGridChild( + QuickLinksViewModel model, + int index, + List quickLinks, + Widget Function(QuickLinksViewModel, int) buildButtonFunction) { + return GestureDetector( + onLongPress: _editMode + ? null + : () { + _controller.repeat(reverse: true); + setState(() { + _editMode = true; + }); + }, + child: AnimatedBuilder( + animation: _animation, + builder: (BuildContext context, Widget child) { + return Transform.rotate( + angle: _editMode ? _animation.value : 0, + child: child, + ); + }, + child: Stack( + children: [ + WebLinkCard(quickLinks[index]), + if (_editMode && + quickLinks[index].id != + 1) // Don't show delete button for Security QuickLink + Positioned( + top: 0, + left: 0, + child: buildButtonFunction(model, index), + ), + ], + ), + ), + ); + } + + Container _buildDeleteButton(QuickLinksViewModel model, int index) { + return Container( + width: 32, + height: 32, + decoration: const BoxDecoration( + color: AppTheme.etsDarkGrey, + shape: BoxShape.circle, + ), + child: IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.close, color: Colors.white, size: 16), + onPressed: () { + setState(() { + model.deleteQuickLink(index); + }); + }, + ), + ); + } + + Container _buildAddButton(QuickLinksViewModel model, int index) { + return Container( + width: 32, + height: 32, + decoration: const BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, + ), + child: IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.add, color: Colors.white, size: 20), + onPressed: () { + setState(() { + model.restoreQuickLink(index); + }); + }, + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } } diff --git a/pubspec.lock b/pubspec.lock index e24d0871f..bcc47f26a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -912,6 +912,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.4" + reorderable_grid_view: + dependency: "direct main" + description: + name: reorderable_grid_view + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.6" rive: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e72117d56..acc62915d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: The 4th generation of ÉTSMobile, the main gateway between the Éco # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.25.3+1 +version: 4.26.0+1 environment: sdk: ">=2.10.0 <3.0.0" @@ -71,6 +71,7 @@ dependencies: auto_size_text: ^3.0.0 easter_egg_trigger: ^1.0.1 calendar_view: ^1.0.1 + reorderable_grid_view: ^2.2.6 dev_dependencies: flutter_test: diff --git a/test/helpers.dart b/test/helpers.dart index 52e32a81d..2b8e67a91 100644 --- a/test/helpers.dart +++ b/test/helpers.dart @@ -28,11 +28,13 @@ import 'package:notredame/core/services/app_widget_service.dart'; import 'package:notredame/core/services/in_app_review_service.dart'; import 'package:notredame/core/services/launch_url_service.dart'; import 'package:notredame/core/services/remote_config_service.dart'; +import 'package:notredame/core/managers/quick_link_repository.dart'; // MOCKS import 'package:ets_api_clients/testing.dart'; import 'mock/managers/cache_manager_mock.dart'; import 'mock/managers/course_repository_mock.dart'; +import 'mock/managers/quick_links_repository_mock.dart'; import 'mock/managers/settings_manager_mock.dart'; import 'mock/managers/user_repository_mock.dart'; import 'mock/services/analytics_service_mock.dart'; @@ -328,6 +330,16 @@ RemoteConfigService setupRemoteConfigServiceMock() { return service; } +/// Load a mock of the [QuickLinkRepository] +QuickLinkRepository setupQuickLinkRepositoryMock() { + unregister(); + final repository = QuickLinkRepositoryMock(); + + locator.registerSingleton(repository); + + return repository; +} + bool getCalendarViewEnabled() { final RemoteConfigService remoteConfigService = locator(); diff --git a/test/managers/quick_link_repository_test.dart b/test/managers/quick_link_repository_test.dart new file mode 100644 index 000000000..f65b26e8e --- /dev/null +++ b/test/managers/quick_link_repository_test.dart @@ -0,0 +1,98 @@ +// FLUTTER / DART / THIRD-PARTIES +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +// SERVICES / MANAGER +import 'package:notredame/core/managers/cache_manager.dart'; +import 'package:notredame/core/managers/quick_link_repository.dart'; + +// MODELS +import 'package:notredame/core/models/quick_link.dart'; +import 'package:notredame/core/models/quick_link_data.dart'; + +// UTILS +import '../helpers.dart'; + +// MOCKS +import '../mock/managers/cache_manager_mock.dart'; + +void main() { + CacheManager cacheManager; + QuickLinkRepository quickLinkRepository; + + group("QuickLinkRepository - ", () { + setUp(() { + // Setup needed services and managers + cacheManager = setupCacheManagerMock(); + + quickLinkRepository = QuickLinkRepository(); + }); + + tearDown(() { + clearInteractions(cacheManager); + unregister(); + }); + + group("getQuickLinkDataFromCache - ", () { + test("QuickLinkData is loaded from cache.", () async { + // Stub the cache to return some QuickLinkData + final quickLinkData = QuickLinkData(id: 1, index: 0); + CacheManagerMock.stubGet( + cacheManager as CacheManagerMock, + QuickLinkRepository.quickLinksCacheKey, + jsonEncode([quickLinkData])); + + final List results = + await quickLinkRepository.getQuickLinkDataFromCache(); + + expect(results, isInstanceOf>()); + expect(results[0].id, quickLinkData.id); + expect(results[0].index, quickLinkData.index); + + verify(cacheManager.get(QuickLinkRepository.quickLinksCacheKey)) + .called(1); + }); + + test( + "Trying to recover QuickLinkData from cache but an exception is raised.", + () async { + // Stub the cache to throw an exception + CacheManagerMock.stubGetException(cacheManager as CacheManagerMock, + QuickLinkRepository.quickLinksCacheKey); + + expect(quickLinkRepository.getQuickLinkDataFromCache(), + throwsA(isInstanceOf())); + }); + }); + + group("updateQuickLinkDataToCache - ", () { + test("Updating QuickLinkData to cache.", () async { + // Prepare some QuickLinkData to be cached + final quickLink = + QuickLink(id: 1, image: const Text(""), name: 'name', link: 'url'); + final quickLinkData = QuickLinkData(id: quickLink.id, index: 0); + + await quickLinkRepository.updateQuickLinkDataToCache([quickLink]); + + verify(cacheManager.update(QuickLinkRepository.quickLinksCacheKey, + jsonEncode([quickLinkData]))).called(1); + }); + + test( + "Trying to update QuickLinkData to cache but an exception is raised.", + () async { + // Stub the cache to throw an exception + CacheManagerMock.stubUpdateException(cacheManager as CacheManagerMock, + QuickLinkRepository.quickLinksCacheKey); + + final quickLink = + QuickLink(id: 1, image: const Text(""), name: 'name', link: 'url'); + + expect(quickLinkRepository.updateQuickLinkDataToCache([quickLink]), + throwsA(isInstanceOf())); + }); + }); + }); +} diff --git a/test/mock/managers/quick_links_repository_mock.dart b/test/mock/managers/quick_links_repository_mock.dart new file mode 100644 index 000000000..af529884d --- /dev/null +++ b/test/mock/managers/quick_links_repository_mock.dart @@ -0,0 +1,49 @@ +// FLUTTER / DART / THIRD-PARTIES +import 'package:mockito/mockito.dart'; + +// MANAGER +import 'package:notredame/core/managers/quick_link_repository.dart'; + +// MODELS +import 'package:notredame/core/models/quick_link.dart'; +import 'package:notredame/core/models/quick_link_data.dart'; + +// UTILS +import 'package:ets_api_clients/exceptions.dart'; + +class QuickLinkRepositoryMock extends Mock implements QuickLinkRepository { + /// Stub the function [getQuickLinkDataFromCache] of [mock] when called will return [toReturn]. + static void stubGetQuickLinkDataFromCache(QuickLinkRepositoryMock mock, + {List toReturn = const []}) { + when(mock.getQuickLinkDataFromCache()).thenAnswer((_) async => toReturn); + } + + /// Stub the function [getQuickLinkDataFromCache] of [mock] when called will throw [toThrow]. + static void stubGetQuickLinkDataFromCacheException( + QuickLinkRepositoryMock mock, + {Exception toThrow = const ApiException(prefix: 'ApiException')}) { + when(mock.getQuickLinkDataFromCache()).thenAnswer((_) => + Future.delayed(const Duration(milliseconds: 50)) + .then((value) => throw toThrow)); + } + + /// Stub the function [getDefaultQuickLinks] of [mock] when called will return [toReturn]. + static void stubGetDefaultQuickLinks(QuickLinkRepositoryMock mock, + {List toReturn = const []}) { + when(mock.getDefaultQuickLinks(any)).thenAnswer((_) => toReturn); + } + + /// Stub the function [updateQuickLinkDataToCache] of [mock] when called will complete without errors. + static void stubUpdateQuickLinkDataToCache(QuickLinkRepositoryMock mock) { + when(mock.updateQuickLinkDataToCache(any)).thenAnswer((_) async => {}); + } + + /// Stub the function [updateQuickLinkDataToCache] of [mock] when called will throw [toThrow]. + static void stubUpdateQuickLinkDataToCacheException( + QuickLinkRepositoryMock mock, + {Exception toThrow = const ApiException(prefix: 'ApiException')}) { + when(mock.updateQuickLinkDataToCache(any)).thenAnswer((_) => + Future.delayed(const Duration(milliseconds: 50)) + .then((value) => throw toThrow)); + } +} diff --git a/test/ui/views/goldenFiles/quicksLinksView_1.png b/test/ui/views/goldenFiles/quicksLinksView_1.png index 3172fac2e..0d550ce2f 100644 Binary files a/test/ui/views/goldenFiles/quicksLinksView_1.png and b/test/ui/views/goldenFiles/quicksLinksView_1.png differ diff --git a/test/ui/views/quick_links_view_test.dart b/test/ui/views/quick_links_view_test.dart index 3ee1dc07a..4e0c7e0c3 100644 --- a/test/ui/views/quick_links_view_test.dart +++ b/test/ui/views/quick_links_view_test.dart @@ -12,13 +12,20 @@ import 'package:notredame/core/constants/quick_links.dart'; import 'package:notredame/core/services/networking_service.dart'; import 'package:notredame/core/services/launch_url_service.dart'; +// MANAGERS +import 'package:notredame/core/managers/quick_link_repository.dart'; + // VIEW import 'package:notredame/ui/views/quick_links_view.dart'; // WIDGETS import 'package:notredame/ui/widgets/web_link_card.dart'; +// UTILS import '../../helpers.dart'; + +// MOCKS +import '../../mock/managers/quick_links_repository_mock.dart'; import '../../mock/services/analytics_service_mock.dart'; import '../../mock/services/internal_info_service_mock.dart'; import '../../mock/services/navigation_service_mock.dart'; @@ -26,6 +33,8 @@ import '../../mock/services/navigation_service_mock.dart'; void main() { AppIntl intl; + QuickLinkRepository quickLinkRepository; + group('QuickLinksView - ', () { setUp(() async { intl = await setupAppIntl(); @@ -34,6 +43,13 @@ void main() { setupInternalInfoServiceMock(); setupNetworkingServiceMock(); setupLaunchUrlServiceMock(); + quickLinkRepository = setupQuickLinkRepositoryMock(); + QuickLinkRepositoryMock.stubGetDefaultQuickLinks( + quickLinkRepository as QuickLinkRepositoryMock, + toReturn: quickLinks(intl)); + + QuickLinkRepositoryMock.stubGetQuickLinkDataFromCacheException( + quickLinkRepository as QuickLinkRepositoryMock); }); tearDown(() { @@ -42,6 +58,7 @@ void main() { unregister(); unregister(); unregister(); + unregister(); }); group('UI - ', () { @@ -51,8 +68,8 @@ void main() { useScaffold: false)); await tester.pumpAndSettle(); - expect( - find.byType(WebLinkCard), findsNWidgets(quickLinks(intl).length)); + expect(find.byType(WebLinkCard, skipOffstage: false), + findsNWidgets(quickLinks(intl).length)); }); group("golden - ", () { diff --git a/test/ui/widgets/link_web_view_test.dart b/test/ui/widgets/link_web_view_test.dart index b7b1e48a1..93ce97810 100644 --- a/test/ui/widgets/link_web_view_test.dart +++ b/test/ui/widgets/link_web_view_test.dart @@ -13,6 +13,7 @@ import 'package:notredame/ui/widgets/link_web_view.dart'; import '../../helpers.dart'; final _quickLink = QuickLink( + id: 1, image: const Icon(Icons.ac_unit), name: 'test', link: 'https://clubapplets.ca/'); diff --git a/test/ui/widgets/web_link_card_test.dart b/test/ui/widgets/web_link_card_test.dart index beb09a3b7..aaba00a20 100644 --- a/test/ui/widgets/web_link_card_test.dart +++ b/test/ui/widgets/web_link_card_test.dart @@ -16,8 +16,8 @@ import 'package:notredame/ui/widgets/web_link_card.dart'; import '../../helpers.dart'; -final _quickLink = - QuickLink(image: const Icon(Icons.ac_unit), name: 'test', link: 'testlink'); +final _quickLink = QuickLink( + id: 1, image: const Icon(Icons.ac_unit), name: 'test', link: 'testlink'); void main() { AnalyticsService analyticsService; diff --git a/test/viewmodels/quick_links_viewmodel_test.dart b/test/viewmodels/quick_links_viewmodel_test.dart new file mode 100644 index 000000000..0f075cd17 --- /dev/null +++ b/test/viewmodels/quick_links_viewmodel_test.dart @@ -0,0 +1,123 @@ +// FLUTTER / DART / THIRD-PARTIES +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:notredame/core/constants/quick_links.dart'; + +// MODELS +import 'package:notredame/core/models/quick_link.dart'; +import 'package:notredame/core/models/quick_link_data.dart'; + +// VIEWMODEL +import 'package:notredame/core/viewmodels/quick_links_viewmodel.dart'; + +// MANAGER +import 'package:notredame/core/managers/quick_link_repository.dart'; + +// OTHERS +import '../helpers.dart'; +import '../mock/managers/quick_links_repository_mock.dart'; + +void main() { + AppIntl intl; + QuickLinksViewModel viewModel; + QuickLinkRepository quickLinkRepository; + + // Sample data for tests + QuickLinkData quickLinkDataSample; + QuickLink quickLinkSample; + + group("QuickLinksViewModel - ", () { + setUp(() async { + // Setting up mocks + quickLinkRepository = setupQuickLinkRepositoryMock(); + intl = await setupAppIntl(); + + viewModel = QuickLinksViewModel(intl); + quickLinkDataSample = QuickLinkData(id: 1, index: 0); + quickLinkSample = quickLinks(intl).first; + }); + + tearDown(() { + unregister(); + }); + + group('getQuickLinks -', () { + test('Should get quick links from cache', () async { + QuickLinkRepositoryMock.stubGetQuickLinkDataFromCache( + quickLinkRepository as QuickLinkRepositoryMock, + toReturn: [quickLinkDataSample]); + + QuickLinkRepositoryMock.stubGetDefaultQuickLinks( + quickLinkRepository as QuickLinkRepositoryMock, + toReturn: [quickLinkSample]); + + final result = await viewModel.getQuickLinks(); + + expect(result, [quickLinkSample]); + }); + + test('Should return default quick links if cache is not initialized', + () async { + QuickLinkRepositoryMock.stubGetQuickLinkDataFromCacheException( + quickLinkRepository as QuickLinkRepositoryMock); + + QuickLinkRepositoryMock.stubGetDefaultQuickLinks( + quickLinkRepository as QuickLinkRepositoryMock, + toReturn: [quickLinkSample]); + + final result = await viewModel.getQuickLinks(); + + expect(result, [quickLinkSample]); + }); + }); + + group('deleteQuickLink -', () { + test('Should delete a quick link and update cache', () async { + viewModel.quickLinkList = [quickLinkSample]; + + await viewModel.deleteQuickLink(0); + + expect(viewModel.quickLinkList, isEmpty); + expect(viewModel.deletedQuickLinks, [quickLinkSample]); + }); + }); + + group('restoreQuickLink -', () { + test('Should restore a deleted quick link and update cache', () async { + viewModel.deletedQuickLinks = [quickLinkSample]; + + await viewModel.restoreQuickLink(0); + + expect(viewModel.deletedQuickLinks, isEmpty); + expect(viewModel.quickLinkList, [quickLinkSample]); + }); + }); + + group('reorderQuickLinks -', () { + test('Should reorder quick links and update cache', () async { + final anotherQuickLink = quickLinks(intl).last; + viewModel.quickLinkList = [quickLinkSample, anotherQuickLink]; + + await viewModel.reorderQuickLinks(0, 1); + + expect(viewModel.quickLinkList, [anotherQuickLink, quickLinkSample]); + }); + }); + + group('futureToRun -', () { + test('Should fetch and set quick links', () async { + QuickLinkRepositoryMock.stubGetQuickLinkDataFromCache( + quickLinkRepository as QuickLinkRepositoryMock, + toReturn: [quickLinkDataSample]); + + QuickLinkRepositoryMock.stubGetDefaultQuickLinks( + quickLinkRepository as QuickLinkRepositoryMock, + toReturn: [quickLinkSample]); + + final result = await viewModel.futureToRun(); + + expect(result, [quickLinkSample]); + }); + }); + }); +} diff --git a/test/viewmodels/web_link_card_viewmodel_test.dart b/test/viewmodels/web_link_card_viewmodel_test.dart index 990cb269a..e600d9474 100644 --- a/test/viewmodels/web_link_card_viewmodel_test.dart +++ b/test/viewmodels/web_link_card_viewmodel_test.dart @@ -35,9 +35,9 @@ void main() { WebLinkCardViewModel viewModel; final quickLink = QuickLink( - image: const Icon(Icons.ac_unit), name: 'test', link: 'testlink'); + id: 1, image: const Icon(Icons.ac_unit), name: 'test', link: 'testlink'); final securityQuickLink = QuickLink( - image: const Icon(Icons.ac_unit), name: 'test', link: 'security'); + id: 1, image: const Icon(Icons.ac_unit), name: 'test', link: 'security'); group('WebLinkCardViewModel - ', () { setUp(() async {