diff --git a/assets/images/logos/desec.svg b/assets/images/logos/desec.svg
new file mode 100644
index 00000000..cb54b268
--- /dev/null
+++ b/assets/images/logos/desec.svg
@@ -0,0 +1,89 @@
+
+
+
+
diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart
index b6ba018c..44b03f26 100644
--- a/lib/config/hive_config.dart
+++ b/lib/config/hive_config.dart
@@ -93,6 +93,9 @@ class BNames {
/// A String field of [serverInstallationBox] box.
static String serverProvider = 'serverProvider';
+ /// A String field of [serverInstallationBox] box.
+ static String dnsProvider = 'dnsProvider';
+
/// A String field of [serverLocation] box.
static String serverLocation = 'serverLocation';
diff --git a/lib/logic/api_maps/rest_maps/api_factory_creator.dart b/lib/logic/api_maps/rest_maps/api_factory_creator.dart
index 25518f3c..c1762429 100644
--- a/lib/logic/api_maps/rest_maps/api_factory_creator.dart
+++ b/lib/logic/api_maps/rest_maps/api_factory_creator.dart
@@ -1,5 +1,6 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_factory.dart';
+import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_factory.dart';
@@ -30,6 +31,8 @@ class ApiFactoryCreator {
final DnsProviderApiFactorySettings settings,
) {
switch (settings.provider) {
+ case DnsProvider.desec:
+ return DesecApiFactory();
case DnsProvider.cloudflare:
return CloudflareApiFactory();
case DnsProvider.unknown:
diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart
new file mode 100644
index 00000000..1906be55
--- /dev/null
+++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart
@@ -0,0 +1,287 @@
+import 'dart:io';
+
+import 'package:dio/dio.dart';
+import 'package:selfprivacy/config/get_it_config.dart';
+import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
+import 'package:selfprivacy/logic/models/hive/server_domain.dart';
+import 'package:selfprivacy/logic/models/json/dns_records.dart';
+
+class DesecApi extends DnsProviderApi {
+ DesecApi({
+ this.hasLogger = false,
+ this.isWithToken = true,
+ this.customToken,
+ });
+ @override
+ final bool hasLogger;
+ @override
+ final bool isWithToken;
+
+ final String? customToken;
+
+ @override
+ RegExp getApiTokenValidation() =>
+ RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
+
+ @override
+ BaseOptions get options {
+ final BaseOptions options = BaseOptions(baseUrl: rootAddress);
+ if (isWithToken) {
+ final String? token = getIt().cloudFlareKey;
+ assert(token != null);
+ options.headers = {'Authorization': 'Bearer $token'};
+ }
+
+ if (customToken != null) {
+ options.headers = {'Authorization': 'Bearer $customToken'};
+ }
+
+ if (validateStatus != null) {
+ options.validateStatus = validateStatus!;
+ }
+ return options;
+ }
+
+ @override
+ String rootAddress = 'https://desec.io/api/v1/domains/';
+
+ @override
+ Future> isApiTokenValid(final String token) async {
+ bool isValid = false;
+ Response? response;
+ String message = '';
+ final Dio client = await getClient();
+ try {
+ response = await client.get(
+ '',
+ options: Options(
+ followRedirects: false,
+ validateStatus: (final status) =>
+ status != null && (status >= 200 || status == 401),
+ headers: {'Authorization': 'Token $token'},
+ ),
+ );
+ } catch (e) {
+ print(e);
+ isValid = false;
+ message = e.toString();
+ } finally {
+ close(client);
+ }
+
+ if (response == null) {
+ return APIGenericResult(
+ data: isValid,
+ success: false,
+ message: message,
+ );
+ }
+
+ if (response.statusCode == HttpStatus.ok) {
+ isValid = true;
+ } else if (response.statusCode == HttpStatus.unauthorized) {
+ isValid = false;
+ } else {
+ throw Exception('code: ${response.statusCode}');
+ }
+
+ return APIGenericResult(
+ data: isValid,
+ success: true,
+ message: response.statusMessage,
+ );
+ }
+
+ @override
+ Future getZoneId(final String domain) async => domain;
+
+ @override
+ Future> removeSimilarRecords({
+ required final ServerDomain domain,
+ final String? ip4,
+ }) async {
+ final String domainName = domain.domainName;
+ final String url = '/$domainName/rrsets/';
+
+ final Dio client = await getClient();
+ try {
+ final Response response = await client.get(url);
+
+ final List records = response.data['result'] ?? [];
+ await client.put(url, data: records);
+ } catch (e) {
+ print(e);
+ return APIGenericResult(
+ success: false,
+ data: null,
+ message: e.toString(),
+ );
+ } finally {
+ close(client);
+ }
+
+ return APIGenericResult(success: true, data: null);
+ }
+
+ @override
+ Future> getDnsRecords({
+ required final ServerDomain domain,
+ }) async {
+ Response response;
+ final String domainName = domain.domainName;
+ final List allRecords = [];
+
+ final String url = '/$domainName/rrsets/';
+
+ final Dio client = await getClient();
+ try {
+ response = await client.get(url);
+ final List records = response.data['result'] ?? [];
+
+ for (final record in records) {
+ allRecords.add(
+ DnsRecord(
+ name: record['subname'],
+ type: record['type'],
+ content: record['records'],
+ ttl: record['ttl'],
+ ),
+ );
+ }
+ } catch (e) {
+ print(e);
+ } finally {
+ close(client);
+ }
+
+ return allRecords;
+ }
+
+ @override
+ Future> createMultipleDnsRecords({
+ required final ServerDomain domain,
+ final String? ip4,
+ }) async {
+ final String domainName = domain.domainName;
+ final List listDnsRecords = projectDnsRecords(domainName, ip4);
+ final List allCreateFutures = [];
+
+ final Dio client = await getClient();
+ try {
+ for (final DnsRecord record in listDnsRecords) {
+ allCreateFutures.add(
+ client.post(
+ '/$domainName/rrsets/',
+ data: record.toJson(),
+ ),
+ );
+ }
+ await Future.wait(allCreateFutures);
+ } on DioError catch (e) {
+ print(e.message);
+ rethrow;
+ } catch (e) {
+ print(e);
+ return APIGenericResult(
+ success: false,
+ data: null,
+ message: e.toString(),
+ );
+ } finally {
+ close(client);
+ }
+
+ return APIGenericResult(success: true, data: null);
+ }
+
+ List projectDnsRecords(
+ final String? domainName,
+ final String? ip4,
+ ) {
+ final DnsRecord domainA =
+ DnsRecord(type: 'A', name: domainName, content: ip4);
+
+ final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName);
+ final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4);
+ final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4);
+ final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4);
+ final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4);
+ final DnsRecord passwordA =
+ DnsRecord(type: 'A', name: 'password', content: ip4);
+ final DnsRecord socialA =
+ DnsRecord(type: 'A', name: 'social', content: ip4);
+ final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4);
+
+ final DnsRecord txt1 = DnsRecord(
+ type: 'TXT',
+ name: '_dmarc',
+ content: 'v=DMARC1; p=none',
+ ttl: 18000,
+ );
+
+ final DnsRecord txt2 = DnsRecord(
+ type: 'TXT',
+ name: domainName,
+ content: 'v=spf1 a mx ip4:$ip4 -all',
+ ttl: 18000,
+ );
+
+ return [
+ domainA,
+ apiA,
+ cloudA,
+ gitA,
+ meetA,
+ passwordA,
+ socialA,
+ mx,
+ txt1,
+ txt2,
+ vpn
+ ];
+ }
+
+ @override
+ Future setDnsRecord(
+ final DnsRecord record,
+ final ServerDomain domain,
+ ) async {
+ final String domainZoneId = domain.zoneId;
+ final String url = '$rootAddress/zones/$domainZoneId/dns_records';
+
+ final Dio client = await getClient();
+ try {
+ await client.post(
+ url,
+ data: record.toJson(),
+ );
+ } catch (e) {
+ print(e);
+ } finally {
+ close(client);
+ }
+ }
+
+ @override
+ Future> domainList() async {
+ final String url = '$rootAddress/zones';
+ List domains = [];
+
+ final Dio client = await getClient();
+ try {
+ final Response response = await client.get(
+ url,
+ queryParameters: {'per_page': 50},
+ );
+ domains = response.data['result']
+ .map((final el) => el['name'] as String)
+ .toList();
+ } catch (e) {
+ print(e);
+ } finally {
+ close(client);
+ }
+
+ return domains;
+ }
+}
diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart
new file mode 100644
index 00000000..6c10259b
--- /dev/null
+++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart
@@ -0,0 +1,16 @@
+import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec.dart';
+import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
+import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.dart';
+import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
+
+class DesecApiFactory extends DnsProviderApiFactory {
+ @override
+ DnsProviderApi getDnsProvider({
+ final DnsProviderApiSettings settings = const DnsProviderApiSettings(),
+ }) =>
+ DesecApi(
+ hasLogger: settings.hasLogger,
+ isWithToken: settings.isWithToken,
+ customToken: settings.customToken,
+ );
+}
diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart
index 5638b765..06df6d5a 100644
--- a/lib/logic/cubit/server_installation/server_installation_cubit.dart
+++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart
@@ -66,6 +66,15 @@ class ServerInstallationCubit extends Cubit {
);
}
+ void setDnsProviderType(final DnsProvider providerType) async {
+ await repository.saveDnsProviderType(providerType);
+ ApiController.initDnsProviderApiFactory(
+ DnsProviderApiFactorySettings(
+ provider: providerType,
+ ),
+ );
+ }
+
ProviderApiTokenValidation serverProviderApiTokenValidation() =>
ApiController.currentServerProviderApiFactory!
.getServerProvider()
diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart
index 5d45e7b9..851b2be5 100644
--- a/lib/logic/cubit/server_installation/server_installation_repository.dart
+++ b/lib/logic/cubit/server_installation/server_installation_repository.dart
@@ -706,6 +706,10 @@ class ServerInstallationRepository {
getIt().init();
}
+ Future saveDnsProviderType(final DnsProvider type) async {
+ await getIt().storeDnsProviderType(type);
+ }
+
Future saveBackblazeKey(
final BackblazeCredential backblazeCredential,
) async {
diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart
index 434c9b32..11d73a85 100644
--- a/lib/logic/get_it/api_config.dart
+++ b/lib/logic/get_it/api_config.dart
@@ -14,6 +14,8 @@ class ApiConfigModel {
String? get serverType => _serverType;
String? get cloudFlareKey => _cloudFlareKey;
ServerProvider? get serverProvider => _serverProvider;
+ DnsProvider? get dnsProvider => _dnsProvider;
+
BackblazeCredential? get backblazeCredential => _backblazeCredential;
ServerDomain? get serverDomain => _serverDomain;
BackblazeBucket? get backblazeBucket => _backblazeBucket;
@@ -23,6 +25,7 @@ class ApiConfigModel {
String? _cloudFlareKey;
String? _serverType;
ServerProvider? _serverProvider;
+ DnsProvider? _dnsProvider;
ServerHostingDetails? _serverDetails;
BackblazeCredential? _backblazeCredential;
ServerDomain? _serverDomain;
@@ -33,6 +36,11 @@ class ApiConfigModel {
_serverProvider = value;
}
+ Future storeDnsProviderType(final DnsProvider value) async {
+ await _box.put(BNames.dnsProvider, value);
+ _dnsProvider = value;
+ }
+
Future storeServerProviderKey(final String value) async {
await _box.put(BNames.hetznerKey, value);
_serverProviderKey = value;
@@ -75,6 +83,7 @@ class ApiConfigModel {
void clear() {
_serverProviderKey = null;
+ _dnsProvider = null;
_serverLocation = null;
_cloudFlareKey = null;
_backblazeCredential = null;
@@ -95,5 +104,6 @@ class ApiConfigModel {
_backblazeBucket = _box.get(BNames.backblazeBucket);
_serverType = _box.get(BNames.serverTypeIdentifier);
_serverProvider = _box.get(BNames.serverProvider);
+ _dnsProvider = _box.get(BNames.dnsProvider);
}
}
diff --git a/lib/logic/models/hive/server_domain.dart b/lib/logic/models/hive/server_domain.dart
index 9b5d32c1..913fcd42 100644
--- a/lib/logic/models/hive/server_domain.dart
+++ b/lib/logic/models/hive/server_domain.dart
@@ -29,4 +29,6 @@ enum DnsProvider {
unknown,
@HiveField(1)
cloudflare,
+ @HiveField(2)
+ desec
}
diff --git a/lib/ui/pages/setup/initializing/dns_provider_picker.dart b/lib/ui/pages/setup/initializing/dns_provider_picker.dart
new file mode 100644
index 00000000..cb0d2111
--- /dev/null
+++ b/lib/ui/pages/setup/initializing/dns_provider_picker.dart
@@ -0,0 +1,307 @@
+import 'package:cubit_form/cubit_form.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:selfprivacy/config/brand_theme.dart';
+import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
+import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
+import 'package:selfprivacy/logic/models/hive/server_domain.dart';
+import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
+import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
+import 'package:selfprivacy/ui/components/buttons/outlined_button.dart';
+import 'package:selfprivacy/ui/components/cards/outlined_card.dart';
+import 'package:selfprivacy/utils/launch_url.dart';
+
+class DnsProviderPicker extends StatefulWidget {
+ const DnsProviderPicker({
+ required this.formCubit,
+ required this.serverInstallationCubit,
+ super.key,
+ });
+
+ final DnsProviderFormCubit formCubit;
+ final ServerInstallationCubit serverInstallationCubit;
+
+ @override
+ State createState() => _DnsProviderPickerState();
+}
+
+class _DnsProviderPickerState extends State {
+ DnsProvider selectedProvider = DnsProvider.unknown;
+
+ void setProvider(final DnsProvider provider) {
+ setState(() {
+ selectedProvider = provider;
+ });
+ }
+
+ @override
+ Widget build(final BuildContext context) {
+ switch (selectedProvider) {
+ case DnsProvider.unknown:
+ return ProviderSelectionPage(
+ serverInstallationCubit: widget.serverInstallationCubit,
+ callback: setProvider,
+ );
+
+ case DnsProvider.cloudflare:
+ return ProviderInputDataPage(
+ providerCubit: widget.formCubit,
+ providerInfo: ProviderPageInfo(
+ providerType: DnsProvider.cloudflare,
+ pathToHow: 'how_cloudflare',
+ image: Image.asset(
+ 'assets/images/logos/cloudflare.png',
+ width: 150,
+ ),
+ ),
+ );
+
+ case DnsProvider.desec:
+ return ProviderInputDataPage(
+ providerCubit: widget.formCubit,
+ providerInfo: ProviderPageInfo(
+ providerType: DnsProvider.desec,
+ pathToHow: 'how_digital_ocean_dns',
+ image: Image.asset(
+ 'assets/images/logos/digital_ocean.png',
+ width: 150,
+ ),
+ ),
+ );
+ }
+ }
+}
+
+class ProviderPageInfo {
+ const ProviderPageInfo({
+ required this.providerType,
+ required this.pathToHow,
+ required this.image,
+ });
+
+ final String pathToHow;
+ final Image image;
+ final DnsProvider providerType;
+}
+
+class ProviderInputDataPage extends StatelessWidget {
+ const ProviderInputDataPage({
+ required this.providerInfo,
+ required this.providerCubit,
+ super.key,
+ });
+
+ final ProviderPageInfo providerInfo;
+ final DnsProviderFormCubit providerCubit;
+
+ @override
+ Widget build(final BuildContext context) => Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'initializing.connect_to_dns'.tr(),
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'initializing.connect_to_server_provider_text'.tr(),
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ const SizedBox(height: 32),
+ CubitFormTextField(
+ formFieldCubit: providerCubit.apiKey,
+ textAlign: TextAlign.center,
+ scrollPadding: const EdgeInsets.only(bottom: 70),
+ decoration: const InputDecoration(
+ hintText: 'Provider API Token',
+ ),
+ ),
+ const SizedBox(height: 32),
+ BrandButton.rised(
+ text: 'basis.connect'.tr(),
+ onPressed: () => providerCubit.trySubmit(),
+ ),
+ const SizedBox(height: 10),
+ BrandOutlinedButton(
+ child: Text('initializing.how'.tr()),
+ onPressed: () => showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ backgroundColor: Colors.transparent,
+ builder: (final BuildContext context) => Padding(
+ padding: paddingH15V0,
+ child: ListView(
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ children: [
+ BrandMarkdown(
+ fileName: providerInfo.pathToHow,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+}
+
+class ProviderSelectionPage extends StatelessWidget {
+ const ProviderSelectionPage({
+ required this.callback,
+ required this.serverInstallationCubit,
+ super.key,
+ });
+
+ final Function callback;
+ final ServerInstallationCubit serverInstallationCubit;
+
+ @override
+ Widget build(final BuildContext context) => SizedBox(
+ width: double.infinity,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'initializing.select_dns'.tr(),
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ const SizedBox(height: 10),
+ Text(
+ 'initializing.select_provider'.tr(),
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ const SizedBox(height: 10),
+ OutlinedCard(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Container(
+ width: 40,
+ height: 40,
+ padding: const EdgeInsets.all(10),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(40),
+ color: const Color.fromARGB(255, 241, 215, 166),
+ ),
+ child: SvgPicture.asset(
+ 'assets/images/logos/cloudflare.svg',
+ ),
+ ),
+ const SizedBox(width: 16),
+ Text(
+ 'Hetzner Cloud',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'initializing.select_provider_price_title'.tr(),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ Text(
+ 'initializing.select_provider_price_free'.tr(),
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'initializing.select_provider_payment_title'.tr(),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ Text(
+ 'initializing.select_provider_payment_text_cloudflare'
+ .tr(),
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ const SizedBox(height: 16),
+ BrandButton.rised(
+ text: 'basis.select'.tr(),
+ onPressed: () {
+ serverInstallationCubit
+ .setDnsProviderType(DnsProvider.cloudflare);
+ callback(DnsProvider.cloudflare);
+ },
+ ),
+ // Outlined button that will open website
+ BrandOutlinedButton(
+ onPressed: () =>
+ launchURL('https://dash.cloudflare.com/'),
+ title: 'initializing.select_provider_site_button'.tr(),
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 16),
+ OutlinedCard(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Container(
+ width: 40,
+ height: 40,
+ padding: const EdgeInsets.all(10),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(40),
+ color: const Color.fromARGB(255, 245, 229, 82),
+ ),
+ child: SvgPicture.asset(
+ 'assets/images/logos/desec.svg',
+ ),
+ ),
+ const SizedBox(width: 16),
+ Text(
+ 'deSEC',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'initializing.select_provider_price_title'.tr(),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ Text(
+ 'initializing.select_provider_price_free'.tr(),
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'initializing.select_provider_payment_title'.tr(),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ Text(
+ 'initializing.select_provider_payment_text_do'.tr(),
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ const SizedBox(height: 16),
+ BrandButton.rised(
+ text: 'basis.select'.tr(),
+ onPressed: () {
+ serverInstallationCubit
+ .setDnsProviderType(DnsProvider.desec);
+ callback(DnsProvider.desec);
+ },
+ ),
+ // Outlined button that will open website
+ BrandOutlinedButton(
+ onPressed: () => launchURL('https://desec.io/'),
+ title: 'initializing.select_provider_site_button'.tr(),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+}
diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart
index 199203c3..749fbdc9 100644
--- a/lib/ui/pages/setup/initializing/initializing.dart
+++ b/lib/ui/pages/setup/initializing/initializing.dart
@@ -17,6 +17,7 @@ import 'package:selfprivacy/ui/components/drawers/progress_drawer.dart';
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
import 'package:selfprivacy/ui/components/drawers/support_drawer.dart';
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
+import 'package:selfprivacy/ui/pages/setup/initializing/dns_provider_picker.dart';
import 'package:selfprivacy/ui/pages/setup/initializing/server_provider_picker.dart';
import 'package:selfprivacy/ui/pages/setup/initializing/server_type_picker.dart';
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
@@ -39,7 +40,7 @@ class InitializingPage extends StatelessWidget {
actualInitializingPage = [
() => _stepServerProviderToken(cubit),
() => _stepServerType(cubit),
- () => _stepCloudflare(cubit),
+ () => _stepDnsProviderToken(cubit),
() => _stepBackblaze(cubit),
() => _stepDomain(cubit),
() => _stepUser(cubit),
@@ -238,56 +239,19 @@ class InitializingPage extends StatelessWidget {
),
);
- Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) =>
+ Widget _stepDnsProviderToken(
+ final ServerInstallationCubit initializingCubit,
+ ) =>
BlocProvider(
create: (final context) => DnsProviderFormCubit(initializingCubit),
child: Builder(
- builder: (final context) => ResponsiveLayoutWithInfobox(
- topChild: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- '${'initializing.connect_to_server_provider'.tr()}Cloudflare',
- style: Theme.of(context).textTheme.headlineSmall,
- ),
- const SizedBox(height: 16),
- Text(
- 'initializing.manage_domain_dns'.tr(),
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- ],
- ),
- primaryColumn: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- CubitFormTextField(
- formFieldCubit: context.read().apiKey,
- textAlign: TextAlign.center,
- scrollPadding: const EdgeInsets.only(bottom: 70),
- decoration: InputDecoration(
- hintText: 'initializing.cloudflare_api_token'.tr(),
- ),
- ),
- const SizedBox(height: 32),
- BrandButton.filled(
- onPressed: () =>
- context.read().trySubmit(),
- text: 'basis.connect'.tr(),
- ),
- const SizedBox(height: 10),
- BrandOutlinedButton(
- onPressed: () {
- context.read().showArticle(
- article: 'how_cloudflare',
- context: context,
- );
- Scaffold.of(context).openEndDrawer();
- },
- title: 'initializing.how'.tr(),
- ),
- ],
- ),
- ),
+ builder: (final context) {
+ final providerCubit = context.watch();
+ return DnsProviderPicker(
+ formCubit: providerCubit,
+ serverInstallationCubit: initializingCubit,
+ );
+ },
),
);