Merge pull request 'feat: Implement dialogue to choose an domain from several during installation' (#330) from domain-selection into master
continuous-integration/drone/push Build is passing Details

Reviewed-on: #330
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
pull/331/head
Inex Code 2023-09-08 15:42:38 +03:00
commit ef91ffaf2b
15 changed files with 188 additions and 134 deletions

View File

@ -300,7 +300,6 @@
"manage_domain_dns": "Domeninizin DNS-ni idarə etmək üçün",
"use_this_domain": "Biz bu domendən istifadə edirik?",
"use_this_domain_text": "Göstərdiyiniz token bu domen üzərində nəzarəti təmin edir",
"cloudflare_api_token": "CloudFlare API Açarı",
"connect_backblaze_storage": "Backblaze bulud yaddaşınızı birləşdirin",
"no_connected_domains": "Hazırda heç bir bağlı domen yoxdur",
"loading_domain_list": "Domenlərin siyahısı yüklənir",
@ -500,4 +499,4 @@
"reset_onboarding_description": "Enerji ekranını yenidən göstərmək üçün güc açarının sıfırlanması",
"cubit_statuses": "Yükləmə kubitlərinin cari vəziyyəti"
}
}
}

View File

@ -32,7 +32,6 @@
"manage_domain_dns": "Для кіравання DNS вашага дамена",
"use_this_domain": "Ужываем гэты дамен?",
"use_this_domain_text": "Указаны вамі токен дае кантроль над гэтым даменам",
"cloudflare_api_token": "API ключ DNS правайдэра",
"connect_backblaze_storage": "Падлучыце хмарнае сховішча Backblaze",
"no_connected_domains": "У дадзены момант падлучаных даменаў няма",
"loading_domain_list": "Загружаем спіс даменаў",
@ -510,4 +509,4 @@
"support": {
"title": "Падтрымка SelfPrivacy"
}
}
}

View File

@ -144,7 +144,6 @@
"found_more_domains": "Nalezeno více než jedna doména. V zájmu vlastní bezpečnosti vás prosíme o odstranění nepotřebných domén",
"server_created": "Vytvořený server. Probíhá kontrola DNS a spouštění serveru…",
"choose_server_type_notice": "Důležité je zaměřit se na procesor a paměť RAM. Data vašich služeb budou uložena na připojeném svazku, který lze snadno rozšířit a za který se platí zvlášť.",
"cloudflare_api_token": "Klíč API poskytovatele DNS",
"connect_backblaze_storage": "Připojení úložiště Backblaze",
"save_domain": "Uložit doménu",
"final": "Závěrečný krok",
@ -510,4 +509,4 @@
"ignore_tls": "Nekontrolujte certifikáty TLS",
"ignore_tls_description": "Aplikace nebude při připojování k serveru ověřovat certifikáty TLS."
}
}
}

View File

@ -278,7 +278,6 @@
"manage_domain_dns": "Zum Verwalten des DNS Ihrer Domain",
"use_this_domain": "Diese Domäne verwenden?",
"use_this_domain_text": "Das von Ihnen bereitgestellte Token gewährt Zugriff auf die folgende Domäne",
"cloudflare_api_token": "API-Schlüssel des DNS-Anbieters",
"connect_backblaze_storage": "Backblaze-Speicher verbinden",
"no_connected_domains": "Derzeit keine verbundenen Domains",
"loading_domain_list": "Domänenliste wird geladen",
@ -510,4 +509,4 @@
"ignore_tls": "Überprüfen Sie keine TLS-Zertifikate",
"ignore_tls_description": "Die Anwendung validiert TLS-Zertifikate nicht, wenn sie eine Verbindung zum Server herstellt."
}
}
}

View File

@ -370,7 +370,8 @@
"manage_domain_dns": "To manage your domain's DNS",
"use_this_domain": "Use this domain?",
"use_this_domain_text": "The token you provided gives access to the following domain",
"cloudflare_api_token": "DNS Provider API Token",
"multiple_domains_found": "Multiple domains found",
"multiple_domains_found_text": "The token you provided gives access to the following domains. Please select the one you want to use. For the security of your other domains, you should restrict this token's access to only the domain you want to use with SelfPrivacy.",
"connect_backblaze_storage": "Connect Backblaze storage",
"no_connected_domains": "No connected domains at the moment",
"loading_domain_list": "Loading domain list",

