fdroid
Kherel 2021-02-15 19:58:29 +01:00
parent a07a7247f5
commit 20166647ea
29 changed files with 707 additions and 367 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -49,4 +49,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.10.0 COCOAPODS: 1.10.1

View File

@ -1,16 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/ui/components/error/error.dart'; import 'package:selfprivacy/ui/components/error/error.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
class SimpleBlocObserver extends BlocObserver { import 'get_it_config.dart';
final GlobalKey<NavigatorState> navigatorKey;
SimpleBlocObserver({this.navigatorKey}); class SimpleBlocObserver extends BlocObserver {
SimpleBlocObserver();
@override @override
void onError(Cubit cubit, Object error, StackTrace stackTrace) { void onError(Cubit cubit, Object error, StackTrace stackTrace) {
navigatorKey.currentState.push( final navigator = getIt.get<NavigationService>().navigator;
navigator.push(
materialRoute( materialRoute(
BrandError( BrandError(
error: error, error: error,

View File

@ -56,6 +56,7 @@ var darkTheme = ligtTheme.copyWith(
scaffoldBackgroundColor: Color(0xFF202120), scaffoldBackgroundColor: Color(0xFF202120),
iconTheme: IconThemeData(color: BrandColors.gray3), iconTheme: IconThemeData(color: BrandColors.gray3),
cardColor: BrandColors.gray1, cardColor: BrandColors.gray1,
dialogBackgroundColor: Color(0xFF202120),
textTheme: GoogleFonts.interTextTheme( textTheme: GoogleFonts.interTextTheme(
TextTheme( TextTheme(
headline1: headline1Style.copyWith(color: BrandColors.white), headline1: headline1Style.copyWith(color: BrandColors.white),

View File

@ -1,10 +1,17 @@
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:selfprivacy/logic/get_it/console.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'; 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; final getIt = GetIt.instance;
void getItSetup() { void getItSetup() {
getIt.registerSingleton<NavigationService>(NavigationService());
getIt.registerSingleton<ConsoleModel>(ConsoleModel()); getIt.registerSingleton<ConsoleModel>(ConsoleModel());
getIt.registerSingleton<TimerModel>(TimerModel()); getIt.registerSingleton<TimerModel>(TimerModel());
} }

View File

@ -50,7 +50,7 @@ class BNames {
static String cloudFlareKey = 'cloudFlareKey'; static String cloudFlareKey = 'cloudFlareKey';
static String rootUser = 'rootUser'; static String rootUser = 'rootUser';
static String hetznerServer = 'hetznerServer'; static String hetznerServer = 'hetznerServer';
static String isDkimSetted = 'isDkimSetted'; // static String isDkimSetted = 'isDkimSetted';
static String isDnsChecked = 'isDnsChecked'; static String isDnsChecked = 'isDnsChecked';
static String isServerStarted = 'isServerStarted'; static String isServerStarted = 'isServerStarted';
static String backblazeKey = 'backblazeKey'; static String backblazeKey = 'backblazeKey';

View File

@ -46,6 +46,13 @@ final body2Style = defaultTextStyle.copyWith(
color: BrandColors.textColor2, 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 mediumStyle = defaultTextStyle.copyWith(fontSize: 13, height: 1.53);
final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45); final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45);

View File

@ -27,7 +27,6 @@ class BackblazeApi extends ApiMap {
Response response = await loggedClient.get(rootAddress, options: options); Response response = await loggedClient.get(rootAddress, options: options);
if (response.statusCode == HttpStatus.ok) { if (response.statusCode == HttpStatus.ok) {
print(response);
return true; return true;
} else if (response.statusCode == HttpStatus.unauthorized) { } else if (response.statusCode == HttpStatus.unauthorized) {
return false; return false;

View File

@ -105,20 +105,20 @@ class CloudflareApi extends ApiMap {
await Future.wait(allCreateFutures); await Future.wait(allCreateFutures);
} }
setDkim(String dkimRecordString, String domainZoneId) { // setDkim(String dkimRecordString, String domainZoneId) {
var txt3 = DnsRecords( // var txt3 = DnsRecords(
type: 'TXT', // type: 'TXT',
name: 'selector._domainkey', // name: 'selector._domainkey',
content: dkimRecordString, // content: dkimRecordString,
ttl: 18000, // ttl: 18000,
); // );
var url = '$rootAddress/zones/$domainZoneId/dns_records'; // var url = '$rootAddress/zones/$domainZoneId/dns_records';
loggedClient.post( // loggedClient.post(
url, // url,
data: txt3.toJson(), // data: txt3.toJson(),
); // );
} // }
List<DnsRecords> projectDnsRecords(String domainName, String ip4) { List<DnsRecords> projectDnsRecords(String domainName, String ip4) {
var domainA = DnsRecords(type: 'A', name: domainName, content: ip4); var domainA = DnsRecords(type: 'A', name: domainName, content: ip4);
@ -160,4 +160,16 @@ class CloudflareApi extends ApiMap {
vpn vpn
]; ];
} }
Future<List<String>> domainList() async {
var url = '$rootAddress/zones?per_page=50';
var response = await loggedClient.get(
url,
queryParameters: {'per_page': 50},
);
return response.data['result']
.map<String>((el) => el['name'] as String)
.toList();
}
} }

View File

@ -60,6 +60,16 @@ class HetznerApi extends ApiMap {
); );
} }
Future<void> 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<HetznerServerDetails> startServer({ Future<HetznerServerDetails> startServer({
HetznerServerDetails server, HetznerServerDetails server,
}) async { }) async {

View File

@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
@ -26,22 +25,22 @@ class ServerApi extends ApiMap {
return res; return res;
} }
Future<String> getDkim(String domainName) async { // Future<String> getDkim(String domainName) async {
var response = await loggedClient.get( // var response = await loggedClient.get(
'/getDKIM', // '/getDKIM',
options: Options(responseType: ResponseType.plain), // options: Options(responseType: ResponseType.plain),
); // );
return _decodeAndCutData(response.data, domainName); // return _decodeAndCutData(response.data, domainName);
} // }
} }
String _decodeAndCutData(String text, String domainName) { // String _decodeAndCutData(String text, String domainName) {
var decodedTextString = text.substring(1, text.length - 1); // var decodedTextString = text.substring(1, text.length - 1);
var stringToBase64 = utf8.fuse(base64); // var stringToBase64 = utf8.fuse(base64);
return stringToBase64 // return stringToBase64
.decode(decodedTextString) // .decode(decodedTextString)
.replaceAll("selector._domainkey IN TXT ( ", "") // .replaceAll("selector._domainkey IN TXT ( ", "")
.replaceAll("\"\n \"", "") // .replaceAll("\"\n \"", "")
.replaceAll(' ) ; ----- DKIM key selector for $domainName\n', ''); // .replaceAll(' ) ; ----- DKIM key selector for $domainName\n', '');
} // }

View File

@ -14,22 +14,30 @@ import 'app_config_repository.dart';
part 'app_config_state.dart'; part 'app_config_state.dart';
/// Initializing steps: /// Initializing steps:
/// 1. Hetzner key |setHetznerKey ///
/// 2. Cloudflare key |setCloudflareKey /// The set phase.
/// 3. Backblaze Id + Key |setBackblazeKey /// 1.1. Hetzner key |setHetznerKey
/// 1.2. Cloudflare key |setCloudflareKey
/// 4. Set Domain address |setDomain /// 1.3. Backblaze Id + Key |setBackblazeKey
/// 5. Set Root user name password |setRootUser /// 1.4. Set Domain address |setDomain
/// 6. Set Create server ans set DNS-Records |createServerAndSetDnsRecords /// 1.5. Set Root user name password |setRootUser
/// 1.6. Set Create server ans set DNS-Records |createServerAndSetDnsRecords
/// (without start) /// (without start)
/// 7. ChecksAndSets: ///
/// 7.1 checkDnsAndStartServer |checkDnsAndStartServer /// The check phase.
/// 7.2 setDkim |setDkim ///
/// a. checkServer /// 2.1. a. wait 60sec checkDnsAndStartServer |startServerIfDnsIsOkay
/// b. getDkim /// b. checkDns
/// c. Set DKIM /// c. if dns is okay start server
/// d. server restart ///
/// 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<AppConfigState> { class AppConfigCubit extends Cubit<AppConfigState> {
AppConfigCubit() : super(InitialAppConfigState()); AppConfigCubit() : super(InitialAppConfigState());
@ -66,34 +74,22 @@ class AppConfigCubit extends Cubit<AppConfigState> {
emit(state.copyWith(rootUser: rootUser)); emit(state.copyWith(rootUser: rootUser));
} }
void setDkim() async { void serverReset() async {
var callBack = () async { var callBack = () async {
var isServerWorking = await repository.isHttpServerWorking( var isServerWorking = await repository.isHttpServerWorking(
state.cloudFlareDomain.domainName, state.cloudFlareDomain.domainName,
); );
if (!isServerWorking) { if (!isServerWorking) {
var last = DateTime.now(); var last = DateTime.now();
print(last);
emit(state.copyWith(lastServerStatusCheckTime: last)); emit(state.copyWith(lastServerStatusCheckTime: last));
return; return;
} }
await repository.setDkim(
state.cloudFlareDomain.domainName,
state.cloudFlareKey,
state.cloudFlareDomain.zoneId,
);
var hetznerServerDetails = await repository.restart( var hetznerServerDetails = await repository.restart(
state.hetznerKey, state.hetznerKey,
state.hetznerServer, state.hetznerServer,
); );
emit( emit(state.copyWith(hetznerServer: hetznerServerDetails));
state.copyWith(
isDkimSetted: true,
hetznerServer: hetznerServerDetails,
),
);
}; };
_tryOrAddError(state, callBack); _tryOrAddError(state, callBack);
@ -125,14 +121,7 @@ class AppConfigCubit extends Cubit<AppConfigState> {
} }
void createServerAndSetDnsRecords() async { void createServerAndSetDnsRecords() async {
var callback = () async { var onSuccess = (serverDetails) async {
var serverDetails = await repository.createServer(
state.hetznerKey,
state.rootUser,
state.cloudFlareDomain.domainName,
state.cloudFlareKey,
);
await repository.createDnsRecords( await repository.createDnsRecords(
state.cloudFlareKey, state.cloudFlareKey,
serverDetails.ip4, serverDetails.ip4,
@ -144,6 +133,19 @@ class AppConfigCubit extends Cubit<AppConfigState> {
hetznerServer: serverDetails, 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); _tryOrAddError(state, callback);
} }

View File

@ -1,3 +1,4 @@
import 'package:dio/dio.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.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/get_it/console.dart';
import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/logic/models/message.dart';
import 'package:basic_utils/basic_utils.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'; import 'app_config_cubit.dart';
class AppConfigRepository { class AppConfigRepository {
@ -26,7 +29,6 @@ class AppConfigRepository {
hetznerServer: box.get(BNames.hetznerServer), hetznerServer: box.get(BNames.hetznerServer),
isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false),
isDnsChecked: box.get(BNames.isDnsChecked, defaultValue: false), isDnsChecked: box.get(BNames.isDnsChecked, defaultValue: false),
isDkimSetted: box.get(BNames.isDkimSetted, defaultValue: false),
); );
} }
@ -110,19 +112,64 @@ class AppConfigRepository {
return true; return true;
} }
Future<HetznerServerDetails> createServer(String hetznerKey, User rootUser, Future<void> createServer(
String domainName, String cloudFlareKey) async { String hetznerKey,
User rootUser,
String domainName,
String cloudFlareKey, {
void Function() onCancel,
Future<void> Function(HetznerServerDetails serverDetails) onSuccess,
}) async {
var hetznerApi = HetznerApi(hetznerKey); 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<NavigationService>();
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<void> createDnsRecords( Future<void> createDnsRecords(
@ -152,21 +199,6 @@ class AppConfigRepository {
return isHttpServerWorking; return isHttpServerWorking;
} }
Future<void> 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<HetznerServerDetails> restart( Future<HetznerServerDetails> restart(
String hetznerKey, String hetznerKey,
HetznerServerDetails server, HetznerServerDetails server,

View File

@ -10,11 +10,10 @@ class AppConfigState extends Equatable {
this.hetznerServer, this.hetznerServer,
this.isLoading = false, this.isLoading = false,
this.error, this.error,
this.lastDnsCheckTime, // this.lastDnsCheckTime,
this.lastServerStatusCheckTime, // this.lastServerStatusCheckTime,
this.isDnsChecked = false, this.isDnsChecked = false,
this.isServerStarted = false, this.isServerStarted = false,
this.isDkimSetted = false,
}); });
@override @override
@ -28,9 +27,8 @@ class AppConfigState extends Equatable {
isDnsCheckedAndServerStarted, isDnsCheckedAndServerStarted,
isLoading, isLoading,
error, error,
lastDnsCheckTime, // lastDnsCheckTime,
lastServerStatusCheckTime, // lastServerStatusCheckTime,
isDkimSetted,
]; ];
final String hetznerKey; final String hetznerKey;
@ -39,11 +37,10 @@ class AppConfigState extends Equatable {
final CloudFlareDomain cloudFlareDomain; final CloudFlareDomain cloudFlareDomain;
final User rootUser; final User rootUser;
final HetznerServerDetails hetznerServer; final HetznerServerDetails hetznerServer;
final bool isDkimSetted;
final bool isServerStarted; final bool isServerStarted;
final bool isDnsChecked; final bool isDnsChecked;
final DateTime lastDnsCheckTime; // final DateTime lastDnsCheckTime;
final DateTime lastServerStatusCheckTime; // final DateTime lastServerStatusCheckTime;
final bool isLoading; final bool isLoading;
final Exception error; final Exception error;
@ -58,7 +55,6 @@ class AppConfigState extends Equatable {
Exception error, Exception error,
DateTime lastDnsCheckTime, DateTime lastDnsCheckTime,
DateTime lastServerStatusCheckTime, DateTime lastServerStatusCheckTime,
bool isDkimSetted,
bool isServerStarted, bool isServerStarted,
bool isDnsChecked, bool isDnsChecked,
}) => }) =>
@ -73,10 +69,9 @@ class AppConfigState extends Equatable {
isDnsChecked: isDnsChecked ?? this.isDnsChecked, isDnsChecked: isDnsChecked ?? this.isDnsChecked,
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
error: error ?? this.error, error: error ?? this.error,
lastDnsCheckTime: lastDnsCheckTime ?? this.lastDnsCheckTime, // lastDnsCheckTime: lastDnsCheckTime ?? this.lastDnsCheckTime,
lastServerStatusCheckTime: // lastServerStatusCheckTime:
lastServerStatusCheckTime ?? this.lastServerStatusCheckTime, // lastServerStatusCheckTime ?? this.lastServerStatusCheckTime,
isDkimSetted: isDkimSetted ?? this.isDkimSetted,
); );
bool get isHetznerFilled => hetznerKey != null; bool get isHetznerFilled => hetznerKey != null;
@ -85,7 +80,7 @@ class AppConfigState extends Equatable {
bool get isDomainFilled => cloudFlareDomain != null; bool get isDomainFilled => cloudFlareDomain != null;
bool get isUserFilled => rootUser != null; bool get isUserFilled => rootUser != null;
bool get isServerFilled => hetznerServer != null; bool get isServerFilled => hetznerServer != null;
bool get hasFinalChecked => isDnsCheckedAndServerStarted && isDkimSetted; bool get hasFinalChecked => isDnsCheckedAndServerStarted;
bool get isDnsCheckedAndServerStarted => isDnsChecked && isServerStarted; bool get isDnsCheckedAndServerStarted => isDnsChecked && isServerStarted;
@ -107,3 +102,22 @@ class AppConfigState extends Equatable {
class InitialAppConfigState extends AppConfigState { class InitialAppConfigState extends AppConfigState {
InitialAppConfigState() : super(); 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<Object> get props => [
dataState,
timerStart,
duration,
];
}

View File

@ -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<DomainSetupState> {
DomainSetupCubit(this.initializingCubit) : super(Initial()) {
var token = (initializingCubit.state.cloudFlareKey);
assert(token != null, 'no cloudflare token');
api = CloudflareApi(token);
}
AppConfigCubit initializingCubit;
CloudflareApi api;
Future<void> 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<void> close() {
api.close();
return super.close();
}
Future<void> 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 {}

View File

@ -1,68 +1,68 @@
import 'dart:async'; // import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; // import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; // import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; // import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; // import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
class DomainFormCubit extends FormCubit { // class DomainFormCubit extends FormCubit {
CloudflareApi apiClient = CloudflareApi(); // CloudflareApi apiClient = CloudflareApi();
DomainFormCubit(this.initializingCubit) { // DomainFormCubit(this.initializingCubit) {
var regExp = // var regExp =
RegExp(r"^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}"); // RegExp(r"^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}");
domainName = FieldCubit( // domainName = FieldCubit(
initalValue: '', // initalValue: '',
validations: [ // validations: [
RequiredStringValidation('required'), // RequiredStringValidation('required'),
ValidationModel<String>( // ValidationModel<String>(
(s) => !regExp.hasMatch(s), // (s) => !regExp.hasMatch(s),
'invalid domain format', // 'invalid domain format',
), // ),
], // ],
); // );
super.setFields([domainName]); // super.setFields([domainName]);
} // }
@override // @override
FutureOr<void> onSubmit() async { // FutureOr<void> onSubmit() async {
var domain = CloudFlareDomain( // var domain = CloudFlareDomain(
domainName: domainName.state.value, // domainName: domainName.state.value,
zoneId: zoneId, // zoneId: zoneId,
); // );
initializingCubit.setDomain(domain); // initializingCubit.setDomain(domain);
} // }
final AppConfigCubit initializingCubit; // final AppConfigCubit initializingCubit;
FieldCubit<String> domainName; // FieldCubit<String> domainName;
String zoneId; // String zoneId;
@override // @override
FutureOr<bool> asyncValidation() async { // FutureOr<bool> asyncValidation() async {
var key = initializingCubit.state.cloudFlareKey; // var key = initializingCubit.state.cloudFlareKey;
String zoneId; // String zoneId;
try { // try {
zoneId = await apiClient.getZoneId(key, domainName.state.value); // zoneId = await apiClient.getZoneId(key, domainName.state.value);
} catch (e) { // } catch (e) {
addError(e); // addError(e);
} // }
if (zoneId == null) { // if (zoneId == null) {
domainName.setError('Domain not in the list'); // domainName.setError('Domain not in the list');
return false; // return false;
} // }
this.zoneId = zoneId; // this.zoneId = zoneId;
return true; // return true;
} // }
@override // @override
Future<void> close() async { // Future<void> close() async {
apiClient.close(); // apiClient.close();
return super.close(); // return super.close();
} // }
} // }

View File

@ -30,7 +30,9 @@ class RootUserFormCubit extends FormCubit {
], ],
); );
super.setFields([userName, password]); isVisible = FieldCubit(initalValue: false);
super.setFields([userName, password, isVisible]);
} }
@override @override
@ -46,6 +48,7 @@ class RootUserFormCubit extends FormCubit {
FieldCubit<String> userName; FieldCubit<String> userName;
FieldCubit<String> password; FieldCubit<String> password;
FieldCubit<bool> isVisible;
@override @override
Future<void> close() async { Future<void> close() async {

View File

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get navigator => navigatorKey.currentState;
void showPopUpDialog(AlertDialog dialog) {
final context = navigatorKey.currentState.overlay.context;
showDialog(
context: context,
builder: (_) => dialog,
);
}
}

View File

@ -15,11 +15,10 @@ import 'config/get_it_config.dart';
import 'config/localization.dart'; import 'config/localization.dart';
import 'logic/cubit/app_settings/app_settings_cubit.dart'; import 'logic/cubit/app_settings/app_settings_cubit.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() async { void main() async {
await HiveConfig.init(); await HiveConfig.init();
Bloc.observer = SimpleBlocObserver(navigatorKey: navigatorKey); Bloc.observer = SimpleBlocObserver();
Wakelock.enable(); Wakelock.enable();
getItSetup(); getItSetup();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -41,7 +40,7 @@ class MyApp extends StatelessWidget {
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light, // Manually changnig appbar color value: SystemUiOverlayStyle.light, // Manually changnig appbar color
child: MaterialApp( child: MaterialApp(
navigatorKey: navigatorKey, navigatorKey: getIt.get<NavigationService>().navigatorKey,
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,

View File

@ -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();
},
);
}
}

View File

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
class BrandAlert extends AlertDialog {
BrandAlert({
Key key,
String title,
String contentText,
List<Widget> acitons,
}) : super(
key: key,
title: title != null ? Text(title) : null,
content: title != null ? Text(contentText) : null,
actions: acitons,
);
}

View File

@ -6,42 +6,32 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
enum BrandButtonTypes { rised, text, iconText } enum BrandButtonTypes { rised, text, iconText }
class BrandButton extends StatelessWidget { class BrandButton {
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;
static rised({ static rised({
Key key, Key key,
@required VoidCallback onPressed, @required VoidCallback onPressed,
@required String title, String title,
}) => Widget child,
BrandButton( }) {
key: key, assert(title == null || child == null, 'required title or child');
onPressed: onPressed, assert(title != null || child != null, 'required title or child');
title: title, return _RisedButton(
type: BrandButtonTypes.rised, key: key,
); title: title,
onPressed: onPressed,
child: child,
);
}
static text({ static text({
Key key, Key key,
@required VoidCallback onPressed, @required VoidCallback onPressed,
@required String title, @required String title,
}) => }) =>
BrandButton( _TextButton(
key: key, key: key,
onPressed: onPressed,
title: title, title: title,
type: BrandButtonTypes.text, onPressed: onPressed,
); );
static iconText({ static iconText({
@ -50,38 +40,12 @@ class BrandButton extends StatelessWidget {
@required String title, @required String title,
@required Icon icon, @required Icon icon,
}) => }) =>
BrandButton( _IconTextButton(
key: key, key: key,
onPressed: onPressed,
title: title, title: title,
type: BrandButtonTypes.iconText, onPressed: onPressed,
icon: icon, 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 { class _RisedButton extends StatelessWidget {
@ -89,10 +53,12 @@ class _RisedButton extends StatelessWidget {
Key key, Key key,
this.onPressed, this.onPressed,
this.title, this.title,
this.child,
}) : super(key: key); }) : super(key: key);
final VoidCallback onPressed; final VoidCallback onPressed;
final String title; final String title;
final Widget child;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -111,15 +77,7 @@ class _RisedButton extends StatelessWidget {
width: double.infinity, width: double.infinity,
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.all(12), padding: EdgeInsets.all(12),
child: Text( child: child ?? BrandText.buttonTitleText(title),
title,
style: TextStyle(
color: BrandColors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
height: 1,
),
),
), ),
), ),
), ),

View File

@ -10,18 +10,20 @@ enum TextType {
body2, // with opacity body2, // with opacity
medium, medium,
small, small,
onboardingTitle onboardingTitle,
buttonTitleText // risen button title text,
} }
class BrandText extends StatelessWidget { class BrandText extends StatelessWidget {
const BrandText(this.text, const BrandText(
{Key key, this.text, {
this.style, Key key,
@required this.type, this.style,
this.overflow, @required this.type,
this.softWrap, this.overflow,
this.textAlign}) this.softWrap,
: super(key: key); this.textAlign,
}) : super(key: key);
final String text; final String text;
final TextStyle style; final TextStyle style;
@ -53,10 +55,13 @@ class BrandText extends StatelessWidget {
type: TextType.h2, type: TextType.h2,
style: style, style: style,
); );
factory BrandText.h3(String text, {TextStyle style}) => BrandText( factory BrandText.h3(String text, {TextStyle style, TextAlign textAlign}) =>
BrandText(
text, text,
type: TextType.h3, type: TextType.h3,
style: style, style: style,
textAlign: textAlign,
overflow: TextOverflow.ellipsis,
); );
factory BrandText.h4(String text, {TextStyle style}) => BrandText( factory BrandText.h4(String text, {TextStyle style}) => BrandText(
text, text,
@ -75,13 +80,23 @@ class BrandText extends StatelessWidget {
); );
factory BrandText.medium(String text, factory BrandText.medium(String text,
{TextStyle style, TextAlign textAlign}) => {TextStyle style, TextAlign textAlign}) =>
BrandText(text, BrandText(
type: TextType.medium, style: style, textAlign: textAlign); text,
type: TextType.medium,
style: style,
textAlign: textAlign,
);
factory BrandText.small(String text, {TextStyle style}) => BrandText( factory BrandText.small(String text, {TextStyle style}) => BrandText(
text, text,
type: TextType.small, type: TextType.small,
style: style, style: style,
); );
factory BrandText.buttonTitleText(String text, {TextStyle style}) =>
BrandText(
text,
type: TextType.buttonTitleText,
style: style,
);
@override @override
Text build(BuildContext context) { Text build(BuildContext context) {
TextStyle style; TextStyle style;
@ -128,6 +143,11 @@ class BrandText extends StatelessWidget {
style = style =
isDark ? mediumStyle.copyWith(color: Colors.white) : mediumStyle; isDark ? mediumStyle.copyWith(color: Colors.white) : mediumStyle;
break; break;
case TextType.buttonTitleText:
style = !isDark
? buttonTitleText.copyWith(color: Colors.white)
: buttonTitleText;
break;
} }
if (this.style != null) { if (this.style != null) {
style = style.merge(this.style); style = style.merge(this.style);

View File

@ -9,12 +9,10 @@ class BrandTimer extends StatefulWidget {
Key key, Key key,
@required this.startDateTime, @required this.startDateTime,
@required this.duration, @required this.duration,
@required this.callback,
}) : super(key: key); }) : super(key: key);
final DateTime startDateTime; final DateTime startDateTime;
final Duration duration; final Duration duration;
final VoidCallback callback;
@override @override
_BrandTimerState createState() => _BrandTimerState(); _BrandTimerState createState() => _BrandTimerState();
@ -36,7 +34,6 @@ class _BrandTimerState extends State<BrandTimer> {
var timePassed = DateTime.now().difference(widget.startDateTime); var timePassed = DateTime.now().difference(widget.startDateTime);
if (timePassed > widget.duration) { if (timePassed > widget.duration) {
t.cancel(); t.cancel();
widget.callback();
} else { } else {
_getTime(); _getTime();
} }

View File

@ -6,7 +6,7 @@ import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/config/text_themes.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/backblaze_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_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/hetzner_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/root_user_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'; 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_modal_sheet/brand_modal_sheet.dart';
import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.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_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/components/progress_bar/progress_bar.dart';
import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
@ -34,7 +33,8 @@ class InitializingPage extends StatelessWidget {
_stepServer(cubit), _stepServer(cubit),
_stepCheck(cubit), _stepCheck(cubit),
Container(child: Text('Everythigng is initialized')) Container(child: Text('Everythigng is initialized'))
][2]; ][cubit.state.progress];
return BlocListener<AppConfigCubit, AppConfigState>( return BlocListener<AppConfigCubit, AppConfigState>(
listener: (context, state) { listener: (context, state) {
if (state.isFullyInitilized) { if (state.isFullyInitilized) {
@ -96,8 +96,10 @@ class InitializingPage extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Spacer(), Image.asset(
Image.asset('assets/images/logos/hetzner.png'), 'assets/images/logos/hetzner.png',
width: 150,
),
SizedBox(height: 10), SizedBox(height: 10),
BrandText.h2('Подключите сервер Hetzner'), BrandText.h2('Подключите сервер Hetzner'),
SizedBox(height: 10), SizedBox(height: 10),
@ -149,8 +151,11 @@ class InitializingPage extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Spacer(), Image.asset(
Image.asset('assets/images/logos/cloudflare.png'), 'assets/images/logos/cloudflare.png',
width: 150,
),
SizedBox(height: 10),
BrandText.h2('Подключите CloudFlare'), BrandText.h2('Подключите CloudFlare'),
SizedBox(height: 10), SizedBox(height: 10),
BrandText.body2('Для управления DNS вашего домена'), BrandText.body2('Для управления DNS вашего домена'),
@ -188,12 +193,13 @@ class InitializingPage extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Spacer(), Image.asset(
Image.asset('assets/images/logos/backblaze.png'), 'assets/images/logos/backblaze.png',
height: 50,
),
SizedBox(height: 10), SizedBox(height: 10),
BrandText.h2('Подключите облачное хранилище Backblaze'), BrandText.h2('Подключите облачное хранилище Backblaze'),
SizedBox(height: 10), SizedBox(height: 10),
BrandText.body2('Здесь будут храниться данные'),
Spacer(), Spacer(),
CubitFormTextField( CubitFormTextField(
formFieldCubit: formCubit.keyId, formFieldCubit: formCubit.keyId,
@ -231,30 +237,90 @@ class InitializingPage extends StatelessWidget {
Widget _stepDomain(AppConfigCubit initializingCubit) { Widget _stepDomain(AppConfigCubit initializingCubit) {
return BlocProvider( return BlocProvider(
create: (context) => DomainFormCubit(initializingCubit), create: (context) => DomainSetupCubit(initializingCubit)..load(),
child: Builder(builder: (context) { child: Builder(builder: (context) {
var formCubit = context.watch<DomainFormCubit>(); var domainSetup = context.watch<DomainSetupCubit>();
var state = domainSetup.state;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Spacer(), Image.asset(
BrandText.h2('Введите домен:'), 'assets/images/logos/cloudflare.png',
width: 150,
),
SizedBox(height: 30),
BrandText.h2('Домен'),
SizedBox(height: 10), SizedBox(height: 10),
CubitFormTextField( if (state is Empty)
keyboardType: TextInputType.emailAddress, BrandText.body2('На данный момент подлюченных доменов нет'),
formFieldCubit: formCubit.domainName, if (state is Loading)
textAlign: TextAlign.center, BrandText.body2(
scrollPadding: EdgeInsets.only(bottom: 70), state.type == LoadingTypes.loadingDomain
decoration: InputDecoration( ? 'Загружаем список доменов'
hintText: 'Домен', : 'Сохранение..',
), ),
), 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(), Spacer(),
BrandButton.rised(
onPressed:
formCubit.state.isSubmitting ? null : formCubit.trySubmit,
title: 'Подключить',
),
SizedBox(height: 10), SizedBox(height: 10),
BrandButton.text( BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()), onPressed: () => _showModal(context, _HowHetzner()),
@ -286,13 +352,29 @@ class InitializingPage extends StatelessWidget {
), ),
), ),
SizedBox(height: 10), SizedBox(height: 10),
CubitFormTextField( BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
formFieldCubit: formCubit.password, cubit: formCubit.isVisible,
textAlign: TextAlign.center, builder: (context, state) {
scrollPadding: EdgeInsets.only(bottom: 70), var isVisible = state.value;
decoration: InputDecoration( return CubitFormTextField(
hintText: 'Пароль', 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(), Spacer(),
BrandButton.rised( BrandButton.rised(
@ -338,52 +420,40 @@ class InitializingPage extends StatelessWidget {
} }
Widget _stepCheck(AppConfigCubit appConfigCubit) { Widget _stepCheck(AppConfigCubit appConfigCubit) {
var state = appConfigCubit.state; return Text('step check');
var isDnsChecked = state.isDnsCheckedAndServerStarted; // var state = appConfigCubit.state as TimerState;
return Builder(builder: (context) { // var isDnsChecked = state.dataState.isDnsChecked;
return Column( // return Builder(builder: (context) {
crossAxisAlignment: CrossAxisAlignment.start, // return Column(
children: [ // crossAxisAlignment: CrossAxisAlignment.start,
Spacer(flex: 2), // children: [
SizedBox(height: 10), // Spacer(flex: 2),
BrandText.body2( // SizedBox(height: 10),
isDnsChecked // BrandText.body2(
? 'Dns сервера вступили в силу, мы стартанули сервер, как только он поднимется, мы закончим инициализацию.' // isDnsChecked
: 'Мы начали процесс инциализации сервера, раз в минуту мы будем проверять наличие DNS записей, как только они вступят в силу мы продолжим инциализацию', // ? 'Dns сервера вступили в силу, мы стартанули сервер, как только он поднимется, мы закончим инициализацию.'
), // : 'Мы начали процесс инциализации сервера, раз в минуту мы будем проверять наличие DNS записей, как только они вступят в силу мы продолжим инциализацию',
SizedBox(height: 10), // ),
Row( // SizedBox(height: 10),
children: [ // Row(
BrandText.body2('До следующей проверки: '), // children: [
isDnsChecked // BrandText.body2('До следующей проверки: '),
? BrandTimer( // BrandTimer(
startDateTime: state.lastServerStatusCheckTime ?? // startDateTime: state.timerStart,
state.hetznerServer.startTime, // duration: state.duration,
duration: Duration(minutes: 1), // )
callback: () { // ],
appConfigCubit.setDkim(); // ),
}, // Spacer(
) // flex: 2,
: BrandTimer( // ),
startDateTime: state.lastDnsCheckTime ?? // BrandButton.text(
state.hetznerServer.createTime, // onPressed: () => _showModal(context, _HowHetzner()),
duration: Duration(minutes: 1), // title: 'Что это значит?',
callback: () { // ),
appConfigCubit.checkDnsAndStartServer(); // ],
}, // );
) // });
],
),
Spacer(
flex: 2,
),
BrandButton.text(
onPressed: () => _showModal(context, _HowHetzner()),
title: 'Что это значит?',
),
],
);
});
} }
Widget _addCard(Widget child) { Widget _addCard(Widget child) {

View File

@ -3,6 +3,8 @@ import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.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_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_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_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
@ -89,37 +91,26 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
), ),
onPressed: () { onPressed: () {
showDialog( showDialog(
context: context, context: context,
child: AlertDialog( child: BrandAlert(
title: Text('Are you sure?'), title: 'Вы уверенны',
content: SingleChildScrollView( contentText: 'Сбросить все ключи?',
child: ListBody( acitons: [
children: <Widget>[ ActionButton(
Text('Reset all keys?'), text: 'Да, сбросить',
], isRed: true,
), onPressed: () {
context
.read<AppConfigCubit>()
.clearAppConfig();
Navigator.of(context).pop();
}),
ActionButton(
text: 'Отмена',
), ),
actions: <Widget>[ ],
TextButton( ),
child: Text( );
'Reset',
style: TextStyle(
color: BrandColors.red1,
),
),
onPressed: () {
context.read<AppConfigCubit>().clearAppConfig();
Navigator.of(context)..pop()..pop();
},
),
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context)..pop();
},
),
],
));
}, },
) )
], ],

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared name: _fe_analyzer_shared
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "14.0.0" version: "12.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.41.1" version: "0.40.6"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@ -169,6 +169,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.2"
crypto: crypto:
dependency: "direct main" dependency: "direct main"
description: description:
@ -225,6 +232,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.3" 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: email_validator:
dependency: transitive dependency: transitive
description: description:
@ -419,7 +433,7 @@ packages:
name: js name: js
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.2" version: "0.6.3-nullsafety.2"
json_annotation: json_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@ -490,6 +504,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" 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: package_config:
dependency: transitive dependency: transitive
description: description:
@ -552,7 +573,7 @@ packages:
name: pedantic name: pedantic
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.2" version: "1.10.0-nullsafety.2"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -587,7 +608,7 @@ packages:
name: pool name: pool
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.0" version: "1.5.0-nullsafety.2"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -672,6 +693,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.9" 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: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
@ -698,6 +733,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.10+1" 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: source_span:
dependency: transitive dependency: transitive
description: description:
@ -740,6 +789,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0-nullsafety.1" 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: test_api:
dependency: transitive dependency: transitive
description: description:
@ -747,6 +803,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.19-nullsafety.2" 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: time:
dependency: transitive dependency: transitive
description: description:
@ -824,6 +887,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0-nullsafety.3" 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: wakelock:
dependency: "direct main" dependency: "direct main"
description: description:
@ -859,6 +929,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" 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: win32:
dependency: transitive dependency: transitive
description: description:

View File

@ -14,6 +14,7 @@ dependencies:
cupertino_icons: ^1.0.0 cupertino_icons: ^1.0.0
dio: ^3.0.10 dio: ^3.0.10
easy_localization: ^2.3.3 easy_localization: ^2.3.3
either_option: ^1.0.6
equatable: ^1.2.5 equatable: ^1.2.5
flutter_bloc: ^6.1.1 flutter_bloc: ^6.1.1
flutter_secure_storage: ^3.3.5 flutter_secure_storage: ^3.3.5