selfprivacy.org.app/lib/ui/pages/setup/initializing.dart

561 lines
20 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.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';
import 'package:selfprivacy/ui/components/brand_md/brand_md.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/root_route.dart';
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class InitializingPage extends StatelessWidget {
const InitializingPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var cubit = context.watch<ServerInstallationCubit>();
if (cubit.state is ServerInstallationRecovery) {
return const RecoveryRouting();
} else {
var actualInitializingPage = [
() => _stepHetzner(cubit),
() => _stepCloudflare(cubit),
() => _stepBackblaze(cubit),
() => _stepDomain(cubit),
() => _stepUser(cubit),
() => _stepServer(cubit),
() => _stepCheck(cubit),
() => _stepCheck(cubit),
() => _stepCheck(cubit),
() => Center(child: Text('initializing.finish'.tr()))
][cubit.state.progress.index]();
return BlocListener<ServerInstallationCubit, ServerInstallationState>(
listener: (context, state) {
if (cubit.state is ServerInstallationFinished) {
Navigator.of(context)
.pushReplacement(materialRoute(const RootPage()));
}
},
child: SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: paddingH15V0.copyWith(top: 10, bottom: 10),
child: cubit.state.isFullyInitilized
? const SizedBox(
height: 80,
)
: ProgressBar(
steps: const [
'Hetzner',
'CloudFlare',
'Backblaze',
'Domain',
'User',
'Server',
'✅ Check',
],
activeIndex: cubit.state.porgressBar,
),
),
_addCard(
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: actualInitializingPage,
),
),
ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
MediaQuery.of(context).padding.top -
MediaQuery.of(context).padding.bottom -
566,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
child: BrandButton.text(
title: cubit.state is ServerInstallationFinished
? 'basis.close'.tr()
: 'basis.later'.tr(),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(const RootPage()),
(predicate) => false,
);
},
),
),
(cubit.state is ServerInstallationFinished)
? Container()
: Container(
alignment: Alignment.center,
child: BrandButton.text(
title: 'basis.connect_to_existing'.tr(),
onPressed: () {
Navigator.of(context).push(materialRoute(
const RecoveryRouting()));
},
),
)
],
)),
],
),
),
),
),
);
}
}
Widget _stepHetzner(ServerInstallationCubit serverInstallationCubit) {
return BlocProvider(
create: (context) => HetznerFormCubit(serverInstallationCubit),
child: Builder(builder: (context) {
var formCubitState = context.watch<HetznerFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/logos/hetzner.png',
width: 150,
),
const SizedBox(height: 10),
BrandText.h2('initializing.1'.tr()),
const SizedBox(height: 10),
BrandText.body2('initializing.2'.tr()),
const Spacer(),
CubitFormTextField(
formFieldCubit: context.read<HetznerFormCubit>().apiKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'Hetzner API Token',
),
),
const Spacer(),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<HetznerFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
const SizedBox(height: 10),
BrandButton.text(
onPressed: () =>
_showModal(context, const _HowTo(fileName: 'how_hetzner')),
title: 'initializing.how'.tr(),
),
],
);
}),
);
}
void _showModal(BuildContext context, Widget widget) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return widget;
},
);
}
Widget _stepCloudflare(ServerInstallationCubit initializingCubit) {
return BlocProvider(
create: (context) => CloudFlareFormCubit(initializingCubit),
child: Builder(builder: (context) {
var formCubitState = context.watch<CloudFlareFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/logos/cloudflare.png',
width: 150,
),
const SizedBox(height: 10),
BrandText.h2('initializing.3'.tr()),
const SizedBox(height: 10),
BrandText.body2('initializing.4'.tr()),
const Spacer(),
CubitFormTextField(
formFieldCubit: context.read<CloudFlareFormCubit>().apiKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'initializing.5'.tr(),
),
),
const Spacer(),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<CloudFlareFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
const SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(
context,
const _HowTo(
fileName: 'how_cloudflare',
)),
title: 'initializing.how'.tr(),
),
],
);
}),
);
}
Widget _stepBackblaze(ServerInstallationCubit initializingCubit) {
return BlocProvider(
create: (context) => BackblazeFormCubit(initializingCubit),
child: Builder(builder: (context) {
var formCubitState = context.watch<BackblazeFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/logos/backblaze.png',
height: 50,
),
const SizedBox(height: 10),
BrandText.h2('initializing.6'.tr()),
const SizedBox(height: 10),
const Spacer(),
CubitFormTextField(
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'KeyID',
),
),
const Spacer(),
CubitFormTextField(
formFieldCubit: context.read<BackblazeFormCubit>().applicationKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'Master Application Key',
),
),
const Spacer(),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<BackblazeFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
const SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(
context,
const _HowTo(
fileName: 'how_backblaze',
)),
title: 'initializing.how'.tr(),
),
],
);
}),
);
}
Widget _stepDomain(ServerInstallationCubit initializingCubit) {
return BlocProvider(
create: (context) => DomainSetupCubit(initializingCubit)..load(),
child: Builder(builder: (context) {
DomainSetupState state = context.watch<DomainSetupCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/logos/cloudflare.png',
width: 150,
),
const SizedBox(height: 30),
BrandText.h2('basis.domain'.tr()),
const SizedBox(height: 10),
if (state is Empty) BrandText.body2('initializing.7'.tr()),
if (state is Loading)
BrandText.body2(
state.type == LoadingTypes.loadingDomain
? 'initializing.8'.tr()
: 'basis.saving'.tr(),
),
if (state is MoreThenOne)
BrandText.body2(
'initializing.9'.tr(),
),
if (state is Loaded) ...[
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: BrandText.h3(
state.domain,
textAlign: TextAlign.center,
),
),
SizedBox(
width: 50,
child: BrandButton.rised(
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: const [
Icon(
Icons.refresh,
color: Colors.white,
),
],
),
),
),
],
)
],
if (state is Empty) ...[
const SizedBox(height: 30),
BrandButton.rised(
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.refresh,
color: Colors.white,
),
const SizedBox(width: 10),
BrandText.buttonTitleText('Обновить cписок'),
],
),
),
],
if (state is Loaded) ...[
const SizedBox(height: 30),
BrandButton.rised(
onPressed: () => context.read<DomainSetupCubit>().saveDomain(),
text: 'initializing.10'.tr(),
),
],
const SizedBox(
height: 10,
width: double.infinity,
),
],
);
}),
);
}
Widget _stepUser(ServerInstallationCubit initializingCubit) {
return BlocProvider(
create: (context) =>
RootUserFormCubit(initializingCubit, FieldCubitFactory(context)),
child: Builder(builder: (context) {
var formCubitState = context.watch<RootUserFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BrandText.h2('initializing.22'.tr()),
const SizedBox(height: 10),
BrandText.body2('initializing.23'.tr()),
const Spacer(),
CubitFormTextField(
formFieldCubit: context.read<RootUserFormCubit>().userName,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'basis.nickname'.tr(),
),
),
const SizedBox(height: 10),
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
bloc: context.read<RootUserFormCubit>().isVisible,
builder: (context, state) {
var isVisible = state.value;
return CubitFormTextField(
obscureText: !isVisible,
formFieldCubit: context.read<RootUserFormCubit>().password,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'basis.password'.tr(),
suffixIcon: IconButton(
icon: Icon(
isVisible ? Icons.visibility : Icons.visibility_off,
),
onPressed: () => context
.read<RootUserFormCubit>()
.isVisible
.setValue(!isVisible),
),
suffixIconConstraints: const BoxConstraints(minWidth: 60),
prefixIconConstraints: const BoxConstraints(maxWidth: 85),
prefixIcon: Container(),
),
);
},
),
const Spacer(),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<RootUserFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
],
);
}),
);
}
Widget _stepServer(ServerInstallationCubit appConfigCubit) {
var isLoading =
(appConfigCubit.state as ServerInstallationNotFinished).isLoading;
return Builder(builder: (context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Spacer(flex: 2),
BrandText.h2('initializing.final'.tr()),
const SizedBox(height: 10),
BrandText.body2('initializing.11'.tr()),
const Spacer(),
BrandButton.rised(
onPressed: isLoading
? null
: () => appConfigCubit.createServerAndSetDnsRecords(),
text: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(),
),
],
);
});
}
Widget _stepCheck(ServerInstallationCubit appConfigCubit) {
assert(
appConfigCubit.state is ServerInstallationNotFinished, 'wrong state');
var state = appConfigCubit.state as TimerState;
late int doneCount;
late String? text;
if (state.isServerResetedSecondTime) {
text = 'initializing.13'.tr();
doneCount = 3;
} else if (state.isServerResetedFirstTime) {
text = 'initializing.21'.tr();
doneCount = 2;
} else if (state.isServerStarted) {
text = 'initializing.14'.tr();
doneCount = 1;
} else if (state.isServerCreated) {
text = 'initializing.15'.tr();
doneCount = 0;
}
return Builder(builder: (context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 15),
BrandText.h4(
'initializing.checks'.tr(args: [doneCount.toString(), '4']),
),
const Spacer(flex: 2),
const SizedBox(height: 10),
BrandText.body2(text),
const SizedBox(height: 10),
if (doneCount == 0 && state.dnsMatches != null)
Column(
children: state.dnsMatches!.entries.map((entry) {
var domain = entry.key;
var isCorrect = entry.value;
return Row(
children: [
if (isCorrect) const Icon(Icons.check, color: Colors.green),
if (!isCorrect)
const Icon(Icons.schedule, color: Colors.amber),
const SizedBox(width: 10),
Text(domain),
],
);
}).toList(),
),
const SizedBox(height: 10),
if (!state.isLoading)
Row(
children: [
BrandText.body2('initializing.16'.tr()),
BrandTimer(
startDateTime: state.timerStart!,
duration: state.duration!,
)
],
),
if (state.isLoading) BrandText.body2('initializing.17'.tr()),
],
);
});
}
Widget _addCard(Widget child) {
return Container(
height: 450,
padding: paddingH15V0,
child: BrandCards.big(child: child),
);
}
}
class _HowTo extends StatelessWidget {
const _HowTo({
Key? key,
required this.fileName,
}) : super(key: key);
final String fileName;
@override
Widget build(BuildContext context) {
return BrandBottomSheet(
isExpended: true,
child: Padding(
padding: paddingH15V0,
child: BrandMarkdown(
fileName: fileName,
),
),
);
}
}