View File

@ -321,7 +321,6 @@
"choose_server_type_payment_per_month": "{} miesięcznie",
"no_server_types_found": "Nie znaleziono dostępnych typów serwerów! Proszę upewnić się, że masz dostęp do dostawcy serwera...",
"use_this_domain": "Kto używa ten domen?",
"cloudflare_api_token": "Klucz API dostawcy DNS",
"connect_backblaze_storage": "Dodajcie Blackblaze",
"no_connected_domains": "Niema podłączonych domenów",
"what": "Co to znaczy?",
@ -509,4 +508,4 @@
"cubit_statuses": "Aktualny stan qubitów ładujących",
"ignore_tls": "Używane podczas konfigurowania nowego serwera."
}
}
}

View File

@ -350,7 +350,6 @@
"manage_domain_dns": "Для управления DNS вашего домена",
"use_this_domain": "Используем этот домен?",
"use_this_domain_text": "Указанный вами токен даёт контроль над этим доменом",
"cloudflare_api_token": "API ключ DNS провайдера",
"connect_backblaze_storage": "Подключите облачное хранилище Backblaze",
"no_connected_domains": "На данный момент подлюченных доменов нет",
"loading_domain_list": "Загружаем список доменов",
@ -540,4 +539,4 @@
"ignore_tls_description": "Приложение не будет проверять сертификаты TLS при подключении к серверу.",
"ignore_tls": "Не проверять сертификаты TLS"
}
}
}

View File

@ -280,7 +280,6 @@
"enter_username_and_password": "Zadajte používateľské meno a zložité heslo",
"finish": "Všetko je inicializované",
"use_this_domain_text": "Token, ktorý ste poskytli, poskytuje prístup k nasledujúcej doméne",
"cloudflare_api_token": "CloudFlare API Token",
"connect_backblaze_storage": "Pripojte svoje cloudové úložisko Backblaze",
"no_connected_domains": "Momentálne nie sú pripojené žiadne domény",
"loading_domain_list": "Načítava sa zoznam domén",
@ -500,4 +499,4 @@
"reset_onboarding_description": "Resetovanie vypínača na opätovné zobrazenie obrazovky zapnutia",
"cubit_statuses": "Aktuálny stav načítavania qubitov"
}
}
}

View File

@ -134,7 +134,6 @@
"select_dns": "Тепер давайте оберемо DNS-провайдера",
"manage_domain_dns": "Для управління DNS домену",
"use_this_domain": "Скористатися цим доменом?",
"cloudflare_api_token": "CloudFlare API токен",
"connect_backblaze_storage": "Підключити Backblaze сховище",
"no_connected_domains": "Наразі немає пов'язаних доменів",
"save_domain": "Зберегти домен",
@ -469,4 +468,4 @@
"root_name": "Не може бути 'root'",
"length_not_equal": "Довжина [], має бути {}"
}
}
}

View File

