From ffbe0d3b3cb906a29445da3fd7a32d1853fc316d Mon Sep 17 00:00:00 2001 From: Emma Date: Thu, 22 Sep 2022 23:02:17 -0400 Subject: [PATCH 01/21] Add broadcast card logic (WIP) --- lib/core/constants/preferences_flags.dart | 1 + lib/core/services/remote_config_service.dart | 10 +++++++++ lib/core/viewmodels/dashboard_viewmodel.dart | 22 ++++++++++++++++++++ lib/ui/views/dashboard_view.dart | 1 + 4 files changed, 34 insertions(+) diff --git a/lib/core/constants/preferences_flags.dart b/lib/core/constants/preferences_flags.dart index d6df3ff2b..183576e69 100644 --- a/lib/core/constants/preferences_flags.dart +++ b/lib/core/constants/preferences_flags.dart @@ -37,6 +37,7 @@ enum PreferencesFlag { progressBarCard, gradesCard, progressBarText, + broadcastCard, // Rating flag ratingTimer, diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index 8eda07a09..3f9cb2326 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -23,6 +23,16 @@ class RemoteConfigService { return _remoteConfig.getBool(_serviceIsDown); } + Future get dashboardMessageFr async { + fetch(); + return _remoteConfig.getString("dashboard_message_fr"); + } + + Future get dashboardMessageEn async { + fetch(); + return _remoteConfig.getString("dashboard_message_en"); + } + Future fetch() async { final AnalyticsService analyticsService = locator(); try { diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 3188bd4e7..ac0a320ff 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -21,6 +21,7 @@ import 'package:notredame/core/services/siren_flutter_service.dart'; import 'package:notredame/core/services/preferences_service.dart'; import 'package:notredame/core/services/analytics_service.dart'; import 'package:notredame/core/services/app_widget_service.dart'; +import 'package:notredame/core/services/remote_config_service.dart'; // MODEL import 'package:notredame/core/models/widget_models.dart'; @@ -58,6 +59,10 @@ class DashboardViewModel extends FutureViewModel> { /// Numbers of days elapsed and total number of days of the current session List _sessionDays = [0, 0]; + /// Message to display in case of urgent/important broadcast need (Firebase + /// remote config) + String broadcastMessage; + /// Get progress of the session double get progress => _progress; @@ -535,4 +540,21 @@ class DashboardViewModel extends FutureViewModel> { PreferencesFlag.updateAskedVersion, storeVersion.toString()); } } + + Future futureToRunBroadcast() async { + final RemoteConfigService remoteConfigService = locator(); + + String broadcastMessage = ""; + if (_appIntl.localeName == "fr") { + broadcastMessage = await remoteConfigService.dashboardMessageFr; + } else { + broadcastMessage = await remoteConfigService.dashboardMessageEn; + } + + if (broadcastMessage != "") { + _cardsToDisplay.add(PreferencesFlag.broadcastCard); + this.broadcastMessage = broadcastMessage; + // TODO: notify view ? + } + } } diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index b4e8608f2..5f34e0156 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -97,6 +97,7 @@ class _DashboardViewState extends State }); } + // TODO: add broadcast card List _buildCards(DashboardViewModel model) { final List cards = List.empty(growable: true); From 124ed42d5d88b9fae02cee3763f15bc2d674b3de Mon Sep 17 00:00:00 2001 From: Emma Date: Sun, 8 Jan 2023 17:34:02 -0500 Subject: [PATCH 02/21] WIP broadcast card UI & logic w/ remote config --- l10n/intl_en.arb | 1 + l10n/intl_fr.arb | 1 + lib/core/managers/settings_manager.dart | 2 + lib/core/viewmodels/dashboard_viewmodel.dart | 10 +++- lib/ui/views/dashboard_view.dart | 52 +++++++++++++++++++- 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/l10n/intl_en.arb b/l10n/intl_en.arb index 29365528d..2b40dd81b 100644 --- a/l10n/intl_en.arb +++ b/l10n/intl_en.arb @@ -45,6 +45,7 @@ "dashboard_msg_card_removed": "Card removed", "dashboard_restore_all_cards_title": "Restore all cards", + "card_broadcast_title": "Important notice", "card_schedule_tomorrow": " - tomorrow", diff --git a/l10n/intl_fr.arb b/l10n/intl_fr.arb index 3e051b10c..b99a2a7f6 100644 --- a/l10n/intl_fr.arb +++ b/l10n/intl_fr.arb @@ -44,6 +44,7 @@ "dashboard_msg_card_removed": "Carte supprimée", "dashboard_restore_all_cards_title": "Restaurer toutes les cartes", + "card_broadcast_title": "Annonce importante", "card_schedule_tomorrow": " - demain", diff --git a/lib/core/managers/settings_manager.dart b/lib/core/managers/settings_manager.dart index 455d76523..ac9dd8e35 100644 --- a/lib/core/managers/settings_manager.dart +++ b/lib/core/managers/settings_manager.dart @@ -89,6 +89,7 @@ class SettingsManager with ChangeNotifier { Future> getDashboard() async { final Map dashboard = {}; + // TODO: add broadcast card order final aboutUsIndex = await _preferencesService.getInt(PreferencesFlag.aboutUsCard) ?? getDefaultCardIndex(PreferencesFlag.aboutUsCard); @@ -246,6 +247,7 @@ class SettingsManager with ChangeNotifier { } /// Get the default index of each card + // TODO: use broadcast card as default 0 index int getDefaultCardIndex(PreferencesFlag flag) => flag.index - PreferencesFlag.aboutUsCard.index; } diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index ac0a320ff..522fa6562 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -194,6 +194,7 @@ class DashboardViewModel extends FutureViewModel> { Future loadDataAndUpdateWidget() async { return Future.wait([ + futureToRunBroadcast(), futureToRunGrades(), futureToRunSessionProgressBar(), futureToRunSchedule() @@ -284,6 +285,7 @@ class DashboardViewModel extends FutureViewModel> { } /// Reset all card indexes to their default values + // TODO: order for broadcast card void setAllCardsVisible() { _cards.updateAll((key, value) { _settingsManager @@ -542,8 +544,10 @@ class DashboardViewModel extends FutureViewModel> { } Future futureToRunBroadcast() async { - final RemoteConfigService remoteConfigService = locator(); + final RemoteConfigService remoteConfigService = + locator(); + // TODO: maybe set busy broadcastMessage? String broadcastMessage = ""; if (_appIntl.localeName == "fr") { broadcastMessage = await remoteConfigService.dashboardMessageFr; @@ -554,7 +558,9 @@ class DashboardViewModel extends FutureViewModel> { if (broadcastMessage != "") { _cardsToDisplay.add(PreferencesFlag.broadcastCard); this.broadcastMessage = broadcastMessage; - // TODO: notify view ? + } else { + // TODO: check if the logic is correct + _cardsToDisplay.remove(PreferencesFlag.broadcastCard); } } } diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 5f34e0156..11590fa4c 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -97,12 +97,19 @@ class _DashboardViewState extends State }); } - // TODO: add broadcast card List _buildCards(DashboardViewModel model) { final List cards = List.empty(growable: true); + // always try to build broadcast cart so the user doesn't miss out on + // important info if they dismissed it previously + // TODO: fetch if broadcast message first ? + for (final PreferencesFlag element in model.cardsToDisplay) { switch (element) { + // TODO: move first somehow + case PreferencesFlag.broadcastCard: + cards.add(_buildMessageBroadcastCard(model, element)); + break; case PreferencesFlag.aboutUsCard: cards.add(_buildAboutUsCard(model, element)); break; @@ -395,6 +402,49 @@ class _DashboardViewState extends State ]), ); + Widget _buildMessageBroadcastCard( + DashboardViewModel model, PreferencesFlag flag) => + DismissibleCard( + key: UniqueKey(), + onDismissed: (DismissDirection direction) { + dismissCard(model, flag); + }, + // isBusy: model.busy(model.courses), // TODO: see if model should be used + cardColor: AppTheme + .etsDarkRed, // TODO: maybe change color w/ remote config ? + child: Padding( + padding: const EdgeInsets.fromLTRB(17, 10, 15, 20), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + // title row + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: Text(AppIntl.of(context).card_broadcast_title, + style: Theme.of(context).primaryTextTheme.headline6), + ), + ), + const Align( + alignment: Alignment.centerRight, + child: InkWell( + child: Icon( + Icons.campaign, + color: AppTheme.lightThemeBackground, + size: 36.0, + ), + ), + ), + ], + ), + // main text + Text(model.broadcastMessage == null + ? "Null" + : model.broadcastMessage) + ]), + )); + void dismissCard(DashboardViewModel model, PreferencesFlag flag) { model.hideCard(flag); } From f629348ac9eace50754881752ac7d7dcfecadf2c Mon Sep 17 00:00:00 2001 From: Emma Date: Sun, 8 Jan 2023 18:06:02 -0500 Subject: [PATCH 03/21] Fix card order in dashboard+ config default Fix card order in dashboard and prevent duplicates from appearing Add default values for fr & en remote configs --- lib/core/services/remote_config_service.dart | 15 +++++++++++---- lib/core/viewmodels/dashboard_viewmodel.dart | 11 ++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index 3f9cb2326..d1db9194b 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -8,10 +8,17 @@ import 'package:notredame/locator.dart'; /// Manage the analytics of the application class RemoteConfigService { + static const String tag = "RemoteConfigService"; static const _serviceIsDown = "service_is_down"; + static const _dashboardMsgFr = "dashboard_message_fr"; + static const _dashboardMsgEn = "dashboard_message_en"; + final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; - final defaults = {_serviceIsDown: false}; - static const String tag = "RemoteConfigService"; + final defaults = { + _serviceIsDown: false, + _dashboardMsgFr: "", + _dashboardMsgEn: "" + }; Future initialize() async { await _remoteConfig.setDefaults(defaults); @@ -25,12 +32,12 @@ class RemoteConfigService { Future get dashboardMessageFr async { fetch(); - return _remoteConfig.getString("dashboard_message_fr"); + return _remoteConfig.getString(_dashboardMsgFr); } Future get dashboardMessageEn async { fetch(); - return _remoteConfig.getString("dashboard_message_en"); + return _remoteConfig.getString(_dashboardMsgEn); } Future fetch() async { diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 522fa6562..dbe7ca2cb 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -546,8 +546,10 @@ class DashboardViewModel extends FutureViewModel> { Future futureToRunBroadcast() async { final RemoteConfigService remoteConfigService = locator(); + const PreferencesFlag flag = PreferencesFlag.broadcastCard; + + setBusyForObject(this.broadcastMessage, true); - // TODO: maybe set busy broadcastMessage? String broadcastMessage = ""; if (_appIntl.localeName == "fr") { broadcastMessage = await remoteConfigService.dashboardMessageFr; @@ -556,11 +558,14 @@ class DashboardViewModel extends FutureViewModel> { } if (broadcastMessage != "") { - _cardsToDisplay.add(PreferencesFlag.broadcastCard); + _cardsToDisplay.remove(flag); + _cardsToDisplay.insert(0, flag); this.broadcastMessage = broadcastMessage; } else { // TODO: check if the logic is correct - _cardsToDisplay.remove(PreferencesFlag.broadcastCard); + _cardsToDisplay.remove(flag); } + + setBusyForObject(this.broadcastMessage, false); } } From 8ea729ed47a7a26c41cb2c6c71020401c60f1977 Mon Sep 17 00:00:00 2001 From: Emma Date: Sun, 8 Jan 2023 18:21:38 -0500 Subject: [PATCH 04/21] Set card busy when model loading And minor code improvements --- lib/ui/views/dashboard_view.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 11590fa4c..d8e375a1f 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -106,7 +106,6 @@ class _DashboardViewState extends State for (final PreferencesFlag element in model.cardsToDisplay) { switch (element) { - // TODO: move first somehow case PreferencesFlag.broadcastCard: cards.add(_buildMessageBroadcastCard(model, element)); break; @@ -409,7 +408,7 @@ class _DashboardViewState extends State onDismissed: (DismissDirection direction) { dismissCard(model, flag); }, - // isBusy: model.busy(model.courses), // TODO: see if model should be used + isBusy: model.busy(model.broadcastMessage), cardColor: AppTheme .etsDarkRed, // TODO: maybe change color w/ remote config ? child: Padding( @@ -417,7 +416,6 @@ class _DashboardViewState extends State child: Column(mainAxisSize: MainAxisSize.min, children: [ // title row Row( - mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Align( @@ -439,9 +437,7 @@ class _DashboardViewState extends State ], ), // main text - Text(model.broadcastMessage == null - ? "Null" - : model.broadcastMessage) + Text(model.broadcastMessage ?? "") ]), )); From cbf71621537780462bd7f182a667e67fd459215b Mon Sep 17 00:00:00 2001 From: Emma Neiss Date: Fri, 3 Feb 2023 18:29:57 -0500 Subject: [PATCH 05/21] WIP add title & toggle remote configs --- lib/core/services/remote_config_service.dart | 21 +++++++++++++++++++ lib/core/viewmodels/dashboard_viewmodel.dart | 22 ++++++++++---------- lib/ui/views/dashboard_view.dart | 2 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index d1db9194b..eb358d299 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -10,14 +10,20 @@ import 'package:notredame/locator.dart'; class RemoteConfigService { static const String tag = "RemoteConfigService"; static const _serviceIsDown = "service_is_down"; + + // dashboard message remote config keys + static const _dashboardMsgToggle = "dashboard_message_toggle"; static const _dashboardMsgFr = "dashboard_message_fr"; static const _dashboardMsgEn = "dashboard_message_en"; + static const _dashboardMsgTitleFr = "dashboard_message_title_fr"; + static const _dashboardMsgTitleEn = "dashboard_message_title_en"; final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; final defaults = { _serviceIsDown: false, _dashboardMsgFr: "", _dashboardMsgEn: "" + // TODO: either add default values for the rest or delete the ones above }; Future initialize() async { @@ -29,6 +35,11 @@ class RemoteConfigService { fetch(); return _remoteConfig.getBool(_serviceIsDown); } + + bool get dashboardMessageActive { + fetch(); + return _remoteConfig.getBool(_dashboardMsgToggle); + } Future get dashboardMessageFr async { fetch(); @@ -40,6 +51,16 @@ class RemoteConfigService { return _remoteConfig.getString(_dashboardMsgEn); } + Future get dashboardMessageTitleFr async { + fetch(); + return _remoteConfig.getString(_dashboardMsgTitleFr); + } + + Future get dashboardMessageTitleEn async { + fetch(); + return _remoteConfig.getString(_dashboardMsgTitleEn); + } + Future fetch() async { final AnalyticsService analyticsService = locator(); try { diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index dbe7ca2cb..aa8b4e627 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -60,8 +60,9 @@ class DashboardViewModel extends FutureViewModel> { List _sessionDays = [0, 0]; /// Message to display in case of urgent/important broadcast need (Firebase - /// remote config) + /// remote config), and the associated card title String broadcastMessage; + String broadcastTitle; /// Get progress of the session double get progress => _progress; @@ -550,19 +551,18 @@ class DashboardViewModel extends FutureViewModel> { setBusyForObject(this.broadcastMessage, true); - String broadcastMessage = ""; - if (_appIntl.localeName == "fr") { - broadcastMessage = await remoteConfigService.dashboardMessageFr; - } else { - broadcastMessage = await remoteConfigService.dashboardMessageEn; - } - - if (broadcastMessage != "") { + if (await remoteConfigService.dashboardMessageActive) { _cardsToDisplay.remove(flag); _cardsToDisplay.insert(0, flag); - this.broadcastMessage = broadcastMessage; + + if (_appIntl.localeName == "fr") { + this.broadcastMessage = await remoteConfigService.dashboardMessageFr; + this.broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; + } else { + this.broadcastMessage = await remoteConfigService.dashboardMessageEn; + this.broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; + } } else { - // TODO: check if the logic is correct _cardsToDisplay.remove(flag); } diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index d8e375a1f..9ff1b575b 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -420,7 +420,7 @@ class _DashboardViewState extends State Expanded( child: Align( alignment: Alignment.centerLeft, - child: Text(AppIntl.of(context).card_broadcast_title, + child: Text(AppIntl.of(context).card_broadcast_title, // TODO: use remote config style: Theme.of(context).primaryTextTheme.headline6), ), ), From ef56097156bfd5867346188eeb2f5f8ab499e293 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Mon, 31 Jul 2023 22:28:11 -0400 Subject: [PATCH 06/21] Add remote config based color --- lib/core/services/remote_config_service.dart | 12 +++++++++--- lib/core/viewmodels/dashboard_viewmodel.dart | 20 +++++++++++--------- lib/ui/views/dashboard_view.dart | 3 +-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index 0e6dd7c8e..0ec51e352 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -17,6 +17,7 @@ class RemoteConfigService { static const _dashboardMsgEn = "dashboard_message_en"; static const _dashboardMsgTitleFr = "dashboard_message_title_fr"; static const _dashboardMsgTitleEn = "dashboard_message_title_en"; + static const _dashboardMsgColor = "dashboard_message_color"; static const _scheduleListViewDefault = "schedule_list_view_default"; final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; @@ -24,12 +25,12 @@ class RemoteConfigService { _serviceIsDown: false, _dashboardMsgFr: "", _dashboardMsgEn: "", + _dashboardMsgTitleFr: "", + _dashboardMsgTitleEn: "", + _dashboardMsgColor: "", _scheduleListViewDefault: true - // TODO: either add default values for the rest or delete the ones above }; - static const String tag = "RemoteConfigService"; - Future initialize() async { await _remoteConfig.setDefaults(defaults); await _fetchAndActivate(); @@ -70,6 +71,11 @@ class RemoteConfigService { return _remoteConfig.getString(_dashboardMsgTitleEn); } + String get dashboardMsgColor { + fetch(); + return _remoteConfig.getString(_dashboardMsgColor); + } + Future fetch() async { final AnalyticsService analyticsService = locator(); try { diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 34096c0bf..fef8e55a9 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -40,6 +40,8 @@ class DashboardViewModel extends FutureViewModel> { final CourseRepository _courseRepository = locator(); final AnalyticsService _analyticsService = locator(); final AppWidgetService _appWidgetService = locator(); + final RemoteConfigService remoteConfigService = + locator(); /// All dashboard displayable cards Map _cards; @@ -96,6 +98,8 @@ class DashboardViewModel extends FutureViewModel> { ProgressBarText get currentProgressBarText => _currentProgressBarText; + String get dashboardMsgColor => remoteConfigService.dashboardMsgColor; + /// Return session progress based on today's [date] double getSessionProgress() { if (_courseRepository.activeSessions.isEmpty) { @@ -545,27 +549,25 @@ class DashboardViewModel extends FutureViewModel> { } Future futureToRunBroadcast() async { - final RemoteConfigService remoteConfigService = - locator(); const PreferencesFlag flag = PreferencesFlag.broadcastCard; - setBusyForObject(this.broadcastMessage, true); + setBusyForObject(broadcastMessage, true); - if (await remoteConfigService.dashboardMessageActive) { + if (remoteConfigService.dashboardMessageActive) { _cardsToDisplay.remove(flag); _cardsToDisplay.insert(0, flag); if (_appIntl.localeName == "fr") { - this.broadcastMessage = await remoteConfigService.dashboardMessageFr; - this.broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; + broadcastMessage = await remoteConfigService.dashboardMessageFr; + broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; } else { - this.broadcastMessage = await remoteConfigService.dashboardMessageEn; - this.broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; + broadcastMessage = await remoteConfigService.dashboardMessageEn; + broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; } } else { _cardsToDisplay.remove(flag); } - setBusyForObject(this.broadcastMessage, false); + setBusyForObject(broadcastMessage, false); } } diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 52dc97eeb..4692513cc 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -409,8 +409,7 @@ class _DashboardViewState extends State dismissCard(model, flag); }, isBusy: model.busy(model.broadcastMessage), - cardColor: AppTheme - .etsDarkRed, // TODO: maybe change color w/ remote config ? + cardColor: Color(int.parse(model.dashboardMsgColor)), child: Padding( padding: const EdgeInsets.fromLTRB(17, 10, 15, 20), child: Column(mainAxisSize: MainAxisSize.min, children: [ From a78ff385da1f91e9de065263d0d1067324980193 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Thu, 3 Aug 2023 11:00:03 -0400 Subject: [PATCH 07/21] Use color from remote config, or default --- lib/ui/views/dashboard_view.dart | 75 +++++++++++++++++--------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 4692513cc..db0f227f9 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -402,45 +402,48 @@ class _DashboardViewState extends State ); Widget _buildMessageBroadcastCard( - DashboardViewModel model, PreferencesFlag flag) => - DismissibleCard( - key: UniqueKey(), - onDismissed: (DismissDirection direction) { - dismissCard(model, flag); - }, - isBusy: model.busy(model.broadcastMessage), - cardColor: Color(int.parse(model.dashboardMsgColor)), - child: Padding( - padding: const EdgeInsets.fromLTRB(17, 10, 15, 20), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - // title row - Row( - children: [ - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Text( - AppIntl.of(context) - .card_broadcast_title, // TODO: use remote config - style: Theme.of(context).primaryTextTheme.headline6), - ), + DashboardViewModel model, PreferencesFlag flag) { + final broadcastMsgColor = + Color(int.parse(model.dashboardMsgColor)) ?? AppTheme.etsDarkRed; + return DismissibleCard( + key: UniqueKey(), + onDismissed: (DismissDirection direction) { + dismissCard(model, flag); + }, + isBusy: model.busy(model.broadcastMessage), + cardColor: broadcastMsgColor, + child: Padding( + padding: const EdgeInsets.fromLTRB(17, 10, 15, 20), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + // title row + Row( + children: [ + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: Text( + AppIntl.of(context) + .card_broadcast_title, // TODO: use remote config + style: Theme.of(context).primaryTextTheme.headline6), ), - const Align( - alignment: Alignment.centerRight, - child: InkWell( - child: Icon( - Icons.campaign, - color: AppTheme.lightThemeBackground, - size: 36.0, - ), + ), + const Align( + alignment: Alignment.centerRight, + child: InkWell( + child: Icon( + Icons.campaign, + color: AppTheme.lightThemeBackground, + size: 36.0, ), ), - ], - ), - // main text - Text(model.broadcastMessage ?? "") - ]), - )); + ), + ], + ), + // main text + Text(model.broadcastMessage ?? "") + ]), + )); + } void dismissCard(DashboardViewModel model, PreferencesFlag flag) { model.hideCard(flag); From 9a7bbc343878fde2c8a7f19527ebe429975d24f9 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Thu, 3 Aug 2023 12:58:44 -0400 Subject: [PATCH 08/21] Fix color variable and text color --- lib/ui/views/dashboard_view.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index db0f227f9..8b94c7ded 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -403,8 +403,9 @@ class _DashboardViewState extends State Widget _buildMessageBroadcastCard( DashboardViewModel model, PreferencesFlag flag) { - final broadcastMsgColor = - Color(int.parse(model.dashboardMsgColor)) ?? AppTheme.etsDarkRed; + final broadcastMsgColor = model.dashboardMsgColor.isNotEmpty + ? Color(int.parse(model.dashboardMsgColor)) + : AppTheme.etsDarkRed; return DismissibleCard( key: UniqueKey(), onDismissed: (DismissDirection direction) { @@ -440,7 +441,8 @@ class _DashboardViewState extends State ], ), // main text - Text(model.broadcastMessage ?? "") + Text(model.broadcastMessage ?? "", + style: Theme.of(context).primaryTextTheme.bodyText2) ]), )); } From f37930f6422a137611e866ff9e7f8154933abf32 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Tue, 8 Aug 2023 22:47:46 -0400 Subject: [PATCH 09/21] Add reorder broadcast --- lib/core/managers/settings_manager.dart | 16 ++++++++++++---- lib/core/services/remote_config_service.dart | 2 +- lib/core/viewmodels/dashboard_viewmodel.dart | 12 ++++-------- lib/ui/views/dashboard_view.dart | 12 ++++-------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/core/managers/settings_manager.dart b/lib/core/managers/settings_manager.dart index cc42707ae..23f3c5eba 100644 --- a/lib/core/managers/settings_manager.dart +++ b/lib/core/managers/settings_manager.dart @@ -94,7 +94,13 @@ class SettingsManager with ChangeNotifier { Future> getDashboard() async { final Map dashboard = {}; - // TODO: add broadcast card order + final broadcastCardIndex = + await _preferencesService.getInt(PreferencesFlag.broadcastCard) ?? + getDefaultCardIndex(PreferencesFlag.broadcastCard); + + dashboard.putIfAbsent( + PreferencesFlag.broadcastCard, () => broadcastCardIndex); + final aboutUsIndex = await _preferencesService.getInt(PreferencesFlag.aboutUsCard) ?? getDefaultCardIndex(PreferencesFlag.aboutUsCard); @@ -266,9 +272,11 @@ class SettingsManager with ChangeNotifier { } /// Get the default index of each card - // TODO: use broadcast card as default 0 index - int getDefaultCardIndex(PreferencesFlag flag) => - flag.index - PreferencesFlag.aboutUsCard.index; + int getDefaultCardIndex(PreferencesFlag flag) { + return _remoteConfigService.dashboardMessageActive + ? flag.index - PreferencesFlag.broadcastCard.index + : flag.index - PreferencesFlag.aboutUsCard.index; + } bool get calendarViewSetting => _remoteConfigService.scheduleListViewDefault; } diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index 0ec51e352..7ed41ad56 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -71,7 +71,7 @@ class RemoteConfigService { return _remoteConfig.getString(_dashboardMsgTitleEn); } - String get dashboardMsgColor { + Future get dashboardMsgColor async { fetch(); return _remoteConfig.getString(_dashboardMsgColor); } diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index fef8e55a9..e18ac99ff 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -65,6 +65,7 @@ class DashboardViewModel extends FutureViewModel> { /// remote config), and the associated card title String broadcastMessage; String broadcastTitle; + String broadcastColor; /// Get progress of the session double get progress => _progress; @@ -98,8 +99,6 @@ class DashboardViewModel extends FutureViewModel> { ProgressBarText get currentProgressBarText => _currentProgressBarText; - String get dashboardMsgColor => remoteConfigService.dashboardMsgColor; - /// Return session progress based on today's [date] double getSessionProgress() { if (_courseRepository.activeSessions.isEmpty) { @@ -290,17 +289,16 @@ class DashboardViewModel extends FutureViewModel> { } /// Reset all card indexes to their default values - // TODO: order for broadcast card void setAllCardsVisible() { _cards.updateAll((key, value) { _settingsManager - .setInt(key, key.index - PreferencesFlag.aboutUsCard.index) + .setInt(key, key.index - PreferencesFlag.broadcastCard.index) .then((value) { if (!value) { Fluttertoast.showToast(msg: _appIntl.error); } }); - return key.index - PreferencesFlag.aboutUsCard.index; + return key.index - PreferencesFlag.broadcastCard.index; }); getCardsToDisplay(); @@ -554,9 +552,6 @@ class DashboardViewModel extends FutureViewModel> { setBusyForObject(broadcastMessage, true); if (remoteConfigService.dashboardMessageActive) { - _cardsToDisplay.remove(flag); - _cardsToDisplay.insert(0, flag); - if (_appIntl.localeName == "fr") { broadcastMessage = await remoteConfigService.dashboardMessageFr; broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; @@ -564,6 +559,7 @@ class DashboardViewModel extends FutureViewModel> { broadcastMessage = await remoteConfigService.dashboardMessageEn; broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; } + broadcastColor = await remoteConfigService.dashboardMsgColor; } else { _cardsToDisplay.remove(flag); } diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 8b94c7ded..91cfdd564 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -102,7 +102,6 @@ class _DashboardViewState extends State // always try to build broadcast cart so the user doesn't miss out on // important info if they dismissed it previously - // TODO: fetch if broadcast message first ? for (final PreferencesFlag element in model.cardsToDisplay) { switch (element) { @@ -403,9 +402,7 @@ class _DashboardViewState extends State Widget _buildMessageBroadcastCard( DashboardViewModel model, PreferencesFlag flag) { - final broadcastMsgColor = model.dashboardMsgColor.isNotEmpty - ? Color(int.parse(model.dashboardMsgColor)) - : AppTheme.etsDarkRed; + final broadcastMsgColor = Color(int.parse(model.broadcastColor)); return DismissibleCard( key: UniqueKey(), onDismissed: (DismissDirection direction) { @@ -422,9 +419,7 @@ class _DashboardViewState extends State Expanded( child: Align( alignment: Alignment.centerLeft, - child: Text( - AppIntl.of(context) - .card_broadcast_title, // TODO: use remote config + child: Text(model.broadcastTitle, style: Theme.of(context).primaryTextTheme.headline6), ), ), @@ -432,7 +427,8 @@ class _DashboardViewState extends State alignment: Alignment.centerRight, child: InkWell( child: Icon( - Icons.campaign, + Icons + .campaign, // TODO Chose from a pre-defined list depending on the case color: AppTheme.lightThemeBackground, size: 36.0, ), From 4fb98af7fdbdf81f4511e0c045385e2a5b4b8cd1 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Tue, 19 Sep 2023 19:55:18 -0400 Subject: [PATCH 10/21] Add custom icon, onClick event and reorderable/hide functions --- lib/core/constants/preferences_flags.dart | 2 +- lib/core/managers/settings_manager.dart | 6 ++- lib/core/services/remote_config_service.dart | 14 ++++++ lib/core/viewmodels/dashboard_viewmodel.dart | 21 +++++++-- lib/ui/views/dashboard_view.dart | 48 ++++++++++++++++---- 5 files changed, 76 insertions(+), 15 deletions(-) diff --git a/lib/core/constants/preferences_flags.dart b/lib/core/constants/preferences_flags.dart index febb4e56b..8da03fa8d 100644 --- a/lib/core/constants/preferences_flags.dart +++ b/lib/core/constants/preferences_flags.dart @@ -34,12 +34,12 @@ enum PreferencesFlag { discoveryMore, // Dashboard flags + broadcastCard, aboutUsCard, scheduleCard, progressBarCard, gradesCard, progressBarText, - broadcastCard, // Rating flag ratingTimer, diff --git a/lib/core/managers/settings_manager.dart b/lib/core/managers/settings_manager.dart index 23f3c5eba..f18cfd966 100644 --- a/lib/core/managers/settings_manager.dart +++ b/lib/core/managers/settings_manager.dart @@ -98,8 +98,10 @@ class SettingsManager with ChangeNotifier { await _preferencesService.getInt(PreferencesFlag.broadcastCard) ?? getDefaultCardIndex(PreferencesFlag.broadcastCard); - dashboard.putIfAbsent( - PreferencesFlag.broadcastCard, () => broadcastCardIndex); + if (_remoteConfigService.dashboardMessageActive) { + dashboard.putIfAbsent( + PreferencesFlag.broadcastCard, () => broadcastCardIndex); + } final aboutUsIndex = await _preferencesService.getInt(PreferencesFlag.aboutUsCard) ?? diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index 7ed41ad56..dbb9a3973 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -18,6 +18,8 @@ class RemoteConfigService { static const _dashboardMsgTitleFr = "dashboard_message_title_fr"; static const _dashboardMsgTitleEn = "dashboard_message_title_en"; static const _dashboardMsgColor = "dashboard_message_color"; + static const _dashboardMsgUrl = "dashboard_message_url"; + static const _dashboardMsgType = "dashboard_message_type"; static const _scheduleListViewDefault = "schedule_list_view_default"; final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; @@ -28,6 +30,8 @@ class RemoteConfigService { _dashboardMsgTitleFr: "", _dashboardMsgTitleEn: "", _dashboardMsgColor: "", + _dashboardMsgUrl: "", + _dashboardMsgType: "", _scheduleListViewDefault: true }; @@ -76,6 +80,16 @@ class RemoteConfigService { return _remoteConfig.getString(_dashboardMsgColor); } + Future get dashboardMsgUrl async { + fetch(); + return _remoteConfig.getString(_dashboardMsgUrl); + } + + Future get dashboardMsgType async { + fetch(); + return _remoteConfig.getString(_dashboardMsgType); + } + Future fetch() async { final AnalyticsService analyticsService = locator(); try { diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index e18ac99ff..53609837e 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -66,6 +66,8 @@ class DashboardViewModel extends FutureViewModel> { String broadcastMessage; String broadcastTitle; String broadcastColor; + String broadcastUrl; + String broadcastType; /// Get progress of the session double get progress => _progress; @@ -290,15 +292,16 @@ class DashboardViewModel extends FutureViewModel> { /// Reset all card indexes to their default values void setAllCardsVisible() { + final firstIndex = remoteConfigService.dashboardMessageActive + ? PreferencesFlag.broadcastCard.index + : PreferencesFlag.aboutUsCard.index; _cards.updateAll((key, value) { - _settingsManager - .setInt(key, key.index - PreferencesFlag.broadcastCard.index) - .then((value) { + _settingsManager.setInt(key, key.index - firstIndex).then((value) { if (!value) { Fluttertoast.showToast(msg: _appIntl.error); } }); - return key.index - PreferencesFlag.broadcastCard.index; + return key.index - firstIndex; }); getCardsToDisplay(); @@ -550,6 +553,10 @@ class DashboardViewModel extends FutureViewModel> { const PreferencesFlag flag = PreferencesFlag.broadcastCard; setBusyForObject(broadcastMessage, true); + setBusyForObject(broadcastTitle, true); + setBusyForObject(broadcastColor, true); + setBusyForObject(broadcastUrl, true); + setBusyForObject(broadcastType, true); if (remoteConfigService.dashboardMessageActive) { if (_appIntl.localeName == "fr") { @@ -560,10 +567,16 @@ class DashboardViewModel extends FutureViewModel> { broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; } broadcastColor = await remoteConfigService.dashboardMsgColor; + broadcastUrl = await remoteConfigService.dashboardMsgUrl; + broadcastType = await remoteConfigService.dashboardMsgType; } else { _cardsToDisplay.remove(flag); } setBusyForObject(broadcastMessage, false); + setBusyForObject(broadcastTitle, false); + setBusyForObject(broadcastColor, false); + setBusyForObject(broadcastUrl, false); + setBusyForObject(broadcastType, false); } } diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 91cfdd564..321fe3247 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -1,4 +1,5 @@ // FLUTTER / DART / THIRD-PARTIES +import 'package:auto_size_text/auto_size_text.dart'; import 'package:feature_discovery/feature_discovery.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -35,6 +36,7 @@ import 'package:notredame/ui/utils/discovery_components.dart'; // SERVICES import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/services/analytics_service.dart'; +import 'package:url_launcher/url_launcher.dart'; class DashboardView extends StatefulWidget { final UpdateCode updateCode; @@ -403,6 +405,8 @@ class _DashboardViewState extends State Widget _buildMessageBroadcastCard( DashboardViewModel model, PreferencesFlag flag) { final broadcastMsgColor = Color(int.parse(model.broadcastColor)); + final broadcastMsgType = model.broadcastType; + final broadcastMsgUrl = model.broadcastUrl; return DismissibleCard( key: UniqueKey(), onDismissed: (DismissDirection direction) { @@ -423,26 +427,54 @@ class _DashboardViewState extends State style: Theme.of(context).primaryTextTheme.headline6), ), ), - const Align( + Align( alignment: Alignment.centerRight, child: InkWell( - child: Icon( - Icons - .campaign, // TODO Chose from a pre-defined list depending on the case - color: AppTheme.lightThemeBackground, - size: 36.0, - ), + child: getBroadcastIcon(broadcastMsgType, broadcastMsgUrl), ), ), ], ), // main text - Text(model.broadcastMessage ?? "", + AutoSizeText(model.broadcastMessage ?? "", style: Theme.of(context).primaryTextTheme.bodyText2) ]), )); } + Widget getBroadcastIcon(String type, String url) { + switch (type) { + case "warning": + return const Icon( + Icons.warning_rounded, + color: AppTheme.lightThemeBackground, + size: 36.0, + ); + case "alert": + return const Icon( + Icons.error, + color: AppTheme.lightThemeBackground, + size: 36.0, + ); + case "link": + return IconButton( + onPressed: () { + Utils.launchURL(url, AppIntl.of(context)); + }, + icon: const Icon( + Icons.open_in_new, + color: AppTheme.lightThemeBackground, + size: 30.0, + ), + ); + } + return const Icon( + Icons.campaign, + color: AppTheme.lightThemeBackground, + size: 36.0, + ); + } + void dismissCard(DashboardViewModel model, PreferencesFlag flag) { model.hideCard(flag); } From 2947a693431794c7fe7e790d600a58087d663657 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Tue, 19 Sep 2023 20:43:43 -0400 Subject: [PATCH 11/21] Fix cards not appearing --- lib/core/managers/settings_manager.dart | 10 ++----- lib/core/viewmodels/dashboard_viewmodel.dart | 29 ++++++++------------ lib/ui/views/dashboard_view.dart | 4 ++- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/lib/core/managers/settings_manager.dart b/lib/core/managers/settings_manager.dart index f18cfd966..95e1e211c 100644 --- a/lib/core/managers/settings_manager.dart +++ b/lib/core/managers/settings_manager.dart @@ -98,10 +98,8 @@ class SettingsManager with ChangeNotifier { await _preferencesService.getInt(PreferencesFlag.broadcastCard) ?? getDefaultCardIndex(PreferencesFlag.broadcastCard); - if (_remoteConfigService.dashboardMessageActive) { - dashboard.putIfAbsent( - PreferencesFlag.broadcastCard, () => broadcastCardIndex); - } + dashboard.putIfAbsent( + PreferencesFlag.broadcastCard, () => broadcastCardIndex); final aboutUsIndex = await _preferencesService.getInt(PreferencesFlag.aboutUsCard) ?? @@ -275,9 +273,7 @@ class SettingsManager with ChangeNotifier { /// Get the default index of each card int getDefaultCardIndex(PreferencesFlag flag) { - return _remoteConfigService.dashboardMessageActive - ? flag.index - PreferencesFlag.broadcastCard.index - : flag.index - PreferencesFlag.aboutUsCard.index; + return flag.index - PreferencesFlag.broadcastCard.index; } bool get calendarViewSetting => _remoteConfigService.scheduleListViewDefault; diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 53609837e..ac22df373 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -292,16 +292,15 @@ class DashboardViewModel extends FutureViewModel> { /// Reset all card indexes to their default values void setAllCardsVisible() { - final firstIndex = remoteConfigService.dashboardMessageActive - ? PreferencesFlag.broadcastCard.index - : PreferencesFlag.aboutUsCard.index; _cards.updateAll((key, value) { - _settingsManager.setInt(key, key.index - firstIndex).then((value) { + _settingsManager + .setInt(key, key.index - PreferencesFlag.broadcastCard.index) + .then((value) { if (!value) { Fluttertoast.showToast(msg: _appIntl.error); } }); - return key.index - firstIndex; + return key.index - PreferencesFlag.broadcastCard.index; }); getCardsToDisplay(); @@ -558,20 +557,16 @@ class DashboardViewModel extends FutureViewModel> { setBusyForObject(broadcastUrl, true); setBusyForObject(broadcastType, true); - if (remoteConfigService.dashboardMessageActive) { - if (_appIntl.localeName == "fr") { - broadcastMessage = await remoteConfigService.dashboardMessageFr; - broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; - } else { - broadcastMessage = await remoteConfigService.dashboardMessageEn; - broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; - } - broadcastColor = await remoteConfigService.dashboardMsgColor; - broadcastUrl = await remoteConfigService.dashboardMsgUrl; - broadcastType = await remoteConfigService.dashboardMsgType; + if (_appIntl.localeName == "fr") { + broadcastMessage = await remoteConfigService.dashboardMessageFr; + broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; } else { - _cardsToDisplay.remove(flag); + broadcastMessage = await remoteConfigService.dashboardMessageEn; + broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; } + broadcastColor = await remoteConfigService.dashboardMsgColor; + broadcastUrl = await remoteConfigService.dashboardMsgUrl; + broadcastType = await remoteConfigService.dashboardMsgType; setBusyForObject(broadcastMessage, false); setBusyForObject(broadcastTitle, false); diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 321fe3247..25cb1e51a 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -108,7 +108,9 @@ class _DashboardViewState extends State for (final PreferencesFlag element in model.cardsToDisplay) { switch (element) { case PreferencesFlag.broadcastCard: - cards.add(_buildMessageBroadcastCard(model, element)); + if (model.remoteConfigService.dashboardMessageActive) { + cards.add(_buildMessageBroadcastCard(model, element)); + } break; case PreferencesFlag.aboutUsCard: cards.add(_buildAboutUsCard(model, element)); From a40be51b782cfde37092f7468e89462fba1026ae Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Wed, 20 Sep 2023 23:50:03 -0400 Subject: [PATCH 12/21] Tests --- lib/core/services/remote_config_service.dart | 14 +- lib/core/viewmodels/dashboard_viewmodel.dart | 10 +- test/managers/settings_manager_test.dart | 36 +++-- .../services/remote_config_service_mock.dart | 40 +++++ test/ui/views/dashboard_view_test.dart | 149 ++++++++++++------ test/viewmodels/dashboard_viewmodel_test.dart | 64 ++++++-- 6 files changed, 227 insertions(+), 86 deletions(-) diff --git a/lib/core/services/remote_config_service.dart b/lib/core/services/remote_config_service.dart index dbb9a3973..995ed91a0 100644 --- a/lib/core/services/remote_config_service.dart +++ b/lib/core/services/remote_config_service.dart @@ -55,37 +55,37 @@ class RemoteConfigService { return _remoteConfig.getBool(_scheduleListViewDefault); } - Future get dashboardMessageFr async { + String get dashboardMessageFr { fetch(); return _remoteConfig.getString(_dashboardMsgFr); } - Future get dashboardMessageEn async { + String get dashboardMessageEn { fetch(); return _remoteConfig.getString(_dashboardMsgEn); } - Future get dashboardMessageTitleFr async { + String get dashboardMessageTitleFr { fetch(); return _remoteConfig.getString(_dashboardMsgTitleFr); } - Future get dashboardMessageTitleEn async { + String get dashboardMessageTitleEn { fetch(); return _remoteConfig.getString(_dashboardMsgTitleEn); } - Future get dashboardMsgColor async { + String get dashboardMsgColor { fetch(); return _remoteConfig.getString(_dashboardMsgColor); } - Future get dashboardMsgUrl async { + String get dashboardMsgUrl { fetch(); return _remoteConfig.getString(_dashboardMsgUrl); } - Future get dashboardMsgType async { + String get dashboardMsgType { fetch(); return _remoteConfig.getString(_dashboardMsgType); } diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index ac22df373..8c7a584e2 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -63,11 +63,11 @@ class DashboardViewModel extends FutureViewModel> { /// Message to display in case of urgent/important broadcast need (Firebase /// remote config), and the associated card title - String broadcastMessage; - String broadcastTitle; - String broadcastColor; - String broadcastUrl; - String broadcastType; + String broadcastMessage = ""; + String broadcastTitle = ""; + String broadcastColor = ""; + String broadcastUrl = ""; + String broadcastType = ""; /// Get progress of the session double get progress => _progress; diff --git a/test/managers/settings_manager_test.dart b/test/managers/settings_manager_test.dart index 08584dcf3..731eb5c5c 100644 --- a/test/managers/settings_manager_test.dart +++ b/test/managers/settings_manager_test.dart @@ -440,10 +440,11 @@ void main() { // Cards final Map expected = { - PreferencesFlag.aboutUsCard: 0, - PreferencesFlag.scheduleCard: 1, - PreferencesFlag.progressBarCard: 2, - PreferencesFlag.gradesCard: 3 + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.aboutUsCard: 1, + PreferencesFlag.scheduleCard: 2, + PreferencesFlag.progressBarCard: 3, + PreferencesFlag.gradesCard: 4 }; expect( @@ -451,6 +452,8 @@ void main() { expected, ); + verify(preferencesService.getInt(PreferencesFlag.broadcastCard)) + .called(1); verify(preferencesService.getInt(PreferencesFlag.aboutUsCard)) .called(1); verify(preferencesService.getInt(PreferencesFlag.scheduleCard)) @@ -464,30 +467,35 @@ void main() { }); test("validate the loading of the cards", () async { + PreferencesServiceMock.stubGetInt( + preferencesService as PreferencesServiceMock, + PreferencesFlag.broadcastCard, + toReturn: 0); PreferencesServiceMock.stubGetInt( preferencesService as PreferencesServiceMock, PreferencesFlag.aboutUsCard, - // ignore: avoid_redundant_argument_values - toReturn: 1); + toReturn: 2); PreferencesServiceMock.stubGetInt( preferencesService as PreferencesServiceMock, PreferencesFlag.scheduleCard, - toReturn: 2); + toReturn: 3); PreferencesServiceMock.stubGetInt( preferencesService as PreferencesServiceMock, PreferencesFlag.progressBarCard, - toReturn: 0); + // ignore: avoid_redundant_argument_values + toReturn: 1); PreferencesServiceMock.stubGetInt( preferencesService as PreferencesServiceMock, PreferencesFlag.gradesCard, - toReturn: 3); + toReturn: 4); // Cards final Map expected = { - PreferencesFlag.aboutUsCard: 1, - PreferencesFlag.scheduleCard: 2, - PreferencesFlag.progressBarCard: 0, - PreferencesFlag.gradesCard: 3 + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.aboutUsCard: 2, + PreferencesFlag.scheduleCard: 3, + PreferencesFlag.progressBarCard: 1, + PreferencesFlag.gradesCard: 4 }; expect( @@ -495,6 +503,8 @@ void main() { expected, ); + verify(preferencesService.getInt(PreferencesFlag.broadcastCard)) + .called(1); verify(preferencesService.getInt(PreferencesFlag.aboutUsCard)) .called(1); verify(preferencesService.getInt(PreferencesFlag.scheduleCard)) diff --git a/test/mock/services/remote_config_service_mock.dart b/test/mock/services/remote_config_service_mock.dart index c3a1792fb..567653208 100644 --- a/test/mock/services/remote_config_service_mock.dart +++ b/test/mock/services/remote_config_service_mock.dart @@ -9,4 +9,44 @@ class RemoteConfigServiceMock extends Mock implements RemoteConfigService { {bool toReturn = true}) { when(mock.scheduleListViewDefault).thenReturn(toReturn); } + + static void stubGetBroadcastEnabled(RemoteConfigServiceMock mock, + {bool toReturn = true}) { + when(mock.dashboardMessageActive).thenReturn(toReturn); + } + + static void stubGetBroadcastColor(RemoteConfigServiceMock mock, + {String toReturn = "0xffd48404"}) { + when(mock.dashboardMsgColor).thenReturn(toReturn); + } + + static void stubGetBroadcastTitleEn(RemoteConfigServiceMock mock, + {String toReturn = "TitleEn"}) { + when(mock.dashboardMessageTitleEn).thenReturn(toReturn); + } + + static void stubGetBroadcastTitleFr(RemoteConfigServiceMock mock, + {String toReturn = "TitleFr"}) { + when(mock.dashboardMessageTitleFr).thenReturn(toReturn); + } + + static void stubGetBroadcastEn(RemoteConfigServiceMock mock, + {String toReturn = "En"}) { + when(mock.dashboardMessageEn).thenReturn(toReturn); + } + + static void stubGetBroadcastFr(RemoteConfigServiceMock mock, + {String toReturn = "Fr"}) { + when(mock.dashboardMessageFr).thenReturn(toReturn); + } + + static void stubGetBroadcastType(RemoteConfigServiceMock mock, + {String toReturn = "info"}) { + when(mock.dashboardMsgType).thenReturn(toReturn); + } + + static void stubGetBroadcastUrl(RemoteConfigServiceMock mock, + {String toReturn = "https://clubapplets.ca/"}) { + when(mock.dashboardMsgUrl).thenReturn(toReturn); + } } diff --git a/test/ui/views/dashboard_view_test.dart b/test/ui/views/dashboard_view_test.dart index f84dbe8c2..a7bbfc6b6 100644 --- a/test/ui/views/dashboard_view_test.dart +++ b/test/ui/views/dashboard_view_test.dart @@ -20,6 +20,9 @@ import 'package:notredame/ui/widgets/grade_button.dart'; // CONSTANTS import 'package:notredame/core/constants/preferences_flags.dart'; +// SERVICES +import 'package:notredame/core/services/remote_config_service.dart'; + // OTHERS import '../../helpers.dart'; @@ -27,10 +30,12 @@ import '../../helpers.dart'; import '../../mock/managers/course_repository_mock.dart'; import '../../mock/managers/settings_manager_mock.dart'; import '../../mock/services/in_app_review_service_mock.dart'; +import '../../mock/services/remote_config_service_mock.dart'; void main() { SettingsManager settingsManager; CourseRepository courseRepository; + RemoteConfigService remoteConfigService; AppIntl intl; InAppReviewServiceMock inAppReviewServiceMock; @@ -72,10 +77,11 @@ void main() { // Cards Map dashboard = { - PreferencesFlag.aboutUsCard: 0, - PreferencesFlag.scheduleCard: 1, - PreferencesFlag.progressBarCard: 2, - PreferencesFlag.gradesCard: 3 + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.aboutUsCard: 1, + PreferencesFlag.scheduleCard: 2, + PreferencesFlag.progressBarCard: 3, + PreferencesFlag.gradesCard: 4 }; final numberOfCards = dashboard.entries.length; @@ -168,7 +174,7 @@ void main() { // Find schedule card in second position by its title return tester.firstWidget(find.descendant( - of: find.byType(Dismissible).at(1), + of: find.byType(Dismissible, skipOffstage: false).at(2), matching: find.byType(Text), )); } @@ -178,6 +184,7 @@ void main() { intl = await setupAppIntl(); settingsManager = setupSettingsManagerMock(); courseRepository = setupCourseRepositoryMock(); + remoteConfigService = setupRemoteConfigServiceMock(); setupNavigationServiceMock(); courseRepository = setupCourseRepositoryMock(); setupNetworkingServiceMock(); @@ -216,10 +223,30 @@ void main() { courseRepository as CourseRepositoryMock, fromCacheOnly: false); + RemoteConfigServiceMock.stubGetBroadcastEnabled( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastColor( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastEn( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastFr( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastTitleEn( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastTitleFr( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastType( + remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastUrl( + remoteConfigService as RemoteConfigServiceMock); + SettingsManagerMock.stubGetBool(settingsManager as SettingsManagerMock, PreferencesFlag.discoveryDashboard, toReturn: true); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, + PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt( settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); @@ -262,7 +289,8 @@ void main() { expect(restoreCardsIcon, findsOneWidget); // Find cards - expect(find.byType(Card), findsNWidgets(numberOfCards)); + expect(find.byType(Card, skipOffstage: false), + findsNWidgets(numberOfCards)); }); testWidgets('Has card aboutUs displayed properly', @@ -310,7 +338,7 @@ void main() { // Find three activities in the card expect( find.descendant( - of: find.byType(Dismissible), + of: find.byType(Dismissible, skipOffstage: false), matching: find.byType(CourseActivityTile), ), findsNWidgets(3)); @@ -338,7 +366,7 @@ void main() { // Find one activities in the card expect( find.descendant( - of: find.byType(Dismissible), + of: find.byType(Dismissible, skipOffstage: false), matching: find.byType(CourseActivityTile), ), findsNWidgets(1)); @@ -357,10 +385,10 @@ void main() { // Find no activity and no grade available text boxes expect( find.descendant( - of: find.byType(SizedBox), + of: find.byType(SizedBox, skipOffstage: false), matching: find.byType(Text), ), - findsNWidgets(2)); + findsNWidgets(1)); }); }); @@ -381,6 +409,9 @@ void main() { settingsManager as SettingsManagerMock, toReturn: dashboard); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, + PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); @@ -398,16 +429,18 @@ void main() { await tester.pumpAndSettle(); // Find Dismissible Cards - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); expect(find.text(intl.card_applets_title), findsOneWidget); // Swipe Dismissible aboutUs Card horizontally - await tester.drag( - find.byType(Dismissible).first, const Offset(1000.0, 0.0)); + await tester.drag(find.byType(Dismissible, skipOffstage: false).at(1), + const Offset(1000.0, 0.0)); // Check that the card is now absent from the view await tester.pumpAndSettle(); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards - 1)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards - 1)); expect(find.text(intl.card_applets_title), findsNothing); // Tap the restoreCards button @@ -416,7 +449,8 @@ void main() { await tester.pumpAndSettle(); // Check that the card is now present in the view - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); expect(find.text(intl.card_applets_title), findsOneWidget); }); @@ -435,6 +469,9 @@ void main() { courseRepository as CourseRepositoryMock, fromCacheOnly: false); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, + PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); @@ -452,14 +489,15 @@ void main() { await tester.pumpAndSettle(); // Find Dismissible Cards - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); // Find aboutUs card expect(find.text(intl.card_applets_title), findsOneWidget); // Check that the aboutUs card is in the first position var text = tester.firstWidget(find.descendant( - of: find.byType(Dismissible).first, + of: find.byType(Dismissible, skipOffstage: false).at(1), matching: find.byType(Text), )); @@ -474,11 +512,12 @@ void main() { await tester.pumpAndSettle(); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); // Check that the card is now in last position text = tester.firstWidget(find.descendant( - of: find.byType(Dismissible).last, + of: find.byType(Dismissible, skipOffstage: false).last, matching: find.byType(Text), )); expect((text as Text).data, intl.card_applets_title); @@ -489,11 +528,12 @@ void main() { await tester.pumpAndSettle(); text = tester.firstWidget(find.descendant( - of: find.byType(Dismissible).first, + of: find.byType(Dismissible, skipOffstage: false).at(1), matching: find.byType(Text), )); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); // Check that the first card is now AboutUs expect((text as Text).data, intl.card_applets_title); @@ -510,18 +550,20 @@ void main() { await tester.pumpAndSettle(); // Find Dismissible Cards - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); expect(find.widgetWithText(Dismissible, intl.title_schedule), findsOneWidget); // Swipe Dismissible schedule Card horizontally - await tester.drag( - find.byType(Dismissible).at(1), const Offset(1000.0, 0.0)); + await tester.drag(find.byType(Dismissible, skipOffstage: false).at(2), + const Offset(1000.0, 0.0)); // Check that the card is now absent from the view await tester.pumpAndSettle(); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards - 1)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards - 1)); expect(find.widgetWithText(Dismissible, intl.title_schedule), findsNothing); @@ -531,7 +573,8 @@ void main() { await tester.pumpAndSettle(); // Check that the card is now present in the view - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); expect(find.widgetWithText(Dismissible, intl.title_schedule), findsOneWidget); }); @@ -548,16 +591,18 @@ void main() { await tester.pumpAndSettle(); // Find grades card - final gradesCard = find.widgetWithText(Card, intl.grades_title); + final gradesCard = + find.widgetWithText(Card, intl.grades_title, skipOffstage: false); expect(gradesCard, findsOneWidget); // Find grades card Title - final gradesTitle = find.text(intl.grades_title); + final gradesTitle = find.text(intl.grades_title, skipOffstage: false); expect(gradesTitle, findsOneWidget); // Find empty grades card - final gradesEmptyTitle = - find.text(intl.grades_msg_no_grades.split("\n").first); + final gradesEmptyTitle = find.text( + intl.grades_msg_no_grades.split("\n").first, + skipOffstage: false); expect(gradesEmptyTitle, findsOneWidget); }); @@ -584,20 +629,24 @@ void main() { await tester.pumpAndSettle(); // Find grades card - final gradesCard = find.widgetWithText(Card, intl.grades_title); + final gradesCard = + find.widgetWithText(Card, intl.grades_title, skipOffstage: false); expect(gradesCard, findsOneWidget); // Find grades card Title - final gradesTitle = find.text(intl.grades_title); + final gradesTitle = find.text(intl.grades_title, skipOffstage: false); expect(gradesTitle, findsOneWidget); // Find grades buttons in the card - final gradesButtons = find.byType(GradeButton); + final gradesButtons = find.byType(GradeButton, skipOffstage: false); expect(gradesButtons, findsNWidgets(2)); }); testWidgets('gradesCard is dismissible and can be restored', (WidgetTester tester) async { + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, + PreferencesFlag.broadcastCard); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); @@ -618,11 +667,15 @@ void main() { await tester.pumpAndSettle(); // Find Dismissible Cards - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); - expect(find.text(intl.grades_title), findsOneWidget); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); + expect(find.text(intl.grades_title, skipOffstage: false), + findsOneWidget); // Swipe Dismissible grades Card horizontally - await tester.drag(find.widgetWithText(Dismissible, intl.grades_title), + await tester.drag( + find.widgetWithText(Dismissible, intl.grades_title, + skipOffstage: false), const Offset(1000.0, 0.0)); // Check that the card is now absent from the view @@ -636,8 +689,10 @@ void main() { await tester.pumpAndSettle(); // Check that the card is now present in the view - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); - expect(find.text(intl.grades_title), findsOneWidget); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); + expect(find.text(intl.grades_title, skipOffstage: false), + findsOneWidget); }); }); }); @@ -677,7 +732,8 @@ void main() { await tester.pumpAndSettle(); // Find Dismissible Cards - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); expect(find.text(intl.progress_bar_title), findsOneWidget); // Swipe Dismissible progress Card horizontally @@ -687,7 +743,8 @@ void main() { // Check that the card is now absent from the view await tester.pumpAndSettle(); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards - 1)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards - 1)); expect(find.text(intl.progress_bar_title), findsNothing); // Tap the restoreCards button @@ -696,7 +753,8 @@ void main() { await tester.pumpAndSettle(); // Check that the card is now present in the view - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); expect(find.text(intl.progress_bar_title), findsOneWidget); }); @@ -712,7 +770,8 @@ void main() { await tester.pumpAndSettle(); // Find Dismissible Cards - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); // Find progressBar card expect(find.text(intl.progress_bar_title), findsOneWidget); @@ -734,7 +793,8 @@ void main() { await tester.pumpAndSettle(); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); // Check that the card is now in last position text = tester.firstWidget(find.descendant( @@ -753,7 +813,8 @@ void main() { matching: find.byType(Text), )); - expect(find.byType(Dismissible), findsNWidgets(numberOfCards)); + expect(find.byType(Dismissible, skipOffstage: false), + findsNWidgets(numberOfCards)); // Check that the first card is now AboutUs expect((text as Text).data, intl.progress_bar_title); diff --git a/test/viewmodels/dashboard_viewmodel_test.dart b/test/viewmodels/dashboard_viewmodel_test.dart index 9133a7ad7..343be62f7 100644 --- a/test/viewmodels/dashboard_viewmodel_test.dart +++ b/test/viewmodels/dashboard_viewmodel_test.dart @@ -16,6 +16,7 @@ import 'package:ets_api_clients/models.dart'; // SERVICE import 'package:notredame/core/services/preferences_service.dart'; import 'package:notredame/core/services/analytics_service.dart'; +import 'package:notredame/core/services/remote_config_service.dart'; // VIEWMODEL import 'package:notredame/core/viewmodels/dashboard_viewmodel.dart'; @@ -28,12 +29,14 @@ import '../mock/managers/course_repository_mock.dart'; import '../mock/managers/settings_manager_mock.dart'; import '../mock/services/in_app_review_service_mock.dart'; import '../mock/services/preferences_service_mock.dart'; +import '../mock/services/remote_config_service_mock.dart'; void main() { PreferencesService preferenceService; SettingsManager settingsManager; DashboardViewModel viewModel; CourseRepository courseRepository; + RemoteConfigService remoteConfigService; PreferencesServiceMock preferencesServiceMock; InAppReviewServiceMock inAppReviewServiceMock; AnalyticsService analyticsService; @@ -163,23 +166,26 @@ void main() { // Cards final Map dashboard = { - PreferencesFlag.aboutUsCard: 0, - PreferencesFlag.scheduleCard: 1, - PreferencesFlag.progressBarCard: 2, + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.aboutUsCard: 1, + PreferencesFlag.scheduleCard: 2, + PreferencesFlag.progressBarCard: 3, }; // Reorderered Cards final Map reorderedDashboard = { - PreferencesFlag.aboutUsCard: 1, - PreferencesFlag.scheduleCard: 2, + PreferencesFlag.broadcastCard: 1, + PreferencesFlag.aboutUsCard: 2, + PreferencesFlag.scheduleCard: 3, PreferencesFlag.progressBarCard: 0, }; // Reorderered Cards with hidden scheduleCard final Map hiddenCardDashboard = { - PreferencesFlag.aboutUsCard: 0, + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.aboutUsCard: 1, PreferencesFlag.scheduleCard: -1, - PreferencesFlag.progressBarCard: 1, + PreferencesFlag.progressBarCard: 2, }; // Session @@ -202,6 +208,7 @@ void main() { setUp(() async { // Setting up mocks courseRepository = setupCourseRepositoryMock(); + remoteConfigService = setupRemoteConfigServiceMock(); settingsManager = setupSettingsManagerMock(); preferenceService = setupPreferencesServiceMock(); analyticsService = setupAnalyticsServiceMock(); @@ -229,6 +236,9 @@ void main() { settingsManager as SettingsManagerMock, toReturn: DateTime(2020)); + RemoteConfigServiceMock.stubGetBroadcastEnabled( + remoteConfigService as RemoteConfigServiceMock); + inAppReviewServiceMock = setupInAppReviewServiceMock() as InAppReviewServiceMock; }); @@ -376,6 +386,7 @@ void main() { await viewModel.futureToRun(); expect(viewModel.cards, dashboard); expect(viewModel.cardsToDisplay, [ + PreferencesFlag.broadcastCard, PreferencesFlag.aboutUsCard, PreferencesFlag.scheduleCard, PreferencesFlag.progressBarCard @@ -624,6 +635,9 @@ void main() { CourseRepositoryMock.stubGetCourses( courseRepository as CourseRepositoryMock); + PreferencesServiceMock.stubException( + preferenceService as PreferencesServiceMock, + PreferencesFlag.broadcastCard); PreferencesServiceMock.stubException( preferenceService as PreferencesServiceMock, PreferencesFlag.aboutUsCard); @@ -739,6 +753,8 @@ void main() { group("interact with cards - ", () { test("can hide a card and reset cards to default layout", () async { + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, + PreferencesFlag.broadcastCard); SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, @@ -761,26 +777,32 @@ void main() { settingsManager.setInt(PreferencesFlag.scheduleCard, -1)); expect(viewModel.cards, hiddenCardDashboard); - expect(viewModel.cardsToDisplay, - [PreferencesFlag.aboutUsCard, PreferencesFlag.progressBarCard]); + expect(viewModel.cardsToDisplay, [ + PreferencesFlag.broadcastCard, + PreferencesFlag.aboutUsCard, + PreferencesFlag.progressBarCard + ]); verify(analyticsService.logEvent( "DashboardViewModel", "Deleting scheduleCard")); verify(settingsManager.setInt(PreferencesFlag.scheduleCard, -1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 0)) + verify(settingsManager.setInt(PreferencesFlag.broadcastCard, 0)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 1)) + verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 1)) + .called(1); + verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 2)) .called(1); // Call the setter. viewModel.setAllCardsVisible(); await untilCalled( - settingsManager.setInt(PreferencesFlag.progressBarCard, 2)); + settingsManager.setInt(PreferencesFlag.progressBarCard, 3)); expect(viewModel.cards, dashboard); expect(viewModel.cardsToDisplay, [ + PreferencesFlag.broadcastCard, PreferencesFlag.aboutUsCard, PreferencesFlag.scheduleCard, PreferencesFlag.progressBarCard @@ -789,11 +811,13 @@ void main() { verify( analyticsService.logEvent("DashboardViewModel", "Restoring cards")); verify(settingsManager.getDashboard()).called(1); - verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 0)) + verify(settingsManager.setInt(PreferencesFlag.broadcastCard, 0)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.scheduleCard, 1)) + verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 2)) + verify(settingsManager.setInt(PreferencesFlag.scheduleCard, 2)) + .called(1); + verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 3)) .called(1); verify(settingsManager.getString(PreferencesFlag.progressBarText)) .called(2); @@ -813,6 +837,8 @@ void main() { settingsManager as SettingsManagerMock, toReturn: dashboard); + SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, + PreferencesFlag.broadcastCard); SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, PreferencesFlag.aboutUsCard); SettingsManagerMock.stubSetInt(settingsManager as SettingsManagerMock, @@ -824,6 +850,7 @@ void main() { expect(viewModel.cards, dashboard); expect(viewModel.cardsToDisplay, [ + PreferencesFlag.broadcastCard, PreferencesFlag.aboutUsCard, PreferencesFlag.scheduleCard, PreferencesFlag.progressBarCard, @@ -838,6 +865,7 @@ void main() { expect(viewModel.cards, reorderedDashboard); expect(viewModel.cardsToDisplay, [ PreferencesFlag.progressBarCard, + PreferencesFlag.broadcastCard, PreferencesFlag.aboutUsCard, PreferencesFlag.scheduleCard ]); @@ -847,9 +875,11 @@ void main() { verify(settingsManager.getDashboard()).called(1); verify(settingsManager.setInt(PreferencesFlag.progressBarCard, 0)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 1)) + verify(settingsManager.setInt(PreferencesFlag.broadcastCard, 1)) .called(1); - verify(settingsManager.setInt(PreferencesFlag.scheduleCard, 2)) + verify(settingsManager.setInt(PreferencesFlag.aboutUsCard, 2)) + .called(1); + verify(settingsManager.setInt(PreferencesFlag.scheduleCard, 3)) .called(1); verify(settingsManager.getString(PreferencesFlag.progressBarText)) .called(1); From 6c664d50fd782ada190928ae899932c4d0cc5013 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Wed, 20 Sep 2023 23:55:56 -0400 Subject: [PATCH 13/21] Cleanup --- l10n/intl_en.arb | 1 - l10n/intl_fr.arb | 1 - 2 files changed, 2 deletions(-) diff --git a/l10n/intl_en.arb b/l10n/intl_en.arb index 50e605174..8edb72ae5 100644 --- a/l10n/intl_en.arb +++ b/l10n/intl_en.arb @@ -45,7 +45,6 @@ "dashboard_msg_card_removed": "Card removed", "dashboard_restore_all_cards_title": "Restore all cards", - "card_broadcast_title": "Important notice", "card_schedule_tomorrow": " - tomorrow", diff --git a/l10n/intl_fr.arb b/l10n/intl_fr.arb index 536c11e75..dfd485fe3 100644 --- a/l10n/intl_fr.arb +++ b/l10n/intl_fr.arb @@ -44,7 +44,6 @@ "dashboard_msg_card_removed": "Carte supprimée", "dashboard_restore_all_cards_title": "Restaurer toutes les cartes", - "card_broadcast_title": "Annonce importante", "card_schedule_tomorrow": " - demain", From 55714bda6b224a537996e25a352cd4f881139755 Mon Sep 17 00:00:00 2001 From: HugoMigner Date: Thu, 21 Sep 2023 04:00:51 +0000 Subject: [PATCH 14/21] [BOT] Applying version. --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ab708fcf4..5bbbb64c4 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.2+1 +version: 4.26.0+1 environment: sdk: ">=2.10.0 <3.0.0" From 6e16d07d2ae7f32db94c66c2ef2331f1a2981583 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Thu, 21 Sep 2023 00:04:05 -0400 Subject: [PATCH 15/21] Fix tests --- lib/core/viewmodels/dashboard_viewmodel.dart | 16 +++++++--------- lib/ui/views/dashboard_view.dart | 1 - 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 8c7a584e2..14efea0a0 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -549,8 +549,6 @@ class DashboardViewModel extends FutureViewModel> { } Future futureToRunBroadcast() async { - const PreferencesFlag flag = PreferencesFlag.broadcastCard; - setBusyForObject(broadcastMessage, true); setBusyForObject(broadcastTitle, true); setBusyForObject(broadcastColor, true); @@ -558,15 +556,15 @@ class DashboardViewModel extends FutureViewModel> { setBusyForObject(broadcastType, true); if (_appIntl.localeName == "fr") { - broadcastMessage = await remoteConfigService.dashboardMessageFr; - broadcastTitle = await remoteConfigService.dashboardMessageTitleFr; + broadcastMessage = remoteConfigService.dashboardMessageFr; + broadcastTitle = remoteConfigService.dashboardMessageTitleFr; } else { - broadcastMessage = await remoteConfigService.dashboardMessageEn; - broadcastTitle = await remoteConfigService.dashboardMessageTitleEn; + broadcastMessage = remoteConfigService.dashboardMessageEn; + broadcastTitle = remoteConfigService.dashboardMessageTitleEn; } - broadcastColor = await remoteConfigService.dashboardMsgColor; - broadcastUrl = await remoteConfigService.dashboardMsgUrl; - broadcastType = await remoteConfigService.dashboardMsgType; + broadcastColor = remoteConfigService.dashboardMsgColor; + broadcastUrl = remoteConfigService.dashboardMsgUrl; + broadcastType = remoteConfigService.dashboardMsgType; setBusyForObject(broadcastMessage, false); setBusyForObject(broadcastTitle, false); diff --git a/lib/ui/views/dashboard_view.dart b/lib/ui/views/dashboard_view.dart index 25cb1e51a..ec86874da 100644 --- a/lib/ui/views/dashboard_view.dart +++ b/lib/ui/views/dashboard_view.dart @@ -36,7 +36,6 @@ import 'package:notredame/ui/utils/discovery_components.dart'; // SERVICES import 'package:notredame/core/services/navigation_service.dart'; import 'package:notredame/core/services/analytics_service.dart'; -import 'package:url_launcher/url_launcher.dart'; class DashboardView extends StatefulWidget { final UpdateCode updateCode; From 84b36e770421347bc95c982355f44d677be010fc Mon Sep 17 00:00:00 2001 From: HugoMigner Date: Mon, 25 Sep 2023 13:32:24 +0000 Subject: [PATCH 16/21] [BOT] Applying version. --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 3f58d02db..30cf5dc5a 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.27.0+1 +version: 4.28.0+1 environment: sdk: ">=2.10.0 <3.0.0" From c926bed12be37368dc0cf0ef23f61158a2b33b75 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Mon, 25 Sep 2023 12:39:07 -0400 Subject: [PATCH 17/21] Check for broadcast change while card is dismissed --- lib/core/constants/preferences_flags.dart | 1 + lib/core/viewmodels/dashboard_viewmodel.dart | 24 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/core/constants/preferences_flags.dart b/lib/core/constants/preferences_flags.dart index 8da03fa8d..3b578a17c 100644 --- a/lib/core/constants/preferences_flags.dart +++ b/lib/core/constants/preferences_flags.dart @@ -40,6 +40,7 @@ enum PreferencesFlag { progressBarCard, gradesCard, progressBarText, + broadcastChange, // Rating flag ratingTimer, diff --git a/lib/core/viewmodels/dashboard_viewmodel.dart b/lib/core/viewmodels/dashboard_viewmodel.dart index 14efea0a0..2f9752706 100644 --- a/lib/core/viewmodels/dashboard_viewmodel.dart +++ b/lib/core/viewmodels/dashboard_viewmodel.dart @@ -37,6 +37,7 @@ class DashboardViewModel extends FutureViewModel> { static const String tag = "DashboardViewModel"; final SettingsManager _settingsManager = locator(); + final PreferencesService _preferencesService = locator(); final CourseRepository _courseRepository = locator(); final AnalyticsService _analyticsService = locator(); final AppWidgetService _appWidgetService = locator(); @@ -189,6 +190,8 @@ class DashboardViewModel extends FutureViewModel> { _cards = dashboard; + await checkForBroadcastChange(); + getCardsToDisplay(); // load data for both grade cards & grades home screen widget @@ -328,6 +331,27 @@ class DashboardViewModel extends FutureViewModel> { _analyticsService.logEvent(tag, "Restoring cards"); } + Future checkForBroadcastChange() async { + final broadcastChange = + await _preferencesService.getString(PreferencesFlag.broadcastChange) ?? + ""; + if (broadcastChange != remoteConfigService.dashboardMessageEn) { + // Update pref + _preferencesService.setString(PreferencesFlag.broadcastChange, + remoteConfigService.dashboardMessageEn); + if (_cards[PreferencesFlag.broadcastCard] < 0) { + _cards.updateAll((key, value) { + if (value >= 0) { + return value + 1; + } else { + return value; + } + }); + _cards[PreferencesFlag.broadcastCard] = 0; + } + } + } + Future> futureToRunSessionProgressBar() async { String progressBarText = await _settingsManager.getString(PreferencesFlag.progressBarText); From fd7dcb75770cbfcc2b970e1aef10bf8209e3a5a2 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Mon, 25 Sep 2023 12:50:06 -0400 Subject: [PATCH 18/21] Fix tests --- test/viewmodels/dashboard_viewmodel_test.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/viewmodels/dashboard_viewmodel_test.dart b/test/viewmodels/dashboard_viewmodel_test.dart index 343be62f7..e8cf55e2e 100644 --- a/test/viewmodels/dashboard_viewmodel_test.dart +++ b/test/viewmodels/dashboard_viewmodel_test.dart @@ -238,6 +238,9 @@ void main() { RemoteConfigServiceMock.stubGetBroadcastEnabled( remoteConfigService as RemoteConfigServiceMock); + RemoteConfigServiceMock.stubGetBroadcastEn( + remoteConfigService as RemoteConfigServiceMock, + toReturn: ""); inAppReviewServiceMock = setupInAppReviewServiceMock() as InAppReviewServiceMock; @@ -408,6 +411,9 @@ void main() { CourseRepositoryMock.stubGetCourses( courseRepository as CourseRepositoryMock, toReturn: courses); + SettingsManagerMock.stubGetDashboard( + settingsManager as SettingsManagerMock, + toReturn: dashboard); final now = DateTime.now(); SettingsManagerMock.stubDateTimeNow( settingsManager as SettingsManagerMock, @@ -440,6 +446,9 @@ void main() { CourseRepositoryMock.stubGetCourses( courseRepository as CourseRepositoryMock, toReturn: courses); + SettingsManagerMock.stubGetDashboard( + settingsManager as SettingsManagerMock, + toReturn: dashboard); final now = DateTime.now(); SettingsManagerMock.stubDateTimeNow( settingsManager as SettingsManagerMock, @@ -471,6 +480,9 @@ void main() { CourseRepositoryMock.stubGetCourses( courseRepository as CourseRepositoryMock, toReturn: courses); + SettingsManagerMock.stubGetDashboard( + settingsManager as SettingsManagerMock, + toReturn: dashboard); final now = DateTime.now(); SettingsManagerMock.stubDateTimeNow( settingsManager as SettingsManagerMock, @@ -503,6 +515,9 @@ void main() { CourseRepositoryMock.stubGetCourses( courseRepository as CourseRepositoryMock, toReturn: courses); + SettingsManagerMock.stubGetDashboard( + settingsManager as SettingsManagerMock, + toReturn: dashboard); final now = DateTime.now(); SettingsManagerMock.stubDateTimeNow( settingsManager as SettingsManagerMock, From 165a2f490a6661d00e8141dc33214580a41b5a2f Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Mon, 25 Sep 2023 13:05:02 -0400 Subject: [PATCH 19/21] Fix tests again --- test/ui/views/dashboard_view_test.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/ui/views/dashboard_view_test.dart b/test/ui/views/dashboard_view_test.dart index a7bbfc6b6..6813a68e2 100644 --- a/test/ui/views/dashboard_view_test.dart +++ b/test/ui/views/dashboard_view_test.dart @@ -827,10 +827,14 @@ void main() { }); testWidgets("Applets Card", (WidgetTester tester) async { + RemoteConfigServiceMock.stubGetBroadcastEnabled( + remoteConfigService as RemoteConfigServiceMock, + toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - dashboard = { - PreferencesFlag.aboutUsCard: 0, + Map dashboard = { + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.aboutUsCard: 1, }; SettingsManagerMock.stubGetDashboard( From 251ab53b85cfa52d7714d36510a203d33bfe7ced Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Mon, 25 Sep 2023 13:14:23 -0400 Subject: [PATCH 20/21] Fix tests pdv --- test/ui/views/dashboard_view_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ui/views/dashboard_view_test.dart b/test/ui/views/dashboard_view_test.dart index 6813a68e2..d70cc5308 100644 --- a/test/ui/views/dashboard_view_test.dart +++ b/test/ui/views/dashboard_view_test.dart @@ -832,7 +832,7 @@ void main() { toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); - Map dashboard = { + final Map dashboard = { PreferencesFlag.broadcastCard: 0, PreferencesFlag.aboutUsCard: 1, }; From d584ceaeb3339c363eb2cead7cd803ca74425e33 Mon Sep 17 00:00:00 2001 From: Hugo Migner Date: Mon, 25 Sep 2023 13:21:43 -0400 Subject: [PATCH 21/21] Fix tests real --- test/ui/views/dashboard_view_test.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/ui/views/dashboard_view_test.dart b/test/ui/views/dashboard_view_test.dart index d70cc5308..66af76823 100644 --- a/test/ui/views/dashboard_view_test.dart +++ b/test/ui/views/dashboard_view_test.dart @@ -850,6 +850,9 @@ void main() { }); testWidgets("Schedule card", (WidgetTester tester) async { + RemoteConfigServiceMock.stubGetBroadcastEnabled( + remoteConfigService as RemoteConfigServiceMock, + toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); CourseRepositoryMock.stubCoursesActivities( @@ -862,7 +865,8 @@ void main() { fromCacheOnly: false); dashboard = { - PreferencesFlag.scheduleCard: 0, + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.scheduleCard: 1, }; SettingsManagerMock.stubGetDashboard( @@ -877,10 +881,14 @@ void main() { matchesGoldenFile(goldenFilePath("dashboardView_scheduleCard_1"))); }); testWidgets("progressBar Card", (WidgetTester tester) async { + RemoteConfigServiceMock.stubGetBroadcastEnabled( + remoteConfigService as RemoteConfigServiceMock, + toReturn: false); tester.binding.window.physicalSizeTestValue = const Size(800, 1410); dashboard = { - PreferencesFlag.progressBarCard: 0, + PreferencesFlag.broadcastCard: 0, + PreferencesFlag.progressBarCard: 1, }; SettingsManagerMock.stubGetDashboard(