From 20166647ea774192d029bd66b8964c071a335cbb Mon Sep 17 00:00:00 2001 From: Kherel Date: Mon, 15 Feb 2021 19:58:29 +0100 Subject: [PATCH] update --- assets/images/logos/backblaze.png | Bin 9061 -> 34431 bytes assets/images/logos/cloudflare.png | Bin 1595 -> 23882 bytes ios/Podfile.lock | 2 +- lib/config/bloc_observer.dart | 11 +- lib/config/brand_theme.dart | 1 + lib/config/get_it_config.dart | 7 + lib/config/hive_config.dart | 2 +- lib/config/text_themes.dart | 7 + lib/logic/api_maps/backblaze.dart | 1 - lib/logic/api_maps/cloudflare.dart | 38 ++- lib/logic/api_maps/hetzner.dart | 10 + lib/logic/api_maps/server.dart | 33 ++- .../cubit/app_config/app_config_cubit.dart | 76 +++--- .../app_config/app_config_repository.dart | 84 +++++-- .../cubit/app_config/app_config_state.dart | 44 ++-- .../forms/initializing/domain_cloudflare.dart | 78 ++++++ .../forms/initializing/domain_form_cubit.dart | 110 ++++----- .../initializing/root_user_form_cubit.dart | 5 +- lib/logic/get_it/navigation.dart | 16 ++ lib/main.dart | 5 +- .../action_button/action_button.dart | 31 +++ .../components/brand_alert/brand_alert.dart | 15 ++ .../components/brand_button/brand_button.dart | 82 ++----- lib/ui/components/brand_text/brand_text.dart | 44 +++- .../components/brand_timer/brand_timer.dart | 3 - lib/ui/pages/initializing/initializing.dart | 230 ++++++++++++------ .../pages/more/app_settings/app_setting.dart | 51 ++-- pubspec.lock | 87 ++++++- pubspec.yaml | 1 + 29 files changed, 707 insertions(+), 367 deletions(-) create mode 100644 lib/logic/cubit/forms/initializing/domain_cloudflare.dart create mode 100644 lib/logic/get_it/navigation.dart create mode 100644 lib/ui/components/action_button/action_button.dart create mode 100644 lib/ui/components/brand_alert/brand_alert.dart diff --git a/assets/images/logos/backblaze.png b/assets/images/logos/backblaze.png index c4818556738b1d39d3da3ed5674ea46a79888058..141073918fe70bb4e627220c8b1fb8e4d02a6d08 100644 GIT binary patch literal 34431 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 literal 9061 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 WXWX;XjmU8nKVoSHF|E1ko%%13^Uv=9 literal 1595 zcmV-B2E_S^P)&ebKGxmDpWGn6aNTZn@Kff8zn|b?YY=B`H zhN&12Ol9nZLg@0jFphDGupy7ZRwx>0>&YAvZ#MBTtp>BvBOtLMA@A?W+Vc4mHY^)* zKz0@{-a3Fcf5doBNR-riK$u%`{(o;Pn7*mNh(yDy$+aybFscGJWS?v=id#6o5jEnhC-0y_*tJ@qXq0mG zU)D;0lS|!1#U#nW%q!vJ2o*2}=8$Kfi5pVNc@<#_`n|n471va>xi6FRCo$*iUY%BX zgbLslnX5{D~s8AIUg$6u}_KYf0m%3ckza*-R}wWzdV z*|jP%p^ysb-?~hQkPQXeEwcN&l87-zdJpga7}kKc6o$ zLbP8ysRM7R1_ndGdugI=z_E9(AJ$2=A`ia3+0V*sjei)Ock0FnvAr<$pv+r~`kPnP z{$QeA_RdYfOUp&HU1k<##QM~GZDZPy6!h#pZ6~?g$&-hxow`HCm~ln2BeVVJoR!K! zUOlyuJ5Sol#ARIBdVHkLX+$#IAKxMQ_hHv)l+y||jYxVE70o(Mf=0rSowHLJpd<&s ze}k&nQ)%qo>o{}u)5E&tPi4R#zc^wr7s`D)1GA9`cIx#5A4A$Jn(Y7Z73AN)w9Cn_ zcaePmN#8~!JkR|MBVkA{@dkFORg;k0RcXhG9;NqLUqp_Jm6GqBF8w7smCX z&-)q~eIy@SNab`t8kTl+JuLTR$;z5U7P%O32NwpjhJ1wn+IEeX9wQrZni@uX zg^)7(^iLXTGwrlVm|ko4p>H#zkJ7D-oG`8lqh9wV`{kKU=L1VWO&!wmTsEBniV=pM zEx0F&t)C>Heg44FN3%amX}PDZF-PZ`h9J!xy^`p4$_&bVFeD6pG!xfTeUw6UX4YLC zk2uw>_2%_6M(=EnF*v?arsoS(TF#MgrAegnp-S|bM`V%B*2QUl&(M|23c52@q;&$_ zS!J%i5XCEU#iEZ`v)n5%*oq7a`f2J2kx!WwsbyVl=Jj(6N81U|eg0C|4g{{GIaxkV z%aqTgX#wTX=YtV?XTM&BKB^;D>2`tMIdleGrw=d~KDv|brcvkT+|xieuFxKD%ORU} zt+|vr5QuR+H&pn!!YLupyV?hJ>o?Mz{wg-m{R^Ur5;^W!N|T10Ucx0I0!U>{FPH!uLRI66q?tkZ navigatorKey; +import 'get_it_config.dart'; - SimpleBlocObserver({this.navigatorKey}); +class SimpleBlocObserver extends BlocObserver { + SimpleBlocObserver(); @override void onError(Cubit cubit, Object error, StackTrace stackTrace) { - navigatorKey.currentState.push( + final navigator = getIt.get().navigator; + + navigator.push( materialRoute( BrandError( error: error, diff --git a/lib/config/brand_theme.dart b/lib/config/brand_theme.dart index 0505e471e0..c8fd246ee8 100644 --- a/lib/config/brand_theme.dart +++ b/lib/config/brand_theme.dart @@ -56,6 +56,7 @@ var darkTheme = ligtTheme.copyWith( scaffoldBackgroundColor: Color(0xFF202120), iconTheme: IconThemeData(color: BrandColors.gray3), cardColor: BrandColors.gray1, + dialogBackgroundColor: Color(0xFF202120), textTheme: GoogleFonts.interTextTheme( TextTheme( headline1: headline1Style.copyWith(color: BrandColors.white), diff --git a/lib/config/get_it_config.dart b/lib/config/get_it_config.dart index 931adfa266..6a05afc47d 100644 --- a/lib/config/get_it_config.dart +++ b/lib/config/get_it_config.dart @@ -1,10 +1,17 @@ import 'package:get_it/get_it.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'; +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; void getItSetup() { + getIt.registerSingleton(NavigationService()); + getIt.registerSingleton(ConsoleModel()); getIt.registerSingleton(TimerModel()); } diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index b8eae2528e..c3699d938d 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -50,7 +50,7 @@ class BNames { static String cloudFlareKey = 'cloudFlareKey'; static String rootUser = 'rootUser'; static String hetznerServer = 'hetznerServer'; - static String isDkimSetted = 'isDkimSetted'; + // static String isDkimSetted = 'isDkimSetted'; static String isDnsChecked = 'isDnsChecked'; static String isServerStarted = 'isServerStarted'; static String backblazeKey = 'backblazeKey'; diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart index 54d53ee48e..1b4db78495 100644 --- a/lib/config/text_themes.dart +++ b/lib/config/text_themes.dart @@ -46,6 +46,13 @@ final body2Style = defaultTextStyle.copyWith( color: BrandColors.textColor2, ); +final buttonTitleText = defaultTextStyle.copyWith( + color: BrandColors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + height: 1, +); + final mediumStyle = defaultTextStyle.copyWith(fontSize: 13, height: 1.53); final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45); diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/backblaze.dart index 025f0b873f..35782fc700 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/backblaze.dart @@ -27,7 +27,6 @@ class BackblazeApi extends ApiMap { Response response = await loggedClient.get(rootAddress, options: options); if (response.statusCode == HttpStatus.ok) { - print(response); return true; } else if (response.statusCode == HttpStatus.unauthorized) { return false; diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart index cde4f9482b..cb69cdc3c6 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/cloudflare.dart @@ -105,20 +105,20 @@ class CloudflareApi extends ApiMap { await Future.wait(allCreateFutures); } - setDkim(String dkimRecordString, String domainZoneId) { - var txt3 = DnsRecords( - type: 'TXT', - name: 'selector._domainkey', - content: dkimRecordString, - ttl: 18000, - ); + // 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(), - ); - } + // 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); @@ -160,4 +160,16 @@ class CloudflareApi extends ApiMap { vpn ]; } + + Future> domainList() async { + var url = '$rootAddress/zones?per_page=50'; + var response = await loggedClient.get( + url, + queryParameters: {'per_page': 50}, + ); + + 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 54f8f69bf9..26c3434b49 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -60,6 +60,16 @@ class HetznerApi extends ApiMap { ); } + Future deleteSelfprivacyServer({ + @required String cloudFlareKey, + }) async { + Response response = await loggedClient.get(rootAddress); + + List list = response.data['servers']; + var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server'); + return await loggedClient.delete('$rootAddress/${server['id']}'); + } + Future startServer({ HetznerServerDetails server, }) async { diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index d13261e009..c5db15766c 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; @@ -26,22 +25,22 @@ class ServerApi extends ApiMap { return res; } - Future getDkim(String domainName) async { - var response = await loggedClient.get( - '/getDKIM', - options: Options(responseType: ResponseType.plain), - ); - return _decodeAndCutData(response.data, domainName); - } + // Future getDkim(String domainName) async { + // var response = await loggedClient.get( + // '/getDKIM', + // options: Options(responseType: ResponseType.plain), + // ); + // return _decodeAndCutData(response.data, domainName); + // } } -String _decodeAndCutData(String text, String domainName) { - var decodedTextString = text.substring(1, text.length - 1); - var stringToBase64 = utf8.fuse(base64); +// 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', ''); -} +// 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 c4a1f67a04..0b996dbcef 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -14,22 +14,30 @@ import 'app_config_repository.dart'; part 'app_config_state.dart'; /// Initializing steps: -/// 1. Hetzner key |setHetznerKey -/// 2. Cloudflare key |setCloudflareKey -/// 3. Backblaze Id + Key |setBackblazeKey - -/// 4. Set Domain address |setDomain -/// 5. Set Root user name password |setRootUser -/// 6. Set Create server ans set DNS-Records |createServerAndSetDnsRecords +/// +/// The set phase. +/// 1.1. Hetzner key |setHetznerKey +/// 1.2. Cloudflare key |setCloudflareKey +/// 1.3. Backblaze Id + Key |setBackblazeKey +/// 1.4. Set Domain address |setDomain +/// 1.5. Set Root user name password |setRootUser +/// 1.6. Set Create server ans set DNS-Records |createServerAndSetDnsRecords /// (without start) -/// 7. ChecksAndSets: -/// 7.1 checkDnsAndStartServer |checkDnsAndStartServer -/// 7.2 setDkim |setDkim -/// a. checkServer -/// b. getDkim -/// c. Set DKIM -/// d. server restart - +/// +/// The check phase. +/// +/// 2.1. a. wait 60sec checkDnsAndStartServer |startServerIfDnsIsOkay +/// b. checkDns +/// c. if dns is okay start server +/// +/// 2.2. a. wait 60sec |resetServerIfServerIsOkay +/// b. checkServer +/// c. if server is ok wait 30 sec +/// d. reset server +/// +/// 2.3. a. wait 60sec |finishCheckIfServerIsOkay +/// b. checkServer +/// c. if server is okay set that fully checked class AppConfigCubit extends Cubit { AppConfigCubit() : super(InitialAppConfigState()); @@ -66,34 +74,22 @@ class AppConfigCubit extends Cubit { emit(state.copyWith(rootUser: rootUser)); } - void setDkim() async { + void serverReset() async { var callBack = () async { var isServerWorking = await repository.isHttpServerWorking( state.cloudFlareDomain.domainName, ); if (!isServerWorking) { var last = DateTime.now(); - print(last); emit(state.copyWith(lastServerStatusCheckTime: last)); return; } - await repository.setDkim( - state.cloudFlareDomain.domainName, - state.cloudFlareKey, - state.cloudFlareDomain.zoneId, - ); - var hetznerServerDetails = await repository.restart( state.hetznerKey, state.hetznerServer, ); - emit( - state.copyWith( - isDkimSetted: true, - hetznerServer: hetznerServerDetails, - ), - ); + emit(state.copyWith(hetznerServer: hetznerServerDetails)); }; _tryOrAddError(state, callBack); @@ -125,14 +121,7 @@ class AppConfigCubit extends Cubit { } void createServerAndSetDnsRecords() async { - var callback = () async { - var serverDetails = await repository.createServer( - state.hetznerKey, - state.rootUser, - state.cloudFlareDomain.domainName, - state.cloudFlareKey, - ); - + var onSuccess = (serverDetails) async { await repository.createDnsRecords( state.cloudFlareKey, serverDetails.ip4, @@ -144,6 +133,19 @@ class AppConfigCubit extends Cubit { hetznerServer: serverDetails, )); }; + + var onCancel = () => emit(state.copyWith(isLoading: false)); + + var callback = () async { + await repository.createServer( + state.hetznerKey, + state.rootUser, + state.cloudFlareDomain.domainName, + state.cloudFlareKey, + onCancel: onCancel, + onSuccess: onSuccess, + ); + }; _tryOrAddError(state, callback); } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 1b6fee8ae2..d1da034975 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -1,3 +1,4 @@ +import 'package:dio/dio.dart'; import 'package:hive/hive.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; @@ -11,6 +12,8 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/models/message.dart'; 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'; class AppConfigRepository { @@ -26,7 +29,6 @@ class AppConfigRepository { hetznerServer: box.get(BNames.hetznerServer), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), isDnsChecked: box.get(BNames.isDnsChecked, defaultValue: false), - isDkimSetted: box.get(BNames.isDkimSetted, defaultValue: false), ); } @@ -110,19 +112,64 @@ class AppConfigRepository { return true; } - Future createServer(String hetznerKey, User rootUser, - String domainName, String cloudFlareKey) async { + Future createServer( + String hetznerKey, + User rootUser, + String domainName, + String cloudFlareKey, { + void Function() onCancel, + Future Function(HetznerServerDetails serverDetails) onSuccess, + }) async { var hetznerApi = HetznerApi(hetznerKey); - var serverDetails = await hetznerApi.createServer( - cloudFlareKey: cloudFlareKey, - rootUser: rootUser, - domainName: domainName, - ); - await box.put(BNames.hetznerServer, serverDetails); - hetznerApi.close(); + try { + var serverDetails = await hetznerApi.createServer( + cloudFlareKey: cloudFlareKey, + rootUser: rootUser, + domainName: domainName, + ); + await box.put(BNames.hetznerServer, serverDetails); + hetznerApi.close(); + onSuccess(serverDetails); + } on DioError catch (e) { + if (e.response.data['error']['code'] == 'uniqueness_error') { + var nav = getIt.get(); + nav.showPopUpDialog( + BrandAlert( + title: 'Сервер с таким именем уже существует', + contentText: 'Уничтожить сервер и создать новый?', + acitons: [ + ActionButton( + text: 'Удалить', + isRed: true, + onPressed: () async { + await hetznerApi.deleteSelfprivacyServer( + cloudFlareKey: cloudFlareKey, + ); - return serverDetails; + var serverDetails = await hetznerApi.createServer( + cloudFlareKey: cloudFlareKey, + rootUser: rootUser, + domainName: domainName, + ); + hetznerApi.close(); + + await box.put(BNames.hetznerServer, serverDetails); + onSuccess(serverDetails); + }, + ), + ActionButton( + text: 'Отменить', + onPressed: () { + hetznerApi.close(); + onCancel(); + }, + ), + ], + ), + ); + } + } } Future createDnsRecords( @@ -152,21 +199,6 @@ class AppConfigRepository { return isHttpServerWorking; } - Future setDkim( - String domainName, - String cloudFlareKey, - String zoneId, - ) async { - var api = ServerApi(domainName); - var dkimRecordString = await api.getDkim(domainName); - var cloudflareApi = CloudflareApi(cloudFlareKey); - - await cloudflareApi.setDkim(dkimRecordString, zoneId); - box.put(BNames.isDkimSetted, true); - - cloudflareApi.close(); - } - Future restart( String hetznerKey, HetznerServerDetails server, diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 4ce6d7e6b6..b93e3c0ed5 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -10,11 +10,10 @@ class AppConfigState extends Equatable { this.hetznerServer, this.isLoading = false, this.error, - this.lastDnsCheckTime, - this.lastServerStatusCheckTime, + // this.lastDnsCheckTime, + // this.lastServerStatusCheckTime, this.isDnsChecked = false, this.isServerStarted = false, - this.isDkimSetted = false, }); @override @@ -28,9 +27,8 @@ class AppConfigState extends Equatable { isDnsCheckedAndServerStarted, isLoading, error, - lastDnsCheckTime, - lastServerStatusCheckTime, - isDkimSetted, + // lastDnsCheckTime, + // lastServerStatusCheckTime, ]; final String hetznerKey; @@ -39,11 +37,10 @@ class AppConfigState extends Equatable { final CloudFlareDomain cloudFlareDomain; final User rootUser; final HetznerServerDetails hetznerServer; - final bool isDkimSetted; final bool isServerStarted; final bool isDnsChecked; - final DateTime lastDnsCheckTime; - final DateTime lastServerStatusCheckTime; + // final DateTime lastDnsCheckTime; + // final DateTime lastServerStatusCheckTime; final bool isLoading; final Exception error; @@ -58,7 +55,6 @@ class AppConfigState extends Equatable { Exception error, DateTime lastDnsCheckTime, DateTime lastServerStatusCheckTime, - bool isDkimSetted, bool isServerStarted, bool isDnsChecked, }) => @@ -73,10 +69,9 @@ class AppConfigState extends Equatable { isDnsChecked: isDnsChecked ?? this.isDnsChecked, isLoading: isLoading ?? this.isLoading, error: error ?? this.error, - lastDnsCheckTime: lastDnsCheckTime ?? this.lastDnsCheckTime, - lastServerStatusCheckTime: - lastServerStatusCheckTime ?? this.lastServerStatusCheckTime, - isDkimSetted: isDkimSetted ?? this.isDkimSetted, + // lastDnsCheckTime: lastDnsCheckTime ?? this.lastDnsCheckTime, + // lastServerStatusCheckTime: + // lastServerStatusCheckTime ?? this.lastServerStatusCheckTime, ); bool get isHetznerFilled => hetznerKey != null; @@ -85,7 +80,7 @@ class AppConfigState extends Equatable { bool get isDomainFilled => cloudFlareDomain != null; bool get isUserFilled => rootUser != null; bool get isServerFilled => hetznerServer != null; - bool get hasFinalChecked => isDnsCheckedAndServerStarted && isDkimSetted; + bool get hasFinalChecked => isDnsCheckedAndServerStarted; bool get isDnsCheckedAndServerStarted => isDnsChecked && isServerStarted; @@ -107,3 +102,22 @@ class AppConfigState extends Equatable { class InitialAppConfigState extends AppConfigState { InitialAppConfigState() : super(); } + +class TimerState extends AppConfigState { + TimerState({ + this.dataState, + this.timerStart, + this.duration, + }) : super(); + + final AppConfigState dataState; + final DateTime timerStart; + final Duration duration; + + @override + List get props => [ + dataState, + timerStart, + duration, + ]; +} diff --git a/lib/logic/cubit/forms/initializing/domain_cloudflare.dart b/lib/logic/cubit/forms/initializing/domain_cloudflare.dart new file mode 100644 index 0000000000..b526b0ce58 --- /dev/null +++ b/lib/logic/cubit/forms/initializing/domain_cloudflare.dart @@ -0,0 +1,78 @@ +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 DomainSetupCubit extends Cubit { + DomainSetupCubit(this.initializingCubit) : super(Initial()) { + var token = (initializingCubit.state.cloudFlareKey); + + assert(token != null, 'no cloudflare token'); + + api = CloudflareApi(token); + } + + AppConfigCubit initializingCubit; + CloudflareApi api; + + Future load() async { + emit(Loading(LoadingTypes.loadingDomain)); + var list = await api.domainList(); + if (list.isEmpty) { + emit(Empty()); + } else if (list.length == 1) { + emit(Loaded(list.first)); + } else { + emit(MoreThenOne()); + } + } + + @override + Future close() { + api.close(); + return super.close(); + } + + Future saveDomain() async { + assert(state is Loaded, 'wrong state'); + var domainName = (state as Loaded).domain; + + emit(Loading(LoadingTypes.saving)); + + var zoneId = await api.getZoneId( + initializingCubit.state.cloudFlareKey, + domainName, + ); + + var domain = CloudFlareDomain( + domainName: domainName, + zoneId: zoneId, + ); + + initializingCubit.setDomain(domain); + emit(DomainSetted()); + } +} + +abstract class DomainSetupState {} + +class Initial extends DomainSetupState {} + +class Empty extends DomainSetupState {} + +class MoreThenOne extends DomainSetupState {} + +class Loading extends DomainSetupState { + Loading(this.type); + final LoadingTypes type; +} + +enum LoadingTypes { loadingDomain, saving } + +class Loaded extends DomainSetupState { + final String domain; + + Loaded(this.domain); +} + +class DomainSetted extends DomainSetupState {} diff --git a/lib/logic/cubit/forms/initializing/domain_form_cubit.dart b/lib/logic/cubit/forms/initializing/domain_form_cubit.dart index 6d77bf62b0..e2dfad83b3 100644 --- a/lib/logic/cubit/forms/initializing/domain_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/domain_form_cubit.dart @@ -1,68 +1,68 @@ -import 'dart:async'; +// 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'; +// 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(); +// 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', - ), - ], - ); +// 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]); - } +// super.setFields([domainName]); +// } - @override - FutureOr onSubmit() async { - var domain = CloudFlareDomain( - domainName: domainName.state.value, - zoneId: zoneId, - ); - initializingCubit.setDomain(domain); - } +// @override +// FutureOr onSubmit() async { +// var domain = CloudFlareDomain( +// domainName: domainName.state.value, +// zoneId: zoneId, +// ); +// initializingCubit.setDomain(domain); +// } - final AppConfigCubit initializingCubit; +// final AppConfigCubit initializingCubit; - FieldCubit domainName; - String zoneId; +// FieldCubit domainName; +// String zoneId; - @override - FutureOr asyncValidation() async { - var key = initializingCubit.state.cloudFlareKey; +// @override +// FutureOr asyncValidation() async { +// var key = initializingCubit.state.cloudFlareKey; - String zoneId; +// String zoneId; - try { - zoneId = await apiClient.getZoneId(key, domainName.state.value); - } catch (e) { - addError(e); - } +// 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; - } +// if (zoneId == null) { +// domainName.setError('Domain not in the list'); +// return false; +// } +// this.zoneId = zoneId; +// return true; +// } - @override - Future close() async { - apiClient.close(); +// @override +// Future close() async { +// apiClient.close(); - return super.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 bd98debd33..2fdc6e8aa2 100644 --- a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart @@ -30,7 +30,9 @@ class RootUserFormCubit extends FormCubit { ], ); - super.setFields([userName, password]); + isVisible = FieldCubit(initalValue: false); + + super.setFields([userName, password, isVisible]); } @override @@ -46,6 +48,7 @@ class RootUserFormCubit extends FormCubit { FieldCubit userName; FieldCubit password; + FieldCubit isVisible; @override Future close() async { diff --git a/lib/logic/get_it/navigation.dart b/lib/logic/get_it/navigation.dart new file mode 100644 index 0000000000..cf5dc8ff9e --- /dev/null +++ b/lib/logic/get_it/navigation.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class NavigationService { + final GlobalKey navigatorKey = GlobalKey(); + NavigatorState get navigator => navigatorKey.currentState; + + void showPopUpDialog(AlertDialog dialog) { + final context = navigatorKey.currentState.overlay.context; + + showDialog( + context: context, + builder: (_) => dialog, + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index eb76d4646b..200fd96e7e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,11 +15,10 @@ import 'config/get_it_config.dart'; import 'config/localization.dart'; import 'logic/cubit/app_settings/app_settings_cubit.dart'; -final navigatorKey = GlobalKey(); void main() async { await HiveConfig.init(); - Bloc.observer = SimpleBlocObserver(navigatorKey: navigatorKey); + Bloc.observer = SimpleBlocObserver(); Wakelock.enable(); getItSetup(); WidgetsFlutterBinding.ensureInitialized(); @@ -41,7 +40,7 @@ class MyApp extends StatelessWidget { return AnnotatedRegion( value: SystemUiOverlayStyle.light, // Manually changnig appbar color child: MaterialApp( - navigatorKey: navigatorKey, + navigatorKey: getIt.get().navigatorKey, localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, diff --git a/lib/ui/components/action_button/action_button.dart b/lib/ui/components/action_button/action_button.dart new file mode 100644 index 0000000000..15565e343c --- /dev/null +++ b/lib/ui/components/action_button/action_button.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; + +class ActionButton extends StatelessWidget { + const ActionButton({ + Key key, + this.text, + this.onPressed, + this.isRed = false, + }) : super(key: key); + + final VoidCallback onPressed; + final String text; + final bool isRed; + + @override + Widget build(BuildContext context) { + var navigator = Navigator.of(context); + + return TextButton( + child: Text( + text, + style: isRed ? TextStyle(color: BrandColors.red1) : null, + ), + onPressed: () { + navigator.pop(); + if (onPressed != null) onPressed(); + }, + ); + } +} diff --git a/lib/ui/components/brand_alert/brand_alert.dart b/lib/ui/components/brand_alert/brand_alert.dart new file mode 100644 index 0000000000..cf58606120 --- /dev/null +++ b/lib/ui/components/brand_alert/brand_alert.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class BrandAlert extends AlertDialog { + BrandAlert({ + Key key, + String title, + String contentText, + List acitons, + }) : super( + key: key, + title: title != null ? Text(title) : null, + content: title != null ? Text(contentText) : null, + actions: acitons, + ); +} diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart index 163e3f1b6a..d7e7fb67c6 100644 --- a/lib/ui/components/brand_button/brand_button.dart +++ b/lib/ui/components/brand_button/brand_button.dart @@ -6,42 +6,32 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; enum BrandButtonTypes { rised, text, iconText } -class BrandButton extends StatelessWidget { - const BrandButton({ - Key key, - this.onPressed, - this.type, - this.title, - this.icon, - }) : super(key: key); - - final VoidCallback onPressed; - final BrandButtonTypes type; - final String title; - final Icon icon; - +class BrandButton { static rised({ Key key, @required VoidCallback onPressed, - @required String title, - }) => - BrandButton( - key: key, - onPressed: onPressed, - title: title, - type: BrandButtonTypes.rised, - ); + String title, + Widget child, + }) { + assert(title == null || child == null, 'required title or child'); + assert(title != null || child != null, 'required title or child'); + return _RisedButton( + key: key, + title: title, + onPressed: onPressed, + child: child, + ); + } static text({ Key key, @required VoidCallback onPressed, @required String title, }) => - BrandButton( + _TextButton( key: key, - onPressed: onPressed, title: title, - type: BrandButtonTypes.text, + onPressed: onPressed, ); static iconText({ @@ -50,38 +40,12 @@ class BrandButton extends StatelessWidget { @required String title, @required Icon icon, }) => - BrandButton( + _IconTextButton( key: key, - onPressed: onPressed, title: title, - type: BrandButtonTypes.iconText, + onPressed: onPressed, icon: icon, ); - @override - Widget build(BuildContext context) { - switch (type) { - case BrandButtonTypes.rised: - return _RisedButton( - title: title, - onPressed: onPressed, - ); - case BrandButtonTypes.text: - return _TextButton( - title: title, - onPressed: onPressed, - ); - break; - case BrandButtonTypes.iconText: - return _IconTextButton( - title: title, - onPressed: onPressed, - icon: icon, - ); - break; - } - - return null; - } } class _RisedButton extends StatelessWidget { @@ -89,10 +53,12 @@ class _RisedButton extends StatelessWidget { Key key, this.onPressed, this.title, + this.child, }) : super(key: key); final VoidCallback onPressed; final String title; + final Widget child; @override Widget build(BuildContext context) { @@ -111,15 +77,7 @@ class _RisedButton extends StatelessWidget { width: double.infinity, alignment: Alignment.center, padding: EdgeInsets.all(12), - child: Text( - title, - style: TextStyle( - color: BrandColors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - height: 1, - ), - ), + child: child ?? BrandText.buttonTitleText(title), ), ), ), diff --git a/lib/ui/components/brand_text/brand_text.dart b/lib/ui/components/brand_text/brand_text.dart index b216e61b7c..4440987be5 100644 --- a/lib/ui/components/brand_text/brand_text.dart +++ b/lib/ui/components/brand_text/brand_text.dart @@ -10,18 +10,20 @@ enum TextType { body2, // with opacity medium, small, - onboardingTitle + onboardingTitle, + buttonTitleText // risen button title text, } class BrandText extends StatelessWidget { - const BrandText(this.text, - {Key key, - this.style, - @required this.type, - this.overflow, - this.softWrap, - this.textAlign}) - : super(key: key); + const BrandText( + this.text, { + Key key, + this.style, + @required this.type, + this.overflow, + this.softWrap, + this.textAlign, + }) : super(key: key); final String text; final TextStyle style; @@ -53,10 +55,13 @@ class BrandText extends StatelessWidget { type: TextType.h2, style: style, ); - factory BrandText.h3(String text, {TextStyle style}) => BrandText( + factory BrandText.h3(String text, {TextStyle style, TextAlign textAlign}) => + BrandText( text, type: TextType.h3, style: style, + textAlign: textAlign, + overflow: TextOverflow.ellipsis, ); factory BrandText.h4(String text, {TextStyle style}) => BrandText( text, @@ -75,13 +80,23 @@ class BrandText extends StatelessWidget { ); factory BrandText.medium(String text, {TextStyle style, TextAlign textAlign}) => - BrandText(text, - type: TextType.medium, style: style, textAlign: textAlign); + BrandText( + text, + type: TextType.medium, + style: style, + textAlign: textAlign, + ); factory BrandText.small(String text, {TextStyle style}) => BrandText( text, type: TextType.small, style: style, ); + factory BrandText.buttonTitleText(String text, {TextStyle style}) => + BrandText( + text, + type: TextType.buttonTitleText, + style: style, + ); @override Text build(BuildContext context) { TextStyle style; @@ -128,6 +143,11 @@ class BrandText extends StatelessWidget { style = isDark ? mediumStyle.copyWith(color: Colors.white) : mediumStyle; break; + case TextType.buttonTitleText: + style = !isDark + ? buttonTitleText.copyWith(color: Colors.white) + : buttonTitleText; + break; } if (this.style != null) { style = style.merge(this.style); diff --git a/lib/ui/components/brand_timer/brand_timer.dart b/lib/ui/components/brand_timer/brand_timer.dart index a365c39bee..17177c65e5 100644 --- a/lib/ui/components/brand_timer/brand_timer.dart +++ b/lib/ui/components/brand_timer/brand_timer.dart @@ -9,12 +9,10 @@ class BrandTimer extends StatefulWidget { Key key, @required this.startDateTime, @required this.duration, - @required this.callback, }) : super(key: key); final DateTime startDateTime; final Duration duration; - final VoidCallback callback; @override _BrandTimerState createState() => _BrandTimerState(); @@ -36,7 +34,6 @@ class _BrandTimerState extends State { var timePassed = DateTime.now().difference(widget.startDateTime); if (timePassed > widget.duration) { t.cancel(); - widget.callback(); } else { _getTime(); } diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index 04266bc867..6a1db999db 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -6,7 +6,7 @@ import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/backblaze_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/initializing/domain_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/initializing/domain_cloudflare.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/hetzner_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/root_user_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; @@ -16,7 +16,6 @@ import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart'; import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; @@ -34,7 +33,8 @@ class InitializingPage extends StatelessWidget { _stepServer(cubit), _stepCheck(cubit), Container(child: Text('Everythigng is initialized')) - ][2]; + ][cubit.state.progress]; + return BlocListener( listener: (context, state) { if (state.isFullyInitilized) { @@ -96,8 +96,10 @@ class InitializingPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Spacer(), - Image.asset('assets/images/logos/hetzner.png'), + Image.asset( + 'assets/images/logos/hetzner.png', + width: 150, + ), SizedBox(height: 10), BrandText.h2('Подключите сервер Hetzner'), SizedBox(height: 10), @@ -149,8 +151,11 @@ class InitializingPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Spacer(), - Image.asset('assets/images/logos/cloudflare.png'), + Image.asset( + 'assets/images/logos/cloudflare.png', + width: 150, + ), + SizedBox(height: 10), BrandText.h2('Подключите CloudFlare'), SizedBox(height: 10), BrandText.body2('Для управления DNS вашего домена'), @@ -188,12 +193,13 @@ class InitializingPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Spacer(), - Image.asset('assets/images/logos/backblaze.png'), + Image.asset( + 'assets/images/logos/backblaze.png', + height: 50, + ), SizedBox(height: 10), BrandText.h2('Подключите облачное хранилище Backblaze'), SizedBox(height: 10), - BrandText.body2('Здесь будут храниться данные'), Spacer(), CubitFormTextField( formFieldCubit: formCubit.keyId, @@ -231,30 +237,90 @@ class InitializingPage extends StatelessWidget { Widget _stepDomain(AppConfigCubit initializingCubit) { return BlocProvider( - create: (context) => DomainFormCubit(initializingCubit), + create: (context) => DomainSetupCubit(initializingCubit)..load(), child: Builder(builder: (context) { - var formCubit = context.watch(); + var domainSetup = context.watch(); + var state = domainSetup.state; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Spacer(), - BrandText.h2('Введите домен:'), + Image.asset( + 'assets/images/logos/cloudflare.png', + width: 150, + ), + SizedBox(height: 30), + BrandText.h2('Домен'), SizedBox(height: 10), - CubitFormTextField( - keyboardType: TextInputType.emailAddress, - formFieldCubit: formCubit.domainName, - textAlign: TextAlign.center, - scrollPadding: EdgeInsets.only(bottom: 70), - decoration: InputDecoration( - hintText: 'Домен', + if (state is Empty) + BrandText.body2('На данный момент подлюченных доменов нет'), + if (state is Loading) + BrandText.body2( + state.type == LoadingTypes.loadingDomain + ? 'Загружаем список доменов' + : 'Сохранение..', ), - ), + if (state is MoreThenOne) + BrandText.body2( + 'Найдено больше одного домена, для вашей безопастности, просим вам удалить не нужные домены', + ), + if (state is Loaded) ...[ + SizedBox(height: 10), + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: BrandText.h3( + '${state.domain}', + textAlign: TextAlign.center, + ), + ), + Container( + width: 50, + child: BrandButton.rised( + onPressed: () => domainSetup.load(), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.refresh, + color: Colors.white, + ), + ], + ), + ), + ), + ], + ) + ], + if (state is Empty) ...[ + SizedBox(height: 30), + BrandButton.rised( + onPressed: () => domainSetup.load(), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.refresh, + color: Colors.white, + ), + SizedBox(width: 10), + BrandText.buttonTitleText('Обновить cписок'), + ], + ), + ), + ], + if (state is Loaded) ...[ + SizedBox(height: 30), + BrandButton.rised( + onPressed: () => domainSetup.saveDomain(), + title: 'Сохранить домен', + ), + ], + SizedBox(height: 10), Spacer(), - BrandButton.rised( - onPressed: - formCubit.state.isSubmitting ? null : formCubit.trySubmit, - title: 'Подключить', - ), SizedBox(height: 10), BrandButton.text( onPressed: () => _showModal(context, _HowHetzner()), @@ -286,13 +352,29 @@ class InitializingPage extends StatelessWidget { ), ), SizedBox(height: 10), - CubitFormTextField( - formFieldCubit: formCubit.password, - textAlign: TextAlign.center, - scrollPadding: EdgeInsets.only(bottom: 70), - decoration: InputDecoration( - hintText: 'Пароль', - ), + BlocBuilder, FieldCubitState>( + cubit: formCubit.isVisible, + builder: (context, state) { + var isVisible = state.value; + return CubitFormTextField( + obscureText: !isVisible, + formFieldCubit: formCubit.password, + textAlign: TextAlign.center, + scrollPadding: EdgeInsets.only(bottom: 70), + decoration: InputDecoration( + hintText: 'Пароль', + suffixIcon: IconButton( + icon: Icon( + isVisible ? Icons.visibility : Icons.visibility_off, + ), + onPressed: () => formCubit.isVisible.setValue(!isVisible), + ), + suffixIconConstraints: BoxConstraints(minWidth: 60), + prefixIconConstraints: BoxConstraints(maxWidth: 85), + prefixIcon: Container(), + ), + ); + }, ), Spacer(), BrandButton.rised( @@ -338,52 +420,40 @@ class InitializingPage extends StatelessWidget { } Widget _stepCheck(AppConfigCubit appConfigCubit) { - var state = appConfigCubit.state; - var isDnsChecked = state.isDnsCheckedAndServerStarted; - return Builder(builder: (context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Spacer(flex: 2), - SizedBox(height: 10), - BrandText.body2( - isDnsChecked - ? 'Dns сервера вступили в силу, мы стартанули сервер, как только он поднимется, мы закончим инициализацию.' - : 'Мы начали процесс инциализации сервера, раз в минуту мы будем проверять наличие DNS записей, как только они вступят в силу мы продолжим инциализацию', - ), - SizedBox(height: 10), - Row( - children: [ - BrandText.body2('До следующей проверки: '), - isDnsChecked - ? BrandTimer( - startDateTime: state.lastServerStatusCheckTime ?? - state.hetznerServer.startTime, - duration: Duration(minutes: 1), - callback: () { - appConfigCubit.setDkim(); - }, - ) - : BrandTimer( - startDateTime: state.lastDnsCheckTime ?? - state.hetznerServer.createTime, - duration: Duration(minutes: 1), - callback: () { - appConfigCubit.checkDnsAndStartServer(); - }, - ) - ], - ), - Spacer( - flex: 2, - ), - BrandButton.text( - onPressed: () => _showModal(context, _HowHetzner()), - title: 'Что это значит?', - ), - ], - ); - }); + return Text('step check'); + // var state = appConfigCubit.state as TimerState; + // var isDnsChecked = state.dataState.isDnsChecked; + // return Builder(builder: (context) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Spacer(flex: 2), + // SizedBox(height: 10), + // BrandText.body2( + // isDnsChecked + // ? 'Dns сервера вступили в силу, мы стартанули сервер, как только он поднимется, мы закончим инициализацию.' + // : 'Мы начали процесс инциализации сервера, раз в минуту мы будем проверять наличие DNS записей, как только они вступят в силу мы продолжим инциализацию', + // ), + // SizedBox(height: 10), + // Row( + // children: [ + // BrandText.body2('До следующей проверки: '), + // BrandTimer( + // startDateTime: state.timerStart, + // duration: state.duration, + // ) + // ], + // ), + // Spacer( + // flex: 2, + // ), + // BrandButton.text( + // onPressed: () => _showModal(context, _HowHetzner()), + // title: 'Что это значит?', + // ), + // ], + // ); + // }); } Widget _addCard(Widget child) { diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 1b00529a5b..5063fe31b6 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -3,6 +3,8 @@ import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/ui/components/action_button/action_button.dart'; +import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; @@ -89,37 +91,26 @@ class _AppSettingsPageState extends State { ), onPressed: () { showDialog( - context: context, - child: AlertDialog( - title: Text('Are you sure?'), - content: SingleChildScrollView( - child: ListBody( - children: [ - Text('Reset all keys?'), - ], - ), + context: context, + child: BrandAlert( + title: 'Вы уверенны', + contentText: 'Сбросить все ключи?', + acitons: [ + ActionButton( + text: 'Да, сбросить', + isRed: true, + onPressed: () { + context + .read() + .clearAppConfig(); + Navigator.of(context).pop(); + }), + ActionButton( + text: 'Отмена', ), - actions: [ - TextButton( - child: Text( - 'Reset', - style: TextStyle( - color: BrandColors.red1, - ), - ), - onPressed: () { - context.read().clearAppConfig(); - Navigator.of(context)..pop()..pop(); - }, - ), - TextButton( - child: Text('Cancel'), - onPressed: () { - Navigator.of(context)..pop(); - }, - ), - ], - )); + ], + ), + ); }, ) ], diff --git a/pubspec.lock b/pubspec.lock index cb1cd60306..7dd044ad63 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: "12.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.41.1" + version: "0.40.6" archive: dependency: transitive description: @@ -169,6 +169,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.2" crypto: dependency: "direct main" description: @@ -225,6 +232,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.3" + either_option: + dependency: "direct main" + description: + name: either_option + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" email_validator: dependency: transitive description: @@ -419,7 +433,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3-nullsafety.2" json_annotation: dependency: "direct main" description: @@ -490,6 +504,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.13" package_config: dependency: transitive description: @@ -552,7 +573,7 @@ packages: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.2" + version: "1.10.0-nullsafety.2" petitparser: dependency: transitive description: @@ -587,7 +608,7 @@ packages: name: pool url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.5.0-nullsafety.2" process: dependency: transitive description: @@ -672,6 +693,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.7.9" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.9+2" shelf_web_socket: dependency: transitive description: @@ -698,6 +733,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.9.10+1" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10-nullsafety.2" source_span: dependency: transitive description: @@ -740,6 +789,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0-nullsafety.1" + test: + dependency: transitive + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0-nullsafety.5" test_api: dependency: transitive description: @@ -747,6 +803,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.19-nullsafety.2" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.12-nullsafety.5" time: dependency: transitive description: @@ -824,6 +887,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0-nullsafety.3" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "5.5.0" wakelock: dependency: "direct main" description: @@ -859,6 +929,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.5" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 19dc788e15..0c8e417936 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: cupertino_icons: ^1.0.0 dio: ^3.0.10 easy_localization: ^2.3.3 + either_option: ^1.0.6 equatable: ^1.2.5 flutter_bloc: ^6.1.1 flutter_secure_storage: ^3.3.5