diff --git a/assets/images/logos/backblaze.png b/assets/images/logos/backblaze.png index c481855673..141073918f 100644 Binary files a/assets/images/logos/backblaze.png and b/assets/images/logos/backblaze.png differ diff --git a/assets/images/logos/cloudflare.png b/assets/images/logos/cloudflare.png index 2e6ca435d6..f5966f6764 100644 Binary files a/assets/images/logos/cloudflare.png and b/assets/images/logos/cloudflare.png differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 43fe023c1d..075d04f638 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -49,4 +49,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c -COCOAPODS: 1.10.0 +COCOAPODS: 1.10.1 diff --git a/lib/config/bloc_observer.dart b/lib/config/bloc_observer.dart index d69c287644..f19a42af5a 100644 --- a/lib/config/bloc_observer.dart +++ b/lib/config/bloc_observer.dart @@ -1,16 +1,17 @@ -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/ui/components/error/error.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; -class SimpleBlocObserver extends BlocObserver { - final GlobalKey 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