Hot bug fixing of recovery flow
Co-authored-by: Inex Code <inex.code@selfprivacy.org>
This commit is contained in:
parent
a096e7e732
commit
edce25ec55
|
@ -23,7 +23,7 @@ linter:
|
||||||
# producing the lint.
|
# producing the lint.
|
||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|
|
@ -313,11 +313,25 @@
|
||||||
"choose_server_description": "We couldn't figure out which server your are trying to connect to.",
|
"choose_server_description": "We couldn't figure out which server your are trying to connect to.",
|
||||||
"no_servers": "There is no available servers on your account.",
|
"no_servers": "There is no available servers on your account.",
|
||||||
"domain_not_available_on_token": "Selected domain is not available on this token.",
|
"domain_not_available_on_token": "Selected domain is not available on this token.",
|
||||||
|
"modal_confirmation_title": "Is it really your server?",
|
||||||
|
"modal_confirmation_description": "If you connect to a wrong server you may lose all your data.",
|
||||||
|
"modal_confirmation_dns_valid": "Reverse DNS is valid",
|
||||||
|
"modal_confirmation_dns_invalid": "Reverse DNS points to another domain",
|
||||||
|
"modal_confirmation_ip_valid": "IP is the same as in DNS record",
|
||||||
|
"modal_confirmation_ip_invalid": "IP is not the same as in DNS record",
|
||||||
"confirm_cloudflare": "Connect to CloudFlare",
|
"confirm_cloudflare": "Connect to CloudFlare",
|
||||||
"confirm_cloudflare_description": "Enter a Cloudflare token with access to {}:",
|
"confirm_cloudflare_description": "Enter a Cloudflare token with access to {}:",
|
||||||
"confirm_backblze": "Connect to Backblaze",
|
"confirm_backblaze": "Connect to Backblaze",
|
||||||
"confirm_backblaze_description": "Enter a Backblaze token with access to backup storage:"
|
"confirm_backblaze_description": "Enter a Backblaze token with access to backup storage:"
|
||||||
|
},
|
||||||
|
"recovery_key": {
|
||||||
|
"key_main_header": "Recovery key",
|
||||||
|
"key_main_description": "Is needed for SelfPrivacy authorization when all your other authorized devices aren't available.",
|
||||||
|
"key_amount_toggle": "Limit by number of uses",
|
||||||
|
"key_amount_field_title": "Max number of uses",
|
||||||
|
"key_duedate_toggle": "Limit by time",
|
||||||
|
"key_duedate_field_title": "Due date of expiration",
|
||||||
|
"key_receive_button": "Receive key"
|
||||||
},
|
},
|
||||||
"modals": {
|
"modals": {
|
||||||
"_comment": "messages in modals",
|
"_comment": "messages in modals",
|
||||||
|
@ -331,7 +345,8 @@
|
||||||
"8": "Remove task",
|
"8": "Remove task",
|
||||||
"9": "Reboot",
|
"9": "Reboot",
|
||||||
"10": "You cannot use this API for domains with such TLD.",
|
"10": "You cannot use this API for domains with such TLD.",
|
||||||
"yes": "Yes"
|
"yes": "Yes",
|
||||||
|
"no": "No"
|
||||||
},
|
},
|
||||||
"timer": {
|
"timer": {
|
||||||
"sec": "{} sec"
|
"sec": "{} sec"
|
||||||
|
|
|
@ -315,11 +315,22 @@
|
||||||
"choose_server_description": "Не удалось определить, с каким сервером вы устанавливаете связь.",
|
"choose_server_description": "Не удалось определить, с каким сервером вы устанавливаете связь.",
|
||||||
"no_servers": "На вашем аккаунте нет доступных серверов.",
|
"no_servers": "На вашем аккаунте нет доступных серверов.",
|
||||||
"domain_not_available_on_token": "Введённый токен не имеет доступа к нужному домену.",
|
"domain_not_available_on_token": "Введённый токен не имеет доступа к нужному домену.",
|
||||||
|
"modal_confirmation_title": "Это действительно ваш сервер?",
|
||||||
|
"modal_confirmation_description": "Подключение к неправильному серверу может привести к деструктивным последствиям.",
|
||||||
"confirm_cloudflare": "Подключение к Cloudflare",
|
"confirm_cloudflare": "Подключение к Cloudflare",
|
||||||
"confirm_cloudflare_description": "Введите токен Cloudflare, который имеет права на {}:",
|
"confirm_cloudflare_description": "Введите токен Cloudflare, который имеет права на {}:",
|
||||||
"confirm_backblze": "Подключение к Backblaze",
|
"confirm_backblze": "Подключение к Backblaze",
|
||||||
"confirm_backblaze_description": "Введите токен Backblaze, который имеет права на хранилище резервных копий:"
|
"confirm_backblaze_description": "Введите токен Backblaze, который имеет права на хранилище резервных копий:"
|
||||||
},
|
},
|
||||||
|
"recovery_key": {
|
||||||
|
"key_main_header": "Ключ восстановления",
|
||||||
|
"key_main_description": "Требуется для авторизации SelfPrivacy, когда авторизованные устройства недоступны.",
|
||||||
|
"key_amount_toggle": "Ограничить использования",
|
||||||
|
"key_amount_field_title": "Макс. кол-во использований",
|
||||||
|
"key_duedate_toggle": "Ограничить срок использования",
|
||||||
|
"key_duedate_field_title": "Дата окончания срока",
|
||||||
|
"key_receive_button": "Получить ключ"
|
||||||
|
},
|
||||||
"modals": {
|
"modals": {
|
||||||
"_comment": "messages in modals",
|
"_comment": "messages in modals",
|
||||||
"1": "Сервер с таким именем уже существует",
|
"1": "Сервер с таким именем уже существует",
|
||||||
|
@ -332,7 +343,8 @@
|
||||||
"8": "Удалить задачу",
|
"8": "Удалить задачу",
|
||||||
"9": "Перезагрузить",
|
"9": "Перезагрузить",
|
||||||
"10": "API не поддерживает домены с таким TLD.",
|
"10": "API не поддерживает домены с таким TLD.",
|
||||||
"yes": "Да"
|
"yes": "Да",
|
||||||
|
"no": "Нет"
|
||||||
},
|
},
|
||||||
"timer": {
|
"timer": {
|
||||||
"sec": "{} сек"
|
"sec": "{} сек"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.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/backups/backups_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||||
|
@ -22,6 +23,7 @@ class BlocAndProviderConfig extends StatelessWidget {
|
||||||
var servicesCubit = ServicesCubit(serverInstallationCubit);
|
var servicesCubit = ServicesCubit(serverInstallationCubit);
|
||||||
var backupsCubit = BackupsCubit(serverInstallationCubit);
|
var backupsCubit = BackupsCubit(serverInstallationCubit);
|
||||||
var dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
|
var dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
|
||||||
|
var recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit);
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
|
@ -36,6 +38,7 @@ class BlocAndProviderConfig extends StatelessWidget {
|
||||||
BlocProvider(create: (_) => servicesCubit..load(), lazy: false),
|
BlocProvider(create: (_) => servicesCubit..load(), lazy: false),
|
||||||
BlocProvider(create: (_) => backupsCubit..load(), lazy: false),
|
BlocProvider(create: (_) => backupsCubit..load(), lazy: false),
|
||||||
BlocProvider(create: (_) => dnsRecordsCubit..load()),
|
BlocProvider(create: (_) => dnsRecordsCubit..load()),
|
||||||
|
BlocProvider(create: (_) => recoveryKeyCubit..load()),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) =>
|
create: (_) =>
|
||||||
JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit),
|
JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit),
|
||||||
|
|
|
@ -628,7 +628,7 @@ class ServerApi extends ApiMap {
|
||||||
.replaceAll('"', '');
|
.replaceAll('"', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<RecoveryKeyStatus>> getRecoveryTokenStatus() async {
|
Future<ApiResponse<RecoveryKeyStatus?>> getRecoveryTokenStatus() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
var client = await getClient();
|
||||||
|
@ -649,7 +649,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: response.data != null
|
data: response.data != null
|
||||||
? response.data.fromJson(response.data)
|
? RecoveryKeyStatus.fromJson(response.data)
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
isKeyValid = await apiClient.isValid(encodedApiKey);
|
isKeyValid = await apiClient.isValid(encodedApiKey);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addError(e);
|
addError(e);
|
||||||
|
isKeyValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isKeyValid) {
|
if (!isKeyValid) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ class RecoveryKeyCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<RecoveryKeyStatus?> _getRecoveryKeyStatus() async {
|
Future<RecoveryKeyStatus?> _getRecoveryKeyStatus() async {
|
||||||
final ApiResponse<RecoveryKeyStatus> response =
|
final ApiResponse<RecoveryKeyStatus?> response =
|
||||||
await api.getRecoveryTokenStatus();
|
await api.getRecoveryTokenStatus();
|
||||||
if (response.isSuccess) {
|
if (response.isSuccess) {
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|
|
@ -64,6 +64,10 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCloudflareKey(String cloudFlareKey) async {
|
void setCloudflareKey(String cloudFlareKey) async {
|
||||||
|
if (state is ServerInstallationRecovery) {
|
||||||
|
setAndValidateCloudflareToken(cloudFlareKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await repository.saveCloudFlareKey(cloudFlareKey);
|
await repository.saveCloudFlareKey(cloudFlareKey);
|
||||||
emit((state as ServerInstallationNotFinished)
|
emit((state as ServerInstallationNotFinished)
|
||||||
.copyWith(cloudFlareKey: cloudFlareKey));
|
.copyWith(cloudFlareKey: cloudFlareKey));
|
||||||
|
@ -431,12 +435,19 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
.showSnackBar('recovering.domain_not_available_on_token'.tr());
|
.showSnackBar('recovering.domain_not_available_on_token'.tr());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await repository.saveDomain(ServerDomain(
|
||||||
|
domainName: serverDomain.domainName,
|
||||||
|
zoneId: zoneId,
|
||||||
|
provider: DnsProvider.Cloudflare,
|
||||||
|
));
|
||||||
|
await repository.saveCloudFlareKey(token);
|
||||||
emit(dataState.copyWith(
|
emit(dataState.copyWith(
|
||||||
serverDomain: ServerDomain(
|
serverDomain: ServerDomain(
|
||||||
domainName: serverDomain.domainName,
|
domainName: serverDomain.domainName,
|
||||||
zoneId: zoneId,
|
zoneId: zoneId,
|
||||||
provider: DnsProvider.Cloudflare,
|
provider: DnsProvider.Cloudflare,
|
||||||
),
|
),
|
||||||
|
cloudFlareKey: token,
|
||||||
currentStep: RecoveryStep.BackblazeToken,
|
currentStep: RecoveryStep.BackblazeToken,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,9 @@ class MyApp extends StatelessWidget {
|
||||||
: RootPage(),
|
: RootPage(),
|
||||||
builder: (BuildContext context, Widget? widget) {
|
builder: (BuildContext context, Widget? widget) {
|
||||||
Widget error = Text('...rendering error...');
|
Widget error = Text('...rendering error...');
|
||||||
if (widget is Scaffold || widget is Navigator)
|
if (widget is Scaffold || widget is Navigator) {
|
||||||
error = Scaffold(body: Center(child: error));
|
error = Scaffold(body: Center(child: error));
|
||||||
|
}
|
||||||
ErrorWidget.builder =
|
ErrorWidget.builder =
|
||||||
(FlutterErrorDetails errorDetails) => error;
|
(FlutterErrorDetails errorDetails) => error;
|
||||||
return widget!;
|
return widget!;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*import 'package:flutter/src/foundation/key.dart';
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
|
||||||
|
class RecoveryKey extends StatefulWidget {
|
||||||
|
const RecoveryKey({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RecoveryKey> createState() => _RecoveryKeyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecoveryKeyState extends State<RecoveryKey> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {}
|
||||||
|
}*/
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -40,34 +40,44 @@ class RecoverByNewDeviceKeyInput extends StatelessWidget {
|
||||||
FieldCubitFactory(context),
|
FieldCubitFactory(context),
|
||||||
ServerRecoveryMethods.newDeviceKey,
|
ServerRecoveryMethods.newDeviceKey,
|
||||||
),
|
),
|
||||||
child: Builder(
|
child: BlocListener<ServerInstallationCubit, ServerInstallationState>(
|
||||||
builder: (context) {
|
listener: (context, state) {
|
||||||
var formCubitState = context.watch<RecoveryDeviceFormCubit>().state;
|
if (state is ServerInstallationRecovery &&
|
||||||
|
state.currentStep != RecoveryStep.NewDeviceKey) {
|
||||||
return BrandHeroScreen(
|
Navigator.of(context).pop();
|
||||||
heroTitle: "recovering.recovery_main_header".tr(),
|
}
|
||||||
heroSubtitle: "recovering.method_device_input_description".tr(),
|
|
||||||
hasBackButton: true,
|
|
||||||
hasFlashButton: false,
|
|
||||||
children: [
|
|
||||||
CubitFormTextField(
|
|
||||||
formFieldCubit:
|
|
||||||
context.read<RecoveryDeviceFormCubit>().tokenField,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: "recovering.method_device_input_placeholder".tr(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
FilledButton(
|
|
||||||
title: "more.continue".tr(),
|
|
||||||
onPressed: formCubitState.isSubmitting
|
|
||||||
? null
|
|
||||||
: () => context.read<RecoveryDeviceFormCubit>().trySubmit(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
var formCubitState = context.watch<RecoveryDeviceFormCubit>().state;
|
||||||
|
|
||||||
|
return BrandHeroScreen(
|
||||||
|
heroTitle: "recovering.recovery_main_header".tr(),
|
||||||
|
heroSubtitle: "recovering.method_device_input_description".tr(),
|
||||||
|
hasBackButton: true,
|
||||||
|
hasFlashButton: false,
|
||||||
|
children: [
|
||||||
|
CubitFormTextField(
|
||||||
|
formFieldCubit:
|
||||||
|
context.read<RecoveryDeviceFormCubit>().tokenField,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText:
|
||||||
|
"recovering.method_device_input_placeholder".tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
FilledButton(
|
||||||
|
title: "more.continue".tr(),
|
||||||
|
onPressed: formCubitState.isSubmitting
|
||||||
|
? null
|
||||||
|
: () =>
|
||||||
|
context.read<RecoveryDeviceFormCubit>().trySubmit(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,24 +13,33 @@ class RecoverByOldTokenInstruction extends StatelessWidget {
|
||||||
RecoverByOldTokenInstruction({required this.instructionFilename});
|
RecoverByOldTokenInstruction({required this.instructionFilename});
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BrandHeroScreen(
|
return BlocListener<ServerInstallationCubit, ServerInstallationState>(
|
||||||
heroTitle: "recovering.recovery_main_header".tr(),
|
listener: (context, state) {
|
||||||
hasBackButton: true,
|
if (state is ServerInstallationRecovery &&
|
||||||
hasFlashButton: false,
|
state.currentStep != RecoveryStep.Selecting) {
|
||||||
onBackButtonPressed: () =>
|
Navigator.of(context).pop();
|
||||||
context.read<ServerInstallationCubit>().revertRecoveryStep(),
|
Navigator.of(context).pop();
|
||||||
children: [
|
}
|
||||||
BrandMarkdown(
|
},
|
||||||
fileName: instructionFilename,
|
child: BrandHeroScreen(
|
||||||
),
|
heroTitle: "recovering.recovery_main_header".tr(),
|
||||||
SizedBox(height: 16),
|
hasBackButton: true,
|
||||||
FilledButton(
|
hasFlashButton: false,
|
||||||
title: "recovering.method_device_button".tr(),
|
onBackButtonPressed: () =>
|
||||||
onPressed: () => context
|
context.read<ServerInstallationCubit>().revertRecoveryStep(),
|
||||||
.read<ServerInstallationCubit>()
|
children: [
|
||||||
.selectRecoveryMethod(ServerRecoveryMethods.oldToken),
|
BrandMarkdown(
|
||||||
)
|
fileName: instructionFilename,
|
||||||
],
|
),
|
||||||
|
SizedBox(height: 18),
|
||||||
|
FilledButton(
|
||||||
|
title: "recovering.method_device_button".tr(),
|
||||||
|
onPressed: () => context
|
||||||
|
.read<ServerInstallationCubit>()
|
||||||
|
.selectRecoveryMethod(ServerRecoveryMethods.oldToken),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +75,7 @@ class RecoverByOldToken extends StatelessWidget {
|
||||||
labelText: "recovering.method_device_input_placeholder".tr(),
|
labelText: "recovering.method_device_input_placeholder".tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 18),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
title: "more.continue".tr(),
|
title: "more.continue".tr(),
|
||||||
onPressed: formCubitState.isSubmitting
|
onPressed: formCubitState.isSubmitting
|
||||||
|
|
|
@ -30,26 +30,28 @@ class RecoveryConfirmBackblaze extends StatelessWidget {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
hintText: 'KeyID',
|
hintText: 'KeyID',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
const SizedBox(height: 18),
|
||||||
CubitFormTextField(
|
CubitFormTextField(
|
||||||
formFieldCubit: context.read<BackblazeFormCubit>().applicationKey,
|
formFieldCubit: context.read<BackblazeFormCubit>().applicationKey,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
hintText: 'Master Application Key',
|
hintText: 'Master Application Key',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
const SizedBox(height: 18),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed: formCubitState.isSubmitting
|
onPressed: formCubitState.isSubmitting
|
||||||
? null
|
? null
|
||||||
: () => context.read<BackblazeFormCubit>().trySubmit(),
|
: () => context.read<BackblazeFormCubit>().trySubmit(),
|
||||||
text: 'basis.connect'.tr(),
|
text: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
const SizedBox(height: 18),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => showModalBottomSheet<void>(
|
onPressed: () => showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -32,17 +32,18 @@ class RecoveryConfirmCloudflare extends StatelessWidget {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
scrollPadding: EdgeInsets.only(bottom: 70),
|
scrollPadding: EdgeInsets.only(bottom: 70),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
hintText: 'initializing.5'.tr(),
|
hintText: 'initializing.5'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
const SizedBox(height: 18),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed: formCubitState.isSubmitting
|
onPressed: formCubitState.isSubmitting
|
||||||
? null
|
? null
|
||||||
: () => context.read<CloudFlareFormCubit>().trySubmit(),
|
: () => context.read<CloudFlareFormCubit>().trySubmit(),
|
||||||
text: 'basis.connect'.tr(),
|
text: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
const SizedBox(height: 18),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () => showModalBottomSheet<void>(
|
onPressed: () => showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -36,11 +36,11 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BrandHeroScreen(
|
return BrandHeroScreen(
|
||||||
heroTitle: _isExtended
|
heroTitle: _isExtended
|
||||||
? "recovering.choose_server".tr()
|
? 'recovering.choose_server'.tr()
|
||||||
: "recovering.confirm_server".tr(),
|
: 'recovering.confirm_server'.tr(),
|
||||||
heroSubtitle: _isExtended
|
heroSubtitle: _isExtended
|
||||||
? "recovering.choose_server_description".tr()
|
? 'recovering.choose_server_description'.tr()
|
||||||
: "recovering.confirm_server_description".tr(),
|
: 'recovering.confirm_server_description'.tr(),
|
||||||
hasBackButton: true,
|
hasBackButton: true,
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
children: [
|
children: [
|
||||||
|
@ -68,7 +68,7 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
if (servers?.isEmpty ?? true)
|
if (servers?.isEmpty ?? true)
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
"recovering.no_servers".tr(),
|
'recovering.no_servers'.tr(),
|
||||||
style: Theme.of(context).textTheme.headline6,
|
style: Theme.of(context).textTheme.headline6,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -99,7 +99,7 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
title: "recovering.confirm_server_accept".tr(),
|
title: 'recovering.confirm_server_accept'.tr(),
|
||||||
onPressed: () => _showConfirmationDialog(context, server),
|
onPressed: () => _showConfirmationDialog(context, server),
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
|
@ -166,19 +166,65 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text('ssh.delete'.tr()),
|
title: Column(
|
||||||
content: SingleChildScrollView(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
child: ListBody(
|
children: [
|
||||||
children: <Widget>[
|
const Icon(Icons.warning_amber_outlined),
|
||||||
Text("WOW DIALOGUE TEXT WOW :)"),
|
const SizedBox(height: 8),
|
||||||
],
|
Text(
|
||||||
),
|
'recovering.modal_confirmation_title'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('recovering.modal_confirmation_description'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium),
|
||||||
|
const Divider(),
|
||||||
|
Text(
|
||||||
|
server.name,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(server.isReverseDnsValid ? Icons.check : Icons.close),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(server.isReverseDnsValid
|
||||||
|
? 'recovering.modal_confirmation_dns_valid'.tr()
|
||||||
|
: 'recovering.modal_confirmation_dns_invalid'.tr()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(server.isIpValid ? Icons.check : Icons.close),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(server.isIpValid
|
||||||
|
? 'recovering.modal_confirmation_ip_valid'.tr()
|
||||||
|
: 'recovering.modal_confirmation_ip_invalid'.tr()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('basis.cancel'.tr()),
|
child: Text('modals.no'.tr()),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context)..pop();
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('modals.yes'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
context.read<ServerInstallationCubit>().setServerId(server);
|
||||||
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -9,6 +9,8 @@ import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.da
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_recovery_key.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_recovery_key.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_new_device_key.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_new_device_key.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_backblaze.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_hentzner_connected.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_hentzner_connected.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart';
|
||||||
|
@ -43,8 +45,10 @@ class RecoveryRouting extends StatelessWidget {
|
||||||
currentPage = RecoveryConfirmServer();
|
currentPage = RecoveryConfirmServer();
|
||||||
break;
|
break;
|
||||||
case RecoveryStep.CloudflareToken:
|
case RecoveryStep.CloudflareToken:
|
||||||
|
currentPage = RecoveryConfirmCloudflare();
|
||||||
break;
|
break;
|
||||||
case RecoveryStep.BackblazeToken:
|
case RecoveryStep.BackblazeToken:
|
||||||
|
currentPage = RecoveryConfirmBackblaze();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
pubspec.lock
14
pubspec.lock
|
@ -363,6 +363,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.2"
|
version: "0.9.2"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -560,6 +567,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.0"
|
version: "6.2.0"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
local_auth:
|
local_auth:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -52,6 +52,7 @@ dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.9.2
|
flutter_launcher_icons: ^0.9.2
|
||||||
hive_generator: ^1.0.0
|
hive_generator: ^1.0.0
|
||||||
json_serializable: ^6.1.4
|
json_serializable: ^6.1.4
|
||||||
|
flutter_lints: ^2.0.1
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: "launcher_icon"
|
android: "launcher_icon"
|
||||||
|
|
Loading…
Reference in New Issue