From 6613949d127680f79a9497927003ad341e0060f0 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 30 Dec 2020 15:13:25 +0100 Subject: [PATCH] update --- assets/fonts/BrandIcons.ttf | Bin 7308 -> 7556 bytes assets/images/icon/check.svg | 4 + assets/images/onboarding/logos_line.png | Bin 0 -> 5046 bytes assets/images/onboarding/onboarding1.png | Bin 0 -> 13674 bytes assets/images/onboarding/onboarding2.png | Bin 0 -> 16832 bytes lib/config/bloc_config.dart | 2 + lib/config/brand_colors.dart | 9 +- lib/config/text_themes.dart | 18 +- .../initializing/cloudflare_form_cubit.dart | 0 .../forms/initializing/domain_form_cubit.dart | 0 .../initializing/hetzner_form_cubit.dart | 44 ++ .../forms/initializing/user_form_cubit.dart | 0 .../initializing/initializing_cubit.dart | 30 + .../initializing/initializing_state.dart | 26 + .../cubit/providers/providers_cubit.dart | 11 +- .../cubit/providers/providers_state.dart | 14 + lib/logic/models/config.dart | 36 ++ .../components/brand_button/brand_button.dart | 2 +- lib/ui/components/brand_card/brand_card.dart | 49 +- .../components/brand_icons/brand_icons.dart | 2 + .../components/progress_bar/progress_bar.dart | 105 ++++ lib/ui/pages/initializing/initializing.dart | 559 ++++++++++++------ lib/ui/pages/more/more.dart | 6 + lib/ui/pages/onboarding/onboarding.dart | 141 +++-- lib/ui/pages/onboarding/onboarding.full.dart | 434 +++++++------- lib/ui/pages/providers/providers.dart | 6 +- .../settings/{setting.dart => settings.dart} | 0 lib/ui/pages/rootRoute.dart | 14 +- lib/ui/pages/services/services.dart | 6 +- pubspec.lock | 37 +- pubspec.yaml | 3 + 31 files changed, 1093 insertions(+), 465 deletions(-) create mode 100644 assets/images/icon/check.svg create mode 100644 assets/images/onboarding/logos_line.png create mode 100644 assets/images/onboarding/onboarding1.png create mode 100644 assets/images/onboarding/onboarding2.png create mode 100644 lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart create mode 100644 lib/logic/cubit/forms/initializing/domain_form_cubit.dart create mode 100644 lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart create mode 100644 lib/logic/cubit/forms/initializing/user_form_cubit.dart create mode 100644 lib/logic/cubit/initializing/initializing_cubit.dart create mode 100644 lib/logic/cubit/initializing/initializing_state.dart create mode 100644 lib/logic/models/config.dart create mode 100644 lib/ui/components/progress_bar/progress_bar.dart rename lib/ui/pages/providers/settings/{setting.dart => settings.dart} (100%) diff --git a/assets/fonts/BrandIcons.ttf b/assets/fonts/BrandIcons.ttf index 7c9759696eab2556c10d66864de6b7a1311a12cd..bf40d0094279e4922f0ff0db67c9c8e90e3216f0 100644 GIT binary patch delta 771 zcmXw#Ur5tY6vxlKzrXu!?zidO);6YYW6S>7n*Y|EjwB0I4D_LqqHJq(piaq=+6aB{ zDI|>F0*Q#~smIEf0v~!Q!l!%`HF%!`J5kf(gJ@%x6=#JT2?50N?=W%40$9URKg zggQAppGid{?NI=JA9dSYYI%X2hePZ;Q8yP-bJ^Yc@;Jb$H2}`JFuzz=JpF#g1K`<0 z!|}r%f5Hv~Q{>Lid~QmO{{SOb!3ykKn>YT)>Yr+g|Hh4?mVvS=E;eV^yyRFD=+)M8 zO#?gtu9ls{sGz9~JfzX7)Fx>~){3-6aDfZF+GBcFXhe^w?a|-b0X3-j52$N9CXLz` zF0(2FR1h#=#1k3p5Ym8wN11U1t!O z*`Wi>jGxN@W&{|8r^H3p$RWMW$y}2A$OnX&@JTq4Tv9>0FI(g%hOiP)VgTE}_}Ahp z2T*;>J)%``LkA4ud!-CxsZ%G zP2rL0rQXqzp?FW?Z++A=Je~=sH*Ja%Otg;W`b(3M?U%+D3$bgv?lY7)v=et+6f*g2 KW=0EpCjJ5P)t$Zo delta 523 zcmXw#ODIHP7>3{foHH|Lm~k0~lNpRyxJ)P!nIbzI3oP7<)3{8UF+v$D7P6AY91AuU zlq_tdW}(T#QnRsZQ{ZVkRo&$VqmhW+S9Xu* z2fmOio3z2*d))W0nV*cVyvY6JRY-hGkN4b01$#4`QcqnX>w!(k*~w3uL!kr|Oi(f> z+muY35tFJc8Z>x~eW`)M*28OwxhUcw{;tqaY&^)pRXSl3u#-9k{H&QLKrU&%fcMQp z0eF^fa=^1i0`P3H00pFO0eG)O0NyJV1i*{*H#wkU_Vr_*m9aJUC@sjUJR~QTv}MGy oVLh?I=CvKDjq0U6X-}UyET`79J%_J=xs7vgz47V|7`5eNzqW{PjQ{`u diff --git a/assets/images/icon/check.svg b/assets/images/icon/check.svg new file mode 100644 index 00000000..876185df --- /dev/null +++ b/assets/images/icon/check.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/onboarding/logos_line.png b/assets/images/onboarding/logos_line.png new file mode 100644 index 0000000000000000000000000000000000000000..eb8ecf82ce63fef99f9dbff7510d0bacad344b13 GIT binary patch literal 5046 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 R4?J*z!PC{xWt~$(69DfnYr+5k literal 0 HcmV?d00001 diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index ac0d9874..414c2418 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; +import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; @@ -20,6 +21,7 @@ class BlocAndProviderConfig extends StatelessWidget { return MultiProvider( providers: [ BlocProvider(create: (_) => AppSettingsCubit(isDarkModeOn: isDark)), + BlocProvider(create: (_) => InitializingCubit()), BlocProvider(create: (_) => ServicesCubit()), BlocProvider(create: (_) => ProvidersCubit()), BlocProvider(create: (_) => UsersCubit()), diff --git a/lib/config/brand_colors.dart b/lib/config/brand_colors.dart index 1ca3f87f..0bd0c44c 100644 --- a/lib/config/brand_colors.dart +++ b/lib/config/brand_colors.dart @@ -30,7 +30,6 @@ class BrandColors { /// ![](https://www.colorhexa.com/0F8849.png) static const Color green2 = Color(0xFF0F8849); - static get navBackgroundLight => white.withOpacity(0.8); static get navBackgroundDark => black.withOpacity(0.8); @@ -42,12 +41,16 @@ class BrandColors { Color(0xFF093CEF), Color(0xFF14A1CB), ]; + + static const List progressGradientColors = [ + Color(0xFF093CEF), + Color(0xFF14A1CB), + ]; static const List warningGradientColors = [ Color(0xFFEF4E09), Color(0xFFEFD135), ]; - static const primary = blue; static const headlineColor = black; static const inactive = gray2; @@ -58,6 +61,4 @@ class BrandColors { static const textColor2 = gray1; static const dividerColor = gray5; static const warning = red; - - } diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart index 5be2aa5c..acc9425f 100644 --- a/lib/config/text_themes.dart +++ b/lib/config/text_themes.dart @@ -18,7 +18,7 @@ final headline1Style = GoogleFonts.inter( ); final headline2Style = GoogleFonts.inter( - fontSize: 24, + fontSize: 30, fontWeight: NamedFontWeight.extraBold, color: BrandColors.headlineColor, ); @@ -41,3 +41,19 @@ final body2Style = defaultTextStyle.copyWith( ); final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45); + +final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue); + +final progressTextStyleLight = GoogleFonts.inter( + textStyle: TextStyle( + fontSize: 13, + color: BrandColors.textColor1, + ), +); + +final progressTextStyleDark = GoogleFonts.inter( + textStyle: TextStyle( + fontSize: 13, + color: BrandColors.white, + ), +); diff --git a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/logic/cubit/forms/initializing/domain_form_cubit.dart b/lib/logic/cubit/forms/initializing/domain_form_cubit.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart new file mode 100644 index 00000000..0a62d715 --- /dev/null +++ b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart @@ -0,0 +1,44 @@ +import 'dart:async'; + +import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart'; + +class HetznerFormCubit extends FormCubit { + HetznerFormCubit(this.initializingCubit) { + var regExp = RegExp(r"\s+|[-!$%^&*()_@+|~=`{}\[\]:" ";<>?,.\/]"); + apiKey = FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('required'), + ValidationModel( + (s) => regExp.hasMatch(s), 'invalid key format'), + LegnthStringValidation(11, 'length is [] shoud be 11') + ], + ); + + super.setFields([apiKey]); + } + + @override + FutureOr onSubmit() async { + print(apiKey.state.value); + await Future.delayed(const Duration(milliseconds: 300)); + // initializingCubit.setHetznerKey(apiKey.state.value); + } + + final InitializingCubit initializingCubit; + + FieldCubit apiKey; +} + +class LegnthStringValidation extends ValidationModel { + LegnthStringValidation(int length, String errorText) + : super((n) => n.length != length, errorText); + + @override + String check(String val) { + var length = val.length; + var errorMassage = this.errorMassage.replaceAll("[]", length.toString()); + return test(val) ? errorMassage : null; + } +} diff --git a/lib/logic/cubit/forms/initializing/user_form_cubit.dart b/lib/logic/cubit/forms/initializing/user_form_cubit.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/logic/cubit/initializing/initializing_cubit.dart b/lib/logic/cubit/initializing/initializing_cubit.dart new file mode 100644 index 00000000..0fe6bcd9 --- /dev/null +++ b/lib/logic/cubit/initializing/initializing_cubit.dart @@ -0,0 +1,30 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/logic/models/config.dart'; +import 'package:selfprivacy/logic/models/user.dart'; + +part 'initializing_state.dart'; + +class InitializingCubit extends Cubit { + InitializingCubit() : super(InitialInitializingState()); + + void setHetznerKey(String key) { + var newCofig = state.appConfig.copyWith(hatzner: key); + emit(InitializingState(newCofig)); + } + + void setCloudFlare(String cloudFlareKey) { + var newCofig = state.appConfig.copyWith(cloudFlare: cloudFlareKey); + emit(InitializingState(newCofig)); + } + + void setDomain(String domain) { + var newCofig = state.appConfig.copyWith(domain: domain); + emit(InitializingState(newCofig)); + } + + void setRootUser(User rootUser) { + var newCofig = state.appConfig.copyWith(rootUser: rootUser); + emit(InitializingState(newCofig)); + } +} diff --git a/lib/logic/cubit/initializing/initializing_state.dart b/lib/logic/cubit/initializing/initializing_state.dart new file mode 100644 index 00000000..7827987d --- /dev/null +++ b/lib/logic/cubit/initializing/initializing_state.dart @@ -0,0 +1,26 @@ +part of 'initializing_cubit.dart'; + +class InitializingState extends Equatable { + const InitializingState(this.appConfig); + + final AppConfig appConfig; + + @override + List get props => [appConfig]; + + bool get isHatznerFilled => appConfig.hatzner != null; + bool get isCloudFlareFilled => appConfig.cloudFlare != null; + bool get isDomainFilled => appConfig.domain != null; + bool get isUserFilled => appConfig.rootUser != null; + + bool get isFullyInitilized => _fulfilementList.every((el) => el); + + int get progress => _fulfilementList.where((el) => el).length; + + List get _fulfilementList => + [isHatznerFilled, isCloudFlareFilled, isDomainFilled, isUserFilled]; +} + +class InitialInitializingState extends InitializingState { + InitialInitializingState() : super(AppConfig.empty()); +} diff --git a/lib/logic/cubit/providers/providers_cubit.dart b/lib/logic/cubit/providers/providers_cubit.dart index 88fee648..9482a194 100644 --- a/lib/logic/cubit/providers/providers_cubit.dart +++ b/lib/logic/cubit/providers/providers_cubit.dart @@ -9,7 +9,7 @@ export 'package:provider/provider.dart'; part 'providers_state.dart'; class ProvidersCubit extends Cubit { - ProvidersCubit() : super(ProvidersState(all)); + ProvidersCubit() : super(InitialProviderState()); void connect(ProviderModel provider) { var newState = state.updateElement(provider, StateType.stable); @@ -17,11 +17,4 @@ class ProvidersCubit extends Cubit { } } -final all = ProviderType.values - .map( - (type) => ProviderModel( - state: StateType.uninitialized, - type: type, - ), - ) - .toList(); + diff --git a/lib/logic/cubit/providers/providers_state.dart b/lib/logic/cubit/providers/providers_state.dart index 8e85d62a..8297699d 100644 --- a/lib/logic/cubit/providers/providers_state.dart +++ b/lib/logic/cubit/providers/providers_state.dart @@ -23,3 +23,17 @@ class ProvidersState extends Equatable { @override List get props => all; } + +class InitialProviderState extends ProvidersState { + InitialProviderState() + : super( + ProviderType.values + .map( + (type) => ProviderModel( + state: StateType.uninitialized, + type: type, + ), + ) + .toList(), + ); +} diff --git a/lib/logic/models/config.dart b/lib/logic/models/config.dart new file mode 100644 index 00000000..82552241 --- /dev/null +++ b/lib/logic/models/config.dart @@ -0,0 +1,36 @@ +import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/logic/models/user.dart'; + +class AppConfig extends Equatable { + const AppConfig({ + this.hatzner, + this.cloudFlare, + this.domain, + this.rootUser, + }); + + final String hatzner; + final String cloudFlare; + final String domain; + final User rootUser; + + factory AppConfig.empty() { + return AppConfig(); + } + + AppConfig copyWith({ + hatzner, + cloudFlare, + domain, + rootUser, + }) => + AppConfig( + hatzner: hatzner ?? this.hatzner, + cloudFlare: cloudFlare ?? this.cloudFlare, + domain: domain ?? this.domain, + rootUser: rootUser ?? this.rootUser, + ); + + @override + List get props => [hatzner, cloudFlare, domain, rootUser]; +} diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart index d410a4d4..163e3f1b 100644 --- a/lib/ui/components/brand_button/brand_button.dart +++ b/lib/ui/components/brand_button/brand_button.dart @@ -117,7 +117,7 @@ class _RisedButton extends StatelessWidget { color: BrandColors.white, fontSize: 16, fontWeight: FontWeight.bold, - height: 1.5, + height: 1, ), ), ), diff --git a/lib/ui/components/brand_card/brand_card.dart b/lib/ui/components/brand_card/brand_card.dart index 632ebd95..6908b363 100644 --- a/lib/ui/components/brand_card/brand_card.dart +++ b/lib/ui/components/brand_card/brand_card.dart @@ -1,15 +1,21 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/utils/extensions/elevation_extension.dart'; class BrandCard extends StatelessWidget { - const BrandCard({Key key, this.child}) : super(key: key); + const BrandCard({ + Key key, + this.child, + this.isBlocked = false, + }) : super(key: key); final Widget child; + final bool isBlocked; @override Widget build(BuildContext context) { - return Container( + Widget res = Container( margin: EdgeInsets.only(bottom: 30), decoration: BoxDecoration( color: Theme.of(context).brightness == Brightness.dark @@ -23,5 +29,44 @@ class BrandCard extends StatelessWidget { ), child: child, ); + + if (!isBlocked) { + return res; + } + + return IgnorePointer( + child: Stack( + children: [ + ColorFiltered( + colorFilter: ColorFilter.mode( + Colors.white, + BlendMode.saturation, + ), + child: res, + ), + Positioned( + top: 0, + left: 0, + right: 0, + bottom: 0, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.white.withOpacity(0.8), + ), + padding: EdgeInsets.all(10), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + BrandText.h3('Blocked'), + BrandText.h4('finish initializing first') + ], + ), + ), + ) + ], + ), + ); } } diff --git a/lib/ui/components/brand_icons/brand_icons.dart b/lib/ui/components/brand_icons/brand_icons.dart index fa198d3a..06bad438 100644 --- a/lib/ui/components/brand_icons/brand_icons.dart +++ b/lib/ui/components/brand_icons/brand_icons.dart @@ -37,6 +37,8 @@ class BrandIcons { IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData refresh_1 = IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData check = + IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData refresh = IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData settings = diff --git a/lib/ui/components/progress_bar/progress_bar.dart b/lib/ui/components/progress_bar/progress_bar.dart new file mode 100644 index 00000000..ed3fc7ea --- /dev/null +++ b/lib/ui/components/progress_bar/progress_bar.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; +import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; + +class ProgressBar extends StatefulWidget { + ProgressBar({ + Key key, + @required this.steps, + @required this.activeIndex, + }) : super(key: key); + + final int activeIndex; + + final List steps; + + @override + _ProgressBarState createState() => _ProgressBarState(); +} + +class _ProgressBarState extends State { + @override + Widget build(BuildContext context) { + double progress = 1 / widget.steps.length * (widget.activeIndex + 0.3); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BrandText.h4('Progress'), + SizedBox(height: 10), + Row( + children: widget.steps + .asMap() + .map( + (i, step) { + var isActive = i == widget.activeIndex; + var checked = i < widget.activeIndex; + + var isDark = + context.watch().state.isDarkModeOn; + var style = + isDark ? progressTextStyleDark : progressTextStyleLight; + + style = isActive + ? style.copyWith(fontWeight: FontWeight.w700) + : style; + return MapEntry( + i, + Expanded( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: progressTextStyleLight, + children: [ + checked + ? WidgetSpan( + child: Padding( + padding: const EdgeInsets.only( + bottom: 1, right: 2), + child: Icon(BrandIcons.check, size: 14), + )) + : TextSpan(text: '${i + 1}.', style: style), + TextSpan(text: step, style: style) + ], + ), + ), + )); + }, + ) + .values + .toList(), + ), + SizedBox(height: 3), + Container( + alignment: Alignment.centerLeft, + decoration: BoxDecoration( + color: BrandColors.gray4, + borderRadius: BorderRadius.circular(5), + ), + child: LayoutBuilder( + builder: (_, constraints) { + return AnimatedContainer( + width: constraints.maxWidth * progress, + height: 5, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: BrandColors.stableGradientColors, + ), + ), + duration: Duration( + milliseconds: 300, + ), + ); + }, + ), + ) + ], + ); + } +} diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index b15e8a7e..4e8448ee 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -1,55 +1,153 @@ +import 'package:cubit_form/cubit_form.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/logic/cubit/forms/initializing/hetzner_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; -import 'package:selfprivacy/logic/models/provider.dart'; +import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; +import 'package:selfprivacy/ui/pages/rootRoute.dart'; +import 'package:selfprivacy/utils/route_transitions/basic.dart'; -class InitializingPage extends StatelessWidget { +class InitializingPage extends StatefulWidget { const InitializingPage({Key key}) : super(key: key); + @override + _InitializingPageState createState() => _InitializingPageState(); +} + +class _InitializingPageState extends State { + PageController pageController = PageController(viewportFraction: 1); + + @override + void dispose() { + pageController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - var cubit = context.watch(); - var connected = cubit.state.connected; - var uninitialized = cubit.state.uninitialized; - return Scaffold( - body: ListView( - padding: brandPagePadding1, - children: [ - BrandText.h4('Начало'), - BrandText.h1('SelfPrivacy'), - SizedBox( - height: 10, - ), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: - 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', - style: body2Style, - ), - BrandSpanButton.link( - text: 'selfprivacy.org/start', - urlString: 'https://selfprivacy.org/start', - ), - ], + var cubit = context.watch(); + + return SafeArea( + child: Scaffold( + body: ListView( + children: [ + Padding( + padding: brandPagePadding1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BrandText.h4('Начало'), + BrandText.h1('SelfPrivacy'), + SizedBox( + height: 10, + ), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: + 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', + style: body2Style, + ), + BrandSpanButton.link( + text: + 'https://selfprivacy.org/posts/getting_started/', + urlString: + 'https://selfprivacy.org/posts/getting_started/', + ), + ], + ), + ), + SizedBox(height: 10), + ProgressBar( + steps: ['Server', 'DNS', 'Domain', 'User'], + // progress: cubit.state.progress, + activeIndex: cubit.state.progress, + ), + SizedBox(height: 20), + ], + ), ), - ), - SizedBox(height: 50), - ...connected.map((p) => getCard(context, p)).toList(), - ...uninitialized.map((p) => getCard(context, p)).toList(), - ], + Container( + height: 500, + child: PageView( + // physics: NeverScrollableScrollPhysics(), + controller: pageController, + children: [ + _addCard(_stepOne(cubit)), + _addCard(_stepTwo(cubit)), + _addCard(_stepThree(cubit)), + _addCard(_stepFour(cubit)), + ], + ), + ), + BrandButton.text(title: 'Настрою потом', onPressed: _goToMainPage), + SizedBox(height: 30), + ], + ), ), ); } + void _goToMainPage() { + Navigator.of(context).pushAndRemoveUntil( + materialRoute(RootPage()), + (predicate) => predicate == null, + ); + } + + Widget _stepOne(InitializingCubit initializingCubit) { + return BlocProvider( + create: (context) => HetznerFormCubit(initializingCubit), + child: Builder(builder: (context) { + var formCubit = context.watch(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Spacer(), + Image.asset('assets/images/logos/hetzner.png'), + SizedBox(height: 10), + BrandText.h2('Подключите сервер Hetzner'), + SizedBox(height: 10), + BrandText.body2( + 'Здесь будут жить наши данные и SelfPrivacy-сервисы'), + Spacer(), + CubitFormTextField( + formFieldCubit: formCubit.apiKey, + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + scrollPadding: EdgeInsets.only(bottom: 70), + decoration: InputDecoration( + hintText: 'Hetzner API Token', + ), + ), + SizedBox(height: 20), + BrandButton.rised( + onPressed: + formCubit.state.isSubmitting ? null : formCubit.trySubmit, + title: 'Подключить', + ), + Spacer(), + SizedBox(height: 10), + BrandButton.text( + onPressed: () => _showModal(context, _HowHetzner()), + title: 'Как получить API Token', + ), + ], + ); + }), + ); + } + void _showModal(BuildContext context, Widget widget) { showModalBottomSheet( context: context, @@ -61,135 +159,115 @@ class InitializingPage extends StatelessWidget { ); } - Widget getCard(BuildContext context, ProviderModel model) { - var cubit = context.watch(); - if (model.state == StateType.stable) { - return _MockSuccess(type: model.type); - } - switch (model.type) { - case ProviderType.server: - return BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/hetzner.png'), - SizedBox(height: 10), - BrandText.h2('1. Подключите сервер Hetzner'), - SizedBox(height: 10), - BrandText.body2( - 'Здесь будут жить наши данные и SelfPrivacy-сервисы'), - _MockForm( - hintText: 'Hetzner API Token', - length: 2, - onPressed: () { - var provider = cubit.state.all - .firstWhere((p) => p.type == ProviderType.server); - cubit.connect(provider); - }, - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () => _showModal(context, _HowHetzner()), - title: 'Как получить API Token', - ), - ], + Widget _stepTwo(InitializingCubit cubit) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset('assets/images/logos/cloudflare.png'), + BrandText.h2('Подключите CloudFlare DNS'), + SizedBox(height: 10), + BrandText.body2('Для управления DNS вашего домена'), + Expanded( + child: _MockForm( + hintText: 'CloudFlare API Token', + length: 64, + onPressed: () { + cubit.setCloudFlare('key'); + pageController.animateToPage( + 2, + curve: Curves.easeIn, + duration: Duration(milliseconds: 200), + ); + }, ), - ); - break; - case ProviderType.domain: - return BrandCard( + ), + SizedBox(height: 20), + BrandButton.text( + onPressed: () {}, + title: 'Как получить API Token', + ), + ], + ); + } + + Widget _stepThree(InitializingCubit cubit) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 10), + BrandText.h2('Введите домен:'), + Expanded( + child: _MockForm( + hintText: 'домен', + length: 10, + onPressed: () { + cubit.setDomain('domain'); + pageController.animateToPage( + 3, + curve: Curves.easeIn, + duration: Duration(milliseconds: 200), + ); + }, + ), + ), + SizedBox(height: 10), + BrandButton.text( + onPressed: () => _showModal(context, _HowHetzner()), + title: 'Как получить API Token', + ), + ], + ); + } + + Widget _stepFour(InitializingCubit cubit) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 10), + Expanded( child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset('assets/images/logos/namecheap.png'), - SizedBox(height: 10), - BrandText.h2('2. Настройте домен'), - SizedBox(height: 10), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'Зарегистрируйте домен в ', - style: body2Style, - ), - BrandSpanButton.link( - text: 'NameCheap', - urlString: 'https://www.namecheap.com', - ), - TextSpan( - text: - ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', - style: body2Style, - ), - ], + TextField( + decoration: InputDecoration( + hintText: 'нинейм', ), ), - _MockForm( - hintText: 'Домен, например, selfprivacy.org', - submitButtonText: 'Проверить DNS', - length: 2, - onPressed: () {}, + SizedBox(height: 10), + TextField( + obscureText: true, + decoration: InputDecoration( + hintText: 'пароль', + ), ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как настроить DNS CloudFlare', - ), - SizedBox(height: 10), - Image.asset('assets/images/logos/cloudflare.png'), - SizedBox(height: 10), - BrandText.h2('3. Подключите CloudFlare DNS'), - SizedBox(height: 10), - BrandText.body2('Для управления DNS вашего домена'), - _MockForm( - hintText: 'CloudFlare API Token', - length: 2, + Spacer(), + BrandButton.rised( onPressed: () { - var provider = cubit.state.all - .firstWhere((p) => p.type == ProviderType.domain); - cubit.connect(provider); + cubit.setRootUser( + User(login: 'aa', password: 'bbb'), + ); + _goToMainPage(); }, - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как получить API Token', + title: 'some text', ), ], ), - ); - break; - case ProviderType.backup: - return BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/aws.png'), - SizedBox(height: 10), - BrandText.h2('4. Подключите Amazon AWS для бекапа'), - SizedBox(height: 10), - BrandText.body2( - 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'), - _MockForm( - hintText: 'Amazon AWS Access Key', - length: 2, - onPressed: () { - var provider = cubit.state.all - .firstWhere((p) => p.type == ProviderType.backup); - cubit.connect(provider); - }, - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как получить API Token', - ), - ], - ), - ); - } + ), + SizedBox(height: 10), + BrandButton.text( + onPressed: () => _showModal(context, _HowHetzner()), + title: 'Как получить API Token', + ), + ], + ); + } - return null; + Widget _addCard(Widget child) { + return Padding( + padding: brandPagePadding2, + child: BrandCard( + child: child, + ), + ); } } @@ -245,40 +323,40 @@ class _HowHetzner extends StatelessWidget { } } -class _MockSuccess extends StatelessWidget { - const _MockSuccess({Key key, this.type}) : super(key: key); +// class _MockSuccess extends StatelessWidget { +// const _MockSuccess({Key key, this.type}) : super(key: key); - final ProviderType type; +// final ProviderType type; - @override - Widget build(BuildContext context) { - String text; +// @override +// Widget build(BuildContext context) { +// String text; - switch (type) { - case ProviderType.server: - text = '1. Cервер подключен'; - break; - case ProviderType.domain: - text = '2. Домен настроен'; - break; - case ProviderType.backup: - text = '3. Резервное копирование настроенно'; - break; - } - return BrandCard( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - BrandText.h3(text), - Icon( - Icons.check, - color: BrandColors.green1, - ), - ], - ), - ); - } -} +// switch (type) { +// case ProviderType.server: +// text = '1. Cервер подключен'; +// break; +// case ProviderType.domain: +// text = '2. Домен настроен'; +// break; +// case ProviderType.backup: +// text = '3. Резервное копирование настроенно'; +// break; +// } +// return BrandCard( +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// BrandText.h3(text), +// Icon( +// Icons.check, +// color: BrandColors.green1, +// ), +// ], +// ), +// ); +// } +// } class _MockForm extends StatefulWidget { const _MockForm({ @@ -322,6 +400,7 @@ class __MockFormState extends State<_MockForm> { @override Widget build(BuildContext context) { return Column( + mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: 20), TextField( @@ -350,7 +429,7 @@ class __MockFormState extends State<_MockForm> { _valid ? null : 'Длинна должна быть ${widget.length} символа', ), ), - SizedBox(height: 20), + Spacer(), BrandButton.rised( onPressed: _valid ? onPressed : null, title: widget.submitButtonText, @@ -359,3 +438,119 @@ class __MockFormState extends State<_MockForm> { ); } } + +// Widget getCard(BuildContext context, ProviderModel model) { +// var cubit = context.watch(); +// if (model.state == StateType.stable) { +// return _MockSuccess(type: model.type); +// } +// switch (model.type) { +// case ProviderType.server: +// return BrandCard( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/hetzner.png'), +// SizedBox(height: 10), +// BrandText.h2('1. Подключите сервер Hetzner'), +// SizedBox(height: 10), +// BrandText.body2( +// 'Здесь будут жить наши данные и SelfPrivacy-сервисы'), +// _MockForm( +// hintText: 'Hetzner API Token', +// length: 48, +// onPressed: () { +// var provider = cubit.state.all +// .firstWhere((p) => p.type == ProviderType.server); +// cubit.connect(provider); +// }, +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () => _showModal(context, _HowHetzner()), +// title: 'Как получить API Token', +// ), +// ], +// ), +// ); +// break; +// case ProviderType.domain: +// return BrandCard( +// isBlocked: true, +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/namecheap.png'), +// SizedBox(height: 10), +// BrandText.h2('2. Настройте домен'), +// SizedBox(height: 10), +// RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'Зарегистрируйте домен в ', +// style: body2Style, +// ), +// BrandSpanButton.link( +// text: 'NameCheap', +// urlString: 'https://www.namecheap.com', +// ), +// TextSpan( +// text: +// ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', +// style: body2Style, +// ), +// ], +// ), +// ), +// _MockForm( +// hintText: 'Домен, например, selfprivacy.org', +// submitButtonText: 'Проверить DNS', +// length: 2, +// onPressed: () {}, +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () {}, +// title: 'Как настроить DNS CloudFlare', +// ), +// SizedBox(height: 10), +// Image.asset('assets/images/logos/cloudflare.png'), +// SizedBox(height: 10), +// ], +// ), +// ); +// break; +// case ProviderType.backup: +// return BrandCard( +// isBlocked: true, +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/aws.png'), +// SizedBox(height: 10), +// BrandText.h2('4. Подключите Amazon AWS для бекапа'), +// SizedBox(height: 10), +// BrandText.body2( +// 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'), +// _MockForm( +// hintText: 'Amazon AWS Access Key', +// length: 2, +// onPressed: () { +// var provider = cubit.state.all +// .firstWhere((p) => p.type == ProviderType.backup); +// cubit.connect(provider); +// }, +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () {}, +// title: 'Как получить API Token', +// ), +// ], +// ), +// ); +// } + +// return null; +// } diff --git a/lib/ui/pages/more/more.dart b/lib/ui/pages/more/more.dart index ea24ba42..6a712b54 100644 --- a/lib/ui/pages/more/more.dart +++ b/lib/ui/pages/more/more.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/ui/pages/initializing/initializing.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'about/about.dart'; @@ -28,6 +29,11 @@ class MorePage extends StatelessWidget { child: Column( children: [ BrandDivider(), + _NavItem( + title: 'Мастер Подключения', + iconData: BrandIcons.settings, + goTo: InitializingPage(), + ), _NavItem( title: 'Настройки приложения', iconData: BrandIcons.settings, diff --git a/lib/ui/pages/onboarding/onboarding.dart b/lib/ui/pages/onboarding/onboarding.dart index e5adbb8d..2b10a335 100644 --- a/lib/ui/pages/onboarding/onboarding.dart +++ b/lib/ui/pages/onboarding/onboarding.dart @@ -1,53 +1,120 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/ui/pages/rootRoute.dart'; +import 'package:selfprivacy/ui/pages/initializing/initializing.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; -class OnboardingPage extends StatelessWidget { +class OnboardingPage extends StatefulWidget { const OnboardingPage({Key key}) : super(key: key); + @override + _OnboardingPageState createState() => _OnboardingPageState(); +} + +class _OnboardingPageState extends State { + PageController pageController = PageController(); + + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( - body: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15, - vertical: 45, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BrandText.h1( - 'Онбординг', - ), - SizedBox(height: 20), - BrandText.body2( - 'Тут рассказ на 1-2 слайда о том, что делает это приложение, какие твои проблемы решает и как (в общем чего ожидать от сервиса).', - ), - ], - ), - ), - ), - BrandButton.rised( - onPressed: () { - Navigator.of(context) - .pushReplacement(materialRoute(RootPage())); - }, - title: 'Приступим!', - ) - ], - ), + body: PageView( + controller: pageController, + children: [ + _withPadding(firstPage()), + _withPadding(secondPage()), + ], ), ), ); } + + Widget _withPadding(Widget child) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: child, + ); + } + + Widget firstPage() { + return ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 30), + BrandText.h2( + 'Цифровая независимость и приватность, доступная каждому'), + SizedBox(height: 20), + BrandText.body2( + 'Почта и мессенджер с открытым исходным кодом на вашем личном сервере под вашим полным контролем.'), + Flexible( + child: Center( + child: Image.asset( + 'assets/images/onboarding/onboarding1.png', + ), + ), + ), + BrandButton.rised( + onPressed: () { + pageController.animateToPage( + 1, + duration: Duration(milliseconds: 300), + curve: Curves.easeIn, + ); + }, + title: 'Далее', + ), + SizedBox(height: 30), + ], + ), + ); + } + + Widget secondPage() { + return ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height, + ), + child: Column( + children: [ + SizedBox(height: 30), + BrandText.h2('Для работы понадобятся ваши аккаунты'), + SizedBox(height: 20), + BrandText.body2( + 'Для максимальноей приватности и независимости SelfPrivacy не использует свои серверы. \n \n Если у вас нет домена, аккаунтов на Hetzner, AWS и Clouflare, мы поможем их создать и подключить.'), + SizedBox(height: 20), + Center( + child: Image.asset( + 'assets/images/onboarding/logos_line.png', + ), + ), + Flexible( + child: Center( + child: Image.asset( + 'assets/images/onboarding/onboarding2.png', + ), + ), + ), + BrandButton.rised( + onPressed: () { + Navigator.of(context) + .pushReplacement(materialRoute(InitializingPage())); + }, + title: 'Понял', + ), + SizedBox(height: 30), + ], + ), + ); + } } diff --git a/lib/ui/pages/onboarding/onboarding.full.dart b/lib/ui/pages/onboarding/onboarding.full.dart index cf311f46..7123261f 100644 --- a/lib/ui/pages/onboarding/onboarding.full.dart +++ b/lib/ui/pages/onboarding/onboarding.full.dart @@ -1,231 +1,231 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/config/text_themes.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; -import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +// import 'package:flutter/material.dart'; +// import 'package:selfprivacy/config/brand_theme.dart'; +// import 'package:selfprivacy/config/text_themes.dart'; +// import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; +// import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; +// import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; +// import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; +// import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -class OnboardingPage extends StatelessWidget { - const OnboardingPage({Key key}) : super(key: key); +// class OnboardingPage extends StatelessWidget { +// const OnboardingPage({Key key}) : super(key: key); - @override - Widget build(BuildContext context) { - return Scaffold( - body: ListView( - padding: brandPagePadding1, - children: [ - BrandText.h4('Начало'), - BrandText.h1('SelfPrivacy'), - SizedBox( - height: 10, - ), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: - 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', - style: body2Style, - ), - BrandSpanButton.link( - text: 'selfprivacy.org/start', - urlString: 'https://selfprivacy.org/start', - ), - ], - ), - ), - SizedBox(height: 50), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/hetzner.png'), - SizedBox(height: 10), - BrandText.h2('1. Подключите сервер Hetzner'), - SizedBox(height: 10), - BrandText.body2( - 'Здесь будут жить наши данные и SelfPrivacy-сервисы'), - _MockForm( - hintText: 'Hetzner API Token', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () => _showModal(context, _HowHetzner()), - title: 'Как получить API Token', - ), - ], - ), - ), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/namecheap.png'), - SizedBox(height: 10), - BrandText.h2('2. Настройте домен'), - SizedBox(height: 10), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'Зарегистрируйте домен в ', - style: body2Style, - ), - BrandSpanButton.link( - text: 'NameCheap', - urlString: 'https://www.namecheap.com', - ), - TextSpan( - text: - ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', - style: body2Style, - ), - ], - ), - ), - _MockForm( - hintText: 'Домен, например, selfprivacy.org', - submitButtonText: 'Проверить DNS', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как настроить DNS CloudFlare', - ), - ], - ), - ), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/cloudflare.png'), - SizedBox(height: 10), - BrandText.h2('3. Подключите CloudFlare DNS'), - SizedBox(height: 10), - BrandText.body2('Для управления DNS вашего домена'), - _MockForm( - hintText: 'CloudFlare API Token', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как получить API Token', - ), - ], - ), - ), - BrandCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset('assets/images/logos/aws.png'), - SizedBox(height: 10), - BrandText.h2('4. Подключите Amazon AWS для бекапа'), - SizedBox(height: 10), - BrandText.body2( - 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'), - _MockForm( - hintText: 'Amazon AWS Access Key', - ), - SizedBox(height: 20), - BrandButton.text( - onPressed: () {}, - title: 'Как получить API Token', - ), - ], - ), - ) - ], - ), - ); - } +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// body: ListView( +// padding: brandPagePadding1, +// children: [ +// BrandText.h4('Начало'), +// BrandText.h1('SelfPrivacy'), +// SizedBox( +// height: 10, +// ), +// RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: +// 'Для устойчивости и приватности требует много учёток. Полная инструкция на ', +// style: body2Style, +// ), +// BrandSpanButton.link( +// text: 'selfprivacy.org/start', +// urlString: 'https://selfprivacy.org/start', +// ), +// ], +// ), +// ), +// SizedBox(height: 50), +// BrandCard( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/hetzner.png'), +// SizedBox(height: 10), +// BrandText.h2('1. Подключите сервер Hetzner'), +// SizedBox(height: 10), +// BrandText.body2( +// 'Здесь будут жить наши данные и SelfPrivacy-сервисы'), +// _MockForm( +// hintText: 'Hetzner API Token', +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () => _showModal(context, _HowHetzner()), +// title: 'Как получить API Token', +// ), +// ], +// ), +// ), +// BrandCard( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/namecheap.png'), +// SizedBox(height: 10), +// BrandText.h2('2. Настройте домен'), +// SizedBox(height: 10), +// RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'Зарегистрируйте домен в ', +// style: body2Style, +// ), +// BrandSpanButton.link( +// text: 'NameCheap', +// urlString: 'https://www.namecheap.com', +// ), +// TextSpan( +// text: +// ' или у любого другого регистратора. После этого настройте его на DNS-сервер CloudFlare', +// style: body2Style, +// ), +// ], +// ), +// ), +// _MockForm( +// hintText: 'Домен, например, selfprivacy.org', +// submitButtonText: 'Проверить DNS', +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () {}, +// title: 'Как настроить DNS CloudFlare', +// ), +// ], +// ), +// ), +// BrandCard( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/cloudflare.png'), +// SizedBox(height: 10), +// BrandText.h2('3. Подключите CloudFlare DNS'), +// SizedBox(height: 10), +// BrandText.body2('Для управления DNS вашего домена'), +// _MockForm( +// hintText: 'CloudFlare API Token', +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () {}, +// title: 'Как получить API Token', +// ), +// ], +// ), +// ), +// BrandCard( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Image.asset('assets/images/logos/aws.png'), +// SizedBox(height: 10), +// BrandText.h2('4. Подключите Amazon AWS для бекапа'), +// SizedBox(height: 10), +// BrandText.body2( +// 'IaaS-провайдер, для бесплатного хранения резервных копии ваших данных в зашифрованном виде'), +// _MockForm( +// hintText: 'Amazon AWS Access Key', +// ), +// SizedBox(height: 20), +// BrandButton.text( +// onPressed: () {}, +// title: 'Как получить API Token', +// ), +// ], +// ), +// ) +// ], +// ), +// ); +// } - void _showModal(BuildContext context, Widget widget) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (BuildContext context) { - return widget; - }, - ); - } -} +// void _showModal(BuildContext context, Widget widget) { +// showModalBottomSheet( +// context: context, +// isScrollControlled: true, +// backgroundColor: Colors.transparent, +// builder: (BuildContext context) { +// return widget; +// }, +// ); +// } +// } -class _HowHetzner extends StatelessWidget { - const _HowHetzner({ - Key key, - }) : super(key: key); +// class _HowHetzner extends StatelessWidget { +// const _HowHetzner({ +// Key key, +// }) : super(key: key); - @override - Widget build(BuildContext context) { - return BrandModalSheet( - child: Padding( - padding: brandPagePadding2, - child: Column( - children: [ - SizedBox(height: 40), - BrandText.h2('Как получить Hetzner API Token'), - SizedBox(height: 20), - RichText( - text: TextSpan( - children: [ - TextSpan( - text: '1 Переходим по ссылке ', - style: body1Style, - ), - BrandSpanButton.link( - text: 'hetzner.com/sdfsdfsdfsdf', - urlString: 'https://hetzner.com/sdfsdfsdfsdf', - ), - TextSpan( - text: ''' +// @override +// Widget build(BuildContext context) { +// return BrandModalSheet( +// child: Padding( +// padding: brandPagePadding2, +// child: Column( +// children: [ +// SizedBox(height: 40), +// BrandText.h2('Как получить Hetzner API Token'), +// SizedBox(height: 20), +// RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: '1 Переходим по ссылке ', +// style: body1Style, +// ), +// BrandSpanButton.link( +// text: 'hetzner.com/sdfsdfsdfsdf', +// urlString: 'https://hetzner.com/sdfsdfsdfsdf', +// ), +// TextSpan( +// text: ''' -2 Заходим в созданный нами проект. Если такового - нет, значит создаём. +// 2 Заходим в созданный нами проект. Если такового - нет, значит создаём. -3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). +// 3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). -4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему. +// 4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему. -5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку. +// 5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку. -6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. +// 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. - ''', - style: body1Style, - ), - ], - ), - ), - ], - ), - ), - ); - } -} +// ''', +// style: body1Style, +// ), +// ], +// ), +// ), +// ], +// ), +// ), +// ); +// } +// } -class _MockForm extends StatelessWidget { - const _MockForm({ - Key key, - @required this.hintText, - this.submitButtonText = 'Подключить', - }) : super(key: key); +// class _MockForm extends StatelessWidget { +// const _MockForm({ +// Key key, +// @required this.hintText, +// this.submitButtonText = 'Подключить', +// }) : super(key: key); - final String hintText; - final String submitButtonText; +// final String hintText; +// final String submitButtonText; - @override - Widget build(BuildContext context) { - return Column( - children: [ - SizedBox(height: 20), - TextField(decoration: InputDecoration(hintText: hintText)), - SizedBox(height: 20), - BrandButton.rised(onPressed: () {}, title: submitButtonText), - ], - ); - } -} +// @override +// Widget build(BuildContext context) { +// return Column( +// children: [ +// SizedBox(height: 20), +// TextField(decoration: InputDecoration(hintText: hintText)), +// SizedBox(height: 20), +// BrandButton.rised(onPressed: () {}, title: submitButtonText), +// ], +// ); +// } +// } diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 75788a5f..dc9552a3 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/logic/models/provider.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; @@ -8,7 +9,7 @@ import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; -import 'package:selfprivacy/ui/pages/providers/settings/setting.dart'; +import 'package:selfprivacy/ui/pages/providers/settings/settings.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; class ProvidersPage extends StatefulWidget { @@ -47,6 +48,8 @@ class _Card extends StatelessWidget { String title; String message; String stableText; + var isFullyInitilized = + context.watch().state.isFullyInitilized; switch (provider.type) { case ProviderType.server: @@ -77,6 +80,7 @@ class _Card extends StatelessWidget { }, ), child: BrandCard( + isBlocked: !isFullyInitilized, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/ui/pages/providers/settings/setting.dart b/lib/ui/pages/providers/settings/settings.dart similarity index 100% rename from lib/ui/pages/providers/settings/setting.dart rename to lib/ui/pages/providers/settings/settings.dart diff --git a/lib/ui/pages/rootRoute.dart b/lib/ui/pages/rootRoute.dart index 0dc102fc..949180b4 100644 --- a/lib/ui/pages/rootRoute.dart +++ b/lib/ui/pages/rootRoute.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; @@ -8,9 +9,6 @@ import 'package:selfprivacy/ui/pages/providers/providers.dart'; import 'package:selfprivacy/ui/pages/services/services.dart'; import 'package:selfprivacy/ui/pages/users/users.dart'; -import 'initializing/initializing.dart'; - - class RootPage extends StatefulWidget { const RootPage({Key key}) : super(key: key); @@ -36,17 +34,17 @@ class _RootPageState extends State @override Widget build(BuildContext context) { - var isReady = - context.select((ProvidersCubit bloc) => bloc.state.isFullyInitialized); + var isUserFilled = + context.watch().state.isFullyInitilized; return SafeArea( child: Scaffold( body: TabBarView( controller: tabController, children: [ - isReady ? ProvidersPage() : InitializingPage(), - isReady ? ServicesPage() : _NotReady(), - isReady ? UsersPage() : _NotReady(), + ProvidersPage(), + ServicesPage(), + isUserFilled ? UsersPage() : _NotReady(), MorePage(), ], ), diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index e0902ed1..8fde4106 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; @@ -52,6 +53,7 @@ class _Card extends StatelessWidget { String title; IconData iconData; String description; + var isFullyInitilized = context.watch().state.isFullyInitilized; switch (service.type) { case ServiceTypes.messanger: @@ -83,6 +85,7 @@ class _Card extends StatelessWidget { break; } return BrandCard( + isBlocked: !isFullyInitilized, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -102,8 +105,7 @@ class _Card extends StatelessWidget { context.read().connect(service); }) ], - if (service.state == StateType.stable) - BrandText.body2('Подключен'), + if (service.state == StateType.stable) BrandText.body2('Подключен'), ], ), ); diff --git a/pubspec.lock b/pubspec.lock index 69f3d6fe..82c5c198 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -28,7 +28,7 @@ packages: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.1.1" boolean_selector: dependency: transitive description: @@ -78,6 +78,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + cubit_form: + dependency: "direct main" + description: + name: cubit_form + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.14" cupertino_icons: dependency: "direct main" description: @@ -92,6 +99,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.3" + email_validator: + dependency: transitive + description: + name: email_validator + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" equatable: dependency: "direct main" description: @@ -189,6 +203,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.16.1" + mask_text_input_formatter: + dependency: transitive + description: + name: mask_text_input_formatter + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" matcher: dependency: transitive description: @@ -343,6 +364,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+3" + shortuuid: + dependency: transitive + description: + name: shortuuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" sky_engine: dependency: transitive description: flutter @@ -439,6 +467,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+3" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a774e87b..8c176b10 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + cubit_form: ^0.0.14 cupertino_icons: ^1.0.0 easy_localization: ^2.3.3 equatable: ^1.2.5 @@ -31,6 +32,8 @@ flutter_icons: flutter: uses-material-design: true assets: + - assets/images/ + - assets/images/onboarding/ - assets/images/logos/ - assets/translations/ fonts: