From 25a386d511d58b05c88b6086f6bf3cc03d2a52b0 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 3 Feb 2021 20:51:07 +0100 Subject: [PATCH] update --- assets/images/logos/backblaze.png | Bin 0 -> 9061 bytes lib/config/hive_config.dart | 2 +- lib/logic/api_maps/backblaze.dart | 38 ++ lib/logic/api_maps/hetzner.dart | 10 + .../cubit/app_config/app_config_cubit.dart | 22 +- .../app_config/app_config_repository.dart | 23 +- .../cubit/app_config/app_config_state.dart | 7 + .../initializing/backblaze_form_cubit.dart | 81 +++++ .../components/progress_bar/progress_bar.dart | 19 +- lib/ui/pages/initializing/initializing.dart | 60 ++- .../pages/more/app_settings/app_setting.dart | 2 +- lib/ui/pages/onboarding/onboarding.full.dart | 231 ------------ lib/ui/pages/onboarding/onboarding.old.dart | 341 ------------------ 13 files changed, 244 insertions(+), 592 deletions(-) create mode 100644 assets/images/logos/backblaze.png create mode 100644 lib/logic/api_maps/backblaze.dart create mode 100644 lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart delete mode 100644 lib/ui/pages/onboarding/onboarding.full.dart delete mode 100644 lib/ui/pages/onboarding/onboarding.old.dart diff --git a/assets/images/logos/backblaze.png b/assets/images/logos/backblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..c4818556738b1d39d3da3ed5674ea46a79888058 GIT binary patch literal 9061 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 isValid(String token) async { + var options = Options( + headers: {'Authorization': 'Basic $token'}, + validateStatus: (status) { + return status == HttpStatus.ok || status == HttpStatus.unauthorized; + }, + ); + + Response response = await loggedClient.get(rootAddress, options: options); + + if (response.statusCode == HttpStatus.ok) { + print(response); + return true; + } else if (response.statusCode == HttpStatus.unauthorized) { + return false; + } else { + throw Exception('code: ${response.statusCode}'); + } + } +} \ No newline at end of file diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 802f8333..1753b13c 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -69,4 +69,14 @@ class HetznerApi extends ApiMap { startTime: DateTime.now(), ); } + + Future reset({ + HetznerServerDetails server, + }) async { + await loggedClient.post('/${server.id}/actions/poweron'); + + return server.copyWith( + startTime: DateTime.now(), + ); + } } diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 20948829..8b0828bf 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -12,7 +12,7 @@ import 'app_config_repository.dart'; part 'app_config_state.dart'; -/// initializeing steps: +/// initializeing steps: /// 1. Hetzner key |setHetznerKey /// 2. Cloudflare key |setCloudflareKey /// 3. Set Domain address |setDomain @@ -34,8 +34,8 @@ class AppConfigCubit extends Cubit { emit(state); } - void reset() { - repository.reset(); + void clearAppConfig() { + repository.clearAppConfig(); emit(InitialAppConfigState()); } @@ -76,7 +76,16 @@ class AppConfigCubit extends Cubit { state.cloudFlareKey, state.cloudFlareDomain.zoneId, ); - emit(state.copyWith(isDkimSetted: true)); + var hetznerServerDetails = await repository.reset( + state.hetznerKey, + state.hetznerServer, + ); + emit( + state.copyWith( + isDkimSetted: true, + hetznerServer: hetznerServerDetails, + ), + ); }; _tryOrAddError(state, callBack); @@ -143,4 +152,9 @@ class AppConfigCubit extends Cubit { emit(state); } } + + void setBackblazeKey(String backblazeKey) { + repository.saveBackblazeKey(backblazeKey); + emit(state.copyWith(backblazeKey: backblazeKey)); + } } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index d9beba3e..d0844470 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -20,6 +20,7 @@ class AppConfigRepository { hetznerKey: box.get(BNames.hetznerKey), cloudFlareKey: box.get(BNames.cloudFlareKey), cloudFlareDomain: box.get(BNames.cloudFlareDomain), + backblazeKey: box.get(BNames.backblazeKey), rootUser: box.get(BNames.rootUser), hetznerServer: box.get(BNames.hetznerServer), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), @@ -28,7 +29,7 @@ class AppConfigRepository { ); } - void reset() { + void clearAppConfig() { box.clear(); } @@ -36,6 +37,10 @@ class AppConfigRepository { box.put(BNames.hetznerKey, key); } + void saveBackblazeKey(String key) { + box.put(BNames.backblazeKey, key); + } + void saveCloudFlare(String key) { box.put(BNames.cloudFlareKey, key); } @@ -104,12 +109,8 @@ class AppConfigRepository { return true; } - Future createServer( - String hetznerKey, - User rootUser, - String domainName, - String cloudFlareKey - ) async { + Future createServer(String hetznerKey, User rootUser, + String domainName, String cloudFlareKey) async { var hetznerApi = HetznerApi(hetznerKey); var serverDetails = await hetznerApi.createServer( cloudFlareKey: cloudFlareKey, @@ -164,4 +165,12 @@ class AppConfigRepository { cloudflareApi.close(); } + + Future reset( + String hetznerKey, + HetznerServerDetails server, + ) async { + var hetznerApi = HetznerApi(hetznerKey); + return await hetznerApi.reset(server: server); + } } diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index aa6ecb6b..37cb368d 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -4,6 +4,7 @@ class AppConfigState extends Equatable { const AppConfigState({ this.hetznerKey, this.cloudFlareKey, + this.backblazeKey, this.cloudFlareDomain, this.rootUser, this.hetznerServer, @@ -20,6 +21,7 @@ class AppConfigState extends Equatable { List get props => [ hetznerKey, cloudFlareKey, + backblazeKey, cloudFlareDomain, rootUser, hetznerServer, @@ -33,6 +35,7 @@ class AppConfigState extends Equatable { final String hetznerKey; final String cloudFlareKey; + final String backblazeKey; final CloudFlareDomain cloudFlareDomain; final User rootUser; final HetznerServerDetails hetznerServer; @@ -47,6 +50,7 @@ class AppConfigState extends Equatable { AppConfigState copyWith({ String hetznerKey, String cloudFlareKey, + String backblazeKey, CloudFlareDomain cloudFlareDomain, User rootUser, HetznerServerDetails hetznerServer, @@ -61,6 +65,7 @@ class AppConfigState extends Equatable { AppConfigState( hetznerKey: hetznerKey ?? this.hetznerKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + backblazeKey: backblazeKey ?? this.backblazeKey, cloudFlareDomain: cloudFlareDomain ?? this.cloudFlareDomain, rootUser: rootUser ?? this.rootUser, hetznerServer: hetznerServer ?? this.hetznerServer, @@ -76,6 +81,7 @@ class AppConfigState extends Equatable { bool get isHetznerFilled => hetznerKey != null; bool get isCloudFlareFilled => cloudFlareKey != null; + bool get isBackblazeFilled => backblazeKey != null; bool get isDomainFilled => cloudFlareDomain != null; bool get isUserFilled => rootUser != null; bool get isServerFilled => hetznerServer != null; @@ -90,6 +96,7 @@ class AppConfigState extends Equatable { List get _fulfilementList => [ isHetznerFilled, isCloudFlareFilled, + isBackblazeFilled, isDomainFilled, isUserFilled, isServerFilled, diff --git a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart new file mode 100644 index 00000000..8b594c09 --- /dev/null +++ b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart @@ -0,0 +1,81 @@ + +import 'dart:async'; +import 'dart:convert'; +import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/logic/api_maps/backblaze.dart'; +import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; + +class BackblazeFormCubit extends FormCubit { + BackblazeApi apiClient = BackblazeApi(); + + BackblazeFormCubit(this.initializingCubit) { + //var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); + keyId = FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('required'), + //ValidationModel( + //(s) => regExp.hasMatch(s), 'invalid key format'), + //LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64') + ], + ); + + applicationKey = FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('required'), + //ValidationModel( + //(s) => regExp.hasMatch(s), 'invalid key format'), + //LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64') + ], + ); + + super.setFields([keyId, applicationKey]); + } + + @override + FutureOr onSubmit() async { + String encodedApiKey = + encodeToBase64(keyId.state.value, applicationKey.state.value); + + initializingCubit.setBackblazeKey(encodedApiKey); + } + + final AppConfigCubit initializingCubit; + + FieldCubit keyId; + + FieldCubit applicationKey; + + @override + FutureOr asyncValidation() async { + bool isKeyValid; + try { + String encodedApiKey = + encodeToBase64(keyId.state.value, applicationKey.state.value); + isKeyValid = await apiClient.isValid(encodedApiKey); + } catch (e) { + addError(e); + } + + if (!isKeyValid) { + keyId.setError('bad key'); + applicationKey.setError('bad key'); + return false; + } + return true; + } + + @override + Future close() async { + apiClient.close(); + + return super.close(); + } + + String encodeToBase64(String keyId, String applicationKey) { + String _apiKey = '$keyId:$applicationKey'; + String encodedApiKey = base64.encode(utf8.encode(_apiKey)); + return encodedApiKey; + } +} \ No newline at end of file diff --git a/lib/ui/components/progress_bar/progress_bar.dart b/lib/ui/components/progress_bar/progress_bar.dart index 00f196cc..2adf8a97 100644 --- a/lib/ui/components/progress_bar/progress_bar.dart +++ b/lib/ui/components/progress_bar/progress_bar.dart @@ -44,10 +44,23 @@ class _ProgressBarState extends State { } else { odd.add(step); } - i++; + i++; } - even.add(Spacer()); - odd.insert(0, Spacer()); + // even.add(SizedBox( + // width: 0, + // )); + odd + ..insert( + 0, + SizedBox( + width: 50, + ), + ) + ..add( + SizedBox( + width: 50, + ), + ); return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index 65ee206e..04266bc8 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/logic/cubit/forms/initializing/backblaze_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/domain_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/hetzner_form_cubit.dart'; @@ -27,12 +28,13 @@ class InitializingPage extends StatelessWidget { var actualPage = [ _stepHetzner(cubit), _stepCloudflare(cubit), + _stepBackblaze(cubit), _stepDomain(cubit), _stepUser(cubit), _stepServer(cubit), _stepCheck(cubit), Container(child: Text('Everythigng is initialized')) - ][cubit.state.progress]; + ][2]; return BlocListener( listener: (context, state) { if (state.isFullyInitilized) { @@ -46,12 +48,13 @@ class InitializingPage extends StatelessWidget { Padding( padding: brandPagePadding1, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ ProgressBar( steps: [ 'Hetzner', 'CloudFlare', + 'Backblaze', 'Domain', 'User', 'Server', @@ -177,6 +180,55 @@ class InitializingPage extends StatelessWidget { ); } + Widget _stepBackblaze(AppConfigCubit initializingCubit) { + return BlocProvider( + create: (context) => BackblazeFormCubit(initializingCubit), + child: Builder(builder: (context) { + var formCubit = context.watch(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Spacer(), + Image.asset('assets/images/logos/backblaze.png'), + SizedBox(height: 10), + BrandText.h2('Подключите облачное хранилище Backblaze'), + SizedBox(height: 10), + BrandText.body2('Здесь будут храниться данные'), + Spacer(), + CubitFormTextField( + formFieldCubit: formCubit.keyId, + textAlign: TextAlign.center, + scrollPadding: EdgeInsets.only(bottom: 70), + decoration: InputDecoration( + hintText: 'KeyID', + ), + ), + Spacer(), + CubitFormTextField( + formFieldCubit: formCubit.applicationKey, + textAlign: TextAlign.center, + scrollPadding: EdgeInsets.only(bottom: 70), + decoration: InputDecoration( + hintText: 'Master Application Key', + ), + ), + Spacer(), + BrandButton.rised( + onPressed: + formCubit.state.isSubmitting ? null : formCubit.trySubmit, + title: 'Подключить', + ), + SizedBox(height: 10), + BrandButton.text( + onPressed: () => _showModal(context, _HowHetzner()), + title: 'Как получить API Token', + ), + ], + ); + }), + ); + } + Widget _stepDomain(AppConfigCubit initializingCubit) { return BlocProvider( create: (context) => DomainFormCubit(initializingCubit), @@ -296,8 +348,8 @@ class InitializingPage extends StatelessWidget { SizedBox(height: 10), BrandText.body2( isDnsChecked - ? 'Dns сервера вступили в силу, мы стартанули сервер, как только он поднимиться, мы закончим инициализацию.' - : 'Мы начали процесс инциализации сервера, раз в минуты мы будем проверять наличие DNS записей, как только они вступят в силу мы продолжим инциализацию', + ? 'Dns сервера вступили в силу, мы стартанули сервер, как только он поднимется, мы закончим инициализацию.' + : 'Мы начали процесс инциализации сервера, раз в минуту мы будем проверять наличие DNS записей, как только они вступят в силу мы продолжим инциализацию', ), SizedBox(height: 10), Row( diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 2e6113f5..1b00529a 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -108,7 +108,7 @@ class _AppSettingsPageState extends State { ), ), onPressed: () { - context.read().reset(); + context.read().clearAppConfig(); Navigator.of(context)..pop()..pop(); }, ), diff --git a/lib/ui/pages/onboarding/onboarding.full.dart b/lib/ui/pages/onboarding/onboarding.full.dart deleted file mode 100644 index 7123261f..00000000 --- a/lib/ui/pages/onboarding/onboarding.full.dart +++ /dev/null @@ -1,231 +0,0 @@ -// 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); - -// @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; -// }, -// ); -// } -// } - -// 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: ''' - -// 2 Заходим в созданный нами проект. Если такового - нет, значит создаём. - -// 3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). - -// 4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему. - -// 5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку. - -// 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. - -// ''', -// style: body1Style, -// ), -// ], -// ), -// ), -// ], -// ), -// ), -// ); -// } -// } - -// class _MockForm extends StatelessWidget { -// const _MockForm({ -// Key key, -// @required this.hintText, -// this.submitButtonText = 'Подключить', -// }) : super(key: key); - -// 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), -// ], -// ); -// } -// } diff --git a/lib/ui/pages/onboarding/onboarding.old.dart b/lib/ui/pages/onboarding/onboarding.old.dart deleted file mode 100644 index ee204670..00000000 --- a/lib/ui/pages/onboarding/onboarding.old.dart +++ /dev/null @@ -1,341 +0,0 @@ -// 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:selfprivacy/ui/components/dots_indicator/dots_indicator.dart'; -// import 'package:selfprivacy/ui/pages/rootRoute.dart'; -// import 'package:selfprivacy/utils/route_transitions/basic.dart'; - -// class InitializingPage extends StatefulWidget { -// const InitializingPage({Key key}) : super(key: key); - -// @override -// _InitializingPageState createState() => _InitializingPageState(); -// } - -// class _InitializingPageState extends State { -// PageController controller; -// var currentPage = 0; - -// @override -// void initState() { -// controller = PageController( -// initialPage: 0, -// )..addListener(() { -// if (currentPage != controller.page.toInt()) { -// setState(() { -// currentPage = controller.page.toInt(); -// }); -// } -// }); -// super.initState(); -// WidgetsBinding.instance.addPostFrameCallback((_) {}); -// } - -// @override -// void dispose() { -// controller.dispose(); -// super.dispose(); -// } - -// @override -// Widget build(BuildContext context) { -// var steps = getSteps(); - -// return SafeArea( -// child: Scaffold( -// body: ListView( -// shrinkWrap: true, -// 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: 'selfprivacy.org/start', -// urlString: 'https://selfprivacy.org/start', -// ), -// ], -// ), -// ), -// ], -// ), -// ), -// Container( -// height: 480, -// child: PageView.builder( -// physics: NeverScrollableScrollPhysics(), -// allowImplicitScrolling: false, -// controller: controller, -// itemBuilder: (_, index) { -// return Padding( -// padding: brandPagePadding2, -// child: steps[index], -// ); -// }, -// itemCount: 4, -// ), -// ), -// DotsIndicator( -// activeIndex: currentPage, -// count: steps.length, -// ), -// SizedBox(height: 50), -// ], -// ), -// ), -// ); -// } - -// List getSteps() => [ -// BrandCard( -// child: Column( -// mainAxisSize: MainAxisSize.min, -// 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( -// onPressed: _nextPage, -// hintText: 'Hetzner API Token', -// length: 2, -// ), -// SizedBox(height: 20), -// Spacer(), -// BrandButton.text( -// onPressed: () => _showModal(context, _HowHetzner()), -// title: 'Как получить API Token', -// ), -// ], -// ), -// ), -// BrandCard( -// child: Column( -// mainAxisSize: MainAxisSize.min, -// 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( -// onPressed: _nextPage, -// hintText: 'Домен, например, selfprivacy.org', -// submitButtonText: 'Проверить DNS', -// length: 2, -// ), -// Spacer(), -// BrandButton.text( -// onPressed: () {}, -// title: 'Как настроить DNS CloudFlare', -// ), -// ], -// ), -// ), -// BrandCard( -// child: Column( -// mainAxisSize: MainAxisSize.min, -// 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( -// onPressed: _nextPage, -// hintText: 'CloudFlare API Token', -// length: 2, -// ), -// Spacer(), -// BrandButton.text( -// onPressed: () {}, -// title: 'Как получить API Token', -// ), -// ], -// ), -// ), -// BrandCard( -// child: Column( -// mainAxisSize: MainAxisSize.min, -// 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( -// onPressed: () { -// Navigator.of(context) -// .pushReplacement(materialRoute(RootPage())); -// }, -// hintText: 'Amazon AWS Access Key', -// length: 2, -// ), -// Spacer(), -// 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 _nextPage() => controller.nextPage( -// duration: Duration(milliseconds: 300), -// curve: Curves.easeIn, -// ); -// } - -// 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: ''' - -// 2 Заходим в созданный нами проект. Если такового - нет, значит создаём. - -// 3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика). - -// 4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему. - -// 5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку. - -// 6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет. - -// ''', -// style: body1Style, -// ), -// ], -// ), -// ), -// ], -// ), -// ), -// ); -// } -// } - -// class _MockForm extends StatefulWidget { -// const _MockForm({ -// Key key, -// @required this.hintText, -// this.submitButtonText = 'Подключить', -// @required this.onPressed, -// @required this.length, -// }) : super(key: key); - -// final String hintText; -// final String submitButtonText; -// final int length; - -// final VoidCallback onPressed; - -// @override -// __MockFormState createState() => __MockFormState(); -// } - -// class __MockFormState extends State<_MockForm> { -// String text = ''; - -// @override -// Widget build(BuildContext context) { -// return Column( -// children: [ -// SizedBox(height: 20), -// TextField( -// onChanged: (value) => { -// setState(() { -// text = value; -// }) -// }, -// decoration: InputDecoration(hintText: widget.hintText), -// ), -// SizedBox(height: 20), -// BrandButton.rised( -// onPressed: -// text.length == widget.length ? widget.onPressed ?? () {} : null, -// title: widget.submitButtonText, -// ), -// ], -// ); -// } -// }