From 481a6e3d47b724c77afe799405712806df3b7ab9 Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 23 Mar 2021 20:21:42 +0100 Subject: [PATCH 01/17] update --- assets/translations/en.json | 5 +++++ assets/translations/ru.json | 5 +++++ lib/logic/api_maps/cloudflare.dart | 4 ++-- .../cubit/app_config/app_config_repository.dart | 8 +++++--- .../forms/initializing/backblaze_form_cubit.dart | 2 +- .../forms/initializing/cloudflare_form_cubit.dart | 2 +- .../forms/initializing/domain_cloudflare.dart | 2 +- .../forms/initializing/hetzner_form_cubit.dart | 2 +- .../forms/initializing/root_user_form_cubit.dart | 2 +- lib/logic/cubit/forms/user/user_form_cubit.dart | 2 +- pubspec.lock | 14 +++++++------- pubspec.yaml | 2 +- 12 files changed, 31 insertions(+), 19 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index dfd5e7f7..e7f9036b 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -167,5 +167,10 @@ "18": "How to obtain Hetzner API Token", "19": "1 Go via this link ", "20": "\n" + }, + "modals": { + "_comment": "messages in modals", + "1": "Сервер с таким именем уже существует", + "2": "Уничтожить сервер и создать новый?" } } \ No newline at end of file diff --git a/assets/translations/ru.json b/assets/translations/ru.json index d3b19c1b..1783dd73 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -167,5 +167,10 @@ "18": "Как получить Hetzner API Token'", "19": "1 Переходим по ссылке ", "20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет." + }, + "modals": { + "_comment": "messages in modals", + "1": "Сервер с таким именем уже существует", + "2": "Уничтожить сервер и создать новый?" } } \ No newline at end of file diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart index b78b8ee6..c72c7bad 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/cloudflare.dart @@ -161,7 +161,7 @@ class CloudflareApi extends ApiMap { ]; } - Future?> domainList() async { + Future> domainList() async { var url = '$rootAddress/zones?per_page=50'; var response = await loggedClient.get( url, @@ -169,7 +169,7 @@ class CloudflareApi extends ApiMap { ); return response.data['result'] - .map((el) => el['name'] as String?) + .map((el) => el['name'] as String) .toList(); } } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 1a9f9d2b..9c3ee1c1 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -15,6 +15,7 @@ import 'package:basic_utils/basic_utils.dart'; import 'package:selfprivacy/ui/components/action_button/action_button.dart'; import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; import 'app_config_cubit.dart'; +import 'package:easy_localization/easy_localization.dart'; class AppConfigRepository { Box box = Hive.box(BNames.appConfig); @@ -121,7 +122,8 @@ class AppConfigRepository { String? domainName, String? cloudFlareKey, { void Function()? onCancel, - required Future Function(HetznerServerDetails serverDetails) onSuccess, + required Future Function(HetznerServerDetails serverDetails) + onSuccess, }) async { var hetznerApi = HetznerApi(hetznerKey); @@ -139,8 +141,8 @@ class AppConfigRepository { var nav = getIt.get(); nav.showPopUpDialog( BrandAlert( - title: 'Сервер с таким именем уже существует', - contentText: 'Уничтожить сервер и создать новый?', + title: 'modals.1'.tr(), + contentText: 'modals.2'.tr(), acitons: [ ActionButton( text: 'Удалить', diff --git a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart index 386882a9..9ecf3eaa 100644 --- a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart @@ -29,7 +29,7 @@ class BackblazeFormCubit extends FormCubit { ], ); - super.setFields([keyId, applicationKey]); + super.addFields([keyId, applicationKey]); } @override diff --git a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart index 2f885436..cd33feea 100644 --- a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart @@ -20,7 +20,7 @@ class CloudFlareFormCubit extends FormCubit { ], ); - super.setFields([apiKey]); + super.addFields([apiKey]); } @override diff --git a/lib/logic/cubit/forms/initializing/domain_cloudflare.dart b/lib/logic/cubit/forms/initializing/domain_cloudflare.dart index 07616b0a..4b1aad91 100644 --- a/lib/logic/cubit/forms/initializing/domain_cloudflare.dart +++ b/lib/logic/cubit/forms/initializing/domain_cloudflare.dart @@ -17,7 +17,7 @@ class DomainSetupCubit extends Cubit { Future load() async { emit(Loading(LoadingTypes.loadingDomain)); - var list = await (api.domainList() as Future>); + var list = await api.domainList(); if (list.isEmpty) { emit(Empty()); } else if (list.length == 1) { diff --git a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart index 2383a986..78674e31 100644 --- a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart @@ -20,7 +20,7 @@ class HetznerFormCubit extends FormCubit { ], ); - super.setFields([apiKey]); + super.addFields([apiKey]); } @override diff --git a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart index f651c251..85be05c7 100644 --- a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart @@ -32,7 +32,7 @@ class RootUserFormCubit extends FormCubit { isVisible = FieldCubit(initalValue: false); - super.setFields([userName, password, isVisible]); + super.addFields([userName, password, isVisible]); } @override diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index 59bd50d3..2c874eb4 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -33,7 +33,7 @@ class UserFormCubit extends FormCubit { ], ); - super.setFields([login, password]); + super.addFields([login, password]); } @override diff --git a/pubspec.lock b/pubspec.lock index 088143aa..b7ef8724 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -189,7 +189,7 @@ packages: name: cubit_form url: "https://pub.dartlang.org" source: hosted - version: "1.0.0-nullsafety.0" + version: "1.0.2-nullsafety.0" cupertino_icons: dependency: "direct main" description: @@ -210,7 +210,7 @@ packages: name: dio url: "https://pub.dartlang.org" source: hosted - version: "4.0.0-prev1" + version: "4.0.0-prev3" easy_localization: dependency: "direct main" description: @@ -304,7 +304,7 @@ packages: name: flutter_markdown url: "https://pub.dartlang.org" source: hosted - version: "0.6.0" + version: "0.6.1" flutter_secure_storage: dependency: "direct main" description: @@ -363,7 +363,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.0" + version: "0.13.1" http_multi_server: dependency: transitive description: @@ -384,7 +384,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" intl: dependency: transitive description: @@ -412,7 +412,7 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.0.1" json_serializable: dependency: "direct dev" description: @@ -580,7 +580,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.1.1" provider: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b891badd..03565a4e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: easy_localization: ^3.0.0 either_option: ^2.0.1-dev.1 equatable: ^2.0.0 - flutter_bloc: ^7.0.0-nullsafety.5 + flutter_bloc: ^7.0.0 flutter_markdown: ^0.6.0 flutter_secure_storage: ^4.1.0 get_it: ^6.0.0 From bb6de7ff678b170c905238369d0d32c86f8b5478 Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 23 Mar 2021 20:50:11 +0100 Subject: [PATCH 02/17] update --- assets/translations/en.json | 3 +++ assets/translations/ru.json | 3 +++ lib/logic/api_maps/server.dart | 3 ++- lib/logic/cubit/app_config/app_config_state.dart | 6 +++--- lib/ui/components/brand_timer/brand_timer.dart | 15 ++++++++------- lib/ui/pages/initializing/initializing.dart | 10 +++++----- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index e7f9036b..c87ff8f9 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -172,5 +172,8 @@ "_comment": "messages in modals", "1": "Сервер с таким именем уже существует", "2": "Уничтожить сервер и создать новый?" + }, + "timer": { + "sec": "{} sec" } } \ No newline at end of file diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 1783dd73..51a3e8c0 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -172,5 +172,8 @@ "_comment": "messages in modals", "1": "Сервер с таким именем уже существует", "2": "Уничтожить сервер и создать новый?" + }, + "timer": { + "sec": "{} сек" } } \ No newline at end of file diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 5f5abda4..fcb22330 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -13,10 +13,11 @@ class ServerApi extends ApiMap { Future isHttpServerWorking() async { bool res; - + print('start'); Response response; try { response = await loggedClient.get('/serviceStatus'); + print(response); res = response.statusCode == HttpStatus.ok; } catch (e) { res = false; diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index d49c3382..ad770fa3 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -36,9 +36,9 @@ class AppConfigState extends Equatable { final CloudFlareDomain? cloudFlareDomain; final User? rootUser; final HetznerServerDetails? hetznerServer; - final bool? isServerStarted; - final bool? isServerReseted; - final bool? hasFinalChecked; + final bool isServerStarted; + final bool isServerReseted; + final bool hasFinalChecked; final bool? isLoading; final Exception? error; diff --git a/lib/ui/components/brand_timer/brand_timer.dart b/lib/ui/components/brand_timer/brand_timer.dart index e1395ced..38eeb356 100644 --- a/lib/ui/components/brand_timer/brand_timer.dart +++ b/lib/ui/components/brand_timer/brand_timer.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/utils/named_font_weight.dart'; +import 'package:easy_localization/easy_localization.dart'; class BrandTimer extends StatefulWidget { const BrandTimer({ @@ -11,8 +12,8 @@ class BrandTimer extends StatefulWidget { required this.duration, }) : super(key: key); - final DateTime? startDateTime; - final Duration? duration; + final DateTime startDateTime; + final Duration duration; @override _BrandTimerState createState() => _BrandTimerState(); @@ -31,8 +32,8 @@ class _BrandTimerState extends State { _timerStart() { _timeString = diffenceFromStart; timer = Timer.periodic(Duration(seconds: 1), (Timer t) { - var timePassed = DateTime.now().difference(widget.startDateTime!); - if (timePassed > widget.duration!) { + var timePassed = DateTime.now().difference(widget.startDateTime); + if (timePassed > widget.duration) { t.cancel(); } else { _getTime(); @@ -66,14 +67,14 @@ class _BrandTimerState extends State { } String get diffenceFromStart => - _durationToString(DateTime.now().difference(widget.startDateTime!)); + _durationToString(DateTime.now().difference(widget.startDateTime)); String _durationToString(Duration duration) { String twoDigits(int n) => n.toString().padLeft(2, "0"); String twoDigitSeconds = - twoDigits(widget.duration!.inSeconds - duration.inSeconds.remainder(60)); + twoDigits(widget.duration.inSeconds - duration.inSeconds.remainder(60)); - return "$twoDigitSeconds cек"; + return "timer.sec".tr(args: [twoDigitSeconds]); } @override diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index 0f914a6b..ad486849 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -431,10 +431,10 @@ class InitializingPage extends StatelessWidget { assert(appConfigCubit.state is TimerState, 'wronge state'); var state = appConfigCubit.state as TimerState; - String? text; - if (state.isServerReseted!) { + late String? text; + if (state.isServerReseted) { text = 'initializing.13'.tr(); - } else if (state.isServerStarted!) { + } else if (state.isServerStarted) { text = 'initializing.14'.tr(); } else if (state.isServerCreated) { text = 'initializing.15'.tr(); @@ -452,8 +452,8 @@ class InitializingPage extends StatelessWidget { children: [ BrandText.body2('initializing.16'.tr()), BrandTimer( - startDateTime: state.timerStart, - duration: state.duration, + startDateTime: state.timerStart!, + duration: state.duration!, ) ], ), From 33f8f033c371e559de05d5a1abfe16c10c61c532 Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 23 Mar 2021 20:57:44 +0100 Subject: [PATCH 03/17] update --- assets/translations/en.json | 5 ++++- assets/translations/ru.json | 5 ++++- lib/ui/pages/more/app_settings/app_setting.dart | 8 ++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index c87ff8f9..4fe7ec04 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -171,7 +171,10 @@ "modals": { "_comment": "messages in modals", "1": "Сервер с таким именем уже существует", - "2": "Уничтожить сервер и создать новый?" + "2": "Уничтожить сервер и создать новый?", + "3": "Вы уверенны", + "4": "Сбросить все ключи?", + "5": "Да, сбросить" }, "timer": { "sec": "{} sec" diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 51a3e8c0..87e8ed04 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -171,7 +171,10 @@ "modals": { "_comment": "messages in modals", "1": "Сервер с таким именем уже существует", - "2": "Уничтожить сервер и создать новый?" + "2": "Уничтожить сервер и создать новый?", + "3": "Вы уверенны", + "4": "Сбросить все ключи?", + "5": "Да, сбросить" }, "timer": { "sec": "{} сек" diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 8d3d33c1..981b3cd6 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -96,11 +96,11 @@ class _AppSettingsPageState extends State { context: context, builder: (_) { return BrandAlert( - title: 'Вы уверенны', - contentText: 'Сбросить все ключи?', + title: 'modals.3'.tr(), + contentText: 'modals.4'.tr(), acitons: [ ActionButton( - text: 'Да, сбросить', + text: 'modals.5'.tr(), isRed: true, onPressed: () { context @@ -109,7 +109,7 @@ class _AppSettingsPageState extends State { Navigator.of(context).pop(); }), ActionButton( - text: 'Отмена', + text: 'basis.cancel'.tr(), ), ], ); From 6a9e72dc4c33ca6367876352b7e0590b82153767 Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 23 Mar 2021 21:00:17 +0100 Subject: [PATCH 04/17] fix --- lib/logic/cubit/app_config/app_config_repository.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 9c3ee1c1..a8b00367 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -145,7 +145,7 @@ class AppConfigRepository { contentText: 'modals.2'.tr(), acitons: [ ActionButton( - text: 'Удалить', + text: 'basis.delete'.tr(), isRed: true, onPressed: () async { await hetznerApi.deleteSelfprivacyServer( @@ -164,7 +164,7 @@ class AppConfigRepository { }, ), ActionButton( - text: 'Отменить', + text: 'basis.cancel'.tr(), onPressed: () { hetznerApi.close(); onCancel!(); From 97c6fa435dbde69edf5e167ecd853f97f2a77089 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 24 Mar 2021 14:12:09 +0100 Subject: [PATCH 05/17] fix --- assets/translations/en.json | 4 +++- assets/translations/ru.json | 4 +++- lib/ui/pages/initializing/initializing.dart | 4 ++-- lib/ui/pages/services/services.dart | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 4fe7ec04..cf05029e 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -20,7 +20,8 @@ "domain": "Domain", "saving": "Saving..", "nickname": "nickname", - "loading": "loading" + "loading": "loading", + "later": "Настрою потом" }, "more": { "_comment": "'More' tab", @@ -157,6 +158,7 @@ "8": "Loading domains list", "9": "Found more than one domain. For your own security, please be asked to delete unnecessary domains", "10": "Save domain", + "final": "Final step", "11": "Create server", "what": "What does it mean?", "13": "Server rebooted. Waiting for the last verification...", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 87e8ed04..2df8b863 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -20,7 +20,8 @@ "domain": "Домен", "saving": "Сохранение..", "nickname": "Никнейм", - "loading": "Загрузка" + "loading": "Загрузка", + "later": "Настрою потом" }, "more": { "_comment": "вкладка еще", @@ -157,6 +158,7 @@ "8": "Загружаем список доменов", "9": "Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены", "10": "Сохранить домен", + "final": "Последний шаг", "11": "Создать сервер", "what": "Что это значит?", "13": "Сервер презагружен, ждем последнюю проверку", diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index ad486849..d35aa90f 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -77,7 +77,7 @@ class InitializingPage extends StatelessWidget { BrandButton.text( title: cubit.state.isFullyInitilized ? 'basis.close'.tr() - : 'Настрою потом', + : 'basis.later'.tr(), onPressed: () { Navigator.of(context).pushAndRemoveUntil( materialRoute(RootPage()), @@ -408,7 +408,7 @@ class InitializingPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Spacer(flex: 2), - BrandText.h2('initializing.how'.tr()), + BrandText.h2('initializing.final'.tr()), SizedBox(height: 10), BrandText.body2('initializing.11'.tr()), Spacer(), diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index ef2284a8..4bdcd740 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -303,7 +303,7 @@ class _ServiceDetails extends StatelessWidget { child: Padding( padding: EdgeInsets.only(bottom: 0.8, left: 5), child: GestureDetector( - onTap: () => _launchURL('https://social_network.$domainName'), + onTap: () => _launchURL('https://social.$domainName'), child: Text( 'social.$domainName', style: linksStyle, From 3c683bef638a25f5ca56e5c5be5f42705a42b7c6 Mon Sep 17 00:00:00 2001 From: Kherel Date: Thu, 25 Mar 2021 09:32:00 +0100 Subject: [PATCH 06/17] fix --- assets/translations/en.json | 11 ++- assets/translations/ru.json | 11 ++- lib/logic/api_maps/server.dart | 2 - .../cubit/app_config/app_config_cubit.dart | 17 ++-- .../app_config/app_config_repository.dart | 78 +++++++++++-------- .../cubit/app_config/app_config_state.dart | 26 ++++--- .../pages/more/app_settings/app_setting.dart | 12 +-- lib/ui/pages/more/more.dart | 2 +- 8 files changed, 96 insertions(+), 63 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index cf05029e..3c7a098a 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -21,18 +21,25 @@ "saving": "Saving..", "nickname": "nickname", "loading": "loading", - "later": "Настрою потом" + "later": "Настрою потом", + "reset": "Reset" }, "more": { "_comment": "'More' tab", "configuration_wizard": "Setup wizard", - "settings": "Application settings", "about_project": "About us", "about_app": "About application", "onboarding": "Onboarding", "console": "Console", "about_app_page": { "text": "Тут любая служебная информация, v.{}" + }, + "settings": { + "title": "Application settings", + "1": "Dark Theme", + "2": "Change your the app theme", + "3": "Reset app config", + "4": "Reset api keys and root user" } }, "onboarding": { diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 2df8b863..a6e0d26b 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -21,18 +21,25 @@ "saving": "Сохранение..", "nickname": "Никнейм", "loading": "Загрузка", - "later": "Настрою потом" + "later": "Настрою потом", + "reset": "Reset" }, "more": { "_comment": "вкладка еще", "configuration_wizard": "Мастер Подключения", - "settings": "Настройки приложения", "about_project": "О проекте SelfPrivacy", "about_app": "О приложении", "onboarding": "Onboarding", "console": "Console", "about_app_page": { "text": "Тут любая служебная информация, v.{}" + }, + "settings": { + "title": "Настройки приложения", + "1": "Темная тема", + "2": "Сменить цветовую тему", + "3": "Сброс настроек", + "4": "Сбросить API ключи а так же root пользвателя" } }, "onboarding": { diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index fcb22330..d1e40535 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -13,11 +13,9 @@ class ServerApi extends ApiMap { Future isHttpServerWorking() async { bool res; - print('start'); Response response; try { response = await loggedClient.get('/serviceStatus'); - print(response); res = response.statusCode == HttpStatus.ok; } catch (e) { res = false; diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 4d8dd8a5..11500276 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -50,15 +50,10 @@ class AppConfigCubit extends Cubit { if (state.progress < 6 || state.isFullyInitilized) { emit(state); } else if (state.progress == 6) { - print('startServerIfDnsIsOkay'); - startServerIfDnsIsOkay(state: state, isImmediate: true); } else if (state.progress == 7) { - print('resetServerIfServerIsOkay'); - resetServerIfServerIsOkay(state: state, isImmediate: true); } else if (state.progress == 8) { - print('finishCheckIfServerIsOkay'); finishCheckIfServerIsOkay(state: state, isImmediate: true); } } @@ -83,6 +78,8 @@ class AppConfigCubit extends Cubit { state.hetznerServer!, ); repository.saveServerDetails(server); + repository.saveIsServerStarted(true); + emit( state.copyWith( isServerStarted: true, @@ -136,6 +133,9 @@ class AppConfigCubit extends Cubit { state!.hetznerKey, state.hetznerServer!, ); + repository.saveIsServerReseted(true); + repository.saveServerDetails(hetznerServerDetails); + emit( state.copyWith( isServerReseted: true, @@ -181,7 +181,12 @@ class AppConfigCubit extends Cubit { ); if (isServerWorking) { - emit(state.copyWith(hasFinalChecked: true, isLoading: false)); + repository.saveHasFinalChecked(true); + + emit(state.copyWith( + hasFinalChecked: true, + isLoading: false, + )); } else { finishCheckIfServerIsOkay(); } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index a8b00367..e59c1255 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -21,6 +21,10 @@ class AppConfigRepository { Box box = Hive.box(BNames.appConfig); AppConfigState load() { + // saveIsServerStarted(false); + // saveIsServerReseted(false); + // saveHasFinalChecked(false); + return AppConfigState( hetznerKey: box.get(BNames.hetznerKey), cloudFlareKey: box.get(BNames.cloudFlareKey), @@ -29,10 +33,10 @@ class AppConfigRepository { rootUser: box.get(BNames.rootUser), hetznerServer: box.get(BNames.hetznerServer), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - error: null, - hasFinalChecked: box.get(BNames.hasFinalChecked, defaultValue: false), - isLoading: box.get(BNames.isLoading, defaultValue: false), isServerReseted: box.get(BNames.isServerReseted, defaultValue: false), + hasFinalChecked: box.get(BNames.hasFinalChecked, defaultValue: false), + error: null, + isLoading: box.get(BNames.isLoading, defaultValue: false), ); } @@ -40,26 +44,6 @@ class AppConfigRepository { box.clear(); } - void saveHetznerKey(String key) { - box.put(BNames.hetznerKey, key); - } - - void saveBackblazeKey(BackblazeCredential backblazeCredential) { - box.put(BNames.backblazeKey, backblazeCredential); - } - - void saveCloudFlare(String key) { - box.put(BNames.cloudFlareKey, key); - } - - void saveDomain(CloudFlareDomain cloudFlareDomain) { - box.put(BNames.cloudFlareDomain, cloudFlareDomain); - } - - void saveRootUser(User rootUser) { - box.put(BNames.rootUser, rootUser); - } - Future startServer( String? hetznerKey, HetznerServerDetails hetznerServer, @@ -67,17 +51,11 @@ class AppConfigRepository { var hetznerApi = HetznerApi(hetznerKey); var serverDetails = await hetznerApi.startServer(server: hetznerServer); hetznerApi.close(); - box.put(BNames.isServerStarted, true); return serverDetails; } - Future saveServerDetails(HetznerServerDetails serverDetails) async { - await box.put(BNames.hetznerServer, serverDetails); - } - Future isDnsAddressesMatch(String? domainName, String? ip4) async { - print(domainName); var addresses = [ '$domainName', 'api.$domainName', @@ -111,8 +89,6 @@ class AppConfigRepository { } } - box.put(BNames.hasFinalChecked, true); - return true; } @@ -133,8 +109,8 @@ class AppConfigRepository { rootUser: rootUser, domainName: domainName, ); - await box.put(BNames.hetznerServer, serverDetails); hetznerApi.close(); + saveServerDetails(serverDetails); onSuccess(serverDetails); } on DioError catch (e) { if (e.response!.data['error']['code'] == 'uniqueness_error') { @@ -159,7 +135,7 @@ class AppConfigRepository { ); hetznerApi.close(); - await box.put(BNames.hetznerServer, serverDetails); + await saveServerDetails(serverDetails); onSuccess(serverDetails); }, ), @@ -211,4 +187,40 @@ class AppConfigRepository { var hetznerApi = HetznerApi(hetznerKey); return await hetznerApi.restart(server: server); } + + Future saveServerDetails(HetznerServerDetails serverDetails) async { + await box.put(BNames.hetznerServer, serverDetails); + } + + Future saveIsServerStarted(bool value) async { + await box.put(BNames.isServerStarted, value); + } + + Future saveHetznerKey(String key) async { + await box.put(BNames.hetznerKey, key); + } + + Future saveIsServerReseted(bool value) async { + await box.put(BNames.isServerReseted, value); + } + + Future saveBackblazeKey(BackblazeCredential backblazeCredential) async { + await box.put(BNames.backblazeKey, backblazeCredential); + } + + Future saveCloudFlare(String key) async { + await box.put(BNames.cloudFlareKey, key); + } + + void saveDomain(CloudFlareDomain cloudFlareDomain) async { + await box.put(BNames.cloudFlareDomain, cloudFlareDomain); + } + + void saveRootUser(User rootUser) async { + await box.put(BNames.rootUser, rootUser); + } + + void saveHasFinalChecked(bool value) async { + await box.put(BNames.hasFinalChecked, value); + } } diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index ad770fa3..6b9f8ee3 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -80,17 +80,21 @@ class AppConfigState extends Equatable { bool get isFullyInitilized => _fulfilementList.every((el) => el!); int get progress => _fulfilementList.where((el) => el!).length; - List get _fulfilementList => [ - isHetznerFilled, - isCloudFlareFilled, - isBackblazeFilled, - isDomainFilled, - isUserFilled, - isServerCreated, - isServerStarted, - isServerReseted, - hasFinalChecked, - ]; + List get _fulfilementList { + var res = [ + isHetznerFilled, + isCloudFlareFilled, + isBackblazeFilled, + isDomainFilled, + isUserFilled, + isServerCreated, + isServerStarted, + isServerReseted, + hasFinalChecked, + ]; + print('progress: $res'); + return res; + } } class InitialAppConfigState extends AppConfigState { diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 981b3cd6..9e89d7dc 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -28,7 +28,7 @@ class _AppSettingsPageState extends State { return Scaffold( appBar: PreferredSize( child: - BrandHeader(title: 'more.settings'.tr(), hasBackButton: true), + BrandHeader(title: 'more.settings.title'.tr(), hasBackButton: true), preferredSize: Size.fromHeight(52), ), body: ListView( @@ -47,8 +47,8 @@ class _AppSettingsPageState extends State { children: [ Flexible( child: _TextColumn( - title: 'Dark Theme', - value: 'Change your the app theme', + title: 'more.settings.1'.tr(), + value: 'more.settings.2'.tr(), ), ), SizedBox(width: 5), @@ -75,8 +75,8 @@ class _AppSettingsPageState extends State { children: [ Flexible( child: _TextColumn( - title: 'Reset app config', - value: 'Reset api keys and root user', + title: 'more.settings.3'.tr(), + value: 'more.settings.4'.tr(), ), ), SizedBox(width: 5), @@ -85,7 +85,7 @@ class _AppSettingsPageState extends State { primary: BrandColors.red1, ), child: Text( - 'Reset', + 'basis.reset'.tr(), style: TextStyle( color: BrandColors.white, fontWeight: NamedFontWeight.demiBold, diff --git a/lib/ui/pages/more/more.dart b/lib/ui/pages/more/more.dart index 8959d87a..112b8c64 100644 --- a/lib/ui/pages/more/more.dart +++ b/lib/ui/pages/more/more.dart @@ -39,7 +39,7 @@ class MorePage extends StatelessWidget { goTo: InitializingPage(), ), _NavItem( - title: 'more.settings'.tr(), + title: 'more.settings.title'.tr(), iconData: BrandIcons.settings, goTo: AppSettingsPage(), ), From e4f154b4f5a578fb0af4c29631759ee621e04888 Mon Sep 17 00:00:00 2001 From: Kherel Date: Thu, 25 Mar 2021 09:46:07 +0100 Subject: [PATCH 07/17] fix --- .../app_config/app_config_repository.dart | 4 --- .../components/brand_button/brand_button.dart | 1 + lib/ui/components/brand_card/brand_card.dart | 1 - lib/ui/pages/initializing/initializing.dart | 36 ++++++++----------- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index e59c1255..5fcba08d 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -21,10 +21,6 @@ class AppConfigRepository { Box box = Hive.box(BNames.appConfig); AppConfigState load() { - // saveIsServerStarted(false); - // saveIsServerReseted(false); - // saveHasFinalChecked(false); - return AppConfigState( hetznerKey: box.get(BNames.hetznerKey), cloudFlareKey: box.get(BNames.cloudFlareKey), diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart index 192f8a8b..df6cfc39 100644 --- a/lib/ui/components/brand_button/brand_button.dart +++ b/lib/ui/components/brand_button/brand_button.dart @@ -100,6 +100,7 @@ class _TextButton extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onTap: onPressed, + behavior: HitTestBehavior.opaque, child: Container( height: 48, width: double.infinity, diff --git a/lib/ui/components/brand_card/brand_card.dart b/lib/ui/components/brand_card/brand_card.dart index 9fcfcb6b..18fd26eb 100644 --- a/lib/ui/components/brand_card/brand_card.dart +++ b/lib/ui/components/brand_card/brand_card.dart @@ -13,7 +13,6 @@ class BrandCard extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - margin: EdgeInsets.only(bottom: 30), decoration: BoxDecoration( color: Theme.of(context).brightness == Brightness.dark ? BrandColors.black diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index d35aa90f..e26ebd54 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -47,25 +47,20 @@ class InitializingPage extends StatelessWidget { body: ListView( children: [ Padding( - padding: brandPagePadding1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ProgressBar( - steps: [ - 'Hetzner', - 'CloudFlare', - 'Backblaze', - 'Domain', - 'User', - 'Server', - ' ✅', - ' ✅', - ' ✅' - ], - activeIndex: cubit.state.progress, - ), + padding: brandPagePadding2.copyWith(top: 10, bottom: 10), + child: ProgressBar( + steps: [ + 'Hetzner', + 'CloudFlare', + 'Backblaze', + 'Domain', + 'User', + 'Server', + ' ✅', + ' ✅', + ' ✅' ], + activeIndex: cubit.state.progress, ), ), _addCard( @@ -84,7 +79,6 @@ class InitializingPage extends StatelessWidget { (predicate) => false, ); }), - SizedBox(height: 30), ], ), ), @@ -415,7 +409,7 @@ class InitializingPage extends StatelessWidget { BrandButton.rised( onPressed: isLoading! ? null : appConfigCubit.createServerAndSetDnsRecords, - title: isLoading ? 'loading' : 'initializing.11'.tr(), + title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(), ), Spacer(flex: 2), BrandButton.text( @@ -472,7 +466,7 @@ class InitializingPage extends StatelessWidget { Widget _addCard(Widget child) { return Container( - height: 500, + height: 450, padding: brandPagePadding2, child: BrandCard(child: child), ); From 7d12b85f8954bbfb4918e8f2851906bcfe82ca34 Mon Sep 17 00:00:00 2001 From: Kherel Date: Thu, 25 Mar 2021 09:54:39 +0100 Subject: [PATCH 08/17] fix markup --- lib/ui/pages/providers/providers.dart | 14 +++++++++++--- lib/ui/pages/services/services.dart | 9 ++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index f4db4a53..89f48653 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -27,9 +27,17 @@ class _ProvidersPageState extends State { var isReady = context.watch().state.isFullyInitilized; final cards = ProviderType.values - .map((type) => _Card( - provider: - ProviderModel(state: StateType.uninitialized, type: type))) + .map( + (type) => Padding( + padding: EdgeInsets.only(bottom: 30), + child: _Card( + provider: ProviderModel( + state: isReady ? StateType.stable : StateType.uninitialized, + type: type, + ), + ), + ), + ) .toList(); return Scaffold( appBar: PreferredSize( diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 4bdcd740..0a023aec 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -40,7 +40,14 @@ class _ServicesPageState extends State { BrandText.body1('services.title'.tr()), SizedBox(height: 24), if (!isReady) ...[NotReadyCard(), SizedBox(height: 24)], - ...ServiceTypes.values.map((t) => _Card(serviceType: t)).toList() + ...ServiceTypes.values + .map((t) => Padding( + padding: EdgeInsets.only( + bottom: 30, + ), + child: _Card(serviceType: t), + )) + .toList() ], ), ); From 20cca91e004461e88135742e73f19aa377e123a4 Mon Sep 17 00:00:00 2001 From: Kherel Date: Thu, 25 Mar 2021 21:09:56 +0100 Subject: [PATCH 09/17] before change api_client --- lib/config/get_it_config.dart | 6 +- lib/logic/api_maps/api_map.dart | 4 +- lib/logic/api_maps/backblaze.dart | 2 +- lib/logic/api_maps/cloudflare.dart | 2 +- lib/logic/api_maps/hetzner.dart | 2 +- lib/logic/api_maps/server.dart | 2 +- .../cubit/app_config/app_config_cubit.dart | 2 +- .../app_config/app_config_repository.dart | 45 ++++++------- lib/logic/get_it/api_config.dart | 63 +++++++++++++++++++ lib/main.dart | 3 +- lib/ui/pages/services/services.dart | 19 +++++- 11 files changed, 116 insertions(+), 34 deletions(-) create mode 100644 lib/logic/get_it/api_config.dart diff --git a/lib/config/get_it_config.dart b/lib/config/get_it_config.dart index 6a05afc4..8d7faa7b 100644 --- a/lib/config/get_it_config.dart +++ b/lib/config/get_it_config.dart @@ -1,4 +1,5 @@ import 'package:get_it/get_it.dart'; +import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/get_it/navigation.dart'; import 'package:selfprivacy/logic/get_it/timer.dart'; @@ -9,9 +10,12 @@ export 'package:selfprivacy/logic/get_it/timer.dart'; final getIt = GetIt.instance; -void getItSetup() { +Future getItSetup() async { getIt.registerSingleton(NavigationService()); getIt.registerSingleton(ConsoleModel()); getIt.registerSingleton(TimerModel()); + getIt.registerSingleton(ApiConfigModel()..init()); + + await getIt.allReady(); } diff --git a/lib/logic/api_maps/api_map.dart b/lib/logic/api_maps/api_map.dart index 505ec178..e67da5c1 100644 --- a/lib/logic/api_maps/api_map.dart +++ b/lib/logic/api_maps/api_map.dart @@ -7,8 +7,8 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/models/message.dart'; -abstract class ApiMap { - ApiMap() { +abstract class ApiMapOld { + ApiMapOld() { var client = Dio()..interceptors.add(ConsoleInterceptor()); (client.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) { diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/backblaze.dart index 5bf84863..de04225e 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/backblaze.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/logic/api_maps/api_map.dart'; -class BackblazeApi extends ApiMap { +class BackblazeApi extends ApiMapOld { BackblazeApi([String? token]) { if (token != null) { loggedClient.options = BaseOptions( diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart index c72c7bad..1f09a0f3 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/cloudflare.dart @@ -4,7 +4,7 @@ import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; import 'package:selfprivacy/logic/models/dns_records.dart'; -class CloudflareApi extends ApiMap { +class CloudflareApi extends ApiMapOld { CloudflareApi([String? token]) { if (token != null) { loggedClient.options = diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 8459d315..dabec6b7 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -7,7 +7,7 @@ import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/utils/password_generator2.dart'; -class HetznerApi extends ApiMap { +class HetznerApi extends ApiMapOld { HetznerApi([String? token]) { if (token != null) { loggedClient.options = BaseOptions( diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index d1e40535..6bf5afa4 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -4,7 +4,7 @@ import 'package:dio/dio.dart'; import 'api_map.dart'; -class ServerApi extends ApiMap { +class ServerApi extends ApiMapOld { ServerApi(String? domainName) { loggedClient.options = BaseOptions( baseUrl: 'https://api.$domainName', diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 11500276..8fc1ef7f 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -219,7 +219,7 @@ class AppConfigCubit extends Cubit { } void setCloudflareKey(String cloudFlareKey) { - repository.saveCloudFlare(cloudFlareKey); + repository.saveCloudFlareKey(cloudFlareKey); emit(state.copyWith(cloudFlareKey: cloudFlareKey)); } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 5fcba08d..3980e624 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -4,6 +4,7 @@ import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; @@ -22,12 +23,12 @@ class AppConfigRepository { AppConfigState load() { return AppConfigState( - hetznerKey: box.get(BNames.hetznerKey), - cloudFlareKey: box.get(BNames.cloudFlareKey), - cloudFlareDomain: box.get(BNames.cloudFlareDomain), - backblazeCredential: box.get(BNames.backblazeKey), + hetznerKey: getIt().hetznerKey, + cloudFlareKey: getIt().cloudFlareKey, + cloudFlareDomain: getIt().cloudFlareDomain, + backblazeCredential: getIt().backblazeCredential, + hetznerServer: getIt().hetznerServer, rootUser: box.get(BNames.rootUser), - hetznerServer: box.get(BNames.hetznerServer), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), isServerReseted: box.get(BNames.isServerReseted, defaultValue: false), hasFinalChecked: box.get(BNames.hasFinalChecked, defaultValue: false), @@ -185,33 +186,33 @@ class AppConfigRepository { } Future saveServerDetails(HetznerServerDetails serverDetails) async { - await box.put(BNames.hetznerServer, serverDetails); + await getIt().storeServerDetails(serverDetails); + } + + Future saveHetznerKey(String key) async { + await getIt().storeHetznerKey(key); + } + + Future saveBackblazeKey(BackblazeCredential backblazeCredential) async { + await getIt().storeBackblazeCredential(backblazeCredential); + } + + Future saveCloudFlareKey(String key) async { + await getIt().storeCloudFlareKey(key); + } + + Future saveDomain(CloudFlareDomain cloudFlareDomain) async { + await getIt().storeCloudFlareDomain(cloudFlareDomain); } Future saveIsServerStarted(bool value) async { await box.put(BNames.isServerStarted, value); } - Future saveHetznerKey(String key) async { - await box.put(BNames.hetznerKey, key); - } - Future saveIsServerReseted(bool value) async { await box.put(BNames.isServerReseted, value); } - Future saveBackblazeKey(BackblazeCredential backblazeCredential) async { - await box.put(BNames.backblazeKey, backblazeCredential); - } - - Future saveCloudFlare(String key) async { - await box.put(BNames.cloudFlareKey, key); - } - - void saveDomain(CloudFlareDomain cloudFlareDomain) async { - await box.put(BNames.cloudFlareDomain, cloudFlareDomain); - } - void saveRootUser(User rootUser) async { await box.put(BNames.rootUser, rootUser); } diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart new file mode 100644 index 00000000..6c5636d1 --- /dev/null +++ b/lib/logic/get_it/api_config.dart @@ -0,0 +1,63 @@ +import 'package:hive/hive.dart'; +import 'package:selfprivacy/config/hive_config.dart'; +import 'package:selfprivacy/logic/models/backblaze_credential.dart'; +import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_details.dart'; + +class ApiConfigModel { + Box _box = Hive.box(BNames.appConfig); + + HetznerServerDetails? get hetznerServer => _hetznerServer; + String? get hetznerKey => _hetznerKey; + String? get cloudFlareKey => _cloudFlareKey; + BackblazeCredential? get backblazeCredential => _backblazeCredential; + CloudFlareDomain? get cloudFlareDomain => _cloudFlareDomain; + + String? _hetznerKey; + String? _cloudFlareKey; + HetznerServerDetails? _hetznerServer; + BackblazeCredential? _backblazeCredential; + CloudFlareDomain? _cloudFlareDomain; + + Future storeHetznerKey(String value) async { + await _box.put(BNames.hetznerKey, value); + _hetznerKey = value; + } + + Future storeCloudFlareKey(String value) async { + await _box.put(BNames.cloudFlareKey, value); + _cloudFlareKey = value; + } + + Future storeBackblazeCredential(BackblazeCredential value) async { + await _box.put(BNames.backblazeKey, value); + + _backblazeCredential = value; + } + + Future storeCloudFlareDomain(CloudFlareDomain value) async { + await _box.put(BNames.cloudFlareDomain, value); + _cloudFlareDomain = value; + } + + Future storeServerDetails(HetznerServerDetails value) async { + await _box.put(BNames.hetznerServer, value); + _hetznerServer = value; + } + + clear() { + _hetznerKey = null; + _cloudFlareKey = null; + _backblazeCredential = null; + _cloudFlareDomain = null; + _hetznerServer = null; + } + + void init() { + _hetznerKey = _box.get(BNames.hetznerKey); + _cloudFlareKey = _box.get(BNames.cloudFlareKey); + _backblazeCredential = _box.get(BNames.backblazeKey); + _cloudFlareDomain = _box.get(BNames.cloudFlareDomain); + _hetznerServer = _box.get(BNames.hetznerServer); + } +} diff --git a/lib/main.dart b/lib/main.dart index 4d678e31..231a7dca 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,12 +15,11 @@ import 'config/get_it_config.dart'; import 'config/localization.dart'; import 'logic/cubit/app_settings/app_settings_cubit.dart'; - void main() async { await HiveConfig.init(); Bloc.observer = SimpleBlocObserver(); Wakelock.enable(); - getItSetup(); + await getItSetup(); WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 0a023aec..dd48ae6d 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -378,6 +378,21 @@ class _ServiceDetails extends StatelessWidget { ); } - void _launchURL(url) async => - await canLaunch(url) ? await launch(url) : throw 'Could not launch $url'; + void _launchURL(url) async { + var _possible = await canLaunch(url); + + if (_possible) { + try { + await launch( + url, + forceSafariVC: true, + enableJavaScript: true, + ); + } catch (e) { + print(e); + } + } else { + throw 'Could not launch $url'; + } + } } From bc6c55b5287daffc8419434fe890c7358c451567 Mon Sep 17 00:00:00 2001 From: Kherel Date: Fri, 26 Mar 2021 00:30:34 +0100 Subject: [PATCH 10/17] change http client --- assets/translations/ru.json | 2 +- lib/config/get_it_config.dart | 2 + lib/logic/api_maps/api_map.dart | 46 ++++++-- lib/logic/api_maps/backblaze.dart | 51 +++++---- lib/logic/api_maps/cloudflare.dart | 104 ++++++++++-------- lib/logic/api_maps/hetzner.dart | 92 +++++++++++----- lib/logic/api_maps/server.dart | 51 ++++----- .../cubit/app_config/app_config_cubit.dart | 28 ++--- .../app_config/app_config_repository.dart | 39 +++---- .../initializing/backblaze_form_cubit.dart | 13 +-- .../initializing/cloudflare_form_cubit.dart | 5 +- .../forms/initializing/domain_cloudflare.dart | 20 +--- .../forms/initializing/domain_form_cubit.dart | 68 ------------ .../initializing/hetzner_form_cubit.dart | 12 +- .../initializing/root_user_form_cubit.dart | 13 --- .../cubit/forms/user/user_form_cubit.dart | 1 - lib/logic/cubit/services/services_cubit.dart | 28 ----- lib/logic/cubit/services/services_state.dart | 26 ----- lib/logic/models/server_info.dart | 23 ++++ lib/logic/models/service.dart | 25 ----- lib/main.dart | 2 + lib/ui/pages/initializing/initializing.dart | 2 +- lib/ui/pages/onboarding/onboarding.dart | 6 +- lib/ui/pages/services/services.dart | 1 + pubspec.lock | 7 ++ pubspec.yaml | 1 + 26 files changed, 292 insertions(+), 376 deletions(-) delete mode 100644 lib/logic/cubit/forms/initializing/domain_form_cubit.dart delete mode 100644 lib/logic/cubit/services/services_cubit.dart delete mode 100644 lib/logic/cubit/services/services_state.dart create mode 100644 lib/logic/models/server_info.dart delete mode 100644 lib/logic/models/service.dart diff --git a/assets/translations/ru.json b/assets/translations/ru.json index a6e0d26b..0a7bbd2e 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -29,7 +29,7 @@ "configuration_wizard": "Мастер Подключения", "about_project": "О проекте SelfPrivacy", "about_app": "О приложении", - "onboarding": "Onboarding", + "onboarding": "Приветствие", "console": "Console", "about_app_page": { "text": "Тут любая служебная информация, v.{}" diff --git a/lib/config/get_it_config.dart b/lib/config/get_it_config.dart index 8d7faa7b..3f01e6d3 100644 --- a/lib/config/get_it_config.dart +++ b/lib/config/get_it_config.dart @@ -4,10 +4,12 @@ import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/get_it/navigation.dart'; import 'package:selfprivacy/logic/get_it/timer.dart'; +export 'package:selfprivacy/logic/get_it/api_config.dart'; export 'package:selfprivacy/logic/get_it/console.dart'; export 'package:selfprivacy/logic/get_it/navigation.dart'; export 'package:selfprivacy/logic/get_it/timer.dart'; + final getIt = GetIt.instance; Future getItSetup() async { diff --git a/lib/logic/api_maps/api_map.dart b/lib/logic/api_maps/api_map.dart index e67da5c1..ce5484a5 100644 --- a/lib/logic/api_maps/api_map.dart +++ b/lib/logic/api_maps/api_map.dart @@ -1,32 +1,58 @@ +import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:dio/adapter.dart'; import 'package:dio/dio.dart'; +import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/models/message.dart'; -abstract class ApiMapOld { - ApiMapOld() { - var client = Dio()..interceptors.add(ConsoleInterceptor()); - (client.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = +abstract class ApiMap { + + Future getClient() async { + var dio = Dio(await options); + if (hasLoger) { + dio.interceptors.add(PrettyDioLogger()); + } + dio..interceptors.add(ConsoleInterceptor()); + (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) { client.badCertificateCallback = (X509Certificate cert, String host, int port) => true; return client; }; - loggedClient = client; + return dio; } - String? rootAddress; - late Dio loggedClient; + FutureOr get options; - void close() { - loggedClient.close(); - } + abstract final String rootAddress; + abstract final bool hasLoger; + abstract final bool isWithToken; } +// abstract class ApiMapOld { +// ApiMapOld() { +// var client = Dio()..interceptors.add(ConsoleInterceptor()); +// (client.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = +// (HttpClient client) { +// client.badCertificateCallback = +// (X509Certificate cert, String host, int port) => true; +// return client; +// }; +// loggedClient = client; +// } +// String? rootAddress; + +// late Dio loggedClient; + +// void close() { +// loggedClient.close(); +// } +// } + class ConsoleInterceptor extends InterceptorsWrapper { void addMessage(Message message) { getIt.get().addMessage(message); diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/backblaze.dart index de04225e..f3989751 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/backblaze.dart @@ -1,31 +1,38 @@ import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/api_map.dart'; -class BackblazeApi extends ApiMapOld { - BackblazeApi([String? token]) { - if (token != null) { - loggedClient.options = BaseOptions( - headers: {'Authorization': 'Basic $token'}, - baseUrl: rootAddress!, - ); +class BackblazeApi extends ApiMap { + BackblazeApi({this.hasLoger = false, this.isWithToken = true}); + + BaseOptions get options { + var options = BaseOptions(baseUrl: rootAddress); + if (isWithToken) { + var backblazeCredential = getIt().backblazeCredential; + var token = backblazeCredential!.applicationKey; + assert(token != null); + options.headers = {'Authorization': 'Basic $token'}; } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + + return options; } + ValidateStatus? validateStatus; @override - String? rootAddress = - 'https://api.backblazeb2.com/b2api/v2/b2_authorize_account'; + String rootAddress = 'https://api.backblazeb2.com/b2api/v2/'; - Future isValid(String token) async { - var options = Options( - headers: {'Authorization': 'Basic $token'}, - validateStatus: (status) { - return status == HttpStatus.ok || status == HttpStatus.unauthorized; - }, + Future isValid(String encodedApiKey) async { + var client = await getClient(); + Response response = await client.get( + 'b2_authorize_account', + options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}), ); - - Response response = await loggedClient.get(rootAddress!, options: options); - + client.close(); if (response.statusCode == HttpStatus.ok) { return true; } else if (response.statusCode == HttpStatus.unauthorized) { @@ -34,4 +41,10 @@ class BackblazeApi extends ApiMapOld { throw Exception('code: ${response.statusCode}'); } } -} \ No newline at end of file + + @override + bool hasLoger; + + @override + bool isWithToken; +} diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart index 1f09a0f3..40cbef42 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/cloudflare.dart @@ -1,30 +1,43 @@ import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; import 'package:selfprivacy/logic/models/dns_records.dart'; -class CloudflareApi extends ApiMapOld { - CloudflareApi([String? token]) { - if (token != null) { - loggedClient.options = - BaseOptions(headers: {'Authorization': 'Bearer $token'}); +class CloudflareApi extends ApiMap { + CloudflareApi({this.hasLoger = false, this.isWithToken = true}); + + BaseOptions get options { + var options = BaseOptions(baseUrl: rootAddress); + if (isWithToken) { + var token = getIt().cloudFlareKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + return options; } + ValidateStatus? validateStatus; + @override - String? rootAddress = 'https://api.cloudflare.com/client/v4'; + String rootAddress = 'https://api.cloudflare.com/client/v4'; Future isValid(String token) async { - var url = '$rootAddress/user/tokens/verify'; - var options = Options( - headers: {'Authorization': 'Bearer $token'}, - validateStatus: (status) { - return status == HttpStatus.ok || status == HttpStatus.unauthorized; - }, - ); + validateStatus = (status) { + return status == HttpStatus.ok || status == HttpStatus.unauthorized; + }; - Response response = await loggedClient.get(url, options: options); + var client = await getClient(); + Response response = await client.get('/user/tokens/verify', + options: Options(headers: {'Authorization': 'Bearer $token'})); + + client.close(); + validateStatus = null; if (response.statusCode == HttpStatus.ok) { return true; @@ -35,22 +48,19 @@ class CloudflareApi extends ApiMapOld { } } - Future getZoneId(String? token, String domain) async { - var url = '$rootAddress/zones'; - - var options = Options( - headers: {'Authorization': 'Bearer $token'}, - validateStatus: (status) { - return status == HttpStatus.ok || status == HttpStatus.forbidden; - }, - ); - - Response response = await loggedClient.get( - url, - options: options, + Future getZoneId(String domain) async { + validateStatus = (status) { + return status == HttpStatus.ok || status == HttpStatus.forbidden; + }; + var client = await getClient(); + Response response = await client.get( + '/zones', queryParameters: {'name': domain}, ); + client.close(); + validateStatus = null; + try { return response.data['result'][0]['id']; } catch (error) { @@ -65,20 +75,24 @@ class CloudflareApi extends ApiMapOld { var domainName = cloudFlareDomain.domainName; var domainZoneId = cloudFlareDomain.zoneId; - var url = '$rootAddress/zones/$domainZoneId/dns_records'; + var url = '/zones/$domainZoneId/dns_records'; + + var client = await getClient(); + Response response = await client.get(url); - var response = await loggedClient.get(url); List records = response.data['result'] ?? []; var allDeleteFutures = []; for (var record in records) { if (record['zone_name'] == domainName) { allDeleteFutures.add( - loggedClient.delete('$url/${record["id"]}'), + client.delete('$url/${record["id"]}'), ); } } + await Future.wait(allDeleteFutures); + client.close(); } Future createMultipleDnsRecords({ @@ -92,10 +106,11 @@ class CloudflareApi extends ApiMapOld { var url = '$rootAddress/zones/$domainZoneId/dns_records'; var allCreateFutures = []; + var client = await getClient(); for (var record in listDnsRecords) { allCreateFutures.add( - loggedClient.post( + client.post( url, data: record.toJson(), ), @@ -103,23 +118,9 @@ class CloudflareApi extends ApiMapOld { } await Future.wait(allCreateFutures); + client.close(); } - // setDkim(String dkimRecordString, String domainZoneId) { - // var txt3 = DnsRecords( - // type: 'TXT', - // name: 'selector._domainkey', - // content: dkimRecordString, - // ttl: 18000, - // ); - - // var url = '$rootAddress/zones/$domainZoneId/dns_records'; - // loggedClient.post( - // url, - // data: txt3.toJson(), - // ); - // } - List projectDnsRecords(String? domainName, String? ip4) { var domainA = DnsRecords(type: 'A', name: domainName, content: ip4); @@ -163,13 +164,22 @@ class CloudflareApi extends ApiMapOld { Future> domainList() async { var url = '$rootAddress/zones?per_page=50'; - var response = await loggedClient.get( + var client = await getClient(); + + var response = await client.get( url, queryParameters: {'per_page': 50}, ); + client.close(); return response.data['result'] .map((el) => el['name'] as String) .toList(); } + + @override + final bool hasLoger; + + @override + final bool isWithToken; } diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index dabec6b7..26018a51 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -2,33 +2,50 @@ import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/utils/password_generator2.dart'; -class HetznerApi extends ApiMapOld { - HetznerApi([String? token]) { - if (token != null) { - loggedClient.options = BaseOptions( - headers: {'Authorization': 'Bearer $token'}, - baseUrl: rootAddress!, - ); +class HetznerApi extends ApiMap { + bool hasLoger; + bool isWithToken; + + HetznerApi({this.hasLoger = false, this.isWithToken = true}); + + BaseOptions get options { + var options = BaseOptions(baseUrl: rootAddress); + if (isWithToken) { + var token = getIt().hetznerKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + + return options; } + ValidateStatus? validateStatus; + @override - String? rootAddress = 'https://api.hetzner.cloud/v1/servers'; + String rootAddress = 'https://api.hetzner.cloud/v1'; Future isValid(String token) async { - var options = Options( - headers: {'Authorization': 'Bearer $token'}, - validateStatus: (status) { - return status == HttpStatus.ok || status == HttpStatus.unauthorized; - }, + validateStatus = (status) { + return status == HttpStatus.ok || status == HttpStatus.unauthorized; + }; + var client = await getClient(); + Response response = await client.get( + '/servers', + options: Options( + headers: {'Authorization': 'Bearer $token'}, + ), ); - - Response response = await loggedClient.get(rootAddress!, options: options); + client.close(); if (response.statusCode == HttpStatus.ok) { return true; @@ -40,9 +57,9 @@ class HetznerApi extends ApiMapOld { } Future createServer({ - required String? cloudFlareKey, + required String cloudFlareKey, required User rootUser, - required String? domainName, + required String domainName, }) async { var dbPassword = getRandomString(40); @@ -50,11 +67,12 @@ class HetznerApi extends ApiMapOld { '''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":false}''', ); - Response response = await loggedClient.post( - rootAddress!, + var client = await getClient(); + Response response = await client.post( + '/servers', data: data, ); - + client.close(); return HetznerServerDetails( id: response.data['server']['id'], ip4: response.data['server']['public_net']['ipv4']['ip'], @@ -62,20 +80,23 @@ class HetznerApi extends ApiMapOld { ); } - Future deleteSelfprivacyServer({ - required String? cloudFlareKey, - }) async { - Response response = await loggedClient.get(rootAddress!); + Future deleteSelfprivacyServer() async { + var client = await getClient(); + Response response = await client.get('/servers'); List list = response.data['servers']; var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server'); - await loggedClient.delete('$rootAddress/${server['id']}'); + await client.delete('/servers/${server['id']}'); + client.close(); } Future startServer({ required HetznerServerDetails server, }) async { - await loggedClient.post('/${server.id}/actions/poweron'); + var client = await getClient(); + + await client.post('/servers/${server.id}/actions/poweron'); + client.close(); return server.copyWith( startTime: DateTime.now(), @@ -85,10 +106,25 @@ class HetznerApi extends ApiMapOld { Future restart({ required HetznerServerDetails server, }) async { - await loggedClient.post('/${server.id}/actions/poweron'); - + var client = await getClient(); + await client.post('/servers/${server.id}/actions/poweron'); + client.close(); return server.copyWith( startTime: DateTime.now(), ); } + + metrics() async { + var hetznerServer = getIt().hetznerServer; + var client = await getClient(); + await client.post('/servers/${hetznerServer!.id}/metrics'); + client.close(); + } + + getInfo() async { + var hetznerServer = getIt().hetznerServer; + var client = await getClient(); + await client.post('/servers/${hetznerServer!.id}'); + client.close(); + } } diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 6bf5afa4..0da2704b 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -1,45 +1,46 @@ +import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'api_map.dart'; -class ServerApi extends ApiMapOld { - ServerApi(String? domainName) { - loggedClient.options = BaseOptions( - baseUrl: 'https://api.$domainName', - ); +class ServerApi extends ApiMap { + bool hasLoger; + bool isWithToken; + + ServerApi({this.hasLoger = false, this.isWithToken = true}); + + BaseOptions get options { + var options = BaseOptions(); + + if (isWithToken) { + var cloudFlareDomain = getIt().cloudFlareDomain; + var domainName = cloudFlareDomain!.domainName; + assert(domainName != null); + + options = BaseOptions(baseUrl: 'https://api.$domainName'); + } + + return options; } Future isHttpServerWorking() async { bool res; Response response; + + var client = await getClient(); try { - response = await loggedClient.get('/serviceStatus'); + response = await client.get('/serviceStatus'); res = response.statusCode == HttpStatus.ok; } catch (e) { res = false; } - + client.close(); return res; } - // Future getDkim(String domainName) async { - // var response = await loggedClient.get( - // '/getDKIM', - // options: Options(responseType: ResponseType.plain), - // ); - // return _decodeAndCutData(response.data, domainName); - // } + String get rootAddress => + throw UnimplementedError('not used in with implementation'); } - -// String _decodeAndCutData(String text, String domainName) { -// var decodedTextString = text.substring(1, text.length - 1); -// var stringToBase64 = utf8.fuse(base64); - -// return stringToBase64 -// .decode(decodedTextString) -// .replaceAll("selector._domainkey IN TXT ( ", "") -// .replaceAll("\"\n \"", "") -// .replaceAll(' ) ; ----- DKIM key selector for $domainName\n', ''); -// } diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 8fc1ef7f..2fcc3652 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -74,7 +74,6 @@ class AppConfigCubit extends Cubit { if (isMatch) { var server = await repository.startServer( - state.hetznerKey, state.hetznerServer!, ); repository.saveServerDetails(server); @@ -111,33 +110,30 @@ class AppConfigCubit extends Cubit { AppConfigState? state, bool isImmediate = false, }) async { - state = state ?? this.state; + var dataState = state ?? this.state; var work = () async { - emit(TimerState(dataState: state!, isLoading: true)); + emit(TimerState(dataState: dataState, isLoading: true)); - var isServerWorking = await repository.isHttpServerWorking( - state.cloudFlareDomain!.domainName, - ); + var isServerWorking = await repository.isHttpServerWorking(); if (isServerWorking) { var pauseDuration = Duration(seconds: 30); emit(TimerState( - dataState: state, + dataState: dataState, timerStart: DateTime.now(), isLoading: false, duration: pauseDuration, )); timer = Timer(pauseDuration, () async { var hetznerServerDetails = await repository.restart( - state!.hetznerKey, - state.hetznerServer!, + dataState.hetznerServer!, ); repository.saveIsServerReseted(true); repository.saveServerDetails(hetznerServerDetails); emit( - state.copyWith( + dataState.copyWith( isServerReseted: true, hetznerServer: hetznerServerDetails, isLoading: false, @@ -155,7 +151,7 @@ class AppConfigCubit extends Cubit { var pauseDuration = Duration(seconds: 60); emit( TimerState( - dataState: state, + dataState: dataState, timerStart: DateTime.now(), duration: pauseDuration, isLoading: false, @@ -176,9 +172,7 @@ class AppConfigCubit extends Cubit { var work = () async { emit(TimerState(dataState: state!, isLoading: true)); - var isServerWorking = await repository.isHttpServerWorking( - state.cloudFlareDomain!.domainName, - ); + var isServerWorking = await repository.isHttpServerWorking(); if (isServerWorking) { repository.saveHasFinalChecked(true); @@ -246,7 +240,6 @@ class AppConfigCubit extends Cubit { AppConfigState _stateCopy = state; var onSuccess = (serverDetails) async { await repository.createDnsRecords( - state.cloudFlareKey, serverDetails.ip4, state.cloudFlareDomain!, ); @@ -263,10 +256,9 @@ class AppConfigCubit extends Cubit { try { emit(state.copyWith(isLoading: true)); await repository.createServer( - state.hetznerKey, state.rootUser!, - state.cloudFlareDomain!.domainName, - state.cloudFlareKey, + state.cloudFlareDomain!.domainName!, + state.cloudFlareKey!, onCancel: onCancel, onSuccess: onSuccess, ); diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 3980e624..e4197d06 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -22,7 +22,7 @@ class AppConfigRepository { Box box = Hive.box(BNames.appConfig); AppConfigState load() { - return AppConfigState( + var res = AppConfigState( hetznerKey: getIt().hetznerKey, cloudFlareKey: getIt().cloudFlareKey, cloudFlareDomain: getIt().cloudFlareDomain, @@ -35,6 +35,8 @@ class AppConfigRepository { error: null, isLoading: box.get(BNames.isLoading, defaultValue: false), ); + + return res; } void clearAppConfig() { @@ -42,12 +44,10 @@ class AppConfigRepository { } Future startServer( - String? hetznerKey, HetznerServerDetails hetznerServer, ) async { - var hetznerApi = HetznerApi(hetznerKey); + var hetznerApi = HetznerApi(); var serverDetails = await hetznerApi.startServer(server: hetznerServer); - hetznerApi.close(); return serverDetails; } @@ -90,15 +90,14 @@ class AppConfigRepository { } Future createServer( - String? hetznerKey, User rootUser, - String? domainName, - String? cloudFlareKey, { - void Function()? onCancel, + String domainName, + String cloudFlareKey, { + required void Function() onCancel, required Future Function(HetznerServerDetails serverDetails) onSuccess, }) async { - var hetznerApi = HetznerApi(hetznerKey); + var hetznerApi = HetznerApi(); try { var serverDetails = await hetznerApi.createServer( @@ -106,7 +105,6 @@ class AppConfigRepository { rootUser: rootUser, domainName: domainName, ); - hetznerApi.close(); saveServerDetails(serverDetails); onSuccess(serverDetails); } on DioError catch (e) { @@ -121,16 +119,13 @@ class AppConfigRepository { text: 'basis.delete'.tr(), isRed: true, onPressed: () async { - await hetznerApi.deleteSelfprivacyServer( - cloudFlareKey: cloudFlareKey, - ); + await hetznerApi.deleteSelfprivacyServer(); var serverDetails = await hetznerApi.createServer( cloudFlareKey: cloudFlareKey, rootUser: rootUser, domainName: domainName, ); - hetznerApi.close(); await saveServerDetails(serverDetails); onSuccess(serverDetails); @@ -139,8 +134,7 @@ class AppConfigRepository { ActionButton( text: 'basis.cancel'.tr(), onPressed: () { - hetznerApi.close(); - onCancel!(); + onCancel(); }, ), ], @@ -151,11 +145,10 @@ class AppConfigRepository { } Future createDnsRecords( - String? cloudFlareKey, String? ip4, CloudFlareDomain cloudFlareDomain, ) async { - var cloudflareApi = CloudflareApi(cloudFlareKey); + var cloudflareApi = CloudflareApi(); await cloudflareApi.removeSimilarRecords( ip4: ip4, @@ -166,22 +159,18 @@ class AppConfigRepository { ip4: ip4, cloudFlareDomain: cloudFlareDomain, ); - - cloudflareApi.close(); } - Future isHttpServerWorking(String? domainName) async { - var api = ServerApi(domainName); + Future isHttpServerWorking() async { + var api = ServerApi(); var isHttpServerWorking = await api.isHttpServerWorking(); - api.close(); return isHttpServerWorking; } Future restart( - String? hetznerKey, HetznerServerDetails server, ) async { - var hetznerApi = HetznerApi(hetznerKey); + var hetznerApi = HetznerApi(); return await hetznerApi.restart(server: server); } diff --git a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart index 9ecf3eaa..123553cf 100644 --- a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart @@ -5,8 +5,6 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; class BackblazeFormCubit extends FormCubit { - BackblazeApi apiClient = BackblazeApi(); - BackblazeFormCubit(this.initializingCubit) { //var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); keyId = FieldCubit( @@ -42,14 +40,14 @@ class BackblazeFormCubit extends FormCubit { final AppConfigCubit initializingCubit; - // ignore: close_sinks late final FieldCubit keyId; - // ignore: close_sinks late final FieldCubit applicationKey; @override FutureOr asyncValidation() async { late bool isKeyValid; + BackblazeApi apiClient = BackblazeApi(isWithToken: false); + try { String encodedApiKey = encodedBackblazeKey( keyId.state.value, @@ -67,11 +65,4 @@ class BackblazeFormCubit extends FormCubit { } return true; } - - @override - Future close() async { - apiClient.close(); - - return super.close(); - } } diff --git a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart index cd33feea..883470c8 100644 --- a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart @@ -6,8 +6,6 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; class CloudFlareFormCubit extends FormCubit { - CloudflareApi apiClient = CloudflareApi(); - CloudFlareFormCubit(this.initializingCubit) { var regExp = RegExp(r"\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); apiKey = FieldCubit( @@ -35,6 +33,7 @@ class CloudFlareFormCubit extends FormCubit { @override FutureOr asyncValidation() async { late bool isKeyValid; + CloudflareApi apiClient = CloudflareApi(isWithToken: false); try { isKeyValid = await apiClient.isValid(apiKey.state.value); @@ -51,8 +50,6 @@ class CloudFlareFormCubit extends FormCubit { @override Future close() async { - apiClient.close(); - return super.close(); } } diff --git a/lib/logic/cubit/forms/initializing/domain_cloudflare.dart b/lib/logic/cubit/forms/initializing/domain_cloudflare.dart index 4b1aad91..1551027c 100644 --- a/lib/logic/cubit/forms/initializing/domain_cloudflare.dart +++ b/lib/logic/cubit/forms/initializing/domain_cloudflare.dart @@ -4,19 +4,14 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; class DomainSetupCubit extends Cubit { - DomainSetupCubit(this.initializingCubit) : super(Initial()) { - var token = initializingCubit.state.cloudFlareKey; + DomainSetupCubit(this.initializingCubit) : super(Initial()); - assert(token != null, 'no cloudflare token'); - - api = CloudflareApi(token); - } - - AppConfigCubit initializingCubit; - late CloudflareApi api; + final AppConfigCubit initializingCubit; Future load() async { emit(Loading(LoadingTypes.loadingDomain)); + var api = CloudflareApi(); + var list = await api.domainList(); if (list.isEmpty) { emit(Empty()); @@ -29,20 +24,17 @@ class DomainSetupCubit extends Cubit { @override Future close() { - api.close(); return super.close(); } Future saveDomain() async { assert(state is Loaded, 'wrong state'); var domainName = (state as Loaded).domain; + var api = CloudflareApi(); emit(Loading(LoadingTypes.saving)); - var zoneId = await api.getZoneId( - initializingCubit.state.cloudFlareKey, - domainName, - ); + var zoneId = await api.getZoneId(domainName); var domain = CloudFlareDomain( domainName: domainName, diff --git a/lib/logic/cubit/forms/initializing/domain_form_cubit.dart b/lib/logic/cubit/forms/initializing/domain_form_cubit.dart deleted file mode 100644 index e2dfad83..00000000 --- a/lib/logic/cubit/forms/initializing/domain_form_cubit.dart +++ /dev/null @@ -1,68 +0,0 @@ -// import 'dart:async'; - -// import 'package:cubit_form/cubit_form.dart'; -// import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; -// import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; -// import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; - -// class DomainFormCubit extends FormCubit { -// CloudflareApi apiClient = CloudflareApi(); - -// DomainFormCubit(this.initializingCubit) { -// var regExp = -// RegExp(r"^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}"); -// domainName = FieldCubit( -// initalValue: '', -// validations: [ -// RequiredStringValidation('required'), -// ValidationModel( -// (s) => !regExp.hasMatch(s), -// 'invalid domain format', -// ), -// ], -// ); - -// super.setFields([domainName]); -// } - -// @override -// FutureOr onSubmit() async { -// var domain = CloudFlareDomain( -// domainName: domainName.state.value, -// zoneId: zoneId, -// ); -// initializingCubit.setDomain(domain); -// } - -// final AppConfigCubit initializingCubit; - -// FieldCubit domainName; -// String zoneId; - -// @override -// FutureOr asyncValidation() async { -// var key = initializingCubit.state.cloudFlareKey; - -// String zoneId; - -// try { -// zoneId = await apiClient.getZoneId(key, domainName.state.value); -// } catch (e) { -// addError(e); -// } - -// if (zoneId == null) { -// domainName.setError('Domain not in the list'); -// return false; -// } -// this.zoneId = zoneId; -// return true; -// } - -// @override -// Future close() async { -// apiClient.close(); - -// return super.close(); -// } -// } diff --git a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart index 78674e31..a9e89501 100644 --- a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart @@ -6,8 +6,6 @@ import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; class HetznerFormCubit extends FormCubit { - HetznerApi apiClient = HetznerApi(); - HetznerFormCubit(this.initializingCubit) { var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); apiKey = FieldCubit( @@ -30,12 +28,13 @@ class HetznerFormCubit extends FormCubit { final AppConfigCubit initializingCubit; - // ignore: close_sinks late final FieldCubit apiKey; @override FutureOr asyncValidation() async { late bool isKeyValid; + HetznerApi apiClient = HetznerApi(isWithToken: false); + try { isKeyValid = await apiClient.isValid(apiKey.state.value); } catch (e) { @@ -48,11 +47,4 @@ class HetznerFormCubit extends FormCubit { } return true; } - - @override - Future close() async { - apiClient.close(); - - return super.close(); - } } diff --git a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart index 85be05c7..b82bd0f3 100644 --- a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart @@ -1,13 +1,10 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/user.dart'; class RootUserFormCubit extends FormCubit { - HetznerApi apiClient = HetznerApi(); - RootUserFormCubit(this.initializingCubit) { var userRegExp = RegExp(r"\W"); var passwordRegExp = RegExp(r"[\n\r\s]+"); @@ -46,17 +43,7 @@ class RootUserFormCubit extends FormCubit { final AppConfigCubit initializingCubit; - // ignore: close_sinks late final FieldCubit userName; - // ignore: close_sinks late final FieldCubit password; - // ignore: close_sinks late final FieldCubit isVisible; - - @override - Future close() async { - apiClient.close(); - - return super.close(); - } } diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index 2c874eb4..50de369e 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -45,7 +45,6 @@ class UserFormCubit extends FormCubit { usersCubit.addUser(user); } - // ignore: close_sinks late FieldCubit login; late FieldCubit password; diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart deleted file mode 100644 index a23b70e7..00000000 --- a/lib/logic/cubit/services/services_cubit.dart +++ /dev/null @@ -1,28 +0,0 @@ -// import 'package:bloc/bloc.dart'; -// import 'package:equatable/equatable.dart'; -// import 'package:meta/meta.dart'; -// import 'package:selfprivacy/logic/models/service.dart'; -// import 'package:selfprivacy/logic/models/state_types.dart'; - -// export 'package:provider/provider.dart'; -// export 'package:selfprivacy/logic/models/state_types.dart'; - -// part 'services_state.dart'; - -// class ServicesCubit extends Cubit { -// ServicesCubit() : super(ServicesState(all)); - -// void connect(Service service) { -// var newState = state.updateElement(service, StateType.stable); -// emit(newState); -// } -// } - -// final all = ServiceTypes.values -// .map( -// (type) => Service( -// state: StateType.uninitialized, -// type: type, -// ), -// ) -// .toList(); diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart deleted file mode 100644 index d20a63a8..00000000 --- a/lib/logic/cubit/services/services_state.dart +++ /dev/null @@ -1,26 +0,0 @@ -// part of 'services_cubit.dart'; - -// @immutable -// class ServicesState extends Equatable{ -// ServicesState(this.all); - -// final List all; - -// ServicesState updateElement(Service service, StateType newState) { -// var newList = [...all]; -// var index = newList.indexOf(service); -// newList[index] = service.updateState(newState); -// return ServicesState(newList); -// } - -// List get connected => all -// .where((service) => service.state != StateType.uninitialized) -// .toList(); - -// List get uninitialized => all -// .where((service) => service.state == StateType.uninitialized) -// .toList(); - -// @override -// List get props => all; -// } diff --git a/lib/logic/models/server_info.dart b/lib/logic/models/server_info.dart new file mode 100644 index 00000000..12c7e0a8 --- /dev/null +++ b/lib/logic/models/server_info.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; + +@JsonSerializable(createFactory: false) +class ServerInfo { + final String id; + final String name; + final ServerStatus status; + final DateTime created; + + ServerInfo(this.id, this.name, this.status, this.created); +} + +enum ServerStatus { + running, + initializing, + starting, + stopping, + off, + deleting, + migrating, + rebuilding, + unknown, +} diff --git a/lib/logic/models/service.dart b/lib/logic/models/service.dart deleted file mode 100644 index 8ce6e310..00000000 --- a/lib/logic/models/service.dart +++ /dev/null @@ -1,25 +0,0 @@ -// import 'package:equatable/equatable.dart'; -// import 'package:selfprivacy/logic/models/state_types.dart'; - -// enum ServiceTypes { -// messanger, -// mail, -// passwordManager, -// github, -// cloud, -// } - -// class Service extends Equatable { -// const Service({required this.state, required this.type}); - -// final StateType state; -// final ServiceTypes type; - -// Service updateState(StateType newState) => Service( -// state: newState, -// type: type, -// ); - -// @override -// List get props => [state, type]; -// } diff --git a/lib/main.dart b/lib/main.dart index 231a7dca..141c95c2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -37,6 +37,8 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { AppSettingsState appSettings = context.watch().state; + var a = DateTime.parse('2021-03-23T20:00:06+00:00'); + print(a); return AnnotatedRegion( value: SystemUiOverlayStyle.light, // Manually changnig appbar color child: MaterialApp( diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index e26ebd54..589ce654 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -408,7 +408,7 @@ class InitializingPage extends StatelessWidget { Spacer(), BrandButton.rised( onPressed: - isLoading! ? null : appConfigCubit.createServerAndSetDnsRecords, + isLoading! ? null : () => appConfigCubit.createServerAndSetDnsRecords(), title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(), ), Spacer(flex: 2), diff --git a/lib/ui/pages/onboarding/onboarding.dart b/lib/ui/pages/onboarding/onboarding.dart index 07790f60..835e40e0 100644 --- a/lib/ui/pages/onboarding/onboarding.dart +++ b/lib/ui/pages/onboarding/onboarding.dart @@ -124,8 +124,10 @@ class _OnboardingPageState extends State { BrandButton.rised( onPressed: () { context.read().turnOffOnboarding(); - Navigator.of(context) - .pushReplacement(materialRoute(widget.nextPage)); + Navigator.of(context).pushAndRemoveUntil( + materialRoute(widget.nextPage), + (route) => false, + ); }, title: 'basis.got_it'.tr(), ), diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index dd48ae6d..69dea2b7 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -366,6 +366,7 @@ class _ServiceDetails extends StatelessWidget { ), SizedBox(height: 10), BrandText.h1(title), + SizedBox(height: 10), child, ], ), diff --git a/pubspec.lock b/pubspec.lock index b7ef8724..83d5ac4b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -574,6 +574,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.0" + pretty_dio_logger: + dependency: "direct main" + description: + name: pretty_dio_logger + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-beta-1" process: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 03565a4e..d692fbcf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: hive_flutter: ^1.0.0 json_annotation: ^4.0.0 package_info: ^2.0.0 + pretty_dio_logger: ^1.1.1 provider: ^5.0.0 url_launcher: ^6.0.2 wakelock: ^0.5.0+2 From 804147b8d6b03c67abf1782d8aca6618cdf5867b Mon Sep 17 00:00:00 2001 From: Kherel Date: Fri, 26 Mar 2021 14:38:39 +0100 Subject: [PATCH 11/17] update --- assets/translations/en.json | 19 +- assets/translations/ru.json | 17 +- build.yaml | 7 + lib/config/text_themes.dart | 6 + lib/logic/api_maps/api_map.dart | 28 +- lib/logic/api_maps/backblaze.dart | 4 +- lib/logic/api_maps/cloudflare.dart | 22 +- lib/logic/api_maps/hetzner.dart | 21 +- lib/logic/api_maps/server.dart | 3 +- .../cubit/app_config/app_config_cubit.dart | 2 +- .../cubit/app_config/app_config_state.dart | 1 - .../server_detailed_info_cubit.dart | 24 ++ .../server_detailed_info_repository.dart | 9 + .../server_detailed_info_state.dart | 29 ++ lib/logic/models/backblaze_credential.dart | 6 +- lib/logic/models/backblaze_credential.g.dart | 4 +- lib/logic/models/cloudflare_domain.dart | 9 +- lib/logic/models/cloudflare_domain.g.dart | 4 +- lib/logic/models/hetzner_server_info.dart | 89 +++++ lib/logic/models/hetzner_server_info.g.dart | 84 +++++ lib/logic/models/server_info.dart | 23 -- lib/main.dart | 2 - .../components/brand_button/brand_button.dart | 2 +- lib/ui/components/brand_text/brand_text.dart | 26 +- lib/ui/components/one_page/one_page.dart | 51 +++ lib/ui/components/pre_styled_buttons.dart | 30 ++ lib/ui/pages/providers/providers.dart | 116 +++--- .../pages/server_details/server_details.dart | 290 +++++++++++++++ .../pages/server_details/server_settings.dart | 136 +++++++ lib/ui/pages/services/services.dart | 346 +++++++++++++++--- lib/ui/pages/users/new_user.dart | 6 +- lib/ui/pages/users/user_details.dart | 26 +- lib/ui/pages/users/users.dart | 1 + lib/utils/route_transitions/slide_bottom.dart | 4 +- lib/utils/ui_helpers.dart | 9 + pubspec.lock | 14 + pubspec.yaml | 2 + 37 files changed, 1241 insertions(+), 231 deletions(-) create mode 100644 build.yaml create mode 100644 lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart create mode 100644 lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart create mode 100644 lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart create mode 100644 lib/logic/models/hetzner_server_info.dart create mode 100644 lib/logic/models/hetzner_server_info.g.dart delete mode 100644 lib/logic/models/server_info.dart create mode 100644 lib/ui/components/one_page/one_page.dart create mode 100644 lib/ui/components/pre_styled_buttons.dart create mode 100644 lib/ui/pages/server_details/server_details.dart create mode 100644 lib/ui/pages/server_details/server_settings.dart create mode 100644 lib/utils/ui_helpers.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 3c7a098a..da4f6a4a 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -20,9 +20,11 @@ "domain": "Domain", "saving": "Saving..", "nickname": "nickname", - "loading": "loading", + "loading": "Loading...", "later": "Настрою потом", - "reset": "Reset" + "reset": "Reset", + "details": "Details", + "no_data": "No data" }, "more": { "_comment": "'More' tab", @@ -54,26 +56,25 @@ "page_title": "Your Data Center", "server": { "card_title": "Server", + "status": "Status — Good", "bottom_sheet": { - "1": "It's a virtual computer, where all your services live.", - "2": "1 CPU, RAM 4Gb, 40Gb — $5 per month", - "3": "Status — Good" + "1": "It's a virtual computer, where all your services live." } }, "domain": { "card_title": "Domain", + "status": "Status — Good", "bottom_sheet": { "1": "It's your personal internet address that will point to the server and other services of yours.", - "2": "{} — expires on {}", - "3": "Status — Good" + "2": "{} — expires on {}" } }, "backup": { "card_title": "Backup", + "status": "Status — Good", "bottom_sheet": { "1": "Will save your day in case of incident: hackers attack, server deletion, etc.", - "2": "3Gb/10Gb, last backup was yesterday {}", - "3": "Status — Good" + "2": "3Gb/10Gb, last backup was yesterday {}" } } }, diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 0a7bbd2e..f6e17b70 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -22,7 +22,9 @@ "nickname": "Никнейм", "loading": "Загрузка", "later": "Настрою потом", - "reset": "Reset" + "reset": "Reset", + "details": "Детальная информация", + "no_data": "Нет данных" }, "more": { "_comment": "вкладка еще", @@ -54,26 +56,25 @@ "page_title": "Ваш Дата-центр", "server": { "card_title": "Сервер", + "status": "Статус — в норме", "bottom_sheet": { - "1": "Это виртульный компьютер на котором работают все ваши сервисы.", - "2": "1 CPU, RAM 4Gb, 40Gb — $5 в месяц", - "3": "Статус — в норме" + "1": "Это виртульный компьютер на котором работают все ваши сервисы." } }, "domain": { "card_title": "Домен", + "status": "Статус — в норме", "bottom_sheet": { "1": "Это ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы.", - "2": "{} — продлен до {}", - "3": "Статус — в норме" + "2": "{} — продлен до {}" } }, "backup": { "card_title": "Резервное копирование", + "status": "Статус — в норме", "bottom_sheet": { "1": "Выручит в любой ситуации: хакерская атака, удаление сервера и т.п.", - "2": "3Gb — бестплатно до 10Gb, последний вчера в {}", - "3": "Статус — в норме" + "2": "3Gb — бестплатно до 10Gb, последний вчера в {}" } } }, diff --git a/build.yaml b/build.yaml new file mode 100644 index 00000000..709b623e --- /dev/null +++ b/build.yaml @@ -0,0 +1,7 @@ +targets: + $default: + builders: + json_serializable: + options: + create_factory: true + create_to_json: false \ No newline at end of file diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart index b64ab1a2..cf383122 100644 --- a/lib/config/text_themes.dart +++ b/lib/config/text_themes.dart @@ -38,6 +38,12 @@ final headline4Style = defaultTextStyle.copyWith( color: BrandColors.headlineColor, ); +final headline5Style = defaultTextStyle.copyWith( + fontSize: 15, + fontWeight: NamedFontWeight.medium, + color: BrandColors.headlineColor.withOpacity(0.8), +); + final body1Style = defaultTextStyle; final body2Style = defaultTextStyle.copyWith( color: BrandColors.textColor2, diff --git a/lib/logic/api_maps/api_map.dart b/lib/logic/api_maps/api_map.dart index ce5484a5..5f4330d4 100644 --- a/lib/logic/api_maps/api_map.dart +++ b/lib/logic/api_maps/api_map.dart @@ -10,7 +10,6 @@ import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/models/message.dart'; abstract class ApiMap { - Future getClient() async { var dio = Dio(await options); if (hasLoger) { @@ -31,28 +30,15 @@ abstract class ApiMap { abstract final String rootAddress; abstract final bool hasLoger; abstract final bool isWithToken; + + ValidateStatus? validateStatus; + + void close(Dio client) { + client.close(); + validateStatus = null; + } } -// abstract class ApiMapOld { -// ApiMapOld() { -// var client = Dio()..interceptors.add(ConsoleInterceptor()); -// (client.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = -// (HttpClient client) { -// client.badCertificateCallback = -// (X509Certificate cert, String host, int port) => true; -// return client; -// }; -// loggedClient = client; -// } -// String? rootAddress; - -// late Dio loggedClient; - -// void close() { -// loggedClient.close(); -// } -// } - class ConsoleInterceptor extends InterceptorsWrapper { void addMessage(Message message) { getIt.get().addMessage(message); diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/backblaze.dart index f3989751..2a9888cd 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/backblaze.dart @@ -11,7 +11,6 @@ class BackblazeApi extends ApiMap { if (isWithToken) { var backblazeCredential = getIt().backblazeCredential; var token = backblazeCredential!.applicationKey; - assert(token != null); options.headers = {'Authorization': 'Basic $token'}; } @@ -22,7 +21,6 @@ class BackblazeApi extends ApiMap { return options; } - ValidateStatus? validateStatus; @override String rootAddress = 'https://api.backblazeb2.com/b2api/v2/'; @@ -32,7 +30,7 @@ class BackblazeApi extends ApiMap { 'b2_authorize_account', options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}), ); - client.close(); + close(client); if (response.statusCode == HttpStatus.ok) { return true; } else if (response.statusCode == HttpStatus.unauthorized) { diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart index 40cbef42..7dc9994d 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/cloudflare.dart @@ -22,8 +22,6 @@ class CloudflareApi extends ApiMap { return options; } - ValidateStatus? validateStatus; - @override String rootAddress = 'https://api.cloudflare.com/client/v4'; @@ -36,8 +34,7 @@ class CloudflareApi extends ApiMap { Response response = await client.get('/user/tokens/verify', options: Options(headers: {'Authorization': 'Bearer $token'})); - client.close(); - validateStatus = null; + close(client); if (response.statusCode == HttpStatus.ok) { return true; @@ -48,7 +45,7 @@ class CloudflareApi extends ApiMap { } } - Future getZoneId(String domain) async { + Future getZoneId(String domain) async { validateStatus = (status) { return status == HttpStatus.ok || status == HttpStatus.forbidden; }; @@ -58,14 +55,9 @@ class CloudflareApi extends ApiMap { queryParameters: {'name': domain}, ); - client.close(); - validateStatus = null; + close(client); - try { - return response.data['result'][0]['id']; - } catch (error) { - return null; - } + return response.data['result'][0]['id']; } Future removeSimilarRecords({ @@ -92,7 +84,7 @@ class CloudflareApi extends ApiMap { } await Future.wait(allDeleteFutures); - client.close(); + close(client); } Future createMultipleDnsRecords({ @@ -118,7 +110,7 @@ class CloudflareApi extends ApiMap { } await Future.wait(allCreateFutures); - client.close(); + close(client); } List projectDnsRecords(String? domainName, String? ip4) { @@ -171,7 +163,7 @@ class CloudflareApi extends ApiMap { queryParameters: {'per_page': 50}, ); - client.close(); + close(client); return response.data['result'] .map((el) => el['name'] as String) .toList(); diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 26018a51..a9cad086 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/api_map.dart'; +import 'package:selfprivacy/logic/models/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/utils/password_generator2.dart'; @@ -29,8 +30,6 @@ class HetznerApi extends ApiMap { return options; } - ValidateStatus? validateStatus; - @override String rootAddress = 'https://api.hetzner.cloud/v1'; @@ -45,7 +44,7 @@ class HetznerApi extends ApiMap { headers: {'Authorization': 'Bearer $token'}, ), ); - client.close(); + close(client); if (response.statusCode == HttpStatus.ok) { return true; @@ -87,7 +86,7 @@ class HetznerApi extends ApiMap { List list = response.data['servers']; var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server'); await client.delete('/servers/${server['id']}'); - client.close(); + close(client); } Future startServer({ @@ -96,7 +95,7 @@ class HetznerApi extends ApiMap { var client = await getClient(); await client.post('/servers/${server.id}/actions/poweron'); - client.close(); + close(client); return server.copyWith( startTime: DateTime.now(), @@ -108,7 +107,7 @@ class HetznerApi extends ApiMap { }) async { var client = await getClient(); await client.post('/servers/${server.id}/actions/poweron'); - client.close(); + close(client); return server.copyWith( startTime: DateTime.now(), ); @@ -118,13 +117,15 @@ class HetznerApi extends ApiMap { var hetznerServer = getIt().hetznerServer; var client = await getClient(); await client.post('/servers/${hetznerServer!.id}/metrics'); - client.close(); + close(client); } - getInfo() async { + Future getInfo() async { var hetznerServer = getIt().hetznerServer; var client = await getClient(); - await client.post('/servers/${hetznerServer!.id}'); - client.close(); + Response response = await client.get('/servers/${hetznerServer!.id}'); + close(client); + + return HetznerServerInfo.fromJson(response.data!['server']); } } diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 0da2704b..2089e633 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -18,7 +18,6 @@ class ServerApi extends ApiMap { if (isWithToken) { var cloudFlareDomain = getIt().cloudFlareDomain; var domainName = cloudFlareDomain!.domainName; - assert(domainName != null); options = BaseOptions(baseUrl: 'https://api.$domainName'); } @@ -37,7 +36,7 @@ class ServerApi extends ApiMap { } catch (e) { res = false; } - client.close(); + close(client); return res; } diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 2fcc3652..15e5f1fa 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -257,7 +257,7 @@ class AppConfigCubit extends Cubit { emit(state.copyWith(isLoading: true)); await repository.createServer( state.rootUser!, - state.cloudFlareDomain!.domainName!, + state.cloudFlareDomain!.domainName, state.cloudFlareKey!, onCancel: onCancel, onSuccess: onSuccess, diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 6b9f8ee3..5a89ed04 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -92,7 +92,6 @@ class AppConfigState extends Equatable { isServerReseted, hasFinalChecked, ]; - print('progress: $res'); return res; } } diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart new file mode 100644 index 00000000..3d46142d --- /dev/null +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -0,0 +1,24 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart'; +import 'package:selfprivacy/logic/models/hetzner_server_info.dart'; + +part 'server_detailed_info_state.dart'; + +class ServerDetailsCubit extends Cubit { + ServerDetailsCubit() : super(ServerDetailsInitial()); + + ServerDetailsRepository repository = ServerDetailsRepository(); + + void check() async { + var isReadyToCheck = getIt().hetznerServer != null; + if (isReadyToCheck) { + emit(ServerDetailsLoading()); + var data = await repository.load(); + emit(Loaded(serverInfo: data, checkTime: DateTime.now())); + } else { + emit(ServerDetailsNotReady()); + } + } +} diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart new file mode 100644 index 00000000..bcd34625 --- /dev/null +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart @@ -0,0 +1,9 @@ +import 'package:selfprivacy/logic/api_maps/hetzner.dart'; +import 'package:selfprivacy/logic/models/hetzner_server_info.dart'; + +class ServerDetailsRepository { + Future load() async { + var client = HetznerApi(); + return await client.getInfo(); + } +} diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart new file mode 100644 index 00000000..cf017658 --- /dev/null +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart @@ -0,0 +1,29 @@ +part of 'server_detailed_info_cubit.dart'; + +abstract class ServerDetailsState extends Equatable { + const ServerDetailsState(); + + @override + List get props => []; +} + +class ServerDetailsInitial extends ServerDetailsState {} + +class ServerDetailsLoading extends ServerDetailsState {} + +class ServerDetailsNotReady extends ServerDetailsState {} + +class Loading extends ServerDetailsState {} + +class Loaded extends ServerDetailsState { + final HetznerServerInfo serverInfo; + final DateTime checkTime; + + Loaded({ + required this.serverInfo, + required this.checkTime, + }); + + @override + List get props => [serverInfo, checkTime]; +} diff --git a/lib/logic/models/backblaze_credential.dart b/lib/logic/models/backblaze_credential.dart index 3496f4ce..b9c06364 100644 --- a/lib/logic/models/backblaze_credential.dart +++ b/lib/logic/models/backblaze_credential.dart @@ -6,13 +6,13 @@ part 'backblaze_credential.g.dart'; @HiveType(typeId: 4) class BackblazeCredential { - BackblazeCredential({this.keyId, this.applicationKey}); + BackblazeCredential({required this.keyId, required this.applicationKey}); @HiveField(0) - final String? keyId; + final String keyId; @HiveField(1) - final String? applicationKey; + final String applicationKey; get encodedApiKey => encodedBackblazeKey(keyId, applicationKey); diff --git a/lib/logic/models/backblaze_credential.g.dart b/lib/logic/models/backblaze_credential.g.dart index 305b3386..c6ad373e 100644 --- a/lib/logic/models/backblaze_credential.g.dart +++ b/lib/logic/models/backblaze_credential.g.dart @@ -17,8 +17,8 @@ class BackblazeCredentialAdapter extends TypeAdapter { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return BackblazeCredential( - keyId: fields[0] as String?, - applicationKey: fields[1] as String?, + keyId: fields[0] as String, + applicationKey: fields[1] as String, ); } diff --git a/lib/logic/models/cloudflare_domain.dart b/lib/logic/models/cloudflare_domain.dart index d66e9a04..9d85bfb1 100644 --- a/lib/logic/models/cloudflare_domain.dart +++ b/lib/logic/models/cloudflare_domain.dart @@ -4,13 +4,16 @@ part 'cloudflare_domain.g.dart'; @HiveType(typeId: 3) class CloudFlareDomain { - CloudFlareDomain({this.domainName, this.zoneId}); + CloudFlareDomain({ + required this.domainName, + required this.zoneId, + }); @HiveField(0) - final String? domainName; + final String domainName; @HiveField(1) - final String? zoneId; + final String zoneId; @override String toString() { diff --git a/lib/logic/models/cloudflare_domain.g.dart b/lib/logic/models/cloudflare_domain.g.dart index d96cf9bc..dcd95317 100644 --- a/lib/logic/models/cloudflare_domain.g.dart +++ b/lib/logic/models/cloudflare_domain.g.dart @@ -17,8 +17,8 @@ class CloudFlareDomainAdapter extends TypeAdapter { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return CloudFlareDomain( - domainName: fields[0] as String?, - zoneId: fields[1] as String?, + domainName: fields[0] as String, + zoneId: fields[1] as String, ); } diff --git a/lib/logic/models/hetzner_server_info.dart b/lib/logic/models/hetzner_server_info.dart new file mode 100644 index 00000000..98af1c3e --- /dev/null +++ b/lib/logic/models/hetzner_server_info.dart @@ -0,0 +1,89 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'hetzner_server_info.g.dart'; + +@JsonSerializable() +class HetznerServerInfo { + final int id; + final String name; + final ServerStatus status; + final DateTime created; + + @JsonKey(name: 'server_type') + final HetznerServerTypeInfo serverType; + + @JsonKey(name: 'datacenter', fromJson: HetznerServerInfo.locationFromJson) + final HetznerLocation location; + + static HetznerLocation locationFromJson(Map json) => + HetznerLocation.fromJson(json['location']); + + static HetznerServerInfo fromJson(Map json) => + _$HetznerServerInfoFromJson(json); + + HetznerServerInfo( + this.id, + this.name, + this.status, + this.created, + this.serverType, + this.location, + ); +} + +enum ServerStatus { + running, + initializing, + starting, + stopping, + off, + deleting, + migrating, + rebuilding, + unknown, +} + +@JsonSerializable() +class HetznerServerTypeInfo { + final int cores; + final num memory; + final int disk; + + final List prices; + + HetznerServerTypeInfo(this.cores, this.memory, this.disk, this.prices); + + static HetznerServerTypeInfo fromJson(Map json) => + _$HetznerServerTypeInfoFromJson(json); +} + +@JsonSerializable() +class HetznerPriceInfo { + HetznerPriceInfo(this.hourly, this.monthly); + + @JsonKey(name: 'price_hourly', fromJson: HetznerPriceInfo.getPrice) + final double hourly; + + @JsonKey(name: 'price_monthly', fromJson: HetznerPriceInfo.getPrice) + final double monthly; + + static HetznerPriceInfo fromJson(Map json) => + _$HetznerPriceInfoFromJson(json); + + static double getPrice(Map json) => double.parse(json['gross'] as String); +} + +@JsonSerializable() +class HetznerLocation { + final String country; + final String city; + final String description; + + @JsonKey(name: 'network_zone') + final String zone; + + HetznerLocation(this.country, this.city, this.description, this.zone); + + static HetznerLocation fromJson(Map json) => + _$HetznerLocationFromJson(json); +} diff --git a/lib/logic/models/hetzner_server_info.g.dart b/lib/logic/models/hetzner_server_info.g.dart new file mode 100644 index 00000000..40055d19 --- /dev/null +++ b/lib/logic/models/hetzner_server_info.g.dart @@ -0,0 +1,84 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'hetzner_server_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HetznerServerInfo _$HetznerServerInfoFromJson(Map json) { + return HetznerServerInfo( + json['id'] as int, + json['name'] as String, + _$enumDecode(_$ServerStatusEnumMap, json['status']), + DateTime.parse(json['created'] as String), + HetznerServerTypeInfo.fromJson(json['server_type'] as Map), + HetznerServerInfo.locationFromJson(json['datacenter'] as Map), + ); +} + +K _$enumDecode( + Map enumValues, + Object? source, { + K? unknownValue, +}) { + if (source == null) { + throw ArgumentError( + 'A value must be provided. Supported values: ' + '${enumValues.values.join(', ')}', + ); + } + + return enumValues.entries.singleWhere( + (e) => e.value == source, + orElse: () { + if (unknownValue == null) { + throw ArgumentError( + '`$source` is not one of the supported values: ' + '${enumValues.values.join(', ')}', + ); + } + return MapEntry(unknownValue, enumValues.values.first); + }, + ).key; +} + +const _$ServerStatusEnumMap = { + ServerStatus.running: 'running', + ServerStatus.initializing: 'initializing', + ServerStatus.starting: 'starting', + ServerStatus.stopping: 'stopping', + ServerStatus.off: 'off', + ServerStatus.deleting: 'deleting', + ServerStatus.migrating: 'migrating', + ServerStatus.rebuilding: 'rebuilding', + ServerStatus.unknown: 'unknown', +}; + +HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( + Map json) { + return HetznerServerTypeInfo( + json['cores'] as int, + json['memory'] as num, + json['disk'] as int, + (json['prices'] as List) + .map((e) => HetznerPriceInfo.fromJson(e as Map)) + .toList(), + ); +} + +HetznerPriceInfo _$HetznerPriceInfoFromJson(Map json) { + return HetznerPriceInfo( + HetznerPriceInfo.getPrice(json['price_hourly'] as Map), + HetznerPriceInfo.getPrice(json['price_monthly'] as Map), + ); +} + +HetznerLocation _$HetznerLocationFromJson(Map json) { + return HetznerLocation( + json['country'] as String, + json['city'] as String, + json['description'] as String, + json['network_zone'] as String, + ); +} diff --git a/lib/logic/models/server_info.dart b/lib/logic/models/server_info.dart deleted file mode 100644 index 12c7e0a8..00000000 --- a/lib/logic/models/server_info.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -@JsonSerializable(createFactory: false) -class ServerInfo { - final String id; - final String name; - final ServerStatus status; - final DateTime created; - - ServerInfo(this.id, this.name, this.status, this.created); -} - -enum ServerStatus { - running, - initializing, - starting, - stopping, - off, - deleting, - migrating, - rebuilding, - unknown, -} diff --git a/lib/main.dart b/lib/main.dart index 141c95c2..231a7dca 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -37,8 +37,6 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { AppSettingsState appSettings = context.watch().state; - var a = DateTime.parse('2021-03-23T20:00:06+00:00'); - print(a); return AnnotatedRegion( value: SystemUiOverlayStyle.light, // Manually changnig appbar color child: MaterialApp( diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart index df6cfc39..a2de8592 100644 --- a/lib/ui/components/brand_button/brand_button.dart +++ b/lib/ui/components/brand_button/brand_button.dart @@ -34,7 +34,7 @@ class BrandButton { onPressed: onPressed, ); - static iconText({ + static emptyWithIconText({ Key? key, required VoidCallback onPressed, required String title, diff --git a/lib/ui/components/brand_text/brand_text.dart b/lib/ui/components/brand_text/brand_text.dart index 9dda8549..1acbb4e8 100644 --- a/lib/ui/components/brand_text/brand_text.dart +++ b/lib/ui/components/brand_text/brand_text.dart @@ -6,6 +6,7 @@ enum TextType { h2, // cards titles h3, // titles in about page h4, // caption + h5, // Table data body1, // normal body2, // with opacity medium, @@ -63,10 +64,28 @@ class BrandText extends StatelessWidget { textAlign: textAlign, overflow: TextOverflow.ellipsis, ); - factory BrandText.h4(String? text, {TextStyle? style}) => BrandText( + factory BrandText.h4( + String? text, { + TextStyle? style, + TextAlign? textAlign, + }) => + BrandText( text, type: TextType.h4, style: style, + textAlign: textAlign, + ); + + factory BrandText.h5( + String? text, { + TextStyle? style, + TextAlign? textAlign, + }) => + BrandText( + text, + type: TextType.h5, + style: style, + textAlign: textAlign, ); factory BrandText.body1(String? text, {TextStyle? style}) => BrandText( text, @@ -123,6 +142,11 @@ class BrandText extends StatelessWidget { ? headline4Style.copyWith(color: Colors.white) : headline4Style; break; + case TextType.h5: + style = isDark + ? headline5Style.copyWith(color: Colors.white) + : headline5Style; + break; case TextType.body1: style = isDark ? body1Style.copyWith(color: Colors.white) : body1Style; break; diff --git a/lib/ui/components/one_page/one_page.dart b/lib/ui/components/one_page/one_page.dart new file mode 100644 index 00000000..7cb27a83 --- /dev/null +++ b/lib/ui/components/one_page/one_page.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; +import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/ui/components/pre_styled_buttons.dart'; + +class OnePage extends StatelessWidget { + const OnePage({ + Key? key, + required this.title, + required this.child, + }) : super(key: key); + + final String title; + final Widget child; + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: PreferredSize( + child: Column( + children: [ + Container( + height: 51, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 15), + child: BrandText.h4('basis.details'.tr()), + ), + BrandDivider(), + ], + ), + preferredSize: Size.fromHeight(52), + ), + body: child, + bottomNavigationBar: SafeArea( + child: Container( + decoration: BoxDecoration(boxShadow: kElevationToShadow[3]), + height: kBottomNavigationBarHeight, + child: Container( + color: Theme.of(context).scaffoldBackgroundColor, + alignment: Alignment.center, + child: PreStyledButtons.close( + onPress: () => Navigator.of(context).pop()), + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/components/pre_styled_buttons.dart b/lib/ui/components/pre_styled_buttons.dart new file mode 100644 index 00000000..9a5e8baf --- /dev/null +++ b/lib/ui/components/pre_styled_buttons.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:easy_localization/easy_localization.dart'; + +class PreStyledButtons { + static Widget close({ + required VoidCallback onPress, + }) => + _CloseButton(onPress: onPress); +} + +class _CloseButton extends StatelessWidget { + const _CloseButton({Key? key, required this.onPress}) : super(key: key); + + final VoidCallback onPress; + + @override + Widget build(BuildContext context) { + return OutlinedButton( + onPressed: () => Navigator.of(context).pop(), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + BrandText.h4('basis.close'.tr()), + Icon(Icons.close), + ], + ), + ); + } +} diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 89f48653..ea6cf8c2 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -10,9 +10,15 @@ import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.da 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/components/not_ready_card/not_ready_card.dart'; +import 'package:selfprivacy/ui/components/one_page/one_page.dart'; import 'package:selfprivacy/ui/pages/providers/settings/settings.dart'; +import 'package:selfprivacy/ui/pages/server_details/server_details.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/utils/route_transitions/slide_bottom.dart'; +import 'package:selfprivacy/utils/ui_helpers.dart'; + +var navigatorKey = GlobalKey(); class ProvidersPage extends StatefulWidget { ProvidersPage({Key? key}) : super(key: key); @@ -64,9 +70,11 @@ class _Card extends StatelessWidget { final ProviderModel provider; @override Widget build(BuildContext context) { - String? title; + late String title; String? message; - String? stableText; + late String stableText; + late VoidCallback onTap; + AppConfigState appConfig = context.watch().state; var domainName = @@ -75,30 +83,54 @@ class _Card extends StatelessWidget { switch (provider.type) { case ProviderType.server: title = 'providers.server.card_title'.tr(); - stableText = 'В норме'; + stableText = 'providers.domain.status'.tr(); + + stableText = 'providers.server.status'.tr(); + onTap = () => Navigator.of(context).push( + SlideBottomRoute( + OnePage( + title: title, + child: ServerDetails(), + ), + ), + ); break; case ProviderType.domain: title = 'providers.domain.card_title'.tr(); message = domainName; - stableText = 'Домен настроен'; + stableText = 'providers.domain.status'.tr(); + + onTap = () => showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return _ProviderDetails( + provider: provider, + statusText: stableText, + ); + }, + ); break; case ProviderType.backup: title = 'providers.backup.card_title'.tr(); - stableText = 'В норме'; + stableText = 'providers.backup.status'.tr(); + + onTap = () => showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return _ProviderDetails( + provider: provider, + statusText: stableText, + ); + }, + ); break; } return GestureDetector( - onTap: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (BuildContext context) { - return _ProviderDetails( - provider: provider, - statusText: stableText, - ); - }, - ), + onTap: onTap, child: BrandCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -139,20 +171,11 @@ class _ProviderDetails extends StatelessWidget { var config = context.watch().state; - var domainName = config.isDomainFilled - ? config.cloudFlareDomain!.domainName! - : 'example.com'; + var domainName = UiHelpers.getDomainName(config); + switch (provider.type) { case ProviderType.server: - title = 'providers.server.card_title'.tr(); - children = [ - BrandText.body1('providers.server.bottom_sheet.1'.tr()), - SizedBox(height: 10), - BrandText.body1('providers.server.bottom_sheet.2'.tr()), - SizedBox(height: 10), - BrandText.body1('providers.server.bottom_sheet.3'.tr()), - ]; - break; + throw ('wrong type'); case ProviderType.domain: title = 'providers.domain.card_title'.tr(); children = [ @@ -161,7 +184,7 @@ class _ProviderDetails extends StatelessWidget { BrandText.body1( 'providers.domain.bottom_sheet.2'.tr(args: [domainName, 'Date'])), SizedBox(height: 10), - BrandText.body1('providers.domain.bottom_sheet.3'.tr()), + BrandText.body1('providers.domain.status'.tr()), ]; break; case ProviderType.backup: @@ -185,38 +208,7 @@ class _ProviderDetails extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Align( - alignment: Alignment.centerRight, - child: Padding( - padding: EdgeInsets.symmetric( - vertical: 4, - horizontal: 2, - ), - child: PopupMenuButton<_PopupMenuItemType>( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - onSelected: (_PopupMenuItemType result) { - switch (result) { - case _PopupMenuItemType.setting: - navigatorKey.currentState! - .push(materialRoute(SettingsPage())); - break; - } - }, - icon: Icon(Icons.more_vert), - itemBuilder: (BuildContext context) => [ - PopupMenuItem<_PopupMenuItemType>( - value: _PopupMenuItemType.setting, - child: Container( - padding: EdgeInsets.only(left: 5), - child: Text('basis.settings'.tr()), - ), - ), - ], - ), - ), - ), + SizedBox(height: 40), Padding( padding: brandPagePadding2, child: Column( @@ -242,5 +234,3 @@ class _ProviderDetails extends StatelessWidget { ); } } - -enum _PopupMenuItemType { setting } diff --git a/lib/ui/pages/server_details/server_details.dart b/lib/ui/pages/server_details/server_details.dart new file mode 100644 index 00000000..e6db48fa --- /dev/null +++ b/lib/ui/pages/server_details/server_details.dart @@ -0,0 +1,290 @@ +import 'package:cubit_form/cubit_form.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; +import 'package:selfprivacy/logic/models/state_types.dart'; +import 'package:selfprivacy/ui/components/brand_divider/brand_divider.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/components/icon_status_mask/icon_status_mask.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart'; +import 'package:selfprivacy/utils/named_font_weight.dart'; + +part 'server_settings.dart'; + +var navigatorKey = GlobalKey(); + +class ServerDetails extends StatefulWidget { + const ServerDetails({Key? key}) : super(key: key); + + @override + _ServerDetailsState createState() => _ServerDetailsState(); +} + +class _ServerDetailsState extends State + with SingleTickerProviderStateMixin { + late TabController tabController; + + @override + void dispose() { + tabController.dispose(); + super.dispose(); + } + + @override + void initState() { + tabController = TabController(length: 2, vsync: this); + tabController.addListener(() { + setState(() {}); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + var isReady = context.watch().state.isFullyInitilized; + var providerState = isReady ? StateType.stable : StateType.uninitialized; + + late String title = 'providers.server.card_title'.tr(); + + return TabBarView( + physics: NeverScrollableScrollPhysics(), + controller: tabController, + children: [ + BlocProvider( + create: (context) => ServerDetailsCubit()..check(), + child: Builder(builder: (context) { + var details = context.watch().state; + if (details is ServerDetailsLoading || + details is ServerDetailsInitial) { + return _TempMessage(message: 'basis.loading'.tr()); + } else if (details is ServerDetailsNotReady) { + return _TempMessage(message: 'basis.no_data'.tr()); + } else if (details is Loaded) { + var data = details.serverInfo; + var checkTime = details.checkTime; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: brandPagePadding2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + IconStatusMask( + status: providerState, + child: Icon( + BrandIcons.server, + size: 40, + color: Colors.white, + ), + ), + SizedBox(width: 10), + BrandText.h2(title), + Spacer(), + Padding( + padding: EdgeInsets.symmetric( + vertical: 4, + horizontal: 2, + ), + child: PopupMenuButton<_PopupMenuItemType>( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + onSelected: (_PopupMenuItemType result) { + switch (result) { + case _PopupMenuItemType.setting: + tabController.animateTo(1); + break; + } + }, + icon: Icon(Icons.more_vert), + itemBuilder: (BuildContext context) => [ + PopupMenuItem<_PopupMenuItemType>( + value: _PopupMenuItemType.setting, + child: Container( + padding: EdgeInsets.only(left: 5), + child: Text('basis.settings'.tr()), + ), + ), + ], + ), + ), + ], + ), + SizedBox(height: 10), + BrandText.body1('providers.server.bottom_sheet.1'.tr()), + SizedBox(height: 30), + Center(child: BrandText.h2('General information')), + SizedBox(height: 10), + Table( + columnWidths: { + 0: FractionColumnWidth(.5), + 1: FractionColumnWidth(.5), + }, + defaultVerticalAlignment: + TableCellVerticalAlignment.middle, + children: [ + TableRow( + children: [ + getRowTitle('Last check'), + getRowValue(formater.format(checkTime)), + ], + ), + TableRow( + children: [ + getRowTitle('Server Id'), + getRowValue(data.id.toString()), + ], + ), + TableRow( + children: [ + getRowTitle('Status:'), + getRowValue( + '${data.status.toString().split('.')[1].toUpperCase()}', + isBold: true, + ), + ], + ), + TableRow( + children: [ + getRowTitle('CPU'), + getRowValue( + data.serverType.cores.toString(), + ), + ], + ), + TableRow( + children: [ + getRowTitle('Memory'), + getRowValue( + '${data.serverType.memory.toString()} GB', + ), + ], + ), + TableRow( + children: [ + getRowTitle('Disk Local'), + getRowValue( + '${data.serverType.disk.toString()} GB', + ), + ], + ), + TableRow( + children: [ + getRowTitle('Price monthly:'), + getRowValue( + '${data.serverType.prices[1].monthly.toString()}', + ), + ], + ), + TableRow( + children: [ + getRowTitle('Price hourly:'), + getRowValue( + '${data.serverType.prices[1].hourly.toString()}', + ), + ], + ), + ], + ), + SizedBox(height: 30), + Center(child: BrandText.h2('Location')), + SizedBox(height: 10), + Table( + columnWidths: { + 0: FractionColumnWidth(.5), + 1: FractionColumnWidth(.5), + }, + defaultVerticalAlignment: + TableCellVerticalAlignment.middle, + children: [ + TableRow( + children: [ + getRowTitle('Country'), + getRowValue( + '${data.location.country}', + ), + ], + ), + TableRow( + children: [ + getRowTitle('City'), + getRowValue(data.location.city), + ], + ), + TableRow( + children: [ + getRowTitle('Description'), + getRowValue(data.location.description), + ], + ), + ], + ), + // BrandText.body1('providers.server.bottom_sheet.2'.tr()), + // SizedBox(height: 10), + // BrandText.body1('providers.server.bottom_sheet.3'.tr()), + ], + ), + ), + ], + ); + } else { + throw Exception('wrong state'); + } + }), + ), + _ServerSettings(tabController: tabController), + ], + ); + } + + Widget getRowTitle(String title) { + return Padding( + padding: const EdgeInsets.only(right: 10), + child: BrandText.h5( + title, + textAlign: TextAlign.right, + ), + ); + } + + Widget getRowValue(String title, {bool isBold = false}) { + return BrandText.body1( + title, + style: isBold + ? TextStyle( + fontWeight: NamedFontWeight.demiBold, + ) + : null, + ); + } +} + +enum _PopupMenuItemType { setting } + +class _TempMessage extends StatelessWidget { + const _TempMessage({ + Key? key, + required this.message, + }) : super(key: key); + + final String message; + @override + Widget build(BuildContext context) { + return SizedBox( + height: MediaQuery.of(context).size.height - 100, + child: Center( + child: BrandText.body2(message), + ), + ); + } +} + +final DateFormat formater = DateFormat('HH:mm:ss'); diff --git a/lib/ui/pages/server_details/server_settings.dart b/lib/ui/pages/server_details/server_settings.dart new file mode 100644 index 00000000..b5d48e08 --- /dev/null +++ b/lib/ui/pages/server_details/server_settings.dart @@ -0,0 +1,136 @@ +part of 'server_details.dart'; + +class _ServerSettings extends StatelessWidget { + const _ServerSettings({ + Key? key, + required this.tabController, + }) : super(key: key); + + final TabController tabController; + + @override + Widget build(BuildContext context) { + return ListView( + padding: brandPagePadding2, + children: [ + SizedBox(height: 10), + Container( + height: 52, + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: 1), + child: Container( + child: Row( + children: [ + IconButton( + icon: Icon(BrandIcons.arrow_left), + onPressed: () => tabController.animateTo(0), + ), + SizedBox(width: 10), + BrandText.h4('basis.settings'.tr()), + ], + ), + ), + ), + BrandDivider(), + SwitcherBlock( + onChange: (_) {}, + child: _TextColumn( + title: 'Allow Auto-upgrade', + value: 'Wether to allow automatic packages upgrades', + ), + isActive: true, + ), + SwitcherBlock( + onChange: (_) {}, + child: _TextColumn( + title: 'Reboot after upgrade', + value: 'Reboot without prompt after applying updates', + ), + isActive: false, + ), + _Button( + onTap: () {}, + child: _TextColumn( + title: 'Server Timezone', + value: 'Europe/Kyiv', + ), + ), + _Button( + onTap: () {}, + child: _TextColumn( + title: 'Server Locale', + value: 'Default', + ), + ), + _Button( + onTap: () {}, + child: _TextColumn( + hasWarning: true, + title: 'Factory Reset', + value: 'Restore default settings on your server', + ), + ) + ], + ); + } +} + +class _Button extends StatelessWidget { + const _Button({ + Key? key, + required this.onTap, + required this.child, + }) : super(key: key); + + final Widget child; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + padding: EdgeInsets.only(top: 20, bottom: 5), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(width: 1, color: BrandColors.dividerColor), + )), + child: child, + ), + ); + } +} + +class _TextColumn extends StatelessWidget { + const _TextColumn({ + Key? key, + required this.title, + required this.value, + this.hasWarning = false, + }) : super(key: key); + + final String title; + final String value; + final bool hasWarning; + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BrandText.body1( + title, + style: TextStyle(color: hasWarning ? BrandColors.warning : null), + ), + SizedBox(height: 5), + BrandText.body1( + value, + style: TextStyle( + fontSize: 13, + height: 1.53, + color: hasWarning ? BrandColors.warning : BrandColors.gray1, + ), + ), + ], + ); + } +} diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 69dea2b7..2407e14a 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -4,6 +4,7 @@ import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/state_types.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_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; @@ -13,6 +14,7 @@ import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; +import 'package:selfprivacy/utils/ui_helpers.dart'; import 'package:url_launcher/url_launcher.dart'; import '../rootRoute.dart'; @@ -105,10 +107,10 @@ class _Card extends StatelessWidget { var isReady = context.watch().state.isFullyInitilized; var changeTab = context.read().onPress; return GestureDetector( - onTap: () => showModalBottomSheet( + onTap: () => showDialog( context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, + // isScrollControlled: true, + // backgroundColor: Colors.transparent, builder: (BuildContext context) { return _ServiceDetails( serviceType: serviceType, @@ -170,9 +172,7 @@ class _ServiceDetails extends StatelessWidget { late Widget child; var config = context.watch().state; - var domainName = config.isDomainFilled - ? config.cloudFlareDomain!.domainName! - : 'example.com'; + var domainName = UiHelpers.getDomainName(config); var linksStyle = body1Style.copyWith( fontSize: 15, @@ -181,7 +181,6 @@ class _ServiceDetails extends StatelessWidget { : BrandColors.black, fontWeight: FontWeight.bold, decoration: TextDecoration.underline, - // height: 1.1, ); var textStyle = body1Style.copyWith( @@ -198,9 +197,10 @@ class _ServiceDetails extends StatelessWidget { text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]), style: textStyle, ), + WidgetSpan(child: SizedBox(width: 5)), WidgetSpan( child: Padding( - padding: EdgeInsets.only(bottom: 0.8, left: 5), + padding: EdgeInsets.only(bottom: 0.8), child: GestureDetector( child: Text( 'services.mail.bottom_sheet.2'.tr(), @@ -236,9 +236,10 @@ class _ServiceDetails extends StatelessWidget { .tr(args: [domainName]), style: textStyle, ), + WidgetSpan(child: SizedBox(width: 5)), WidgetSpan( child: Padding( - padding: EdgeInsets.only(bottom: 0.8, left: 5), + padding: EdgeInsets.only(bottom: 0.8), child: GestureDetector( onTap: () => _launchURL('https://password.$domainName'), child: Text( @@ -259,9 +260,10 @@ class _ServiceDetails extends StatelessWidget { text: 'services.video.bottom_sheet.1'.tr(args: [domainName]), style: textStyle, ), + WidgetSpan(child: SizedBox(width: 5)), WidgetSpan( child: Padding( - padding: EdgeInsets.only(bottom: 0.8, left: 5), + padding: EdgeInsets.only(bottom: 0.8), child: GestureDetector( onTap: () => _launchURL('https://meet.$domainName'), child: Text( @@ -282,9 +284,10 @@ class _ServiceDetails extends StatelessWidget { text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]), style: textStyle, ), + WidgetSpan(child: SizedBox(width: 5)), WidgetSpan( child: Padding( - padding: EdgeInsets.only(bottom: 0.8, left: 5), + padding: EdgeInsets.only(bottom: 0.8), child: GestureDetector( onTap: () => _launchURL('https://cloud.$domainName'), child: Text( @@ -306,9 +309,10 @@ class _ServiceDetails extends StatelessWidget { .tr(args: [domainName]), style: textStyle, ), + WidgetSpan(child: SizedBox(width: 5)), WidgetSpan( child: Padding( - padding: EdgeInsets.only(bottom: 0.8, left: 5), + padding: EdgeInsets.only(bottom: 0.8), child: GestureDetector( onTap: () => _launchURL('https://social.$domainName'), child: Text( @@ -329,9 +333,10 @@ class _ServiceDetails extends StatelessWidget { text: 'services.git.bottom_sheet.1'.tr(args: [domainName]), style: textStyle, ), + WidgetSpan(child: SizedBox(width: 5)), WidgetSpan( child: Padding( - padding: EdgeInsets.only(bottom: 0.8, left: 5), + padding: EdgeInsets.only(bottom: 0.8), child: GestureDetector( onTap: () => _launchURL('https://git.$domainName'), child: Text( @@ -345,36 +350,44 @@ class _ServiceDetails extends StatelessWidget { )); break; } - return BrandModalSheet( - child: Navigator( - key: navigatorKey, - initialRoute: '/', - onGenerateRoute: (_) { - return materialRoute( - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: brandPagePadding1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 13), - IconStatusMask( - status: status, - child: Icon(icon, size: 40, color: Colors.white), + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SingleChildScrollView( + child: Container( + width: 350, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: brandPagePadding1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + IconStatusMask( + status: status, + child: Icon(icon, size: 40, color: Colors.white), + ), + SizedBox(height: 10), + BrandText.h2(title), + SizedBox(height: 10), + child, + SizedBox(height: 40), + Center( + child: Container( + child: BrandButton.rised( + onPressed: () => Navigator.of(context).pop(), + title: 'basis.close'.tr(), + ), ), - SizedBox(height: 10), - BrandText.h1(title), - SizedBox(height: 10), - child, - ], - ), - ) - ], - ), - ); - }, + ), + ], + ), + ) + ], + ), + ), ), ); } @@ -397,3 +410,252 @@ class _ServiceDetails extends StatelessWidget { } } } + + +// class _ServiceDetails extends StatelessWidget { +// const _ServiceDetails({ +// Key? key, +// required this.serviceType, +// required this.icon, +// required this.status, +// required this.title, +// required this.changeTab, +// }) : super(key: key); + +// final ServiceTypes serviceType; +// final IconData icon; +// final StateType status; +// final String title; +// final ValueChanged changeTab; + +// @override +// Widget build(BuildContext context) { +// late Widget child; + +// var config = context.watch().state; +// var domainName = UiHelpers.getDomainName(config); + +// var linksStyle = body1Style.copyWith( +// fontSize: 15, +// color: Theme.of(context).brightness == Brightness.dark +// ? Colors.white +// : BrandColors.black, +// fontWeight: FontWeight.bold, +// decoration: TextDecoration.underline, +// // height: 1.1, +// ); + +// var textStyle = body1Style.copyWith( +// color: Theme.of(context).brightness == Brightness.dark +// ? Colors.white +// : BrandColors.black, +// ); +// switch (serviceType) { +// case ServiceTypes.mail: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]), +// style: textStyle, +// ), +// WidgetSpan( +// child: Padding( +// padding: EdgeInsets.only(bottom: 0.8, left: 5), +// child: GestureDetector( +// child: Text( +// 'services.mail.bottom_sheet.2'.tr(), +// style: linksStyle, +// ), +// onTap: () { +// Navigator.of(context).pop(); +// changeTab(2); +// }, +// ), +// ), +// ), +// ], +// )); +// break; +// case ServiceTypes.messenger: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.messenger.bottom_sheet.1'.tr(args: [domainName]), +// style: textStyle, +// ) +// ], +// )); +// break; +// case ServiceTypes.passwordManager: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.password_manager.bottom_sheet.1' +// .tr(args: [domainName]), +// style: textStyle, +// ), +// WidgetSpan( +// child: Padding( +// padding: EdgeInsets.only(bottom: 0.8, left: 5), +// child: GestureDetector( +// onTap: () => _launchURL('https://password.$domainName'), +// child: Text( +// 'password.$domainName', +// style: linksStyle, +// ), +// ), +// ), +// ), +// ], +// )); +// break; +// case ServiceTypes.video: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.video.bottom_sheet.1'.tr(args: [domainName]), +// style: textStyle, +// ), +// WidgetSpan( +// child: Padding( +// padding: EdgeInsets.only(bottom: 0.8, left: 5), +// child: GestureDetector( +// onTap: () => _launchURL('https://meet.$domainName'), +// child: Text( +// 'meet.$domainName', +// style: linksStyle, +// ), +// ), +// ), +// ), +// ], +// )); +// break; +// case ServiceTypes.cloud: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]), +// style: textStyle, +// ), +// WidgetSpan( +// child: Padding( +// padding: EdgeInsets.only(bottom: 0.8, left: 5), +// child: GestureDetector( +// onTap: () => _launchURL('https://cloud.$domainName'), +// child: Text( +// 'cloud.$domainName', +// style: linksStyle, +// ), +// ), +// ), +// ), +// ], +// )); +// break; +// case ServiceTypes.socialNetwork: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.social_network.bottom_sheet.1' +// .tr(args: [domainName]), +// style: textStyle, +// ), +// WidgetSpan( +// child: Padding( +// padding: EdgeInsets.only(bottom: 0.8, left: 5), +// child: GestureDetector( +// onTap: () => _launchURL('https://social.$domainName'), +// child: Text( +// 'social.$domainName', +// style: linksStyle, +// ), +// ), +// ), +// ), +// ], +// )); +// break; +// case ServiceTypes.git: +// child = RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: 'services.git.bottom_sheet.1'.tr(args: [domainName]), +// style: textStyle, +// ), +// WidgetSpan( +// child: Padding( +// padding: EdgeInsets.only(bottom: 0.8, left: 5), +// child: GestureDetector( +// onTap: () => _launchURL('https://git.$domainName'), +// child: Text( +// 'git.$domainName', +// style: linksStyle, +// ), +// ), +// ), +// ), +// ], +// )); +// break; +// } +// return BrandModalSheet( + // child: Navigator( + // key: navigatorKey, + // initialRoute: '/', + // onGenerateRoute: (_) { + // return materialRoute( +// Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Padding( +// padding: brandPagePadding1, +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// SizedBox(height: 13), +// IconStatusMask( +// status: status, +// child: Icon(icon, size: 40, color: Colors.white), +// ), +// SizedBox(height: 10), +// BrandText.h1(title), +// SizedBox(height: 10), +// child, +// ], +// ), +// ) +// ], +// ), +// ); +// }, +// ), +// ); +// } + +// void _launchURL(url) async { +// var _possible = await canLaunch(url); + +// if (_possible) { +// try { +// await launch( +// url, +// forceSafariVC: true, +// enableJavaScript: true, +// ); +// } catch (e) { +// print(e); +// } +// } else { +// throw 'Could not launch $url'; +// } +// } +// } + diff --git a/lib/ui/pages/users/new_user.dart b/lib/ui/pages/users/new_user.dart index 53636e74..62eb624f 100644 --- a/lib/ui/pages/users/new_user.dart +++ b/lib/ui/pages/users/new_user.dart @@ -5,14 +5,12 @@ class _NewUser extends StatelessWidget { Widget build(BuildContext context) { var config = context.watch().state; - var domainName = config.isDomainFilled - ? config.cloudFlareDomain!.domainName! - : 'example.com'; + var domainName = UiHelpers.getDomainName(config); return BrandModalSheet( child: BlocProvider( create: (context) => - UserFormCubit(usersCubit: context.watch()), + UserFormCubit(usersCubit: context.read()), child: Builder(builder: (context) { var formCubitState = context.watch().state; diff --git a/lib/ui/pages/users/user_details.dart b/lib/ui/pages/users/user_details.dart index 99f43315..3c788bec 100644 --- a/lib/ui/pages/users/user_details.dart +++ b/lib/ui/pages/users/user_details.dart @@ -12,9 +12,7 @@ class _UserDetails extends StatelessWidget { Widget build(BuildContext context) { var config = context.watch().state; - var domainName = config.isDomainFilled - ? config.cloudFlareDomain!.domainName! - : 'example.com'; + var domainName = UiHelpers.getDomainName(config); return BrandModalSheet( child: Column( @@ -44,8 +42,8 @@ class _UserDetails extends StatelessWidget { ), onSelected: (PopupMenuItemType result) { switch (result) { - case PopupMenuItemType.reset: - break; + // case PopupMenuItemType.reset: + // break; case PopupMenuItemType.delete: showDialog( context: context, @@ -88,13 +86,13 @@ class _UserDetails extends StatelessWidget { }, icon: Icon(Icons.more_vert), itemBuilder: (BuildContext context) => [ - PopupMenuItem( - value: PopupMenuItemType.reset, - child: Container( - padding: EdgeInsets.only(left: 5), - child: Text('users.reset_password'.tr()), - ), - ), + // PopupMenuItem( + // value: PopupMenuItemType.reset, + // child: Container( + // padding: EdgeInsets.only(left: 5), + // child: Text('users.reset_password'.tr()), + // ), + // ), PopupMenuItem( value: PopupMenuItemType.delete, child: Container( @@ -145,7 +143,7 @@ class _UserDetails extends StatelessWidget { SizedBox(height: 24), BrandDivider(), SizedBox(height: 20), - BrandButton.iconText( + BrandButton.emptyWithIconText( title: 'users.send_regisration_data'.tr(), icon: Icon(BrandIcons.share), onPressed: () {}, @@ -161,6 +159,6 @@ class _UserDetails extends StatelessWidget { } enum PopupMenuItemType { - reset, + // reset, delete, } diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index 8acb4016..271182e6 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -14,6 +14,7 @@ import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.da import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/utils/ui_helpers.dart'; part 'fab.dart'; part 'new_user.dart'; diff --git a/lib/utils/route_transitions/slide_bottom.dart b/lib/utils/route_transitions/slide_bottom.dart index 607ab105..380b1142 100644 --- a/lib/utils/route_transitions/slide_bottom.dart +++ b/lib/utils/route_transitions/slide_bottom.dart @@ -36,8 +36,10 @@ Function transitionsBuilder = ( class SlideBottomRoute extends PageRouteBuilder { SlideBottomRoute(this.widget) : super( + transitionDuration: Duration(milliseconds: 150), pageBuilder: pageBuilder(widget), - transitionsBuilder: transitionsBuilder as Widget Function(BuildContext, Animation, Animation, Widget), + transitionsBuilder: transitionsBuilder as Widget Function( + BuildContext, Animation, Animation, Widget), ); final Widget widget; diff --git a/lib/utils/ui_helpers.dart b/lib/utils/ui_helpers.dart new file mode 100644 index 00000000..b0b43851 --- /dev/null +++ b/lib/utils/ui_helpers.dart @@ -0,0 +1,9 @@ +import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; + +/// it's ui helpers use only for ui components, don't use for logic components. + +class UiHelpers { + static String getDomainName(AppConfigState config) => config.isDomainFilled + ? config.cloudFlareDomain!.domainName + : 'example.com'; +} diff --git a/pubspec.lock b/pubspec.lock index 83d5ac4b..17c0befd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -357,6 +357,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" http: dependency: transitive description: @@ -789,6 +796,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + unicons: + dependency: "direct main" + description: + name: unicons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" url_launcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d692fbcf..ba5d042c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: package_info: ^2.0.0 pretty_dio_logger: ^1.1.1 provider: ^5.0.0 + unicons: ^1.0.2 url_launcher: ^6.0.2 wakelock: ^0.5.0+2 @@ -36,6 +37,7 @@ dev_dependencies: basic_utils: ^3.0.0-nullsafety.1 build_runner: ^1.11.5 flutter_launcher_icons: ^0.9.0 + hive_generator: ^1.0.0 json_serializable: ^4.0.2 flutter_icons: From 275ba21a475e6402db58074a44be66be96560249 Mon Sep 17 00:00:00 2001 From: Kherel Date: Fri, 26 Mar 2021 14:53:21 +0100 Subject: [PATCH 12/17] fix --- assets/translations/en.json | 4 +++- assets/translations/ru.json | 4 +++- lib/ui/pages/providers/providers.dart | 3 +-- lib/ui/pages/server_details/server_details.dart | 7 ++----- lib/ui/pages/services/services.dart | 2 -- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index da4f6a4a..5b5b8f56 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -58,7 +58,9 @@ "card_title": "Server", "status": "Status — Good", "bottom_sheet": { - "1": "It's a virtual computer, where all your services live." + "1": "It's a virtual computer, where all your services live.", + "2": "General information", + "3": "Location" } }, "domain": { diff --git a/assets/translations/ru.json b/assets/translations/ru.json index f6e17b70..b51f0e9e 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -58,7 +58,9 @@ "card_title": "Сервер", "status": "Статус — в норме", "bottom_sheet": { - "1": "Это виртульный компьютер на котором работают все ваши сервисы." + "1": "Это виртульный компьютер на котором работают все ваши сервисы.", + "2": "General information", + "3": "Location" } }, "domain": { diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index ea6cf8c2..db953281 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -11,7 +11,6 @@ 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/components/not_ready_card/not_ready_card.dart'; import 'package:selfprivacy/ui/components/one_page/one_page.dart'; -import 'package:selfprivacy/ui/pages/providers/settings/settings.dart'; import 'package:selfprivacy/ui/pages/server_details/server_details.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -195,7 +194,7 @@ class _ProviderDetails extends StatelessWidget { BrandText.body1( 'providers.backup.bottom_sheet.2'.tr(args: [domainName, 'Time'])), SizedBox(height: 10), - BrandText.body1('providers.backup.bottom_sheet.3'.tr()), + BrandText.body1('providers.backup.status'.tr()), ]; break; } diff --git a/lib/ui/pages/server_details/server_details.dart b/lib/ui/pages/server_details/server_details.dart index e6db48fa..3e578199 100644 --- a/lib/ui/pages/server_details/server_details.dart +++ b/lib/ui/pages/server_details/server_details.dart @@ -121,7 +121,7 @@ class _ServerDetailsState extends State SizedBox(height: 10), BrandText.body1('providers.server.bottom_sheet.1'.tr()), SizedBox(height: 30), - Center(child: BrandText.h2('General information')), + Center(child: BrandText.h2('providers.server.2'.tr())), SizedBox(height: 10), Table( columnWidths: { @@ -195,7 +195,7 @@ class _ServerDetailsState extends State ], ), SizedBox(height: 30), - Center(child: BrandText.h2('Location')), + Center(child: BrandText.h2('providers.server.3'.tr())), SizedBox(height: 10), Table( columnWidths: { @@ -227,9 +227,6 @@ class _ServerDetailsState extends State ), ], ), - // BrandText.body1('providers.server.bottom_sheet.2'.tr()), - // SizedBox(height: 10), - // BrandText.body1('providers.server.bottom_sheet.3'.tr()), ], ), ), diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 2407e14a..c21b0d72 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -8,12 +8,10 @@ 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_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.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/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; import 'package:url_launcher/url_launcher.dart'; From e69b0de5bded9695d0cb7ed4cf795c3f48d4fe36 Mon Sep 17 00:00:00 2001 From: Illia Chub Date: Mon, 29 Mar 2021 13:59:05 +0300 Subject: [PATCH 13/17] Fixed translations --- assets/translations/en.json | 16 +++---- assets/translations/ru.json | 12 ++--- pubspec.lock | 93 +++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 64 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 5b5b8f56..5a47b566 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -21,7 +21,7 @@ "saving": "Saving..", "nickname": "nickname", "loading": "Loading...", - "later": "Настрою потом", + "later": "I will setup it later", "reset": "Reset", "details": "Details", "no_data": "No data" @@ -133,7 +133,7 @@ } }, "git": { - "title": "Git-server", + "title": "Git Server", "subtitle": "Private alternative to the Github, that belongs to you, but not a Microsoft.", "bottom_sheet": { "1": "You can connect and create a new user here:" @@ -158,7 +158,7 @@ "initializing": { "_comment": "initializing page", "1": "Connect Hetzner server", - "2": "Here, your data and SelfPrivacy services wiil reside", + "2": "Here, your data and SelfPrivacy services wiil reside", "how": "How to obtain API token", "3": "Connect CloudFlare", "4": "To manage your domain's DNS", @@ -182,11 +182,11 @@ }, "modals": { "_comment": "messages in modals", - "1": "Сервер с таким именем уже существует", - "2": "Уничтожить сервер и создать новый?", - "3": "Вы уверенны", - "4": "Сбросить все ключи?", - "5": "Да, сбросить" + "1": "Server with such name, already exist", + "2": "Destroy server and create a new one?", + "3": "Are you sure?", + "4": "Purge all authentication keys?", + "5": "Yes, purge all my tokens" }, "timer": { "sec": "{} sec" diff --git a/assets/translations/ru.json b/assets/translations/ru.json index b51f0e9e..54d4906b 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -32,9 +32,9 @@ "about_project": "О проекте SelfPrivacy", "about_app": "О приложении", "onboarding": "Приветствие", - "console": "Console", + "console": "Консоль", "about_app_page": { - "text": "Тут любая служебная информация, v.{}" + "text": "Версия приложения: v.{}" }, "settings": { "title": "Настройки приложения", @@ -59,8 +59,8 @@ "status": "Статус — в норме", "bottom_sheet": { "1": "Это виртульный компьютер на котором работают все ваши сервисы.", - "2": "General information", - "3": "Location" + "2": "Общая информация", + "3": "Размещение" } }, "domain": { @@ -147,8 +147,8 @@ "not_ready": "Подключите сервер, домен и DNS в разделу Провайдеры, чтобы добавить первого пользователя", "nobody_here": "'Здесь пока никого'", "login": "Логин", - "onboarding": "Onboarding", - "console": "Console", + "onboarding": "Приветствие", + "console": "Консоль разработчика", "new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам. Ещё какое-то описание.", "delete_confirm_question": "удалить учетную запись?", "reset_password": "Сбросить пароль", diff --git a/pubspec.lock b/pubspec.lock index 17c0befd..7be23a73 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "14.0.0" + version: "19.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.41.2" + version: "1.3.0" archive: dependency: transitive description: @@ -42,7 +42,7 @@ packages: name: basic_utils url: "https://pub.dartlang.org" source: hosted - version: "3.0.0-nullsafety.1" + version: "3.0.0-nullsafety.3" bloc: dependency: transitive description: @@ -63,14 +63,14 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.6.2" + version: "2.0.0" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.5" + version: "0.4.7" build_daemon: dependency: transitive description: @@ -84,35 +84,35 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.5.3" + version: "2.0.0" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.11.5" + version: "1.12.2" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.1.10" + version: "6.1.12" built_collection: dependency: transitive description: name: built_collection url: "https://pub.dartlang.org" source: hosted - version: "4.3.2" + version: "5.0.0" built_value: dependency: transitive description: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "7.1.0" + version: "8.0.4" characters: dependency: transitive description: @@ -133,14 +133,14 @@ packages: name: checked_yaml url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" cli_util: dependency: transitive description: name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.3.0" clock: dependency: transitive description: @@ -154,7 +154,7 @@ packages: name: code_builder url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.7.0" collection: dependency: transitive description: @@ -175,7 +175,7 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "0.15.2" + version: "1.0.2" crypto: dependency: "direct main" description: @@ -203,14 +203,14 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.3.12" + version: "2.0.0" dio: dependency: "direct main" description: name: dio url: "https://pub.dartlang.org" source: hosted - version: "4.0.0-prev3" + version: "4.0.0" easy_localization: dependency: "direct main" description: @@ -273,7 +273,7 @@ packages: name: fixnum url: "https://pub.dartlang.org" source: hosted - version: "0.10.11" + version: "1.0.0" flutter: dependency: "direct main" description: flutter @@ -335,21 +335,21 @@ packages: name: glob url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" graphs: dependency: transitive description: name: graphs url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "1.0.0" hive: dependency: "direct main" description: name: hive url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" hive_flutter: dependency: "direct main" description: @@ -363,7 +363,7 @@ packages: name: hive_generator url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" http: dependency: transitive description: @@ -377,7 +377,7 @@ packages: name: http_multi_server url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "3.0.0" http_parser: dependency: transitive description: @@ -405,7 +405,7 @@ packages: name: io url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "1.0.0" js: dependency: transitive description: @@ -426,14 +426,14 @@ packages: name: json_serializable url: "https://pub.dartlang.org" source: hosted - version: "4.0.2" + version: "4.1.0" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" markdown: dependency: transitive description: @@ -468,7 +468,7 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.7" + version: "1.0.0" nested: dependency: transitive description: @@ -489,7 +489,7 @@ packages: name: package_config url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "2.0.0" package_info: dependency: "direct main" description: @@ -573,7 +573,7 @@ packages: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "3.0.0-nullsafety.2" + version: "3.0.1" pool: dependency: transitive description: @@ -594,7 +594,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.1.1" + version: "4.2.1" provider: dependency: "direct main" description: @@ -608,21 +608,14 @@ packages: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "1.4.4" + version: "2.0.0" pubspec_parse: dependency: transitive description: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "0.1.7" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.5" + version: "1.0.0" shared_preferences: dependency: transitive description: @@ -678,21 +671,21 @@ packages: name: shelf_packages_handler url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "3.0.0" shelf_static: dependency: transitive description: name: shelf_static url: "https://pub.dartlang.org" source: hosted - version: "0.2.9+2" + version: "1.0.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket url: "https://pub.dartlang.org" source: hosted - version: "0.2.4+1" + version: "1.0.1" sky_engine: dependency: transitive description: flutter @@ -704,7 +697,7 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "0.9.10+3" + version: "1.0.0" source_map_stack_trace: dependency: transitive description: @@ -746,7 +739,7 @@ packages: name: stream_transform url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" string_scanner: dependency: transitive description: @@ -788,7 +781,7 @@ packages: name: timing url: "https://pub.dartlang.org" source: hosted - version: "0.1.1+3" + version: "1.0.0" typed_data: dependency: transitive description: @@ -809,7 +802,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.2" + version: "6.0.3" url_launcher_linux: dependency: transitive description: @@ -858,7 +851,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "5.5.0" + version: "6.1.0+1" wakelock: dependency: "direct main" description: @@ -900,28 +893,28 @@ packages: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "0.9.7+15" + version: "1.0.0" web_socket_channel: dependency: transitive description: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol url: "https://pub.dartlang.org" source: hosted - version: "0.7.5" + version: "1.0.0" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" xdg_directories: dependency: transitive description: From ac2c157f6be7889a9e73e157c062bb4553a82751 Mon Sep 17 00:00:00 2001 From: Illia Chub Date: Mon, 29 Mar 2021 16:43:09 +0300 Subject: [PATCH 14/17] Updated version number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ba5d042c..f71c9d79 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: selfprivacy description: selfprivacy.org publish_to: 'none' -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: '>=2.12.0 <3.0.0' From ae3ec309cbf0da0eea5f63fb84d82033fac77744 Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 30 Mar 2021 19:38:40 +0200 Subject: [PATCH 15/17] add volume --- lib/config/hive_config.dart | 1 + lib/logic/api_maps/hetzner.dart | 73 ++++++++++++++++--- .../cubit/app_config/app_config_cubit.dart | 7 +- .../app_config/app_config_repository.dart | 2 +- .../cubit/app_config/app_config_state.dart | 2 + lib/logic/get_it/timer.dart | 2 +- lib/logic/models/server_details.dart | 22 +++++- lib/logic/models/server_details.g.dart | 48 +++++++++++- lib/ui/pages/initializing/initializing.dart | 5 +- lib/utils/password_generator2.dart | 6 +- 10 files changed, 141 insertions(+), 27 deletions(-) diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 17a10fb3..67d175f4 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -16,6 +16,7 @@ class HiveConfig { Hive.registerAdapter(HetznerServerDetailsAdapter()); Hive.registerAdapter(CloudFlareDomainAdapter()); Hive.registerAdapter(BackblazeCredentialAdapter()); + Hive.registerAdapter(HetznerDataBaseAdapter()); await Hive.openBox(BNames.appSettings); var cipher = HiveAesCipher(await getEncriptedKey()); diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index a9cad086..f7f6d46a 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -55,6 +55,19 @@ class HetznerApi extends ApiMap { } } + Future isFreeToCreate() async { + var client = await getClient(); + + Response serversReponse = await client.get('/servers'); + List servers = serversReponse.data['servers']; + var server = servers.firstWhere( + (el) => el['name'] == 'selfprivacy-server', + orElse: null, + ); + client.close(); + return server == null; + } + Future createServer({ required String cloudFlareKey, required User rootUser, @@ -62,31 +75,71 @@ class HetznerApi extends ApiMap { }) async { var dbPassword = getRandomString(40); + const chars = + 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + + var dbStorageName = getRandomString(6, chars); + var client = await getClient(); + + Response dbCreateResponse = await client.post( + '/volumes', + data: { + "size": 10, + "name": dbStorageName, + "labels": {"labelkey": "value"}, + "location": "fsn1", + "automount": false, + "format": "ext4" + }, + ); + var dbId = dbCreateResponse.data['volume']['id']; var data = jsonDecode( - '''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":false}''', + '''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''', ); - var client = await getClient(); - Response response = await client.post( + Response serverCreateResponse = await client.post( '/servers', data: data, ); client.close(); return HetznerServerDetails( - id: response.data['server']['id'], - ip4: response.data['server']['public_net']['ipv4']['ip'], + id: serverCreateResponse.data['server']['id'], + ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'], createTime: DateTime.now(), + dataBase: HetznerDataBase( + id: dbId, + name: dbCreateResponse.data['volume']['name'], + ), ); } - Future deleteSelfprivacyServer() async { + Future deleteSelfprivacyServerAndAllVolumes() async { var client = await getClient(); - Response response = await client.get('/servers'); - List list = response.data['servers']; - var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server'); + Response serversReponse = await client.get('/servers'); + List servers = serversReponse.data['servers']; + var server = servers.firstWhere((el) => el['name'] == 'selfprivacy-server'); await client.delete('/servers/${server['id']}'); - close(client); + + Response volumesReponse = await client.get('/volumes'); + List volumes = volumesReponse.data['volumes']; + + var laterFutures = []; + for (var volume in volumes) { + if (volume['server'] == null) { + await client.delete('/volumes/${volume['id']}'); + } else { + laterFutures.add(Future.delayed(Duration(seconds: 60)).then( + (_) => client.delete('/volumes/${volume['id']}'), + )); + } + } + + if (laterFutures.isEmpty) { + close(client); + } else { + Future.wait(laterFutures).then((value) => close(client)); + } } Future startServer({ diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 15e5f1fa..10ebeb68 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -202,7 +202,7 @@ class AppConfigCubit extends Cubit { } void clearAppConfig() { - _closeTimer(); + closeTimer(); repository.clearAppConfig(); emit(InitialAppConfigState()); } @@ -263,17 +263,16 @@ class AppConfigCubit extends Cubit { onSuccess: onSuccess, ); } catch (e) { - addError(e); emit(_stateCopy); } } close() { - _closeTimer(); + closeTimer(); return super.close(); } - void _closeTimer() { + void closeTimer() { if (timer != null && timer!.isActive) { timer!.cancel(); } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index e4197d06..0d2aae5d 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -119,7 +119,7 @@ class AppConfigRepository { text: 'basis.delete'.tr(), isRed: true, onPressed: () async { - await hetznerApi.deleteSelfprivacyServer(); + await hetznerApi.deleteSelfprivacyServerAndAllVolumes(); var serverDetails = await hetznerApi.createServer( cloudFlareKey: cloudFlareKey, diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 5a89ed04..5a22c29a 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -92,6 +92,8 @@ class AppConfigState extends Equatable { isServerReseted, hasFinalChecked, ]; + + print(res); return res; } } diff --git a/lib/logic/get_it/timer.dart b/lib/logic/get_it/timer.dart index 0444b44e..e91d3a20 100644 --- a/lib/logic/get_it/timer.dart +++ b/lib/logic/get_it/timer.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class TimerModel extends ChangeNotifier { DateTime _time = DateTime.now(); - DateTime get messages => _time; + DateTime get time => _time; void restart() { _time = DateTime.now(); diff --git a/lib/logic/models/server_details.dart b/lib/logic/models/server_details.dart index e014de14..4afbc5cd 100644 --- a/lib/logic/models/server_details.dart +++ b/lib/logic/models/server_details.dart @@ -8,14 +8,15 @@ class HetznerServerDetails { required this.ip4, required this.id, required this.createTime, + required this.dataBase, this.startTime, }); @HiveField(0) - final String? ip4; + final String ip4; @HiveField(1) - final int? id; + final int id; @HiveField(3) final DateTime? createTime; @@ -23,14 +24,31 @@ class HetznerServerDetails { @HiveField(2) final DateTime? startTime; + @HiveField(4) + final HetznerDataBase dataBase; + HetznerServerDetails copyWith({DateTime? startTime}) { return HetznerServerDetails( startTime: startTime ?? this.startTime, createTime: createTime, id: id, ip4: ip4, + dataBase: dataBase, ); } String toString() => id.toString(); } + +@HiveType(typeId: 5) +class HetznerDataBase { + HetznerDataBase({ + required this.id, + required this.name, + }); + + @HiveField(1) + int id; + @HiveField(2) + String name; +} diff --git a/lib/logic/models/server_details.g.dart b/lib/logic/models/server_details.g.dart index 0f08e2f2..d59c9020 100644 --- a/lib/logic/models/server_details.g.dart +++ b/lib/logic/models/server_details.g.dart @@ -17,9 +17,10 @@ class HetznerServerDetailsAdapter extends TypeAdapter { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return HetznerServerDetails( - ip4: fields[0] as String?, - id: fields[1] as int?, + ip4: fields[0] as String, + id: fields[1] as int, createTime: fields[3] as DateTime?, + dataBase: fields[4] as HetznerDataBase, startTime: fields[2] as DateTime?, ); } @@ -27,7 +28,7 @@ class HetznerServerDetailsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, HetznerServerDetails obj) { writer - ..writeByte(4) + ..writeByte(5) ..writeByte(0) ..write(obj.ip4) ..writeByte(1) @@ -35,7 +36,9 @@ class HetznerServerDetailsAdapter extends TypeAdapter { ..writeByte(3) ..write(obj.createTime) ..writeByte(2) - ..write(obj.startTime); + ..write(obj.startTime) + ..writeByte(4) + ..write(obj.dataBase); } @override @@ -48,3 +51,40 @@ class HetznerServerDetailsAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +class HetznerDataBaseAdapter extends TypeAdapter { + @override + final int typeId = 5; + + @override + HetznerDataBase read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return HetznerDataBase( + id: fields[1] as int, + name: fields[2] as String, + ); + } + + @override + void write(BinaryWriter writer, HetznerDataBase obj) { + writer + ..writeByte(2) + ..writeByte(1) + ..write(obj.id) + ..writeByte(2) + ..write(obj.name); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is HetznerDataBaseAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index 589ce654..ed41b6f5 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -407,8 +407,9 @@ class InitializingPage extends StatelessWidget { BrandText.body2('initializing.11'.tr()), Spacer(), BrandButton.rised( - onPressed: - isLoading! ? null : () => appConfigCubit.createServerAndSetDnsRecords(), + onPressed: isLoading! + ? null + : () => appConfigCubit.createServerAndSetDnsRecords(), title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(), ), Spacer(flex: 2), diff --git a/lib/utils/password_generator2.dart b/lib/utils/password_generator2.dart index cea7895c..36114d55 100644 --- a/lib/utils/password_generator2.dart +++ b/lib/utils/password_generator2.dart @@ -4,11 +4,11 @@ const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_'; Random _rnd = Random(); -String getRandomString(int length) => String.fromCharCodes( +String getRandomString(int length, [chars = _chars]) => String.fromCharCodes( Iterable.generate( length, - (_) => _chars.codeUnitAt( - _rnd.nextInt(_chars.length), + (_) => chars.codeUnitAt( + _rnd.nextInt(chars.length), ), ), ); From aabad65698c8f6ffb283e18f5f5d92ac4920e7d2 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 31 Mar 2021 13:37:39 +0200 Subject: [PATCH 16/17] update --- assets/translations/en.json | 3 +- assets/translations/ru.json | 3 +- lib/config/hive_config.dart | 3 +- lib/config/text_themes.dart | 4 +- lib/logic/api_maps/hetzner.dart | 8 ++- .../cubit/app_config/app_config_cubit.dart | 68 +++++++++++++++++-- .../app_config/app_config_repository.dart | 18 +++-- .../cubit/app_config/app_config_state.dart | 27 +++++--- .../components/progress_bar/progress_bar.dart | 41 ++++++----- lib/ui/pages/initializing/initializing.dart | 8 ++- 10 files changed, 138 insertions(+), 45 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 5a47b566..a93e7183 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -178,7 +178,8 @@ "17": "Check", "18": "How to obtain Hetzner API Token", "19": "1 Go via this link ", - "20": "\n" + "20": "\n", + "21": "One more restart to apply your security certificates." }, "modals": { "_comment": "messages in modals", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 54d4906b..df845f70 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -178,7 +178,8 @@ "17": "Проверка", "18": "Как получить Hetzner API Token'", "19": "1 Переходим по ссылке ", - "20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет." + "20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.", + "21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности" }, "modals": { "_comment": "messages in modals", diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 67d175f4..eaf7b0eb 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -56,5 +56,6 @@ class BNames { static String isServerStarted = 'isServerStarted'; static String backblazeKey = 'backblazeKey'; static String isLoading = 'isLoading'; - static String isServerReseted = 'isServerReseted'; + static String isServerResetedFirstTime = 'isServerResetedFirstTime'; + static String isServerResetedSecondTime = 'isServerResetedSecondTime'; } diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart index cf383122..c84aeaed 100644 --- a/lib/config/text_themes.dart +++ b/lib/config/text_themes.dart @@ -65,9 +65,9 @@ final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue); final progressTextStyleLight = TextStyle( fontSize: 11, color: BrandColors.textColor1, + height: 1.7, ); -final progressTextStyleDark = TextStyle( - fontSize: 11, +final progressTextStyleDark = progressTextStyleLight.copyWith( color: BrandColors.white, ); diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index f7f6d46a..11e68ab7 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -94,7 +94,7 @@ class HetznerApi extends ApiMap { ); var dbId = dbCreateResponse.data['volume']['id']; var data = jsonDecode( - '''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''', + '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''', ); Response serverCreateResponse = await client.post( @@ -113,12 +113,14 @@ class HetznerApi extends ApiMap { ); } - Future deleteSelfprivacyServerAndAllVolumes() async { + Future deleteSelfprivacyServerAndAllVolumes({ + required String domainName, + }) async { var client = await getClient(); Response serversReponse = await client.get('/servers'); List servers = serversReponse.data['servers']; - var server = servers.firstWhere((el) => el['name'] == 'selfprivacy-server'); + var server = servers.firstWhere((el) => el['name'] == domainName); await client.delete('/servers/${server['id']}'); Response volumesReponse = await client.get('/volumes'); diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 10ebeb68..4d9259f4 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -35,7 +35,10 @@ part 'app_config_state.dart'; /// c. if server is ok wait 30 sec /// d. reset server /// -/// 2.3. a. wait 60sec |finishCheckIfServerIsOkay +/// 2.3. a. wait 60sec |oneMoreReset() +/// d. reset server +/// +/// 2.4. a. wait 30sec |finishCheckIfServerIsOkay /// b. checkServer /// c. if server is okay set that fully checked @@ -54,6 +57,8 @@ class AppConfigCubit extends Cubit { } else if (state.progress == 7) { resetServerIfServerIsOkay(state: state, isImmediate: true); } else if (state.progress == 8) { + oneMoreReset(state: state, isImmediate: true); + } else if (state.progress == 9) { finishCheckIfServerIsOkay(state: state, isImmediate: true); } } @@ -106,6 +111,61 @@ class AppConfigCubit extends Cubit { } } + void oneMoreReset({ + AppConfigState? state, + bool isImmediate = false, + }) async { + var dataState = state ?? this.state; + + var work = () async { + emit(TimerState(dataState: dataState, isLoading: true)); + + var isServerWorking = await repository.isHttpServerWorking(); + + if (isServerWorking) { + var pauseDuration = Duration(seconds: 30); + emit(TimerState( + dataState: dataState, + timerStart: DateTime.now(), + isLoading: false, + duration: pauseDuration, + )); + timer = Timer(pauseDuration, () async { + var hetznerServerDetails = await repository.restart( + dataState.hetznerServer!, + ); + repository.saveIsServerResetedSecondTime(true); + repository.saveServerDetails(hetznerServerDetails); + + emit( + dataState.copyWith( + isServerResetedSecondTime: true, + hetznerServer: hetznerServerDetails, + isLoading: false, + ), + ); + finishCheckIfServerIsOkay(); + }); + } else { + oneMoreReset(); + } + }; + if (isImmediate) { + work(); + } else { + var pauseDuration = Duration(seconds: 60); + emit( + TimerState( + dataState: dataState, + timerStart: DateTime.now(), + duration: pauseDuration, + isLoading: false, + ), + ); + timer = Timer(pauseDuration, work); + } + } + void resetServerIfServerIsOkay({ AppConfigState? state, bool isImmediate = false, @@ -129,17 +189,17 @@ class AppConfigCubit extends Cubit { var hetznerServerDetails = await repository.restart( dataState.hetznerServer!, ); - repository.saveIsServerReseted(true); + repository.saveIsServerResetedFirstTime(true); repository.saveServerDetails(hetznerServerDetails); emit( dataState.copyWith( - isServerReseted: true, + isServerResetedFirstTime: true, hetznerServer: hetznerServerDetails, isLoading: false, ), ); - finishCheckIfServerIsOkay(); + oneMoreReset(); }); } else { resetServerIfServerIsOkay(); diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 0d2aae5d..4408f1f8 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -30,12 +30,17 @@ class AppConfigRepository { hetznerServer: getIt().hetznerServer, rootUser: box.get(BNames.rootUser), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - isServerReseted: box.get(BNames.isServerReseted, defaultValue: false), + isServerResetedFirstTime: + box.get(BNames.isServerResetedFirstTime, defaultValue: false), + isServerResetedSecondTime: + box.get(BNames.isServerResetedSecondTime, defaultValue: false), hasFinalChecked: box.get(BNames.hasFinalChecked, defaultValue: false), error: null, isLoading: box.get(BNames.isLoading, defaultValue: false), ); + + return res; } @@ -119,7 +124,8 @@ class AppConfigRepository { text: 'basis.delete'.tr(), isRed: true, onPressed: () async { - await hetznerApi.deleteSelfprivacyServerAndAllVolumes(); + await hetznerApi.deleteSelfprivacyServerAndAllVolumes( + domainName: domainName); var serverDetails = await hetznerApi.createServer( cloudFlareKey: cloudFlareKey, @@ -198,8 +204,12 @@ class AppConfigRepository { await box.put(BNames.isServerStarted, value); } - Future saveIsServerReseted(bool value) async { - await box.put(BNames.isServerReseted, value); + Future saveIsServerResetedFirstTime(bool value) async { + await box.put(BNames.isServerResetedFirstTime, value); + } + + Future saveIsServerResetedSecondTime(bool value) async { + await box.put(BNames.isServerResetedSecondTime, value); } void saveRootUser(User rootUser) async { diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 5a22c29a..50040e81 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -9,7 +9,8 @@ class AppConfigState extends Equatable { required this.rootUser, required this.hetznerServer, required this.isServerStarted, - required this.isServerReseted, + required this.isServerResetedFirstTime, + required this.isServerResetedSecondTime, required this.hasFinalChecked, required this.isLoading, required this.error, @@ -24,7 +25,7 @@ class AppConfigState extends Equatable { rootUser, hetznerServer, isServerStarted, - isServerReseted, + isServerResetedFirstTime, hasFinalChecked, isLoading, error, @@ -37,7 +38,9 @@ class AppConfigState extends Equatable { final User? rootUser; final HetznerServerDetails? hetznerServer; final bool isServerStarted; - final bool isServerReseted; + final bool isServerResetedFirstTime; + final bool isServerResetedSecondTime; + final bool hasFinalChecked; final bool? isLoading; @@ -51,7 +54,8 @@ class AppConfigState extends Equatable { User? rootUser, HetznerServerDetails? hetznerServer, bool? isServerStarted, - bool? isServerReseted, + bool? isServerResetedFirstTime, + bool? isServerResetedSecondTime, bool? hasFinalChecked, bool? isLoading, Exception? error, @@ -64,7 +68,10 @@ class AppConfigState extends Equatable { rootUser: rootUser ?? this.rootUser, hetznerServer: hetznerServer ?? this.hetznerServer, isServerStarted: isServerStarted ?? this.isServerStarted, - isServerReseted: isServerReseted ?? this.isServerReseted, + isServerResetedFirstTime: + isServerResetedFirstTime ?? this.isServerResetedFirstTime, + isServerResetedSecondTime: + isServerResetedSecondTime ?? this.isServerResetedSecondTime, hasFinalChecked: hasFinalChecked ?? this.hasFinalChecked, isLoading: isLoading ?? this.isLoading, error: error ?? this.error, @@ -89,11 +96,11 @@ class AppConfigState extends Equatable { isUserFilled, isServerCreated, isServerStarted, - isServerReseted, + isServerResetedFirstTime, + isServerResetedSecondTime, hasFinalChecked, ]; - print(res); return res; } } @@ -108,7 +115,8 @@ class InitialAppConfigState extends AppConfigState { rootUser: null, hetznerServer: null, isServerStarted: false, - isServerReseted: false, + isServerResetedFirstTime: false, + isServerResetedSecondTime: false, hasFinalChecked: false, isLoading: false, error: null, @@ -129,7 +137,8 @@ class TimerState extends AppConfigState { rootUser: dataState.rootUser, hetznerServer: dataState.hetznerServer, isServerStarted: dataState.isServerStarted, - isServerReseted: dataState.isServerReseted, + isServerResetedFirstTime: dataState.isServerResetedFirstTime, + isServerResetedSecondTime: dataState.isServerResetedSecondTime, hasFinalChecked: dataState.hasFinalChecked, isLoading: isLoading, error: dataState.error, diff --git a/lib/ui/components/progress_bar/progress_bar.dart b/lib/ui/components/progress_bar/progress_bar.dart index 27033239..a75c44fa 100644 --- a/lib/ui/components/progress_bar/progress_bar.dart +++ b/lib/ui/components/progress_bar/progress_bar.dart @@ -47,25 +47,27 @@ class _ProgressBarState extends State { i++; } - odd - ..insert( - 0, - SizedBox( - width: 20, - ), - ) - ..add( - SizedBox( - width: 10, - ), - ); + odd.insert( + 0, + SizedBox( + width: 10, + ), + ); + even.add( + SizedBox( + width: 10, + ), + ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ BrandText.h2('Progress'), SizedBox(height: 10), - Row(children: even), + Row( + children: even, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + ), SizedBox(height: 7), Container( alignment: Alignment.centerLeft, @@ -96,12 +98,13 @@ class _ProgressBarState extends State { SizedBox(height: 5), Row( children: odd, + mainAxisAlignment: MainAxisAlignment.spaceBetween, ), ], ); } - Expanded _stepTitle({ + Container _stepTitle({ required int index, TextStyle? style, String? step, @@ -110,17 +113,19 @@ class _ProgressBarState extends State { var checked = index < widget.activeIndex; style = isActive ? style!.copyWith(fontWeight: FontWeight.w700) : style; - return Expanded( - flex: 2, + return Container( + padding: EdgeInsets.only(left: 10), + height: 20, + alignment: Alignment.center, child: RichText( - textAlign: TextAlign.center, + textAlign: TextAlign.justify, text: TextSpan( style: progressTextStyleLight, children: [ checked ? WidgetSpan( child: Padding( - padding: const EdgeInsets.only(bottom: 0, right: 2), + padding: const EdgeInsets.only(bottom: 2, right: 2), child: Icon(BrandIcons.check, size: 11), )) : TextSpan(text: '${index + 1}.', style: style), diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index ed41b6f5..3b966c9e 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -34,6 +34,7 @@ class InitializingPage extends StatelessWidget { () => _stepCheck(cubit), () => _stepCheck(cubit), () => _stepCheck(cubit), + () => _stepCheck(cubit), () => Container(child: Text('Everythigng is initialized')) ][cubit.state.progress](); return BlocListener( @@ -58,7 +59,8 @@ class InitializingPage extends StatelessWidget { 'Server', ' ✅', ' ✅', - ' ✅' + ' ✅', + ' ✅', ], activeIndex: cubit.state.progress, ), @@ -427,8 +429,10 @@ class InitializingPage extends StatelessWidget { var state = appConfigCubit.state as TimerState; late String? text; - if (state.isServerReseted) { + if (state.isServerResetedSecondTime) { text = 'initializing.13'.tr(); + } else if (state.isServerResetedFirstTime) { + text = 'initializing.21'.tr(); } else if (state.isServerStarted) { text = 'initializing.14'.tr(); } else if (state.isServerCreated) { From 29b08b111a08624383c06dc41c4f747e61af4295 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 31 Mar 2021 16:33:58 +0200 Subject: [PATCH 17/17] update --- lib/logic/api_maps/hetzner.dart | 24 ++++------ .../cubit/app_config/app_config_cubit.dart | 46 +++++++++---------- .../app_config/app_config_repository.dart | 21 +++++---- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 11e68ab7..88efcb06 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -144,28 +144,24 @@ class HetznerApi extends ApiMap { } } - Future startServer({ - required HetznerServerDetails server, - }) async { - var client = await getClient(); + Future reset() async { + var server = getIt().hetznerServer!; - await client.post('/servers/${server.id}/actions/poweron'); + var client = await getClient(); + await client.post('/servers/${server.id}/actions/reset'); close(client); - return server.copyWith( - startTime: DateTime.now(), - ); + return server.copyWith(startTime: DateTime.now()); } - Future restart({ - required HetznerServerDetails server, - }) async { + Future powerOn() async { + var server = getIt().hetznerServer!; + var client = await getClient(); await client.post('/servers/${server.id}/actions/poweron'); close(client); - return server.copyWith( - startTime: DateTime.now(), - ); + + return server.copyWith(startTime: DateTime.now()); } metrics() async { diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 4d9259f4..6627821c 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -47,8 +47,8 @@ class AppConfigCubit extends Cubit { final repository = AppConfigRepository(); - void load() { - var state = repository.load(); + Future load() async { + var state = await repository.load(); if (state.progress < 6 || state.isFullyInitilized) { emit(state); @@ -81,8 +81,8 @@ class AppConfigCubit extends Cubit { var server = await repository.startServer( state.hetznerServer!, ); - repository.saveServerDetails(server); - repository.saveIsServerStarted(true); + await repository.saveServerDetails(server); + await repository.saveIsServerStarted(true); emit( state.copyWith( @@ -131,11 +131,9 @@ class AppConfigCubit extends Cubit { duration: pauseDuration, )); timer = Timer(pauseDuration, () async { - var hetznerServerDetails = await repository.restart( - dataState.hetznerServer!, - ); - repository.saveIsServerResetedSecondTime(true); - repository.saveServerDetails(hetznerServerDetails); + var hetznerServerDetails = await repository.restart(); + await repository.saveIsServerResetedSecondTime(true); + await repository.saveServerDetails(hetznerServerDetails); emit( dataState.copyWith( @@ -186,11 +184,9 @@ class AppConfigCubit extends Cubit { duration: pauseDuration, )); timer = Timer(pauseDuration, () async { - var hetznerServerDetails = await repository.restart( - dataState.hetznerServer!, - ); - repository.saveIsServerResetedFirstTime(true); - repository.saveServerDetails(hetznerServerDetails); + var hetznerServerDetails = await repository.restart(); + await repository.saveIsServerResetedFirstTime(true); + await repository.saveServerDetails(hetznerServerDetails); emit( dataState.copyWith( @@ -235,7 +231,7 @@ class AppConfigCubit extends Cubit { var isServerWorking = await repository.isHttpServerWorking(); if (isServerWorking) { - repository.saveHasFinalChecked(true); + await repository.saveHasFinalChecked(true); emit(state.copyWith( hasFinalChecked: true, @@ -267,32 +263,32 @@ class AppConfigCubit extends Cubit { emit(InitialAppConfigState()); } - void setHetznerKey(String hetznerKey) { - repository.saveHetznerKey(hetznerKey); + void setHetznerKey(String hetznerKey) async { + await repository.saveHetznerKey(hetznerKey); emit(state.copyWith(hetznerKey: hetznerKey)); } - void setCloudflareKey(String cloudFlareKey) { - repository.saveCloudFlareKey(cloudFlareKey); + void setCloudflareKey(String cloudFlareKey) async { + await repository.saveCloudFlareKey(cloudFlareKey); emit(state.copyWith(cloudFlareKey: cloudFlareKey)); } - void setBackblazeKey(String keyId, String applicationKey) { + void setBackblazeKey(String keyId, String applicationKey) async { var backblazeCredential = BackblazeCredential( keyId: keyId, applicationKey: applicationKey, ); - repository.saveBackblazeKey(backblazeCredential); + await repository.saveBackblazeKey(backblazeCredential); emit(state.copyWith(backblazeCredential: backblazeCredential)); } - void setDomain(CloudFlareDomain cloudFlareDomain) { - repository.saveDomain(cloudFlareDomain); + void setDomain(CloudFlareDomain cloudFlareDomain) async { + await repository.saveDomain(cloudFlareDomain); emit(state.copyWith(cloudFlareDomain: cloudFlareDomain)); } - void setRootUser(User rootUser) { - repository.saveRootUser(rootUser); + void setRootUser(User rootUser) async { + await repository.saveRootUser(rootUser); emit(state.copyWith(rootUser: rootUser)); } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 4408f1f8..ebbbb305 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -21,7 +21,7 @@ import 'package:easy_localization/easy_localization.dart'; class AppConfigRepository { Box box = Hive.box(BNames.appConfig); - AppConfigState load() { + Future load() async { var res = AppConfigState( hetznerKey: getIt().hetznerKey, cloudFlareKey: getIt().cloudFlareKey, @@ -39,8 +39,6 @@ class AppConfigRepository { isLoading: box.get(BNames.isLoading, defaultValue: false), ); - - return res; } @@ -52,7 +50,7 @@ class AppConfigRepository { HetznerServerDetails hetznerServer, ) async { var hetznerApi = HetznerApi(); - var serverDetails = await hetznerApi.startServer(server: hetznerServer); + var serverDetails = await hetznerApi.powerOn(); return serverDetails; } @@ -173,11 +171,14 @@ class AppConfigRepository { return isHttpServerWorking; } - Future restart( - HetznerServerDetails server, - ) async { + Future restart() async { var hetznerApi = HetznerApi(); - return await hetznerApi.restart(server: server); + return await hetznerApi.reset(); + } + + Future powerOn() async { + var hetznerApi = HetznerApi(); + return await hetznerApi.powerOn(); } Future saveServerDetails(HetznerServerDetails serverDetails) async { @@ -212,11 +213,11 @@ class AppConfigRepository { await box.put(BNames.isServerResetedSecondTime, value); } - void saveRootUser(User rootUser) async { + Future saveRootUser(User rootUser) async { await box.put(BNames.rootUser, rootUser); } - void saveHasFinalChecked(bool value) async { + Future saveHasFinalChecked(bool value) async { await box.put(BNames.hasFinalChecked, value); } }