Implement recovery domain page frontend

pull/90/head
NaiJi ✨ 2022-05-11 21:37:08 +03:00
parent ce3e046f5a
commit 01b1f7462d
14 changed files with 177 additions and 54 deletions

View File

@ -22,7 +22,7 @@
"nickname": "Nickname",
"loading": "Loading...",
"later": "Skip to setup later",
"connect_to_existing": "Connect to existing server",
"connect_to_existing": "Connect to an existing server",
"reset": "Reset",
"details": "Details",
"no_data": "No data",
@ -283,6 +283,11 @@
"finish": "Everything is initialized",
"checks": "Checks have been completed \n{} ouf of {}"
},
"recovering": {
"recovery_main_header": "Connect to an existing server",
"domain_recovery_description": "Enter a server domain you want to get access for",
"domain_recover_placeholder": "Your domain"
},
"modals": {
"_comment": "messages in modals",
"1": "Server with such name, already exist",

View File

@ -71,4 +71,5 @@ class BNames {
static String sshConfig = 'sshConfig';
static String sshPrivateKey = "sshPrivateKey";
static String sshPublicKey = "sshPublicKey";
static String serverDomain = "serverDomain";
}

View File

@ -239,6 +239,10 @@ class AppConfigRepository {
await getIt<ApiConfigModel>().storeHetznerKey(key);
}
Future<void> saveServerDomain(String domain) async {
await getIt<ApiConfigModel>().storeServerDomain(domain);
}
Future<void> saveBackblazeKey(BackblazeCredential backblazeCredential) async {
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
}

View File

@ -240,3 +240,39 @@ class AppConfigFinished extends AppConfigState {
isServerResetedFirstTime,
];
}
class AppRecovery extends AppConfigState {
const AppRecovery({
required String hetznerKey,
required String cloudFlareKey,
required BackblazeCredential backblazeCredential,
required CloudFlareDomain cloudFlareDomain,
required User rootUser,
required HetznerServerDetails hetznerServer,
required bool isServerStarted,
required bool isServerResetedFirstTime,
required bool isServerResetedSecondTime,
}) : super(
hetznerKey: hetznerKey,
cloudFlareKey: cloudFlareKey,
backblazeCredential: backblazeCredential,
cloudFlareDomain: cloudFlareDomain,
rootUser: rootUser,
hetznerServer: hetznerServer,
isServerStarted: isServerStarted,
isServerResetedFirstTime: isServerResetedFirstTime,
isServerResetedSecondTime: isServerResetedSecondTime,
);
@override
List<Object?> get props => [
hetznerKey,
cloudFlareKey,
backblazeCredential,
cloudFlareDomain,
rootUser,
hetznerServer,
isServerStarted,
isServerResetedFirstTime,
];
}

View File

@ -0,0 +1,51 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class RecoveryDomainFormCubit extends FormCubit {
RecoveryDomainFormCubit(this.initializingCubit) {
var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
apiKey = FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('validations.required'.tr()),
ValidationModel<String>(
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
LengthStringNotEqualValidation(64)
],
);
super.addFields([apiKey]);
}
@override
FutureOr<void> onSubmit() async {
initializingCubit.setHetznerKey(apiKey.state.value);
}
final AppConfigCubit initializingCubit;
late final FieldCubit<String> apiKey;
@override
FutureOr<bool> asyncValidation() async {
late bool isKeyValid;
HetznerApi apiClient = HetznerApi(isWithToken: false);
try {
isKeyValid = await apiClient.isValid(apiKey.state.value);
} catch (e) {
addError(e);
}
if (!isKeyValid) {
apiKey.setError('bad key');
return false;
}
return true;
}
}

View File

@ -11,12 +11,14 @@ class ApiConfigModel {
HetznerServerDetails? get hetznerServer => _hetznerServer;
String? get hetznerKey => _hetznerKey;
String? get cloudFlareKey => _cloudFlareKey;
String? get serverDomain => _serverDomain;
BackblazeCredential? get backblazeCredential => _backblazeCredential;
CloudFlareDomain? get cloudFlareDomain => _cloudFlareDomain;
BackblazeBucket? get backblazeBucket => _backblazeBucket;
String? _hetznerKey;
String? _cloudFlareKey;
String? _serverDomain;
HetznerServerDetails? _hetznerServer;
BackblazeCredential? _backblazeCredential;
CloudFlareDomain? _cloudFlareDomain;
@ -32,6 +34,11 @@ class ApiConfigModel {
_cloudFlareKey = value;
}
Future<void> storeServerDomain(String value) async {
await _box.put(BNames.serverDomain, value);
_serverDomain = value;
}
Future<void> storeBackblazeCredential(BackblazeCredential value) async {
await _box.put(BNames.backblazeKey, value);

View File

@ -6,7 +6,7 @@ import 'package:selfprivacy/ui/components/pre_styled_buttons/pre_styled_buttons.
class BrandHeader extends StatelessWidget {
const BrandHeader({
Key? key,
required this.title,
this.title = "",
this.hasBackButton = false,
this.hasFlashButton = false,
}) : super(key: key);

View File

@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.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_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/forms/setup/initializing/backblaze_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_cloudflare.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
@ -104,10 +104,8 @@ class InitializingPage extends StatelessWidget {
child: BrandButton.text(
title: 'basis.connect_to_existing'.tr(),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(RecoveryDomain()),
(predicate) => false,
);
Navigator.of(context)
.push(materialRoute(RecoveryDomain()));
},
),
)

View File

@ -1,54 +1,75 @@
import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/pages/rootRoute.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
class RecoveryDomain extends StatelessWidget {
class RecoveryDomain extends StatefulWidget {
@override
State<RecoveryDomain> createState() => _RecoveryDomainState();
}
class _RecoveryDomainState extends State<RecoveryDomain> {
@override
Widget build(BuildContext context) {
var cubit = context.watch<AppConfigCubit>();
return BlocListener<AppConfigCubit, AppConfigState>(
listener: (context, state) {
if (cubit.state is AppConfigFinished) {
Navigator.of(context).pushReplacement(materialRoute(RootPage()));
}
},
child: SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
MediaQuery.of(context).padding.top -
MediaQuery.of(context).padding.bottom -
566,
),
child: Container(
alignment: Alignment.center,
child: BrandButton.text(
title: cubit.state is AppConfigFinished
? 'basis.close'.tr()
: 'basis.later'.tr(),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(RootPage()),
(predicate) => false,
);
},
),
),
),
],
),
return BrandHeroScreen(
children: [
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "recovering.domain_recover_placeholder".tr(),
),
),
SizedBox(height: 16),
BrandButton.rised(
onPressed: () {},
text: "more.continue".tr(),
),
],
heroTitle: "recovering.recovery_main_header".tr(),
heroSubtitle: "recovering.domain_recovery_description".tr(),
hasBackButton: true,
hasFlashButton: false,
heroIcon: Icons.link,
);
}
}
/*class RecoveryDomain extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(52),
child: BrandHeader(hasBackButton: true),
),
body: ListView(
padding: EdgeInsets.all(16),
children: [
Text(
"recovering.recovery_main_header".tr(),
style: Theme.of(context).textTheme.headlineMedium,
),
SizedBox(height: 18),
Text(
"recovering.domain_recovery_description".tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
SizedBox(height: 18),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "recovering.domain_recover_placeholder".tr(),
),
),
SizedBox(height: 18),
BrandButton.rised(
onPressed: () {},
text: "more.continue".tr(),
),
],
),
),
);
}
}
}*/