diff --git a/lib/helpers/account_helper.dart b/lib/helpers/account_helper.dart index d21a580..b4d0425 100644 --- a/lib/helpers/account_helper.dart +++ b/lib/helpers/account_helper.dart @@ -71,6 +71,9 @@ class AccountHelper { await preferences.setInt(PreferencesKeyList.USER_ID, userID); _currentUserID = userID; + // Mark the tour as unseen + await preferences.setBool(PreferencesKeyList.IS_TOUR_SEEN, false); + return AuthResult.SUCCESS; } diff --git a/lib/helpers/preferences_helper.dart b/lib/helpers/preferences_helper.dart index 18dffdc..a6f9cb7 100644 --- a/lib/helpers/preferences_helper.dart +++ b/lib/helpers/preferences_helper.dart @@ -14,6 +14,7 @@ enum PreferencesKeyList { FORCE_MOBILE_MODE, SHOW_PERFORMANCE_OVERLAY, PUSH_NOTIFICATIONS_STATUS, + IS_TOUR_SEEN, } const _PreferenceKeysName = { @@ -23,6 +24,7 @@ const _PreferenceKeysName = { PreferencesKeyList.FORCE_MOBILE_MODE: "force_mobile_mode", PreferencesKeyList.SHOW_PERFORMANCE_OVERLAY: "perfs_overlay", PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS: "push_notifications_status", + PreferencesKeyList.IS_TOUR_SEEN: "is_tour_seen", }; class PreferencesHelper { diff --git a/lib/ui/routes/TourRoute.dart b/lib/ui/routes/TourRoute.dart new file mode 100644 index 0000000..0e650eb --- /dev/null +++ b/lib/ui/routes/TourRoute.dart @@ -0,0 +1,228 @@ +import 'package:comunic/helpers/preferences_helper.dart'; +import 'package:comunic/ui/widgets/login_routes_theme.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:flutter/material.dart'; + +/// Tour route +/// +/// The tour is shown when the client sign into the application +/// +/// @author Pierre Hubert + +Future showTour(BuildContext context) async => await Navigator.of(context) + .push(MaterialPageRoute(builder: (c) => TourRoute())); + +class TourRoute extends StatefulWidget { + @override + _TourRouteState createState() => _TourRouteState(); +} + +class _TourRouteState extends State { + List get _list => [ + _FirstPane(), + _PresentationPane( + icon: Icons.group_add, + title: tr("Friends"), + text: tr( + "You can search the people you know and ask them to become your friends!\n\nThis will help you to reach them to exchange information!"), + ), + _PresentationPane( + icon: Icons.question_answer, + title: tr("Conversations"), + text: tr( + "With Comunic, you can have conversations with all your friends.\n\nIt is also possible to make video calls!"), + ), + _PresentationPane( + icon: Icons.group, + title: tr("Groups"), + text: tr( + "You can join groups where people share the same interests as you!\n\nIt is also easy to create your own groups!"), + ), + _PresentationPane( + icon: Icons.lock, + title: tr("Privacy"), + text: tr( + "Your data is YOUR DATA. We will never use it or sell it.\n\nIf you do not trust us, you can always check out our source code to verify it!"), + ), + _LastPane(), + ]; + + @override + Widget build(BuildContext context) => LoginRoutesTheme( + child: Scaffold( + body: SafeArea( + child: DefaultTextStyle( + style: TextStyle(color: Colors.white), + child: DefaultTabController( + length: _list.length, + child: _RouteBody(panes: _list), + ), + ), + ), + ), + ); +} + +class _RouteBody extends StatefulWidget { + final List panes; + + const _RouteBody({Key key, this.panes}) : super(key: key); + + @override + __RouteBodyState createState() => __RouteBodyState(); +} + +class __RouteBodyState extends State<_RouteBody> { + TabController _controller; + + bool get _isLastPane => _controller.index >= widget.panes.length - 1; + + @override + void didUpdateWidget(_RouteBody oldWidget) { + super.didUpdateWidget(oldWidget); + _updateController(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _updateController(); + } + + void _updateController() { + _controller = DefaultTabController.of(context); + _controller.addListener(() => setState(() {})); + } + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Spacer(flex: 1), + Expanded(child: TabBarView(children: widget.panes), flex: 5), + Spacer(flex: 1), + TabPageSelector(selectedColor: Colors.white), + Spacer(flex: 1), + OutlinedButton( + onPressed: _nextOrFinish, + child: Text(!_isLastPane ? tr("Next") : tr("Let's go!")), + ), + Spacer(flex: 1), + ], + ), + ); + + 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(); + } + } + } +} + +class _PresentationPane extends StatelessWidget { + final IconData icon; + final String title; + final String text; + final String actionTitle; + final Function() onActionTap; + + const _PresentationPane({ + Key key, + @required this.icon, + @required this.title, + @required this.text, + this.actionTitle, + this.onActionTap, + }) : assert(icon != null), + assert(title != null), + assert(text != null), + super(key: key); + + bool get _hasAction => actionTitle != null && onActionTap != null; + + @override + Widget build(BuildContext context) => Column( + children: [ + Spacer(flex: 3), + Icon(icon, color: Colors.white, size: 50), + Spacer(flex: 1), + Text( + title, + style: TextStyle(fontSize: 20), + ), + Spacer(flex: 1), + _FixedSizeTextArea(text), + Spacer(flex: 1), + _hasAction + ? OutlinedButton( + onPressed: onActionTap, + child: Text( + actionTitle, + style: TextStyle(color: Colors.white), + ), + ) + : Opacity( + opacity: 0, + child: OutlinedButton( + onPressed: null, + child: Text(""), + ), + ), + Spacer(flex: 3), + ], + ); +} + +class _FirstPane extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Spacer(flex: 3), + Text( + "Comunic", + style: TextStyle(fontSize: 25), + ), + Spacer(flex: 2), + _FixedSizeTextArea(tr( + "Welcome to Comunic, the social network that respect your privacy !")), + Spacer(flex: 1), + _FixedSizeTextArea( + tr("Let us present you some of the features of the network...")), + Spacer(flex: 3), + ], + ); + } +} + +class _LastPane extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Center( + child: Text( + tr("The application is yours"), + style: TextStyle(fontSize: 25), + ), + ); + } +} + +class _FixedSizeTextArea extends StatelessWidget { + final String text; + + const _FixedSizeTextArea(this.text); + + @override + Widget build(BuildContext context) => ConstrainedBox( + constraints: BoxConstraints(maxWidth: 300), + child: Text(text, textAlign: TextAlign.justify), + ); +} diff --git a/lib/ui/widgets/init_widget.dart b/lib/ui/widgets/init_widget.dart index fb173f7..9dbaca1 100644 --- a/lib/ui/widgets/init_widget.dart +++ b/lib/ui/widgets/init_widget.dart @@ -1,15 +1,15 @@ import 'package:comunic/helpers/account_helper.dart'; import 'package:comunic/helpers/events_helper.dart'; -import 'package:comunic/helpers/push_notifications_helper.dart'; +import 'package:comunic/helpers/preferences_helper.dart'; import 'package:comunic/helpers/server_config_helper.dart'; import 'package:comunic/helpers/version_helper.dart'; import 'package:comunic/helpers/websocket_helper.dart'; import 'package:comunic/models/config.dart'; import 'package:comunic/ui/dialogs/deprecation_dialog.dart'; +import 'package:comunic/ui/routes/TourRoute.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/smartphone_route.dart'; import 'package:comunic/ui/routes/main_route/tablet_route.dart'; -import 'package:comunic/ui/routes/push_notifications_route.dart'; import 'package:comunic/ui/routes/welcome_route.dart'; import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/utils/flutter_utils.dart'; @@ -77,10 +77,9 @@ class _InitializeWidgetState extends SafeState { return; } - print("Check push notifications configuration..."); - if (await PushNotificationsHelper.getLocalStatus() == - PushNotificationsStatus.UNDEFINED) - await showInitialPushNotificationsConfiguration(context); + final prefs = await PreferencesHelper.getInstance(); + if (!prefs.getBool(PreferencesKeyList.IS_TOUR_SEEN)) + await showTour(context); print("Attempting WebSocket connection...");