From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id DCA0A6174C for ; Fri, 20 Nov 2020 14:28:34 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C85BB11E2C for ; Fri, 20 Nov 2020 14:28:04 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 6128D11E1D for ; Fri, 20 Nov 2020 14:27:58 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 1D69043CE6 for ; Fri, 20 Nov 2020 14:27:58 +0100 (CET) From: Aaron Lauterer To: pve-devel@lists.proxmox.com Date: Fri, 20 Nov 2020 14:27:53 +0100 Message-Id: <20201120132753.20966-4-a.lauterer@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201120132753.20966-1-a.lauterer@proxmox.com> References: <20201120132753.20966-1-a.lauterer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.005 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-Mailman-Approved-At: Fri, 20 Nov 2020 14:29:40 +0100 Subject: [pve-devel] [PATCH pve_flutter_frontend v2 3/3] Add first welcome screen X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 20 Nov 2020 13:28:34 -0000 Signed-off-by: Aaron Lauterer --- passing through the page controller to the DotIndicator class is necessary as it needs to be a listenable. v1 -> v2: * change welcome screens * no color change * buttons instead of text links * use async where needed * code cleanup in several places * use SingleChildScrollView for content heavy welcome screens .../ssl_validate/login_manager_screen.png | Bin 0 -> 20389 bytes .../login_manager_screen_settings.png | Bin 0 -> 37362 bytes lib/main.dart | 14 +- lib/utils/dot_indicator.dart | 108 +++++++++++ .../pve_welcome_common.dart | 67 +++++++ .../firstWelcomeScreen/pve_welcome_faq.dart | 59 ++++++ .../firstWelcomeScreen/pve_welcome_last.dart | 72 ++++++++ .../firstWelcomeScreen/pve_welcome_logo.dart | 33 ++++ .../pve_welcome_ssl_hint.dart | 50 +++++ lib/widgets/pve_first_welcome_screen.dart | 171 ++++++++++++++++++ pubspec.yaml | 1 + 11 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 assets/images/ssl_validate/login_manager_screen.png create mode 100644 assets/images/ssl_validate/login_manager_screen_settings.png create mode 100644 lib/utils/dot_indicator.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_common.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_last.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart create mode 100644 lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart create mode 100644 lib/widgets/pve_first_welcome_screen.dart diff --git a/assets/images/ssl_validate/login_manager_screen.png b/assets/images/ssl_validate/login_manager_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..b6668cc8f5b3fcc742ee2a6ce019ea4318fda92a GIT binary patch literal 20389 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 Wyv;~hkMo}ZKjI=X!X<*AzWyH_xa}4I literal 0 HcmV?d00001 diff --git a/lib/main.dart b/lib/main.dart index 8cd6c36..57ad39c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:pve_flutter_frontend/widgets/pve_first_welcome_screen.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:proxmox_login_manager/proxmox_login_manager.dart'; import 'package:pve_flutter_frontend/bloc/pve_authentication_bloc.dart'; import 'package:pve_flutter_frontend/bloc/pve_cluster_status_bloc.dart'; @@ -47,6 +49,7 @@ void main() async { FlutterError.dumpErrorToConsole(details); if (kReleaseMode) ProxmoxGlobalErrorBloc().addError(details.exception); }; + SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); Provider.debugCheckInvalidValueType = null; runApp( @@ -60,6 +63,7 @@ void main() async { ], child: MyApp( authbloc: authBloc, + sharedPreferences: sharedPreferences, ), ), ); @@ -67,9 +71,12 @@ void main() async { class MyApp extends StatelessWidget { final PveAuthenticationBloc authbloc; + final SharedPreferences sharedPreferences; final GlobalKey navigatorKey = GlobalKey(); - MyApp({Key key, this.authbloc}) : super(key: key); + MyApp({Key key, this.authbloc, this.sharedPreferences}) + : assert(sharedPreferences != null), + super(key: key); @override Widget build(BuildContext context) { @@ -142,6 +149,11 @@ class MyApp extends StatelessWidget { builder: (context) => PveSplashScreen(), ); } + if (sharedPreferences.getBool('showWelcomeScreen') ?? true) { + return MaterialPageRoute( + builder: (context) => PveWelcome(), + ); + } if (authbloc.state.value is Unauthenticated || context.name == '/login') { diff --git a/lib/utils/dot_indicator.dart b/lib/utils/dot_indicator.dart new file mode 100644 index 0000000..e4d3027 --- /dev/null +++ b/lib/utils/dot_indicator.dart @@ -0,0 +1,108 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'dart:math'; + +class Dot extends StatelessWidget { + final double dotSpacing; + final double dotSize; + final double zoom; + final double shadowBlurRadius; + final double shadowSpreadRadius; + final Color color; + final int index; + final void Function(int index) onTap; + + const Dot({ + Key key, + this.dotSpacing, + this.dotSize, + this.zoom, + this.shadowBlurRadius, + this.shadowSpreadRadius, + this.color, + this.index, + this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: dotSpacing, + child: Center( + child: Container( + width: dotSize * zoom, + height: dotSize * zoom, + child: GestureDetector( + onTap: () => onTap(index), + ), + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.white.withOpacity(0.72), + blurRadius: shadowBlurRadius, + spreadRadius: shadowSpreadRadius, + offset: Offset(0.0, 0.0)) + ], + ), + ), + ), + ); + } +} + +class DotIndicator extends AnimatedWidget { + DotIndicator({ + this.controller, + this.itemCount, + this.onPageSelected, + this.color: Colors.white, + this.page, + this.initialPage, + }) : super(listenable: controller); + + final PageController controller; + final double page; + final double initialPage; + + final int itemCount; + + final ValueChanged onPageSelected; + final Color color; + + static const double _dotSize = 8.0; + static const double _maxZoom = 1.2; + static const double _dotSpacing = 25.0; + + Widget _buildDot(int index) { + double selectedness = Curves.easeOut.transform( + max( + 0.0, + 1.0 - ((controller.page ?? controller.initialPage) - index).abs(), + ), + ); + double zoom = 1.0 + (_maxZoom - 1.0) * selectedness; + double shadowBlurRadius = 4.0 * selectedness; + double shadowSpreadRadius = 1.0 * selectedness; + return new Dot( + color: color, + shadowBlurRadius: shadowBlurRadius, + shadowSpreadRadius: shadowSpreadRadius, + dotSize: _dotSize, + dotSpacing: _dotSpacing, + zoom: zoom, + index: index, + onTap: onPageSelected, + ); + } + + Widget build(BuildContext contect) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + itemCount, + _buildDot, + )); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_common.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_common.dart new file mode 100644 index 0000000..5f08861 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_common.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; + +class PveQuestion extends StatelessWidget { + final String text; + + const PveQuestion({ + Key key, + this.text, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(10.0, 10.0, 5.0, 0.0), + child: Text( + text, + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ); + } +} + +class PveAnswer extends StatelessWidget { + final String text; + final List spans; + + const PveAnswer({ + Key key, + this.text, + this.spans, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(20.0, 5.0, 5.0, 5.0), + child: RichText( + text: TextSpan( + text: text, + style: DefaultTextStyle.of(context).style, + children: spans, + ), + ), + ); + } +} + +class PveWelcomePageContent extends StatelessWidget { + final Widget child; + const PveWelcomePageContent({ + Key key, + this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(15.0), + child: child, + )), + ); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart new file mode 100644 index 0000000..6612819 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_common.dart'; +import 'package:pve_flutter_frontend/utils/promox_colors.dart'; + +// FAQ +class PveWelcomePageFAQ extends StatelessWidget { + const PveWelcomePageFAQ({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return PveWelcomePageContent( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PveQuestion( + text: + "How do I connect if I am not using the default port 8006?"), + PveAnswer( + text: + "Add the port at the end, separated by a colon. For the default https port add 443."), + PveAnswer( + text: "For example: 192.168.1.10", + spans: [ + TextSpan( + text: ":443", + style: TextStyle( + fontWeight: FontWeight.bold, fontStyle: FontStyle.italic)) + ], + ), + PveQuestion( + text: "What about remote consoles?", + ), + PveAnswer( + text: + "Spice is currently supported. We plan to integrate VNC in the future."), + PveQuestion(text: "Which Spice client works?"), + PveAnswer( + text: + 'Currently only the following 3rd party Spice client works:'), + Center( + child: OutlineButton( + onPressed: () => { + launch( + 'https://play.google.com/store/apps/details?id=com.undatech.opaque') + }, + child: Text('Opague'), + borderSide: BorderSide(color: ProxmoxColors.supportGrey), + textColor: Colors.white, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_last.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_last.dart new file mode 100644 index 0000000..861eac9 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_last.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:pve_flutter_frontend/utils/promox_colors.dart'; + +// goodbye +class PveWelcomePageLast extends StatelessWidget { + const PveWelcomePageLast({Key key, this.onDone}) : super(key: key); + + final VoidCallback onDone; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Enjoy the app"), + Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.emoji_people_rounded, + color: Colors.white, + size: 70, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: RaisedButton( + onPressed: () => {onDone()}, + color: ProxmoxColors.orange, + textColor: Colors.white, + child: Text("Start"), + ), + ), + ], + ), + ), + Column( + children: [ + Text( + 'If you have suggestions or experience any problems, please contact us via', + textAlign: TextAlign.center, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OutlineButton( + onPressed: () => {launch('https://forum.proxmox.com')}, + child: Text('Forum'), + borderSide: BorderSide(color: ProxmoxColors.supportGrey), + textColor: Colors.white, + ), + OutlineButton( + onPressed: () => { + launch( + 'https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-user') + }, + child: Text('User Mailing List'), + borderSide: BorderSide(color: ProxmoxColors.supportGrey), + textColor: Colors.white, + ), + ], + ), + ], + ), + ], + ); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart new file mode 100644 index 0000000..3aee19a --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_logo.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +// Big Logo +class PveWelcomePageLogo extends StatelessWidget { + const PveWelcomePageLogo({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(120.0, 0.0, 120.0, 30.0), + child: Image.asset( + 'assets/images/Proxmox-logo-symbol-white-orange.png', + alignment: Alignment.center, + ), + ), + FittedBox( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Proxmox Virtual Environment', + style: TextStyle(fontSize: 26), + ), + ), + ), + ]); + } +} diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart new file mode 100644 index 0000000..380c772 --- /dev/null +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_common.dart'; + +// disable ssl validation hint +class PveWelcomePageSSLValidation extends StatelessWidget { + const PveWelcomePageSSLValidation({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return PveWelcomePageContent( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PveQuestion( + text: "Are you using a self signed certificate?", + ), + PveAnswer( + text: "Disable SSL Validation in the Login Manager settings."), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.white, width: 0.5)), + child: Image.asset( + 'assets/images/ssl_validate/login_manager_screen.png', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.white, width: 0.5)), + child: Image.asset( + 'assets/images/ssl_validate/login_manager_screen_settings.png', + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/widgets/pve_first_welcome_screen.dart b/lib/widgets/pve_first_welcome_screen.dart new file mode 100644 index 0000000..4cfe563 --- /dev/null +++ b/lib/widgets/pve_first_welcome_screen.dart @@ -0,0 +1,171 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:pve_flutter_frontend/utils/dot_indicator.dart'; +import 'package:pve_flutter_frontend/utils/promox_colors.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_logo.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_faq.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_ssl_hint.dart'; +import 'package:pve_flutter_frontend/widgets/firstWelcomeScreen/pve_welcome_last.dart'; + +class PveWelcome extends StatefulWidget { + @override + _PveWelcomeState createState() => _PveWelcomeState(); +} + +class _PveWelcomeState extends State with TickerProviderStateMixin { + PageController _controller; + SharedPreferences _sharedPreferences; + + final List _pages = [ + PveWelcomePageLogo(), + PveWelcomePageSSLValidation(), + PveWelcomePageFAQ(), + ]; + + static const Duration _pageChangeDuration = Duration(milliseconds: 150); + static const Curve _pageChangeCurve = Curves.easeInOut; + + bool _isLast = false; + bool _isFirst = true; + + final _buttonTextColor = Colors.white; + final _buttonDisabledTextColor = Colors.white30; + + Future _getPrefs() async { + _sharedPreferences = await SharedPreferences.getInstance(); + } + + Future _prefs; + + @override + void initState() { + super.initState(); + + _prefs = _getPrefs(); + _controller = PageController(); + + // add last page here so we can define the callback for the start button + _pages.add(PveWelcomePageLast(onDone: () { + skipDone(); + })); + + _controller.addListener(() { + setState(() { + _isLast = _controller.page.floor() == _pages.length - 1; + _isFirst = _controller.page.floor() == 0; + }); + }); + } + + Future skipDone() async { + await _sharedPreferences.setBool('showWelcomeScreen', false); + Navigator.pushReplacementNamed(context, '/'); + } + + Widget nextDoneButton() { + if (_isLast) { + return FlatButton( + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + child: Text( + "Done", + ), + onPressed: () { + skipDone(); + }, + ); + } else { + return FlatButton( + child: Text("Next"), + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + onPressed: () { + _controller.nextPage( + duration: _pageChangeDuration, curve: _pageChangeCurve); + }, + ); + } + } + + Widget skipPrevButton() { + if (_isFirst) { + return FlatButton( + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + onPressed: () { + skipDone(); + }, + child: Text( + 'Skip', + ), + ); + } else { + return FlatButton( + textColor: _buttonTextColor, + disabledTextColor: _buttonDisabledTextColor, + child: Text( + "Prev", + ), + onPressed: () { + _controller.previousPage( + duration: _pageChangeDuration, curve: _pageChangeCurve); + }, + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: ProxmoxColors.supportBlue, + body: DefaultTextStyle( + style: TextStyle(color: Colors.white, fontSize: 18), + child: FutureBuilder( + future: _prefs, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Column( + children: [ + Expanded( + child: PageView.builder( + controller: _controller, + itemCount: _pages.length, + itemBuilder: (context, index) { + return _pages[index]; + }, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + skipPrevButton(), + DotIndicator( + controller: _controller, + itemCount: _pages.length, + onPageSelected: (int page) { + _controller.animateToPage(page, + duration: _pageChangeDuration, + curve: _pageChangeCurve); + }, + ), + nextDoneButton(), + ], + ), + ], + ); + } else { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white70), + ), + ); + } + }, + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index cb5bcdf..a605eeb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,6 +61,7 @@ flutter: - assets/images/Proxmox_logo_white_orange_800.png - assets/images/Proxmox-logo-symbol-white-orange.png - assets/images/proxmox_logo_icon_white.png + - assets/images/ssl_validate/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. -- 2.20.1