From 116e6ce66db9b9809693578af3e92486f084e6c5 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sat, 17 Apr 2021 19:19:55 +0200 Subject: [PATCH] Can configure push notifications from tour --- lib/ui/routes/TourRoute.dart | 100 ++++++++++++++-- lib/ui/routes/push_notifications_route.dart | 121 ++++++++++++++------ 2 files changed, 173 insertions(+), 48 deletions(-) diff --git a/lib/ui/routes/TourRoute.dart b/lib/ui/routes/TourRoute.dart index d0f2539..7063507 100644 --- a/lib/ui/routes/TourRoute.dart +++ b/lib/ui/routes/TourRoute.dart @@ -1,6 +1,7 @@ import 'package:comunic/helpers/preferences_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/models/user.dart'; +import 'package:comunic/ui/routes/push_notifications_route.dart'; import 'package:comunic/ui/routes/settings/account_image_settings.dart'; import 'package:comunic/ui/widgets/account_image_widget.dart'; import 'package:comunic/ui/widgets/async_screen_widget.dart'; @@ -25,6 +26,8 @@ class TourRoute extends StatefulWidget { class _TourRouteState extends State { final key = GlobalKey(); + final _pushNotificationsKey = + GlobalKey(); User currUser; @@ -37,6 +40,8 @@ class _TourRouteState extends State { List get _list => [ _FirstPane(), + + // Account image _PresentationPane( iconWidget: AccountImageWidget(user: currUser, width: 50), title: tr("Account image"), @@ -49,6 +54,20 @@ class _TourRouteState extends State { await key.currentState.refresh(); }, ), + + // Notifications + _PresentationPane( + icon: Icons.notifications, + title: tr("Push notifications"), + child: (c) => PushNotificationsConfigurationWidget( + key: _pushNotificationsKey, + onConfigured: () => setState(() {}), + onChanged: () => setState(() {}), + ), + canGoNext: _pushNotificationsKey?.currentState?.canSubmit ?? false, + onTapNext: (c) => _pushNotificationsKey.currentState.submit(), + ), + _PresentationPane( icon: Icons.group_add, title: tr("Friends"), @@ -111,6 +130,13 @@ class __RouteBodyState extends State<_RouteBody> { bool get _isLastPane => _controller.index >= widget.panes.length - 1; + _PresentationPane get _currPane => + widget.panes[_controller.index] is _PresentationPane + ? widget.panes[_controller.index] + : null; + + bool get _canGoNext => _currPane?.canGoNext ?? true; + @override void didUpdateWidget(_RouteBody oldWidget) { super.didUpdateWidget(oldWidget); @@ -134,12 +160,17 @@ class __RouteBodyState extends State<_RouteBody> { child: Column( children: [ Spacer(flex: 1), - Expanded(child: TabBarView(children: widget.panes), flex: 5), + Expanded( + child: TabBarView( + children: widget.panes, + physics: NeverScrollableScrollPhysics(), + ), + flex: 10), Spacer(flex: 1), TabPageSelector(selectedColor: Colors.white), Spacer(flex: 1), OutlinedButton( - onPressed: _nextOrFinish, + onPressed: _canGoNext ? _nextOrFinish : null, child: Text(!_isLastPane ? tr("Next") : tr("Let's go!")), ), Spacer(flex: 1), @@ -148,14 +179,19 @@ class __RouteBodyState extends State<_RouteBody> { ); void _nextOrFinish() async { - if (!_controller.indexIsChanging) { - if (!_isLastPane) { - _controller.animateTo(_controller.index + 1); - } else { - (await PreferencesHelper.getInstance()) - .setBool(PreferencesKeyList.IS_TOUR_SEEN, true); - Navigator.of(context).pop(); + if (_controller.indexIsChanging) return; + + if (!_isLastPane) { + // Check if the next click has to be captured + if (_currPane?.onTapNext != null) { + if (!await _currPane.onTapNext(context)) return; } + + _controller.animateTo(_controller.index + 1); + } else { + (await PreferencesHelper.getInstance()) + .setBool(PreferencesKeyList.IS_TOUR_SEEN, true); + Navigator.of(context).pop(); } } } @@ -165,26 +201,34 @@ class _PresentationPane extends StatelessWidget { final Widget iconWidget; final String title; final String text; + final Function(BuildContext) child; final String actionTitle; final Function(BuildContext) onActionTap; + final bool canGoNext; + final Future Function(BuildContext) onTapNext; const _PresentationPane({ Key key, this.icon, this.iconWidget, @required this.title, - @required this.text, + this.text, + this.child, this.actionTitle, this.onActionTap, + this.canGoNext = true, + this.onTapNext, }) : assert(icon != null || iconWidget != null), assert(title != null), - assert(text != null), + assert(text != null || child != null), super(key: key); bool get _hasAction => actionTitle != null && onActionTap != null; @override - Widget build(BuildContext context) => Column( + Widget build(BuildContext context) { + if (text != null) + return Column( children: [ Spacer(flex: 3), icon != null ? Icon(icon, color: Colors.white, size: 50) : iconWidget, @@ -214,6 +258,38 @@ class _PresentationPane extends StatelessWidget { Spacer(flex: 3), ], ); + + return Column( + children: [ + Spacer(flex: 1), + icon != null ? Icon(icon, color: Colors.white, size: 50) : iconWidget, + Spacer(flex: 1), + Text( + title, + style: TextStyle(fontSize: 20), + ), + Spacer(flex: 1), + child(context), + Spacer(flex: 1), + _hasAction + ? OutlinedButton( + onPressed: () => onActionTap(context), + child: Text( + actionTitle, + style: TextStyle(color: Colors.white), + ), + ) + : Opacity( + opacity: 0, + child: OutlinedButton( + onPressed: null, + child: Text(""), + ), + ), + Spacer(flex: 1), + ], + ); + } } class _FirstPane extends StatelessWidget { diff --git a/lib/ui/routes/push_notifications_route.dart b/lib/ui/routes/push_notifications_route.dart index 549e914..fa2fba5 100644 --- a/lib/ui/routes/push_notifications_route.dart +++ b/lib/ui/routes/push_notifications_route.dart @@ -31,6 +31,63 @@ class PushNotificationsConfigurationRoute extends StatefulWidget { class _PushNotificationsConfigurationRouteState extends State { + final _key = GlobalKey(); + + @override + Widget build(BuildContext context) => Material( + textStyle: TextStyle(color: Colors.white), + color: config().splashBackgroundColor, + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Spacer(), + Icon(Icons.notifications_none, color: Colors.white), + Spacer(), + Text( + tr("Comunic can send you notifications to your device when the application is closed if you want."), + textAlign: TextAlign.center, + ), + Spacer(), + PushNotificationsConfigurationWidget( + key: _key, + onConfigured: () => (Navigator.of(context).pop()), + onChanged: () => setState(() {}), + ), + Spacer(), + OutlinedButton( + onPressed: _key?.currentState?.canSubmit ?? false + ? _key.currentState.submit + : null, + child: Text(tr("Configure").toUpperCase()), + style: OutlinedButton.styleFrom(primary: Colors.white)), + Spacer(), + ], + ), + ), + ), + ); +} + +class PushNotificationsConfigurationWidget extends StatefulWidget { + final Function() onConfigured; + final Function() onChanged; + + const PushNotificationsConfigurationWidget({ + Key key, + @required this.onConfigured, + this.onChanged, + }) : super(key: key); + + @override + PushNotificationsConfigurationWidgetState createState() => + PushNotificationsConfigurationWidgetState(); +} + +class PushNotificationsConfigurationWidgetState + extends State { PushNotificationsStatus currStatus; bool get canSubmit => @@ -40,46 +97,36 @@ class _PushNotificationsConfigurationRouteState Future _refresh() async { await PushNotificationsHelper.refreshLocalStatus(); currStatus = await PushNotificationsHelper.getLocalStatus(); + + if (currStatus == PushNotificationsStatus.UNDEFINED && + srvConfig.notificationsPolicy.hasFirebase) + currStatus = PushNotificationsStatus.FIREBASE; } @override Widget build(BuildContext context) { - return Material( - textStyle: TextStyle(color: Colors.white), - color: config().splashBackgroundColor, - child: Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: AsyncScreenWidget( - onReload: _refresh, - onBuild: _buildForm, - errorMessage: tr("Failed to load push notifications settings!"), - ), - ), - )); + return AsyncScreenWidget( + onReload: _refresh, + onBuild: _buildForm, + errorMessage: tr("Failed to load push notifications settings!"), + ); } Widget _buildForm() => ConstrainedBox( constraints: BoxConstraints(maxWidth: 300), child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ - Spacer(), - Icon(Icons.notifications_none, color: Colors.white), - Spacer(), - Text( - tr("Comunic can now send you notifications to your device when the application is closed if you want."), - textAlign: TextAlign.center, - ), - Spacer(), _NotificationOption( - title: tr("Use Google services"), + title: tr("Use Google services (recommended)"), description: tr( "Use the Google Play services to send you notifications. This option is less privacy-friendly, but it will work on most phones and will save your battery life."), option: PushNotificationsStatus.FIREBASE, current: currStatus, available: srvConfig.notificationsPolicy.hasFirebase, - onChanged: (s) => setState(() => currStatus = s), + onChanged: (s) { + setState(() => currStatus = s); + if (widget.onChanged != null) widget.onChanged(); + }, ), _NotificationOption( title: tr("Use independent notifications service"), @@ -89,7 +136,10 @@ class _PushNotificationsConfigurationRouteState current: currStatus, available: srvConfig.notificationsPolicy.hasIndependent && isAndroid, - onChanged: (s) => setState(() => currStatus = s), + onChanged: (s) { + setState(() => currStatus = s); + if (widget.onChanged != null) widget.onChanged(); + }, ), _NotificationOption( title: tr("Do not send notification"), @@ -98,25 +148,22 @@ class _PushNotificationsConfigurationRouteState option: PushNotificationsStatus.DISABLED, current: currStatus, available: true, - onChanged: (s) => setState(() => currStatus = s), + onChanged: (s) { + setState(() => currStatus = s); + if (widget.onChanged != null) widget.onChanged(); + }, ), - Spacer(), - OutlinedButton( - onPressed: canSubmit ? _submit : null, - child: Text(tr("Configure").toUpperCase()), - style: OutlinedButton.styleFrom(primary: Colors.white)), - Spacer(), ], ), ); - Future _submit() async { + Future submit() async { try { String firebaseToken = ""; if (currStatus == await PushNotificationsHelper.getLocalStatus()) { - Navigator.of(context).pop(); - return; + widget.onConfigured(); + return true; } switch (currStatus) { @@ -142,13 +189,15 @@ class _PushNotificationsConfigurationRouteState firebaseToken: firebaseToken); await PushNotificationsHelper.refreshLocalStatus(); - Navigator.of(context).pop(); + widget.onConfigured(); } catch (e, s) { logError(e, s); showAlert( context: context, message: tr("Failed to configure push notifications!")); + return false; } + return true; } }