@ -18,13 +18,11 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
} else if (result.data.length == 1) {
emit(Loaded(result.data.first));
} else {
emit(MoreThenOne());
emit(MoreThenOne(result.data));
}
}
Future<void> saveDomain() async {
assert(state is Loaded, 'wrong state');
final String domainName = (state as Loaded).domain;
Future<void> saveDomain(final String domainName) async {
emit(Loading(LoadingTypes.saving));
final dnsProvider = ProvidersController.currentDnsProvider!;
@ -45,7 +43,10 @@ class Initial extends DomainSetupState {}
class Empty extends DomainSetupState {}
class MoreThenOne extends DomainSetupState {}
class MoreThenOne extends DomainSetupState {
MoreThenOne(this.domains);
final List<String> domains;
}
class Loading extends DomainSetupState {
Loading(this.type);

View File

@ -209,7 +209,7 @@ class ServerInstallationRepository {
return false;
}
return domain == domainResult.data[0];
return domainResult.data.contains(domain);
}
Future<Map<String, bool>> isDnsAddressesMatch(

View File

@ -16,8 +16,8 @@ class SupportDrawer extends StatelessWidget {
return Drawer(
width: 440,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(
minimum: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(

View File

@ -2,11 +2,10 @@ 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/cubit/support_system/support_system_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';
@ -125,22 +124,10 @@ class ProviderInputDataPage extends StatelessWidget {
const SizedBox(height: 10),
BrandOutlinedButton(
child: Text('initializing.how'.tr()),
onPressed: () => showModalBottomSheet<void>(
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,
),
],
onPressed: () => context.read<SupportSystemCubit>().showArticle(
article: providerInfo.pathToHow,
context: context,
),
),
),
),
],
);

View File

@ -0,0 +1,163 @@
import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/cards/outlined_card.dart';
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
class DomainPicker extends StatefulWidget {
const DomainPicker({
super.key,
});
@override
State<DomainPicker> createState() => _DomainPickerState();
}
class _DomainPickerState extends State<DomainPicker> {
String? selectedDomain;
@override
Widget build(final BuildContext context) {
final DomainSetupState state = context.watch<DomainSetupCubit>().state;
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
(state is MoreThenOne)
? 'initializing.multiple_domains_found'.tr()
: 'initializing.use_this_domain'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
(state is MoreThenOne)
? 'initializing.multiple_domains_found_text'.tr()
: 'initializing.use_this_domain_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (state is Empty)
Text(
'initializing.no_connected_domains'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (state is Loading)
Text(
state.type == LoadingTypes.loadingDomain
? 'initializing.loading_domain_list'.tr()
: 'basis.saving'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (state is MoreThenOne)
...state.domains.map(
(final domain) => Column(
children: [
SizedBox(
width: double.infinity,
child: OutlinedCard(
borderColor: domain == selectedDomain
? Theme.of(context).colorScheme.primary
: null,
borderWidth: domain == selectedDomain ? 3 : 1,
child: InkResponse(
highlightShape: BoxShape.rectangle,
onTap: () => setState(() {
selectedDomain = domain;
}),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Radio<String>(
value: domain,
groupValue: selectedDomain,
onChanged: (final String? value) {
setState(() {
selectedDomain = value;
});
},
),
Text(
domain,
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
),
),
),
const SizedBox(height: 8),
// Button to select and save domain
],
),
),
if (state is MoreThenOne)
BrandButton.filled(
onPressed: (selectedDomain != null &&
state.domains.contains(selectedDomain))
? () => context
.read<DomainSetupCubit>()
.saveDomain(selectedDomain!)
: null,
child: Text('initializing.use_this_domain'.tr()),
),
if (state is Loaded) ...[
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
state.domain,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
textAlign: TextAlign.center,
),
],
),
],
if (state is Empty) ...[
const SizedBox(height: 30),
BrandButton.filled(
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.refresh,
color: Colors.white,
),
const SizedBox(width: 10),
Text(
'domain.update_list'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
],
if (state is Loaded) ...[
const SizedBox(height: 32),
BrandButton.filled(
onPressed: () =>
context.read<DomainSetupCubit>().saveDomain(state.domain),
text: 'initializing.save_domain'.tr(),
),
],
],
),
);
}
}

View File

@ -18,6 +18,7 @@ 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/domain_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';
@ -319,98 +320,7 @@ class InitializingPage extends StatelessWidget {
Widget _stepDomain(final ServerInstallationCubit initializingCubit) =>
BlocProvider(
create: (final context) => DomainSetupCubit(initializingCubit)..load(),
child: Builder(
builder: (final context) {
final DomainSetupState state =
context.watch<DomainSetupCubit>().state;
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.use_this_domain'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.use_this_domain_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (state is Empty)
Text(
'initializing.no_connected_domains'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (state is Loading)
Text(
state.type == LoadingTypes.loadingDomain
? 'initializing.loading_domain_list'.tr()
: 'basis.saving'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (state is MoreThenOne)
Text(
'initializing.found_more_domains'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (state is Loaded) ...[
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
state.domain,
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(
color:
Theme.of(context).colorScheme.onBackground,
),
textAlign: TextAlign.center,
),
],
),
],
if (state is Empty) ...[
const SizedBox(height: 30),
BrandButton.filled(
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.refresh,
color: Colors.white,
),
const SizedBox(width: 10),
Text(
'domain.update_list'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
],
if (state is Loaded) ...[
const SizedBox(height: 32),
BrandButton.filled(
onPressed: () =>
context.read<DomainSetupCubit>().saveDomain(),
text: 'initializing.save_domain'.tr(),
),
],
],
),
);
},
),
child: DomainPicker(),
);
Widget _stepUser(final ServerInstallationCubit initializingCubit) =>