From baf2d9fae02500807fa410a6c1b8ce0166250eaf Mon Sep 17 00:00:00 2001 From: Valentin REVERSAT Date: Mon, 8 May 2023 23:00:40 +0200 Subject: [PATCH] feat(notifications): automatic check if the notifications are enabled --- android/app/src/main/AndroidManifest.xml | 1 + lib/bloc/notification/notification_bloc.dart | 75 ++++++++++++--- lib/bloc/notification/notification_event.dart | 8 ++ lib/bloc/notification/notification_state.dart | 4 + lib/chabo.dart | 9 +- lib/cubits/notification_service_cubit.dart | 6 -- .../chaban_bridge_forecast_screen.dart | 18 ++-- lib/screens/notification_screen.dart | 92 ++++++++----------- .../forecast/forecast_list_item_widget.dart | 2 + 9 files changed, 127 insertions(+), 88 deletions(-) delete mode 100644 lib/cubits/notification_service_cubit.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c71fca2b..d060281a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + diff --git a/lib/bloc/notification/notification_bloc.dart b/lib/bloc/notification/notification_bloc.dart index 5c35f70e..4fb0136d 100644 --- a/lib/bloc/notification/notification_bloc.dart +++ b/lib/bloc/notification/notification_bloc.dart @@ -1,22 +1,28 @@ +import 'dart:async'; + import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:chabo/bloc/chabo_event.dart'; import 'package:chabo/const.dart'; +import 'package:chabo/models/abstract_chaban_bridge_forecast.dart'; import 'package:chabo/models/enums/day.dart'; import 'package:chabo/models/time_slot.dart'; +import 'package:chabo/service/notification_service.dart'; import 'package:chabo/service/storage_service.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; part 'notification_event.dart'; - part 'notification_state.dart'; class NotificationBloc extends Bloc { final StorageService storageService; + final NotificationService notificationService; - NotificationBloc({required this.storageService}) - : super( + NotificationBloc({ + required this.storageService, + required this.notificationService, + }) : super( NotificationState( durationNotificationEnabled: Const.notificationDurationEnabledDefaultValue, @@ -35,6 +41,7 @@ class NotificationBloc extends Bloc { timeSlotsEnabledForNotifications: Const.notificationFavoriteSlotsEnabledDefaultValue, timeSlotsValue: Const.notificationFavoriteSlotsDefaultValue, + notificationEnabled: false, ), ) { on( @@ -81,6 +88,10 @@ class NotificationBloc extends Bloc { _onTimeSlotsEventValue, transformer: sequential(), ); + on( + _onComputeNotificationEvent, + transformer: sequential(), + ); on( _onAppEvent, transformer: sequential(), @@ -97,7 +108,9 @@ class NotificationBloc extends Bloc { ); HapticFeedback.lightImpact(); emit( - state.copyWith(openingNotificationEnabled: event.enabled), + state.copyWith( + openingNotificationEnabled: event.enabled, + ), ); } @@ -109,9 +122,14 @@ class NotificationBloc extends Bloc { Const.notificationClosingEnabledKey, event.enabled, ); + HapticFeedback.lightImpact(); + final enabled = await notificationService.areNotificationsEnabled(); emit( - state.copyWith(closingNotificationEnabled: event.enabled), + state.copyWith( + closingNotificationEnabled: event.enabled, + notificationEnabled: enabled, + ), ); } @@ -124,8 +142,12 @@ class NotificationBloc extends Bloc { event.enabled, ); HapticFeedback.lightImpact(); + final enabled = await notificationService.areNotificationsEnabled(); emit( - state.copyWith(dayNotificationEnabled: event.enabled), + state.copyWith( + dayNotificationEnabled: event.enabled, + notificationEnabled: enabled, + ), ); } @@ -133,10 +155,15 @@ class NotificationBloc extends Bloc { DayNotificationValueEvent event, Emitter emit, ) async { - await storageService.saveDay(Const.notificationDayValueKey, event.day); + await storageService.saveDay( + Const.notificationDayValueKey, + event.day, + ); HapticFeedback.lightImpact(); emit( - state.copyWith(dayNotificationValue: event.day), + state.copyWith( + dayNotificationValue: event.day, + ), ); } @@ -150,7 +177,9 @@ class NotificationBloc extends Bloc { ); HapticFeedback.lightImpact(); emit( - state.copyWith(dayNotificationTimeValue: event.time), + state.copyWith( + dayNotificationTimeValue: event.time, + ), ); } @@ -163,8 +192,12 @@ class NotificationBloc extends Bloc { event.enabled, ); HapticFeedback.lightImpact(); + final enabled = await notificationService.areNotificationsEnabled(); emit( - state.copyWith(timeNotificationEnabled: event.enabled), + state.copyWith( + timeNotificationEnabled: event.enabled, + notificationEnabled: enabled, + ), ); } @@ -177,7 +210,9 @@ class NotificationBloc extends Bloc { event.time, ); emit( - state.copyWith(timeNotificationValue: event.time), + state.copyWith( + timeNotificationValue: event.time, + ), ); } @@ -190,9 +225,11 @@ class NotificationBloc extends Bloc { event.enabled, ); HapticFeedback.lightImpact(); + final enabled = await notificationService.areNotificationsEnabled(); emit( state.copyWith( durationNotificationEnabled: event.enabled, + notificationEnabled: enabled, ), ); } @@ -244,10 +281,21 @@ class NotificationBloc extends Bloc { ); } + Future _onComputeNotificationEvent( + ComputeNotificationEvent event, + Emitter emit, + ) async { + await notificationService.computeNotifications( + event.forecasts, + state, + event.context, + ); + } + void _onAppEvent( AppEvent event, Emitter emit, - ) { + ) async { final durationNotificationEnabled = storageService.readBool(Const.notificationDurationEnabledKey) ?? Const.notificationDurationEnabledDefaultValue; @@ -292,6 +340,8 @@ class NotificationBloc extends Bloc { storageService.readBool(Const.notificationFavoriteSlotsEnabledKey) ?? Const.notificationFavoriteSlotsEnabledDefaultValue; + final enabled = await notificationService.areNotificationsEnabled(); + emit( state.copyWith( durationNotificationEnabled: durationNotificationEnabled, @@ -305,6 +355,7 @@ class NotificationBloc extends Bloc { closingNotificationEnabled: closingNotificationEnabled, timeSlotsValue: timeSlots, timeSlotsEnabledForNotifications: enabledForNotifications, + notificationEnabled: enabled, ), ); } diff --git a/lib/bloc/notification/notification_event.dart b/lib/bloc/notification/notification_event.dart index a1f1a056..b0b42dfb 100644 --- a/lib/bloc/notification/notification_event.dart +++ b/lib/bloc/notification/notification_event.dart @@ -69,6 +69,14 @@ class ValueTimeSlotEvent extends NotificationEvent { ValueTimeSlotEvent({required this.timeSlot, required this.index}) : super(); } +class ComputeNotificationEvent extends NotificationEvent { + final List forecasts; + final BuildContext context; + + ComputeNotificationEvent({required this.forecasts, required this.context}) + : super(); +} + class AppEvent extends NotificationEvent { AppEvent() : super(); } diff --git a/lib/bloc/notification/notification_state.dart b/lib/bloc/notification/notification_state.dart index 2bbdbddb..47269678 100644 --- a/lib/bloc/notification/notification_state.dart +++ b/lib/bloc/notification/notification_state.dart @@ -12,8 +12,10 @@ class NotificationState { final bool closingNotificationEnabled; final bool timeSlotsEnabledForNotifications; final List timeSlotsValue; + final bool notificationEnabled; NotificationState({ + required this.notificationEnabled, required this.durationNotificationEnabled, required this.durationNotificationValue, required this.timeNotificationEnabled, @@ -39,6 +41,7 @@ class NotificationState { bool? closingNotificationEnabled, bool? timeSlotsEnabledForNotifications, List? timeSlotsValue, + bool? notificationEnabled, }) { return NotificationState( durationNotificationEnabled: @@ -61,6 +64,7 @@ class NotificationState { timeSlotsEnabledForNotifications: timeSlotsEnabledForNotifications ?? this.timeSlotsEnabledForNotifications, timeSlotsValue: timeSlotsValue ?? this.timeSlotsValue, + notificationEnabled: notificationEnabled ?? this.notificationEnabled, ); } } diff --git a/lib/chabo.dart b/lib/chabo.dart index ebaee445..8569b802 100644 --- a/lib/chabo.dart +++ b/lib/chabo.dart @@ -4,7 +4,6 @@ import 'package:chabo/bloc/notification/notification_bloc.dart'; import 'package:chabo/bloc/scroll_status/scroll_status_bloc.dart'; import 'package:chabo/bloc/theme/theme_bloc.dart'; import 'package:chabo/cubits/floating_actions_cubit.dart'; -import 'package:chabo/cubits/notification_service_cubit.dart'; import 'package:chabo/screens/chaban_bridge_forecast_screen.dart'; import 'package:chabo/service/notification_service.dart'; import 'package:chabo/service/storage_service.dart'; @@ -37,13 +36,6 @@ class Chabo extends StatelessWidget { ), ), - /// Bloc intended to manage the Notifications service - BlocProvider( - create: (_) => NotificationServiceCubit( - notificationService, - ), - ), - /// Bloc intended to manage the FloatingActions BlocProvider( create: (_) => FloatingActionsCubit( @@ -77,6 +69,7 @@ class Chabo extends StatelessWidget { BlocProvider( create: (_) => NotificationBloc( storageService: storageService, + notificationService: notificationService, )..add( AppEvent(), ), diff --git a/lib/cubits/notification_service_cubit.dart b/lib/cubits/notification_service_cubit.dart deleted file mode 100644 index 43df0e87..00000000 --- a/lib/cubits/notification_service_cubit.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:chabo/service/notification_service.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class NotificationServiceCubit extends Cubit { - NotificationServiceCubit(super.initialState); -} diff --git a/lib/screens/chaban_bridge_forecast_screen.dart b/lib/screens/chaban_bridge_forecast_screen.dart index a75ab9bc..a5d5716f 100644 --- a/lib/screens/chaban_bridge_forecast_screen.dart +++ b/lib/screens/chaban_bridge_forecast_screen.dart @@ -3,7 +3,6 @@ import 'package:chabo/bloc/chaban_bridge_status/chaban_bridge_status_bloc.dart'; import 'package:chabo/bloc/notification/notification_bloc.dart'; import 'package:chabo/bloc/scroll_status/scroll_status_bloc.dart'; import 'package:chabo/cubits/floating_actions_cubit.dart'; -import 'package:chabo/cubits/notification_service_cubit.dart'; import 'package:chabo/custom_widget_state.dart'; import 'package:chabo/misc/no_scaling_animation.dart'; import 'package:chabo/screens/error_screen.dart'; @@ -78,16 +77,15 @@ class _ChabanBridgeForecastScreenState duration: state.durationNotificationValue, ), ); - context - .read() - .state - .computeNotifications( - BlocProvider.of( - context, - ).state.chabanBridgeForecasts, - state, + BlocProvider.of(context).add( + ComputeNotificationEvent( + forecasts: + BlocProvider.of( context, - ); + ).state.chabanBridgeForecasts, + context: context, + ), + ); }, ), ], diff --git a/lib/screens/notification_screen.dart b/lib/screens/notification_screen.dart index 90d210e2..c55b8df2 100644 --- a/lib/screens/notification_screen.dart +++ b/lib/screens/notification_screen.dart @@ -3,7 +3,6 @@ import 'dart:ui'; import 'package:app_settings/app_settings.dart'; import 'package:chabo/bloc/notification/notification_bloc.dart'; import 'package:chabo/cubits/floating_actions_cubit.dart'; -import 'package:chabo/cubits/notification_service_cubit.dart'; import 'package:chabo/custom_properties.dart'; import 'package:chabo/custom_widget_state.dart'; import 'package:chabo/dialogs/days_of_the_week_dialog.dart'; @@ -66,62 +65,51 @@ class _NotificationScreenState extends CustomWidgetState { builder: (context, notificationState) { return Column( children: [ - FutureBuilder( - future: context - .read() - .state - .areNotificationsEnabled(), - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - if (snapshot.hasData && - snapshot.data != null && - !snapshot.data!) { - WidgetsBinding.instance.addPostFrameCallback((_) { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - duration: const Duration(seconds: 10), - showCloseIcon: true, - backgroundColor: - Theme.of(context).colorScheme.error, - content: Column( - children: [ - Text( + Builder(builder: (context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + if (!notificationState.notificationEnabled) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: const Duration(seconds: 10), + showCloseIcon: true, + backgroundColor: + Theme.of(context).colorScheme.error, + content: Column( + children: [ + Text( + AppLocalizations.of(context)! + .notificationNotEnabledMessage, + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onError, + ), + overflow: TextOverflow.visible, + ), + const SizedBox( + height: 20, + ), + ElevatedButton( + onPressed: () => + AppSettings.openNotificationSettings(), + child: Text( AppLocalizations.of(context)! - .notificationNotEnabledMessage, - style: Theme.of(context) - .textTheme - .labelLarge - ?.copyWith( - color: Theme.of(context) - .colorScheme - .onError, - ), - overflow: TextOverflow.visible, - ), - const SizedBox( - height: 20, - ), - ElevatedButton( - onPressed: () => AppSettings - .openNotificationSettings(), - child: Text( - AppLocalizations.of(context)! - .notificationNotEnabledOpenSettings, - ), + .notificationNotEnabledOpenSettings, ), - ], - ), + ), + ], ), - ); - }); + ), + ); } + }); - return const SizedBox.shrink(); - }, - ), + return const SizedBox.shrink(); + }), Column( children: [ _CustomListTile( diff --git a/lib/widgets/forecast/forecast_list_item_widget.dart b/lib/widgets/forecast/forecast_list_item_widget.dart index 10a771e5..8150181c 100644 --- a/lib/widgets/forecast/forecast_list_item_widget.dart +++ b/lib/widgets/forecast/forecast_list_item_widget.dart @@ -117,6 +117,7 @@ class ForecastListItemWidget extends StatelessWidget { ), ), Flexible( + flex: 2, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -168,6 +169,7 @@ class ForecastListItemWidget extends StatelessWidget { ), ), Flexible( + flex: 2, child: Column( mainAxisSize: MainAxisSize.min, children: [