Merge pull request 'UI Refactor and move to auto_route' (#203) from ui-refactor into master

Reviewed-on: kherel/selfprivacy.org.app#203
Reviewed-by: NaiJi  <naiji@udongein.xyz>
pull/207/head
Inex Code 2023-04-08 04:00:48 +03:00
commit e180c23cb7
118 changed files with 30367 additions and 16073 deletions

View File

@ -14,3 +14,6 @@ max_line_length = 150
[*.md]
trim_trailing_whitespace = false
[*.json]
indent_size = 4

View File

@ -29,16 +29,16 @@ linter:
# producing the lint.
rules:
avoid_print: false # Uncomment to disable the `avoid_print` rule
prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
always_use_package_imports: true
no_adjacent_strings_in_list: true
unnecessary_statements: true
always_declare_return_types: true
always_put_required_named_parameters_first: true
always_put_control_body_on_new_line: true
always_put_required_named_parameters_first: true
always_use_package_imports: true
avoid_escaping_inner_quotes: true
avoid_setters_without_getters: true
collection_methods_unrelated_type: true
combinators_ordering: true
eol_at_end_of_file: true
no_adjacent_strings_in_list: true
prefer_constructors_over_static_methods: true
prefer_expression_function_bodies: true
prefer_final_in_for_each: true
@ -48,12 +48,18 @@ linter:
prefer_if_elements_to_conditional_expressions: true
prefer_mixin: true
prefer_null_aware_method_calls: true
prefer_single_quotes: true
require_trailing_commas: true
sized_box_shrink_expand: true
sort_constructors_first: true
unawaited_futures: true
unnecessary_await_in_return: true
unnecessary_null_aware_operator_on_extension_on_nullable: true
unnecessary_null_checks: true
unnecessary_parenthesis: true
unnecessary_statements: true
unnecessary_to_list_in_spreads: true
unreachable_from_main: true
use_enums: true
use_if_null_to_convert_nulls_to_bools: true
use_is_even_rather_than_modulo: true
@ -61,6 +67,7 @@ linter:
use_named_constants: true
use_setters_to_change_properties: true
use_string_buffers: true
use_string_in_part_of_directives: true
use_super_parameters: true
use_to_and_as_if_applicable: true

View File

@ -2,6 +2,7 @@
"test": "en-test",
"locale": "en",
"basis": {
"app_name": "SelfPrivacy",
"providers": "Providers",
"providers_title": "Your Data Center",
"select": "Select",
@ -46,7 +47,8 @@
},
"console_page": {
"title": "Console",
"waiting": "Waiting for initialization…"
"waiting": "Waiting for initialization…",
"copy": "Copy"
},
"about_us_page": {
"title": "About us"
@ -59,8 +61,11 @@
},
"application_settings": {
"title": "Application settings",
"system_dark_theme_title": "System default theme",
"system_dark_theme_description": "Use light or dark theme depending on system settings",
"dark_theme_title": "Dark theme",
"dark_theme_description": "Switch your application theme",
"dangerous_settings": "Dangerous settings",
"reset_config_title": "Reset application config",
"reset_config_description": "Reset api keys and root user",
"delete_server_title": "Delete server",
@ -251,6 +256,7 @@
"subtitle": "Private VPN server"
},
"users": {
"details_title": "User details",
"add_new_user": "Add a first user",
"new_user": "New user",
"delete_user": "Delete user",
@ -329,7 +335,20 @@
"create_master_account": "Create master account",
"enter_username_and_password": "Enter username and strong password",
"finish": "Everything is initialized",
"checks": "Checks have been completed \n{} out of {}"
"checks": "Checks have been completed \n{} out of {}",
"steps": {
"hosting": "Hosting",
"server_type": "Server type",
"dns_provider": "DNS provider",
"backups_provider": "Backups",
"domain": "Domain",
"master_account": "Master account",
"server": "Server",
"dns_setup": "DNS setup",
"nixos_installation": "NixOS installation",
"server_reboot": "Server reboot",
"final_checks": "Final checks"
}
},
"recovering": {
"generic_error": "Operation failed, please try again.",
@ -472,5 +491,19 @@
"root_name": "Cannot be 'root'",
"length_not_equal": "Length is [], should be {}",
"length_longer": "Length is [], should be shorter than or equal to {}"
},
"support": {
"title": "SelfPrivacy Support"
},
"developer_settings": {
"title": "Developer settings",
"subtitle": "These settings are for debugging only. Don't change them unless you know what you're doing.",
"server_setup": "Server setup",
"use_staging_acme": "Use staging ACME server",
"use_staging_acme_description": "Rebuild your app to change this value.",
"routing": "App routing",
"reset_onboarding": "Reset onboarding switch",
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
"cubit_statuses": "Cubit loading statuses"
}
}

View File

@ -12,6 +12,7 @@ import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
@ -23,7 +24,9 @@ class BlocAndProviderConfig extends StatelessWidget {
@override
Widget build(final BuildContext context) {
const isDark = false;
const isAutoDark = true;
final serverInstallationCubit = ServerInstallationCubit()..load();
final supportSystemCubit = SupportSystemCubit();
final usersCubit = UsersCubit(serverInstallationCubit);
final servicesCubit = ServicesCubit(serverInstallationCubit);
final backupsCubit = BackupsCubit(serverInstallationCubit);
@ -41,9 +44,13 @@ class BlocAndProviderConfig extends StatelessWidget {
BlocProvider(
create: (final _) => AppSettingsCubit(
isDarkModeOn: isDark,
isAutoDarkModeOn: isAutoDark,
isOnboardingShowing: true,
)..load(),
),
BlocProvider(
create: (final _) => supportSystemCubit,
),
BlocProvider(
create: (final _) => serverInstallationCubit,
lazy: false,

View File

@ -2,53 +2,16 @@ import 'package:flutter/material.dart';
class BrandColors {
static const Color blue = Color(0xFF093CEF);
static const Color white = Colors.white;
static const Color black = Colors.black;
static const Color gray1 = Color(0xFF555555);
static const Color gray2 = Color(0xFF7C7C7C);
static const Color gray3 = Color(0xFFFAFAFA);
static const Color gray4 = Color(0xFFDDDDDD);
static const Color gray5 = Color(0xFFEDEEF1);
static Color gray6 = const Color(0xFF181818).withOpacity(0.7);
static const Color grey7 = Color(0xFFABABAB);
static const Color red1 = Color(0xFFFA0E0E);
static const Color red2 = Color(0xFFE65527);
static const Color green1 = Color(0xFF00AF54);
static const Color green2 = Color(0xFF0F8849);
static Color get navBackgroundLight => white.withOpacity(0.8);
static Color get navBackgroundDark => black.withOpacity(0.8);
static const List<Color> uninitializedGradientColors = [
Color(0xFF555555),
Color(0xFFABABAB),
];
static const List<Color> stableGradientColors = [
Color(0xFF093CEF),
Color(0xFF14A1CB),
];
static const List<Color> progressGradientColors = [
Color(0xFF093CEF),
Color(0xFF14A1CB),
];
static const List<Color> warningGradientColors = [
Color(0xFFEF4E09),
Color(0xFFEFD135),
];
static const Color primary = blue;
static const Color headlineColor = black;
static const Color inactive = gray2;
static const Color scaffoldBackground = gray3;
static const Color inputInactive = gray4;
static const Color textColor1 = black;
static const Color textColor2 = gray1;
static const Color dividerColor = gray5;
static const Color warning = red1;
}

View File

@ -63,6 +63,9 @@ class BNames {
/// A boolean field of [appSettingsBox] box.
static String isDarkModeOn = 'isDarkModeOn';
/// A boolean field of [appSettingsBox] box.
static String isAutoDarkModeOn = 'isAutoDarkModeOn';
/// A boolean field of [appSettingsBox] box.
static String isOnboardingShowing = 'isOnboardingShowing';

View File

@ -1,80 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/utils/named_font_weight.dart';
import 'package:selfprivacy/config/brand_colors.dart';
const TextStyle defaultTextStyle = TextStyle(
fontSize: 15,
color: BrandColors.textColor1,
);
final TextStyle headline1Style = defaultTextStyle.copyWith(
fontSize: 40,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final TextStyle headline2Style = defaultTextStyle.copyWith(
fontSize: 24,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final TextStyle onboardingTitle = defaultTextStyle.copyWith(
fontSize: 30,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final TextStyle headline3Style = defaultTextStyle.copyWith(
fontSize: 20,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final TextStyle headline4Style = defaultTextStyle.copyWith(
fontSize: 18,
fontWeight: NamedFontWeight.medium,
color: BrandColors.headlineColor,
);
final TextStyle headline4UnderlinedStyle = defaultTextStyle.copyWith(
fontSize: 18,
fontWeight: NamedFontWeight.medium,
color: BrandColors.headlineColor,
decoration: TextDecoration.underline,
);
final TextStyle headline5Style = defaultTextStyle.copyWith(
fontSize: 15,
fontWeight: NamedFontWeight.medium,
color: BrandColors.headlineColor.withOpacity(0.8),
);
const TextStyle body1Style = defaultTextStyle;
final TextStyle body2Style = defaultTextStyle.copyWith(
color: BrandColors.textColor2,
);
final TextStyle buttonTitleText = defaultTextStyle.copyWith(
color: BrandColors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
height: 1,
);
final TextStyle mediumStyle =
defaultTextStyle.copyWith(fontSize: 13, height: 1.53);
final TextStyle smallStyle =
defaultTextStyle.copyWith(fontSize: 11, height: 1.45);
const TextStyle progressTextStyleLight = TextStyle(
fontSize: 11,
color: BrandColors.textColor1,
height: 1.7,
);
final TextStyle progressTextStyleDark = progressTextStyleLight.copyWith(
color: BrandColors.white,
);

View File

@ -20,7 +20,13 @@ class RequestLoggingLink extends Link {
final Request request, [
final NextLink? forward,
]) async* {
_logToAppConsole(request);
getIt.get<ConsoleModel>().addMessage(
GraphQlRequestMessage(
operation: request.operation,
variables: request.variables,
context: request.context,
),
);
yield* forward!(request);
}
}
@ -29,7 +35,13 @@ class ResponseLoggingParser extends ResponseParser {
@override
Response parseResponse(final Map<String, dynamic> body) {
final response = super.parseResponse(body);
_logToAppConsole(response);
getIt.get<ConsoleModel>().addMessage(
GraphQlResponseMessage(
data: response.data,
errors: response.errors,
context: response.context,
),
);
return response;
}

View File

@ -1,376 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'disk_volumes.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes _$Query$GetServerDiskVolumesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes(
storage: Query$GetServerDiskVolumes$storage.fromJson(
json['storage'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumesToJson(
Query$GetServerDiskVolumes instance) =>
<String, dynamic>{
'storage': instance.storage.toJson(),
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage _$Query$GetServerDiskVolumes$storageFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage(
volumes: (json['volumes'] as List<dynamic>)
.map((e) => Query$GetServerDiskVolumes$storage$volumes.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumes$storageToJson(
Query$GetServerDiskVolumes$storage instance) =>
<String, dynamic>{
'volumes': instance.volumes.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage$volumes
_$Query$GetServerDiskVolumes$storage$volumesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes(
freeSpace: json['freeSpace'] as String,
model: json['model'] as String?,
name: json['name'] as String,
root: json['root'] as bool,
serial: json['serial'] as String?,
totalSpace: json['totalSpace'] as String,
type: json['type'] as String,
usages: (json['usages'] as List<dynamic>)
.map((e) =>
Query$GetServerDiskVolumes$storage$volumes$usages.fromJson(
e as Map<String, dynamic>))
.toList(),
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumes$storage$volumesToJson(
Query$GetServerDiskVolumes$storage$volumes instance) =>
<String, dynamic>{
'freeSpace': instance.freeSpace,
'model': instance.model,
'name': instance.name,
'root': instance.root,
'serial': instance.serial,
'totalSpace': instance.totalSpace,
'type': instance.type,
'usages': instance.usages.map((e) => e.toJson()).toList(),
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage$volumes$usages
_$Query$GetServerDiskVolumes$storage$volumes$usagesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes$usages(
title: json['title'] as String,
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumes$storage$volumes$usagesToJson(
Query$GetServerDiskVolumes$storage$volumes$usages instance) =>
<String, dynamic>{
'title': instance.title,
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsageFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage(
title: json['title'] as String,
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
service: json['service'] == null
? null
: Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service
.fromJson(json['service'] as Map<String, dynamic>),
);
Map<String, dynamic>
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsageToJson(
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage
instance) =>
<String, dynamic>{
'title': instance.title,
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
'service': instance.service?.toJson(),
};
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$serviceFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service(
id: json['id'] as String,
isMovable: json['isMovable'] as bool,
displayName: json['displayName'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$serviceToJson(
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service
instance) =>
<String, dynamic>{
'id': instance.id,
'isMovable': instance.isMovable,
'displayName': instance.displayName,
'__typename': instance.$__typename,
};
Variables$Mutation$MountVolume _$Variables$Mutation$MountVolumeFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$MountVolume(
name: json['name'] as String,
);
Map<String, dynamic> _$Variables$Mutation$MountVolumeToJson(
Variables$Mutation$MountVolume instance) =>
<String, dynamic>{
'name': instance.name,
};
Mutation$MountVolume _$Mutation$MountVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$MountVolume(
mountVolume: Mutation$MountVolume$mountVolume.fromJson(
json['mountVolume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MountVolumeToJson(
Mutation$MountVolume instance) =>
<String, dynamic>{
'mountVolume': instance.mountVolume.toJson(),
'__typename': instance.$__typename,
};
Mutation$MountVolume$mountVolume _$Mutation$MountVolume$mountVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$MountVolume$mountVolume(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MountVolume$mountVolumeToJson(
Mutation$MountVolume$mountVolume instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$ResizeVolume _$Variables$Mutation$ResizeVolumeFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$ResizeVolume(
name: json['name'] as String,
);
Map<String, dynamic> _$Variables$Mutation$ResizeVolumeToJson(
Variables$Mutation$ResizeVolume instance) =>
<String, dynamic>{
'name': instance.name,
};
Mutation$ResizeVolume _$Mutation$ResizeVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$ResizeVolume(
resizeVolume: Mutation$ResizeVolume$resizeVolume.fromJson(
json['resizeVolume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ResizeVolumeToJson(
Mutation$ResizeVolume instance) =>
<String, dynamic>{
'resizeVolume': instance.resizeVolume.toJson(),
'__typename': instance.$__typename,
};
Mutation$ResizeVolume$resizeVolume _$Mutation$ResizeVolume$resizeVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$ResizeVolume$resizeVolume(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ResizeVolume$resizeVolumeToJson(
Mutation$ResizeVolume$resizeVolume instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$UnmountVolume _$Variables$Mutation$UnmountVolumeFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$UnmountVolume(
name: json['name'] as String,
);
Map<String, dynamic> _$Variables$Mutation$UnmountVolumeToJson(
Variables$Mutation$UnmountVolume instance) =>
<String, dynamic>{
'name': instance.name,
};
Mutation$UnmountVolume _$Mutation$UnmountVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$UnmountVolume(
unmountVolume: Mutation$UnmountVolume$unmountVolume.fromJson(
json['unmountVolume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UnmountVolumeToJson(
Mutation$UnmountVolume instance) =>
<String, dynamic>{
'unmountVolume': instance.unmountVolume.toJson(),
'__typename': instance.$__typename,
};
Mutation$UnmountVolume$unmountVolume
_$Mutation$UnmountVolume$unmountVolumeFromJson(Map<String, dynamic> json) =>
Mutation$UnmountVolume$unmountVolume(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UnmountVolume$unmountVolumeToJson(
Mutation$UnmountVolume$unmountVolume instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$MigrateToBinds _$Variables$Mutation$MigrateToBindsFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$MigrateToBinds(
input: Input$MigrateToBindsInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$MigrateToBindsToJson(
Variables$Mutation$MigrateToBinds instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$MigrateToBinds _$Mutation$MigrateToBindsFromJson(
Map<String, dynamic> json) =>
Mutation$MigrateToBinds(
migrateToBinds: Mutation$MigrateToBinds$migrateToBinds.fromJson(
json['migrateToBinds'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MigrateToBindsToJson(
Mutation$MigrateToBinds instance) =>
<String, dynamic>{
'migrateToBinds': instance.migrateToBinds.toJson(),
'__typename': instance.$__typename,
};
Mutation$MigrateToBinds$migrateToBinds
_$Mutation$MigrateToBinds$migrateToBindsFromJson(
Map<String, dynamic> json) =>
Mutation$MigrateToBinds$migrateToBinds(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
job: json['job'] == null
? null
: Mutation$MigrateToBinds$migrateToBinds$job.fromJson(
json['job'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$MigrateToBinds$migrateToBindsToJson(
Mutation$MigrateToBinds$migrateToBinds instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'job': instance.job?.toJson(),
};
Mutation$MigrateToBinds$migrateToBinds$job
_$Mutation$MigrateToBinds$migrateToBinds$jobFromJson(
Map<String, dynamic> json) =>
Mutation$MigrateToBinds$migrateToBinds$job(
createdAt: dateTimeFromJson(json['createdAt']),
description: json['description'] as String,
error: json['error'] as String?,
finishedAt: _nullable$dateTimeFromJson(json['finishedAt']),
name: json['name'] as String,
progress: json['progress'] as int?,
result: json['result'] as String?,
status: json['status'] as String,
statusText: json['statusText'] as String?,
uid: json['uid'] as String,
updatedAt: dateTimeFromJson(json['updatedAt']),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MigrateToBinds$migrateToBinds$jobToJson(
Mutation$MigrateToBinds$migrateToBinds$job instance) =>
<String, dynamic>{
'createdAt': dateTimeToJson(instance.createdAt),
'description': instance.description,
'error': instance.error,
'finishedAt': _nullable$dateTimeToJson(instance.finishedAt),
'name': instance.name,
'progress': instance.progress,
'result': instance.result,
'status': instance.status,
'statusText': instance.statusText,
'uid': instance.uid,
'updatedAt': dateTimeToJson(instance.updatedAt),
'__typename': instance.$__typename,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,147 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'schema.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Input$AutoUpgradeSettingsInput _$Input$AutoUpgradeSettingsInputFromJson(
Map<String, dynamic> json) =>
Input$AutoUpgradeSettingsInput(
enableAutoUpgrade: json['enableAutoUpgrade'] as bool?,
allowReboot: json['allowReboot'] as bool?,
);
Map<String, dynamic> _$Input$AutoUpgradeSettingsInputToJson(
Input$AutoUpgradeSettingsInput instance) =>
<String, dynamic>{
'enableAutoUpgrade': instance.enableAutoUpgrade,
'allowReboot': instance.allowReboot,
};
Input$MigrateToBindsInput _$Input$MigrateToBindsInputFromJson(
Map<String, dynamic> json) =>
Input$MigrateToBindsInput(
emailBlockDevice: json['emailBlockDevice'] as String,
bitwardenBlockDevice: json['bitwardenBlockDevice'] as String,
giteaBlockDevice: json['giteaBlockDevice'] as String,
nextcloudBlockDevice: json['nextcloudBlockDevice'] as String,
pleromaBlockDevice: json['pleromaBlockDevice'] as String,
);
Map<String, dynamic> _$Input$MigrateToBindsInputToJson(
Input$MigrateToBindsInput instance) =>
<String, dynamic>{
'emailBlockDevice': instance.emailBlockDevice,
'bitwardenBlockDevice': instance.bitwardenBlockDevice,
'giteaBlockDevice': instance.giteaBlockDevice,
'nextcloudBlockDevice': instance.nextcloudBlockDevice,
'pleromaBlockDevice': instance.pleromaBlockDevice,
};
Input$MoveServiceInput _$Input$MoveServiceInputFromJson(
Map<String, dynamic> json) =>
Input$MoveServiceInput(
serviceId: json['serviceId'] as String,
location: json['location'] as String,
);
Map<String, dynamic> _$Input$MoveServiceInputToJson(
Input$MoveServiceInput instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
'location': instance.location,
};
Input$RecoveryKeyLimitsInput _$Input$RecoveryKeyLimitsInputFromJson(
Map<String, dynamic> json) =>
Input$RecoveryKeyLimitsInput(
expirationDate: _nullable$dateTimeFromJson(json['expirationDate']),
uses: json['uses'] as int?,
);
Map<String, dynamic> _$Input$RecoveryKeyLimitsInputToJson(
Input$RecoveryKeyLimitsInput instance) =>
<String, dynamic>{
'expirationDate': _nullable$dateTimeToJson(instance.expirationDate),
'uses': instance.uses,
};
Input$SshMutationInput _$Input$SshMutationInputFromJson(
Map<String, dynamic> json) =>
Input$SshMutationInput(
username: json['username'] as String,
sshKey: json['sshKey'] as String,
);
Map<String, dynamic> _$Input$SshMutationInputToJson(
Input$SshMutationInput instance) =>
<String, dynamic>{
'username': instance.username,
'sshKey': instance.sshKey,
};
Input$UseNewDeviceKeyInput _$Input$UseNewDeviceKeyInputFromJson(
Map<String, dynamic> json) =>
Input$UseNewDeviceKeyInput(
key: json['key'] as String,
deviceName: json['deviceName'] as String,
);
Map<String, dynamic> _$Input$UseNewDeviceKeyInputToJson(
Input$UseNewDeviceKeyInput instance) =>
<String, dynamic>{
'key': instance.key,
'deviceName': instance.deviceName,
};
Input$UseRecoveryKeyInput _$Input$UseRecoveryKeyInputFromJson(
Map<String, dynamic> json) =>
Input$UseRecoveryKeyInput(
key: json['key'] as String,
deviceName: json['deviceName'] as String,
);
Map<String, dynamic> _$Input$UseRecoveryKeyInputToJson(
Input$UseRecoveryKeyInput instance) =>
<String, dynamic>{
'key': instance.key,
'deviceName': instance.deviceName,
};
Input$UserMutationInput _$Input$UserMutationInputFromJson(
Map<String, dynamic> json) =>
Input$UserMutationInput(
username: json['username'] as String,
password: json['password'] as String,
);
Map<String, dynamic> _$Input$UserMutationInputToJson(
Input$UserMutationInput instance) =>
<String, dynamic>{
'username': instance.username,
'password': instance.password,
};
Fragment$dnsRecordFields _$Fragment$dnsRecordFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$dnsRecordFields(
content: json['content'] as String,
name: json['name'] as String,
priority: json['priority'] as int?,
recordType: json['recordType'] as String,
ttl: json['ttl'] as int,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$dnsRecordFieldsToJson(
Fragment$dnsRecordFields instance) =>
<String, dynamic>{
'content': instance.content,
'name': instance.name,
'priority': instance.priority,
'recordType': instance.recordType,
'ttl': instance.ttl,
'__typename': instance.$__typename,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,797 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_api.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$GetApiVersion _$Query$GetApiVersionFromJson(Map<String, dynamic> json) =>
Query$GetApiVersion(
api:
Query$GetApiVersion$api.fromJson(json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiVersionToJson(
Query$GetApiVersion instance) =>
<String, dynamic>{
'api': instance.api.toJson(),
'__typename': instance.$__typename,
};
Query$GetApiVersion$api _$Query$GetApiVersion$apiFromJson(
Map<String, dynamic> json) =>
Query$GetApiVersion$api(
version: json['version'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiVersion$apiToJson(
Query$GetApiVersion$api instance) =>
<String, dynamic>{
'version': instance.version,
'__typename': instance.$__typename,
};
Query$GetApiJobs _$Query$GetApiJobsFromJson(Map<String, dynamic> json) =>
Query$GetApiJobs(
jobs:
Query$GetApiJobs$jobs.fromJson(json['jobs'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiJobsToJson(Query$GetApiJobs instance) =>
<String, dynamic>{
'jobs': instance.jobs.toJson(),
'__typename': instance.$__typename,
};
Query$GetApiJobs$jobs _$Query$GetApiJobs$jobsFromJson(
Map<String, dynamic> json) =>
Query$GetApiJobs$jobs(
getJobs: (json['getJobs'] as List<dynamic>)
.map((e) =>
Query$GetApiJobs$jobs$getJobs.fromJson(e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiJobs$jobsToJson(
Query$GetApiJobs$jobs instance) =>
<String, dynamic>{
'getJobs': instance.getJobs.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetApiJobs$jobs$getJobs _$Query$GetApiJobs$jobs$getJobsFromJson(
Map<String, dynamic> json) =>
Query$GetApiJobs$jobs$getJobs(
createdAt: dateTimeFromJson(json['createdAt']),
description: json['description'] as String,
error: json['error'] as String?,
finishedAt: _nullable$dateTimeFromJson(json['finishedAt']),
name: json['name'] as String,
progress: json['progress'] as int?,
result: json['result'] as String?,
status: json['status'] as String,
statusText: json['statusText'] as String?,
uid: json['uid'] as String,
updatedAt: dateTimeFromJson(json['updatedAt']),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiJobs$jobs$getJobsToJson(
Query$GetApiJobs$jobs$getJobs instance) =>
<String, dynamic>{
'createdAt': dateTimeToJson(instance.createdAt),
'description': instance.description,
'error': instance.error,
'finishedAt': _nullable$dateTimeToJson(instance.finishedAt),
'name': instance.name,
'progress': instance.progress,
'result': instance.result,
'status': instance.status,
'statusText': instance.statusText,
'uid': instance.uid,
'updatedAt': dateTimeToJson(instance.updatedAt),
'__typename': instance.$__typename,
};
Variables$Mutation$RemoveJob _$Variables$Mutation$RemoveJobFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$RemoveJob(
jobId: json['jobId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$RemoveJobToJson(
Variables$Mutation$RemoveJob instance) =>
<String, dynamic>{
'jobId': instance.jobId,
};
Mutation$RemoveJob _$Mutation$RemoveJobFromJson(Map<String, dynamic> json) =>
Mutation$RemoveJob(
removeJob: Mutation$RemoveJob$removeJob.fromJson(
json['removeJob'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveJobToJson(Mutation$RemoveJob instance) =>
<String, dynamic>{
'removeJob': instance.removeJob.toJson(),
'__typename': instance.$__typename,
};
Mutation$RemoveJob$removeJob _$Mutation$RemoveJob$removeJobFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveJob$removeJob(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveJob$removeJobToJson(
Mutation$RemoveJob$removeJob instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RunSystemRebuild _$Mutation$RunSystemRebuildFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRebuild(
runSystemRebuild: Mutation$RunSystemRebuild$runSystemRebuild.fromJson(
json['runSystemRebuild'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRebuildToJson(
Mutation$RunSystemRebuild instance) =>
<String, dynamic>{
'runSystemRebuild': instance.runSystemRebuild.toJson(),
'__typename': instance.$__typename,
};
Mutation$RunSystemRebuild$runSystemRebuild
_$Mutation$RunSystemRebuild$runSystemRebuildFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRebuild$runSystemRebuild(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRebuild$runSystemRebuildToJson(
Mutation$RunSystemRebuild$runSystemRebuild instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RunSystemRollback _$Mutation$RunSystemRollbackFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRollback(
runSystemRollback: Mutation$RunSystemRollback$runSystemRollback.fromJson(
json['runSystemRollback'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRollbackToJson(
Mutation$RunSystemRollback instance) =>
<String, dynamic>{
'runSystemRollback': instance.runSystemRollback.toJson(),
'__typename': instance.$__typename,
};
Mutation$RunSystemRollback$runSystemRollback
_$Mutation$RunSystemRollback$runSystemRollbackFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRollback$runSystemRollback(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRollback$runSystemRollbackToJson(
Mutation$RunSystemRollback$runSystemRollback instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RunSystemUpgrade _$Mutation$RunSystemUpgradeFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemUpgrade(
runSystemUpgrade: Mutation$RunSystemUpgrade$runSystemUpgrade.fromJson(
json['runSystemUpgrade'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemUpgradeToJson(
Mutation$RunSystemUpgrade instance) =>
<String, dynamic>{
'runSystemUpgrade': instance.runSystemUpgrade.toJson(),
'__typename': instance.$__typename,
};
Mutation$RunSystemUpgrade$runSystemUpgrade
_$Mutation$RunSystemUpgrade$runSystemUpgradeFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemUpgrade$runSystemUpgrade(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemUpgrade$runSystemUpgradeToJson(
Mutation$RunSystemUpgrade$runSystemUpgrade instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$PullRepositoryChanges _$Mutation$PullRepositoryChangesFromJson(
Map<String, dynamic> json) =>
Mutation$PullRepositoryChanges(
pullRepositoryChanges:
Mutation$PullRepositoryChanges$pullRepositoryChanges.fromJson(
json['pullRepositoryChanges'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$PullRepositoryChangesToJson(
Mutation$PullRepositoryChanges instance) =>
<String, dynamic>{
'pullRepositoryChanges': instance.pullRepositoryChanges.toJson(),
'__typename': instance.$__typename,
};
Mutation$PullRepositoryChanges$pullRepositoryChanges
_$Mutation$PullRepositoryChanges$pullRepositoryChangesFromJson(
Map<String, dynamic> json) =>
Mutation$PullRepositoryChanges$pullRepositoryChanges(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Mutation$PullRepositoryChanges$pullRepositoryChangesToJson(
Mutation$PullRepositoryChanges$pullRepositoryChanges instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RebootSystem _$Mutation$RebootSystemFromJson(
Map<String, dynamic> json) =>
Mutation$RebootSystem(
rebootSystem: Mutation$RebootSystem$rebootSystem.fromJson(
json['rebootSystem'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RebootSystemToJson(
Mutation$RebootSystem instance) =>
<String, dynamic>{
'rebootSystem': instance.rebootSystem.toJson(),
'__typename': instance.$__typename,
};
Mutation$RebootSystem$rebootSystem _$Mutation$RebootSystem$rebootSystemFromJson(
Map<String, dynamic> json) =>
Mutation$RebootSystem$rebootSystem(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RebootSystem$rebootSystemToJson(
Mutation$RebootSystem$rebootSystem instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$SystemServerProvider _$Query$SystemServerProviderFromJson(
Map<String, dynamic> json) =>
Query$SystemServerProvider(
system: Query$SystemServerProvider$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemServerProviderToJson(
Query$SystemServerProvider instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$SystemServerProvider$system _$Query$SystemServerProvider$systemFromJson(
Map<String, dynamic> json) =>
Query$SystemServerProvider$system(
provider: Query$SystemServerProvider$system$provider.fromJson(
json['provider'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemServerProvider$systemToJson(
Query$SystemServerProvider$system instance) =>
<String, dynamic>{
'provider': instance.provider.toJson(),
'__typename': instance.$__typename,
};
Query$SystemServerProvider$system$provider
_$Query$SystemServerProvider$system$providerFromJson(
Map<String, dynamic> json) =>
Query$SystemServerProvider$system$provider(
provider: $enumDecode(_$Enum$ServerProviderEnumMap, json['provider'],
unknownValue: Enum$ServerProvider.$unknown),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemServerProvider$system$providerToJson(
Query$SystemServerProvider$system$provider instance) =>
<String, dynamic>{
'provider': _$Enum$ServerProviderEnumMap[instance.provider]!,
'__typename': instance.$__typename,
};
const _$Enum$ServerProviderEnumMap = {
Enum$ServerProvider.HETZNER: 'HETZNER',
Enum$ServerProvider.DIGITALOCEAN: 'DIGITALOCEAN',
Enum$ServerProvider.$unknown: r'$unknown',
};
Query$GetApiTokens _$Query$GetApiTokensFromJson(Map<String, dynamic> json) =>
Query$GetApiTokens(
api: Query$GetApiTokens$api.fromJson(json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiTokensToJson(Query$GetApiTokens instance) =>
<String, dynamic>{
'api': instance.api.toJson(),
'__typename': instance.$__typename,
};
Query$GetApiTokens$api _$Query$GetApiTokens$apiFromJson(
Map<String, dynamic> json) =>
Query$GetApiTokens$api(
devices: (json['devices'] as List<dynamic>)
.map((e) => Query$GetApiTokens$api$devices.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiTokens$apiToJson(
Query$GetApiTokens$api instance) =>
<String, dynamic>{
'devices': instance.devices.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetApiTokens$api$devices _$Query$GetApiTokens$api$devicesFromJson(
Map<String, dynamic> json) =>
Query$GetApiTokens$api$devices(
creationDate: dateTimeFromJson(json['creationDate']),
isCaller: json['isCaller'] as bool,
name: json['name'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiTokens$api$devicesToJson(
Query$GetApiTokens$api$devices instance) =>
<String, dynamic>{
'creationDate': dateTimeToJson(instance.creationDate),
'isCaller': instance.isCaller,
'name': instance.name,
'__typename': instance.$__typename,
};
Query$RecoveryKey _$Query$RecoveryKeyFromJson(Map<String, dynamic> json) =>
Query$RecoveryKey(
api: Query$RecoveryKey$api.fromJson(json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$RecoveryKeyToJson(Query$RecoveryKey instance) =>
<String, dynamic>{
'api': instance.api.toJson(),
'__typename': instance.$__typename,
};
Query$RecoveryKey$api _$Query$RecoveryKey$apiFromJson(
Map<String, dynamic> json) =>
Query$RecoveryKey$api(
recoveryKey: Query$RecoveryKey$api$recoveryKey.fromJson(
json['recoveryKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$RecoveryKey$apiToJson(
Query$RecoveryKey$api instance) =>
<String, dynamic>{
'recoveryKey': instance.recoveryKey.toJson(),
'__typename': instance.$__typename,
};
Query$RecoveryKey$api$recoveryKey _$Query$RecoveryKey$api$recoveryKeyFromJson(
Map<String, dynamic> json) =>
Query$RecoveryKey$api$recoveryKey(
creationDate: _nullable$dateTimeFromJson(json['creationDate']),
exists: json['exists'] as bool,
expirationDate: _nullable$dateTimeFromJson(json['expirationDate']),
usesLeft: json['usesLeft'] as int?,
valid: json['valid'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$RecoveryKey$api$recoveryKeyToJson(
Query$RecoveryKey$api$recoveryKey instance) =>
<String, dynamic>{
'creationDate': _nullable$dateTimeToJson(instance.creationDate),
'exists': instance.exists,
'expirationDate': _nullable$dateTimeToJson(instance.expirationDate),
'usesLeft': instance.usesLeft,
'valid': instance.valid,
'__typename': instance.$__typename,
};
Variables$Mutation$GetNewRecoveryApiKey
_$Variables$Mutation$GetNewRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$GetNewRecoveryApiKey(
limits: json['limits'] == null
? null
: Input$RecoveryKeyLimitsInput.fromJson(
json['limits'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$GetNewRecoveryApiKeyToJson(
Variables$Mutation$GetNewRecoveryApiKey instance) =>
<String, dynamic>{
'limits': instance.limits?.toJson(),
};
Mutation$GetNewRecoveryApiKey _$Mutation$GetNewRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewRecoveryApiKey(
getNewRecoveryApiKey:
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey.fromJson(
json['getNewRecoveryApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$GetNewRecoveryApiKeyToJson(
Mutation$GetNewRecoveryApiKey instance) =>
<String, dynamic>{
'getNewRecoveryApiKey': instance.getNewRecoveryApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey
_$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
key: json['key'] as String?,
);
Map<String, dynamic> _$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKeyToJson(
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'key': instance.key,
};
Variables$Mutation$UseRecoveryApiKey
_$Variables$Mutation$UseRecoveryApiKeyFromJson(Map<String, dynamic> json) =>
Variables$Mutation$UseRecoveryApiKey(
input: Input$UseRecoveryKeyInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$UseRecoveryApiKeyToJson(
Variables$Mutation$UseRecoveryApiKey instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$UseRecoveryApiKey _$Mutation$UseRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$UseRecoveryApiKey(
useRecoveryApiKey: Mutation$UseRecoveryApiKey$useRecoveryApiKey.fromJson(
json['useRecoveryApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UseRecoveryApiKeyToJson(
Mutation$UseRecoveryApiKey instance) =>
<String, dynamic>{
'useRecoveryApiKey': instance.useRecoveryApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$UseRecoveryApiKey$useRecoveryApiKey
_$Mutation$UseRecoveryApiKey$useRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$UseRecoveryApiKey$useRecoveryApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
token: json['token'] as String?,
);
Map<String, dynamic> _$Mutation$UseRecoveryApiKey$useRecoveryApiKeyToJson(
Mutation$UseRecoveryApiKey$useRecoveryApiKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'token': instance.token,
};
Mutation$RefreshDeviceApiToken _$Mutation$RefreshDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$RefreshDeviceApiToken(
refreshDeviceApiToken:
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken.fromJson(
json['refreshDeviceApiToken'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RefreshDeviceApiTokenToJson(
Mutation$RefreshDeviceApiToken instance) =>
<String, dynamic>{
'refreshDeviceApiToken': instance.refreshDeviceApiToken.toJson(),
'__typename': instance.$__typename,
};
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken
_$Mutation$RefreshDeviceApiToken$refreshDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
token: json['token'] as String?,
);
Map<String, dynamic>
_$Mutation$RefreshDeviceApiToken$refreshDeviceApiTokenToJson(
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'token': instance.token,
};
Variables$Mutation$DeleteDeviceApiToken
_$Variables$Mutation$DeleteDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$DeleteDeviceApiToken(
device: json['device'] as String,
);
Map<String, dynamic> _$Variables$Mutation$DeleteDeviceApiTokenToJson(
Variables$Mutation$DeleteDeviceApiToken instance) =>
<String, dynamic>{
'device': instance.device,
};
Mutation$DeleteDeviceApiToken _$Mutation$DeleteDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$DeleteDeviceApiToken(
deleteDeviceApiToken:
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken.fromJson(
json['deleteDeviceApiToken'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteDeviceApiTokenToJson(
Mutation$DeleteDeviceApiToken instance) =>
<String, dynamic>{
'deleteDeviceApiToken': instance.deleteDeviceApiToken.toJson(),
'__typename': instance.$__typename,
};
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken
_$Mutation$DeleteDeviceApiToken$deleteDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteDeviceApiToken$deleteDeviceApiTokenToJson(
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$GetNewDeviceApiKey _$Mutation$GetNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewDeviceApiKey(
getNewDeviceApiKey:
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey.fromJson(
json['getNewDeviceApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$GetNewDeviceApiKeyToJson(
Mutation$GetNewDeviceApiKey instance) =>
<String, dynamic>{
'getNewDeviceApiKey': instance.getNewDeviceApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey
_$Mutation$GetNewDeviceApiKey$getNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
key: json['key'] as String?,
);
Map<String, dynamic> _$Mutation$GetNewDeviceApiKey$getNewDeviceApiKeyToJson(
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'key': instance.key,
};
Mutation$InvalidateNewDeviceApiKey _$Mutation$InvalidateNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$InvalidateNewDeviceApiKey(
invalidateNewDeviceApiKey:
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey.fromJson(
json['invalidateNewDeviceApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$InvalidateNewDeviceApiKeyToJson(
Mutation$InvalidateNewDeviceApiKey instance) =>
<String, dynamic>{
'invalidateNewDeviceApiKey': instance.invalidateNewDeviceApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey
_$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKeyToJson(
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey
instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$AuthorizeWithNewDeviceApiKey
_$Variables$Mutation$AuthorizeWithNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$AuthorizeWithNewDeviceApiKey(
input: Input$UseNewDeviceKeyInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$AuthorizeWithNewDeviceApiKeyToJson(
Variables$Mutation$AuthorizeWithNewDeviceApiKey instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$AuthorizeWithNewDeviceApiKey
_$Mutation$AuthorizeWithNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$AuthorizeWithNewDeviceApiKey(
authorizeWithNewDeviceApiKey:
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey
.fromJson(json['authorizeWithNewDeviceApiKey']
as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$AuthorizeWithNewDeviceApiKeyToJson(
Mutation$AuthorizeWithNewDeviceApiKey instance) =>
<String, dynamic>{
'authorizeWithNewDeviceApiKey':
instance.authorizeWithNewDeviceApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey
_$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
token: json['token'] as String?,
);
Map<String, dynamic>
_$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKeyToJson(
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey
instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'token': instance.token,
};

View File

@ -1,315 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_settings.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$SystemSettings _$Query$SystemSettingsFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings(
system: Query$SystemSettings$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettingsToJson(
Query$SystemSettings instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$SystemSettings$system _$Query$SystemSettings$systemFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings$system(
settings: Query$SystemSettings$system$settings.fromJson(
json['settings'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$systemToJson(
Query$SystemSettings$system instance) =>
<String, dynamic>{
'settings': instance.settings.toJson(),
'__typename': instance.$__typename,
};
Query$SystemSettings$system$settings
_$Query$SystemSettings$system$settingsFromJson(Map<String, dynamic> json) =>
Query$SystemSettings$system$settings(
autoUpgrade:
Query$SystemSettings$system$settings$autoUpgrade.fromJson(
json['autoUpgrade'] as Map<String, dynamic>),
ssh: Query$SystemSettings$system$settings$ssh.fromJson(
json['ssh'] as Map<String, dynamic>),
timezone: json['timezone'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$system$settingsToJson(
Query$SystemSettings$system$settings instance) =>
<String, dynamic>{
'autoUpgrade': instance.autoUpgrade.toJson(),
'ssh': instance.ssh.toJson(),
'timezone': instance.timezone,
'__typename': instance.$__typename,
};
Query$SystemSettings$system$settings$autoUpgrade
_$Query$SystemSettings$system$settings$autoUpgradeFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings$system$settings$autoUpgrade(
allowReboot: json['allowReboot'] as bool,
enable: json['enable'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$system$settings$autoUpgradeToJson(
Query$SystemSettings$system$settings$autoUpgrade instance) =>
<String, dynamic>{
'allowReboot': instance.allowReboot,
'enable': instance.enable,
'__typename': instance.$__typename,
};
Query$SystemSettings$system$settings$ssh
_$Query$SystemSettings$system$settings$sshFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings$system$settings$ssh(
enable: json['enable'] as bool,
passwordAuthentication: json['passwordAuthentication'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$system$settings$sshToJson(
Query$SystemSettings$system$settings$ssh instance) =>
<String, dynamic>{
'enable': instance.enable,
'passwordAuthentication': instance.passwordAuthentication,
'__typename': instance.$__typename,
};
Query$SystemIsUsingBinds _$Query$SystemIsUsingBindsFromJson(
Map<String, dynamic> json) =>
Query$SystemIsUsingBinds(
system: Query$SystemIsUsingBinds$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemIsUsingBindsToJson(
Query$SystemIsUsingBinds instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$SystemIsUsingBinds$system _$Query$SystemIsUsingBinds$systemFromJson(
Map<String, dynamic> json) =>
Query$SystemIsUsingBinds$system(
info: Query$SystemIsUsingBinds$system$info.fromJson(
json['info'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemIsUsingBinds$systemToJson(
Query$SystemIsUsingBinds$system instance) =>
<String, dynamic>{
'info': instance.info.toJson(),
'__typename': instance.$__typename,
};
Query$SystemIsUsingBinds$system$info
_$Query$SystemIsUsingBinds$system$infoFromJson(Map<String, dynamic> json) =>
Query$SystemIsUsingBinds$system$info(
usingBinds: json['usingBinds'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemIsUsingBinds$system$infoToJson(
Query$SystemIsUsingBinds$system$info instance) =>
<String, dynamic>{
'usingBinds': instance.usingBinds,
'__typename': instance.$__typename,
};
Query$DomainInfo _$Query$DomainInfoFromJson(Map<String, dynamic> json) =>
Query$DomainInfo(
system: Query$DomainInfo$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$DomainInfoToJson(Query$DomainInfo instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$DomainInfo$system _$Query$DomainInfo$systemFromJson(
Map<String, dynamic> json) =>
Query$DomainInfo$system(
domainInfo: Query$DomainInfo$system$domainInfo.fromJson(
json['domainInfo'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$DomainInfo$systemToJson(
Query$DomainInfo$system instance) =>
<String, dynamic>{
'domainInfo': instance.domainInfo.toJson(),
'__typename': instance.$__typename,
};
Query$DomainInfo$system$domainInfo _$Query$DomainInfo$system$domainInfoFromJson(
Map<String, dynamic> json) =>
Query$DomainInfo$system$domainInfo(
domain: json['domain'] as String,
hostname: json['hostname'] as String,
provider: $enumDecode(_$Enum$DnsProviderEnumMap, json['provider'],
unknownValue: Enum$DnsProvider.$unknown),
requiredDnsRecords: (json['requiredDnsRecords'] as List<dynamic>)
.map((e) =>
Fragment$dnsRecordFields.fromJson(e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$DomainInfo$system$domainInfoToJson(
Query$DomainInfo$system$domainInfo instance) =>
<String, dynamic>{
'domain': instance.domain,
'hostname': instance.hostname,
'provider': _$Enum$DnsProviderEnumMap[instance.provider]!,
'requiredDnsRecords':
instance.requiredDnsRecords.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
const _$Enum$DnsProviderEnumMap = {
Enum$DnsProvider.CLOUDFLARE: 'CLOUDFLARE',
Enum$DnsProvider.$unknown: r'$unknown',
};
Variables$Mutation$ChangeTimezone _$Variables$Mutation$ChangeTimezoneFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$ChangeTimezone(
timezone: json['timezone'] as String,
);
Map<String, dynamic> _$Variables$Mutation$ChangeTimezoneToJson(
Variables$Mutation$ChangeTimezone instance) =>
<String, dynamic>{
'timezone': instance.timezone,
};
Mutation$ChangeTimezone _$Mutation$ChangeTimezoneFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeTimezone(
changeTimezone: Mutation$ChangeTimezone$changeTimezone.fromJson(
json['changeTimezone'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ChangeTimezoneToJson(
Mutation$ChangeTimezone instance) =>
<String, dynamic>{
'changeTimezone': instance.changeTimezone.toJson(),
'__typename': instance.$__typename,
};
Mutation$ChangeTimezone$changeTimezone
_$Mutation$ChangeTimezone$changeTimezoneFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeTimezone$changeTimezone(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
timezone: json['timezone'] as String?,
);
Map<String, dynamic> _$Mutation$ChangeTimezone$changeTimezoneToJson(
Mutation$ChangeTimezone$changeTimezone instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'timezone': instance.timezone,
};
Variables$Mutation$ChangeAutoUpgradeSettings
_$Variables$Mutation$ChangeAutoUpgradeSettingsFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$ChangeAutoUpgradeSettings(
settings: Input$AutoUpgradeSettingsInput.fromJson(
json['settings'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$ChangeAutoUpgradeSettingsToJson(
Variables$Mutation$ChangeAutoUpgradeSettings instance) =>
<String, dynamic>{
'settings': instance.settings.toJson(),
};
Mutation$ChangeAutoUpgradeSettings _$Mutation$ChangeAutoUpgradeSettingsFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeAutoUpgradeSettings(
changeAutoUpgradeSettings:
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings.fromJson(
json['changeAutoUpgradeSettings'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ChangeAutoUpgradeSettingsToJson(
Mutation$ChangeAutoUpgradeSettings instance) =>
<String, dynamic>{
'changeAutoUpgradeSettings': instance.changeAutoUpgradeSettings.toJson(),
'__typename': instance.$__typename,
};
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings
_$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettingsFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
allowReboot: json['allowReboot'] as bool,
enableAutoUpgrade: json['enableAutoUpgrade'] as bool,
);
Map<String, dynamic>
_$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettingsToJson(
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings
instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'allowReboot': instance.allowReboot,
'enableAutoUpgrade': instance.enableAutoUpgrade,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,458 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'services.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$AllServices _$Query$AllServicesFromJson(Map<String, dynamic> json) =>
Query$AllServices(
services: Query$AllServices$services.fromJson(
json['services'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServicesToJson(Query$AllServices instance) =>
<String, dynamic>{
'services': instance.services.toJson(),
'__typename': instance.$__typename,
};
Query$AllServices$services _$Query$AllServices$servicesFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services(
allServices: (json['allServices'] as List<dynamic>)
.map((e) => Query$AllServices$services$allServices.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServices$servicesToJson(
Query$AllServices$services instance) =>
<String, dynamic>{
'allServices': instance.allServices.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$AllServices$services$allServices
_$Query$AllServices$services$allServicesFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices(
description: json['description'] as String,
displayName: json['displayName'] as String,
dnsRecords: (json['dnsRecords'] as List<dynamic>?)
?.map((e) =>
Fragment$dnsRecordFields.fromJson(e as Map<String, dynamic>))
.toList(),
id: json['id'] as String,
isEnabled: json['isEnabled'] as bool,
isMovable: json['isMovable'] as bool,
isRequired: json['isRequired'] as bool,
status: $enumDecode(_$Enum$ServiceStatusEnumEnumMap, json['status'],
unknownValue: Enum$ServiceStatusEnum.$unknown),
storageUsage:
Query$AllServices$services$allServices$storageUsage.fromJson(
json['storageUsage'] as Map<String, dynamic>),
svgIcon: json['svgIcon'] as String,
url: json['url'] as String?,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServices$services$allServicesToJson(
Query$AllServices$services$allServices instance) =>
<String, dynamic>{
'description': instance.description,
'displayName': instance.displayName,
'dnsRecords': instance.dnsRecords?.map((e) => e.toJson()).toList(),
'id': instance.id,
'isEnabled': instance.isEnabled,
'isMovable': instance.isMovable,
'isRequired': instance.isRequired,
'status': _$Enum$ServiceStatusEnumEnumMap[instance.status]!,
'storageUsage': instance.storageUsage.toJson(),
'svgIcon': instance.svgIcon,
'url': instance.url,
'__typename': instance.$__typename,
};
const _$Enum$ServiceStatusEnumEnumMap = {
Enum$ServiceStatusEnum.ACTIVATING: 'ACTIVATING',
Enum$ServiceStatusEnum.ACTIVE: 'ACTIVE',
Enum$ServiceStatusEnum.DEACTIVATING: 'DEACTIVATING',
Enum$ServiceStatusEnum.FAILED: 'FAILED',
Enum$ServiceStatusEnum.INACTIVE: 'INACTIVE',
Enum$ServiceStatusEnum.OFF: 'OFF',
Enum$ServiceStatusEnum.RELOADING: 'RELOADING',
Enum$ServiceStatusEnum.$unknown: r'$unknown',
};
Query$AllServices$services$allServices$storageUsage
_$Query$AllServices$services$allServices$storageUsageFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices$storageUsage(
title: json['title'] as String,
usedSpace: json['usedSpace'] as String,
volume: json['volume'] == null
? null
: Query$AllServices$services$allServices$storageUsage$volume
.fromJson(json['volume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Query$AllServices$services$allServices$storageUsageToJson(
Query$AllServices$services$allServices$storageUsage instance) =>
<String, dynamic>{
'title': instance.title,
'usedSpace': instance.usedSpace,
'volume': instance.volume?.toJson(),
'__typename': instance.$__typename,
};
Query$AllServices$services$allServices$storageUsage$volume
_$Query$AllServices$services$allServices$storageUsage$volumeFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices$storageUsage$volume(
name: json['name'] as String,
$__typename: json['__typename'] as String,
);
Map<String,
dynamic> _$Query$AllServices$services$allServices$storageUsage$volumeToJson(
Query$AllServices$services$allServices$storageUsage$volume instance) =>
<String, dynamic>{
'name': instance.name,
'__typename': instance.$__typename,
};
Variables$Mutation$EnableService _$Variables$Mutation$EnableServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$EnableService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$EnableServiceToJson(
Variables$Mutation$EnableService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$EnableService _$Mutation$EnableServiceFromJson(
Map<String, dynamic> json) =>
Mutation$EnableService(
enableService: Mutation$EnableService$enableService.fromJson(
json['enableService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$EnableServiceToJson(
Mutation$EnableService instance) =>
<String, dynamic>{
'enableService': instance.enableService.toJson(),
'__typename': instance.$__typename,
};
Mutation$EnableService$enableService
_$Mutation$EnableService$enableServiceFromJson(Map<String, dynamic> json) =>
Mutation$EnableService$enableService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$EnableService$enableServiceToJson(
Mutation$EnableService$enableService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$DisableService _$Variables$Mutation$DisableServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$DisableService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$DisableServiceToJson(
Variables$Mutation$DisableService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$DisableService _$Mutation$DisableServiceFromJson(
Map<String, dynamic> json) =>
Mutation$DisableService(
disableService: Mutation$DisableService$disableService.fromJson(
json['disableService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DisableServiceToJson(
Mutation$DisableService instance) =>
<String, dynamic>{
'disableService': instance.disableService.toJson(),
'__typename': instance.$__typename,
};
Mutation$DisableService$disableService
_$Mutation$DisableService$disableServiceFromJson(
Map<String, dynamic> json) =>
Mutation$DisableService$disableService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DisableService$disableServiceToJson(
Mutation$DisableService$disableService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$StopService _$Variables$Mutation$StopServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$StopService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$StopServiceToJson(
Variables$Mutation$StopService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$StopService _$Mutation$StopServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StopService(
stopService: Mutation$StopService$stopService.fromJson(
json['stopService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StopServiceToJson(
Mutation$StopService instance) =>
<String, dynamic>{
'stopService': instance.stopService.toJson(),
'__typename': instance.$__typename,
};
Mutation$StopService$stopService _$Mutation$StopService$stopServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StopService$stopService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StopService$stopServiceToJson(
Mutation$StopService$stopService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$StartService _$Variables$Mutation$StartServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$StartService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$StartServiceToJson(
Variables$Mutation$StartService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$StartService _$Mutation$StartServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StartService(
startService: Mutation$StartService$startService.fromJson(
json['startService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StartServiceToJson(
Mutation$StartService instance) =>
<String, dynamic>{
'startService': instance.startService.toJson(),
'__typename': instance.$__typename,
};
Mutation$StartService$startService _$Mutation$StartService$startServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StartService$startService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StartService$startServiceToJson(
Mutation$StartService$startService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$RestartService _$Variables$Mutation$RestartServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$RestartService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$RestartServiceToJson(
Variables$Mutation$RestartService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$RestartService _$Mutation$RestartServiceFromJson(
Map<String, dynamic> json) =>
Mutation$RestartService(
restartService: Mutation$RestartService$restartService.fromJson(
json['restartService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RestartServiceToJson(
Mutation$RestartService instance) =>
<String, dynamic>{
'restartService': instance.restartService.toJson(),
'__typename': instance.$__typename,
};
Mutation$RestartService$restartService
_$Mutation$RestartService$restartServiceFromJson(
Map<String, dynamic> json) =>
Mutation$RestartService$restartService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RestartService$restartServiceToJson(
Mutation$RestartService$restartService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$MoveService _$Variables$Mutation$MoveServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$MoveService(
input: Input$MoveServiceInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$MoveServiceToJson(
Variables$Mutation$MoveService instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$MoveService _$Mutation$MoveServiceFromJson(
Map<String, dynamic> json) =>
Mutation$MoveService(
moveService: Mutation$MoveService$moveService.fromJson(
json['moveService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MoveServiceToJson(
Mutation$MoveService instance) =>
<String, dynamic>{
'moveService': instance.moveService.toJson(),
'__typename': instance.$__typename,
};
Mutation$MoveService$moveService _$Mutation$MoveService$moveServiceFromJson(
Map<String, dynamic> json) =>
Mutation$MoveService$moveService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
job: json['job'] == null
? null
: Mutation$MoveService$moveService$job.fromJson(
json['job'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$MoveService$moveServiceToJson(
Mutation$MoveService$moveService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'job': instance.job?.toJson(),
};
Mutation$MoveService$moveService$job
_$Mutation$MoveService$moveService$jobFromJson(Map<String, dynamic> json) =>
Mutation$MoveService$moveService$job(
createdAt: dateTimeFromJson(json['createdAt']),
description: json['description'] as String,
error: json['error'] as String?,
finishedAt: _nullable$dateTimeFromJson(json['finishedAt']),
name: json['name'] as String,
progress: json['progress'] as int?,
result: json['result'] as String?,
status: json['status'] as String,
statusText: json['statusText'] as String?,
uid: json['uid'] as String,
updatedAt: dateTimeFromJson(json['updatedAt']),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MoveService$moveService$jobToJson(
Mutation$MoveService$moveService$job instance) =>
<String, dynamic>{
'createdAt': dateTimeToJson(instance.createdAt),
'description': instance.description,
'error': instance.error,
'finishedAt': _nullable$dateTimeToJson(instance.finishedAt),
'name': instance.name,
'progress': instance.progress,
'result': instance.result,
'status': instance.status,
'statusText': instance.statusText,
'uid': instance.uid,
'updatedAt': dateTimeToJson(instance.updatedAt),
'__typename': instance.$__typename,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,366 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'users.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Fragment$userFields _$Fragment$userFieldsFromJson(Map<String, dynamic> json) =>
Fragment$userFields(
username: json['username'] as String,
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
sshKeys:
(json['sshKeys'] as List<dynamic>).map((e) => e as String).toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$userFieldsToJson(
Fragment$userFields instance) =>
<String, dynamic>{
'username': instance.username,
'userType': _$Enum$UserTypeEnumMap[instance.userType]!,
'sshKeys': instance.sshKeys,
'__typename': instance.$__typename,
};
const _$Enum$UserTypeEnumMap = {
Enum$UserType.NORMAL: 'NORMAL',
Enum$UserType.PRIMARY: 'PRIMARY',
Enum$UserType.ROOT: 'ROOT',
Enum$UserType.$unknown: r'$unknown',
};
Query$AllUsers _$Query$AllUsersFromJson(Map<String, dynamic> json) =>
Query$AllUsers(
users:
Query$AllUsers$users.fromJson(json['users'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllUsersToJson(Query$AllUsers instance) =>
<String, dynamic>{
'users': instance.users.toJson(),
'__typename': instance.$__typename,
};
Query$AllUsers$users _$Query$AllUsers$usersFromJson(
Map<String, dynamic> json) =>
Query$AllUsers$users(
allUsers: (json['allUsers'] as List<dynamic>)
.map((e) => Fragment$userFields.fromJson(e as Map<String, dynamic>))
.toList(),
rootUser: json['rootUser'] == null
? null
: Fragment$userFields.fromJson(
json['rootUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllUsers$usersToJson(
Query$AllUsers$users instance) =>
<String, dynamic>{
'allUsers': instance.allUsers.map((e) => e.toJson()).toList(),
'rootUser': instance.rootUser?.toJson(),
'__typename': instance.$__typename,
};
Variables$Query$GetUser _$Variables$Query$GetUserFromJson(
Map<String, dynamic> json) =>
Variables$Query$GetUser(
username: json['username'] as String,
);
Map<String, dynamic> _$Variables$Query$GetUserToJson(
Variables$Query$GetUser instance) =>
<String, dynamic>{
'username': instance.username,
};
Query$GetUser _$Query$GetUserFromJson(Map<String, dynamic> json) =>
Query$GetUser(
users:
Query$GetUser$users.fromJson(json['users'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetUserToJson(Query$GetUser instance) =>
<String, dynamic>{
'users': instance.users.toJson(),
'__typename': instance.$__typename,
};
Query$GetUser$users _$Query$GetUser$usersFromJson(Map<String, dynamic> json) =>
Query$GetUser$users(
getUser: json['getUser'] == null
? null
: Fragment$userFields.fromJson(
json['getUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetUser$usersToJson(
Query$GetUser$users instance) =>
<String, dynamic>{
'getUser': instance.getUser?.toJson(),
'__typename': instance.$__typename,
};
Variables$Mutation$CreateUser _$Variables$Mutation$CreateUserFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$CreateUser(
user: Input$UserMutationInput.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$CreateUserToJson(
Variables$Mutation$CreateUser instance) =>
<String, dynamic>{
'user': instance.user.toJson(),
};
Mutation$CreateUser _$Mutation$CreateUserFromJson(Map<String, dynamic> json) =>
Mutation$CreateUser(
createUser: Mutation$CreateUser$createUser.fromJson(
json['createUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$CreateUserToJson(
Mutation$CreateUser instance) =>
<String, dynamic>{
'createUser': instance.createUser.toJson(),
'__typename': instance.$__typename,
};
Mutation$CreateUser$createUser _$Mutation$CreateUser$createUserFromJson(
Map<String, dynamic> json) =>
Mutation$CreateUser$createUser(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Fragment$userFields.fromJson(json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$CreateUser$createUserToJson(
Mutation$CreateUser$createUser instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Variables$Mutation$DeleteUser _$Variables$Mutation$DeleteUserFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$DeleteUser(
username: json['username'] as String,
);
Map<String, dynamic> _$Variables$Mutation$DeleteUserToJson(
Variables$Mutation$DeleteUser instance) =>
<String, dynamic>{
'username': instance.username,
};
Mutation$DeleteUser _$Mutation$DeleteUserFromJson(Map<String, dynamic> json) =>
Mutation$DeleteUser(
deleteUser: Mutation$DeleteUser$deleteUser.fromJson(
json['deleteUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteUserToJson(
Mutation$DeleteUser instance) =>
<String, dynamic>{
'deleteUser': instance.deleteUser.toJson(),
'__typename': instance.$__typename,
};
Mutation$DeleteUser$deleteUser _$Mutation$DeleteUser$deleteUserFromJson(
Map<String, dynamic> json) =>
Mutation$DeleteUser$deleteUser(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteUser$deleteUserToJson(
Mutation$DeleteUser$deleteUser instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$UpdateUser _$Variables$Mutation$UpdateUserFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$UpdateUser(
user: Input$UserMutationInput.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$UpdateUserToJson(
Variables$Mutation$UpdateUser instance) =>
<String, dynamic>{
'user': instance.user.toJson(),
};
Mutation$UpdateUser _$Mutation$UpdateUserFromJson(Map<String, dynamic> json) =>
Mutation$UpdateUser(
updateUser: Mutation$UpdateUser$updateUser.fromJson(
json['updateUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UpdateUserToJson(
Mutation$UpdateUser instance) =>
<String, dynamic>{
'updateUser': instance.updateUser.toJson(),
'__typename': instance.$__typename,
};
Mutation$UpdateUser$updateUser _$Mutation$UpdateUser$updateUserFromJson(
Map<String, dynamic> json) =>
Mutation$UpdateUser$updateUser(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Fragment$userFields.fromJson(json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$UpdateUser$updateUserToJson(
Mutation$UpdateUser$updateUser instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Variables$Mutation$AddSshKey _$Variables$Mutation$AddSshKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$AddSshKey(
sshInput: Input$SshMutationInput.fromJson(
json['sshInput'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$AddSshKeyToJson(
Variables$Mutation$AddSshKey instance) =>
<String, dynamic>{
'sshInput': instance.sshInput.toJson(),
};
Mutation$AddSshKey _$Mutation$AddSshKeyFromJson(Map<String, dynamic> json) =>
Mutation$AddSshKey(
addSshKey: Mutation$AddSshKey$addSshKey.fromJson(
json['addSshKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$AddSshKeyToJson(Mutation$AddSshKey instance) =>
<String, dynamic>{
'addSshKey': instance.addSshKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$AddSshKey$addSshKey _$Mutation$AddSshKey$addSshKeyFromJson(
Map<String, dynamic> json) =>
Mutation$AddSshKey$addSshKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Fragment$userFields.fromJson(json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$AddSshKey$addSshKeyToJson(
Mutation$AddSshKey$addSshKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Variables$Mutation$RemoveSshKey _$Variables$Mutation$RemoveSshKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$RemoveSshKey(
sshInput: Input$SshMutationInput.fromJson(
json['sshInput'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$RemoveSshKeyToJson(
Variables$Mutation$RemoveSshKey instance) =>
<String, dynamic>{
'sshInput': instance.sshInput.toJson(),
};
Mutation$RemoveSshKey _$Mutation$RemoveSshKeyFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveSshKey(
removeSshKey: Mutation$RemoveSshKey$removeSshKey.fromJson(
json['removeSshKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveSshKeyToJson(
Mutation$RemoveSshKey instance) =>
<String, dynamic>{
'removeSshKey': instance.removeSshKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$RemoveSshKey$removeSshKey _$Mutation$RemoveSshKey$removeSshKeyFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveSshKey$removeSshKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Fragment$userFields.fromJson(json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$RemoveSshKey$removeSshKeyToJson(
Mutation$RemoveSshKey$removeSshKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};

View File

@ -65,9 +65,11 @@ class ConsoleInterceptor extends InterceptorsWrapper {
final RequestInterceptorHandler handler,
) async {
addMessage(
Message(
text:
'request-uri: ${options.uri}\nheaders: ${options.headers}\ndata: ${options.data}',
RestApiRequestMessage(
method: options.method,
data: options.data.toString(),
headers: options.headers,
uri: options.uri,
),
);
return super.onRequest(options, handler);
@ -79,9 +81,11 @@ class ConsoleInterceptor extends InterceptorsWrapper {
final ResponseInterceptorHandler handler,
) async {
addMessage(
Message(
text:
'response-uri: ${response.realUri}\ncode: ${response.statusCode}\ndata: ${response.toString()}\n',
RestApiResponseMessage(
method: response.requestOptions.method,
statusCode: response.statusCode,
data: response.data.toString(),
uri: response.realUri,
),
);
return super.onResponse(

View File

@ -15,10 +15,12 @@ part 'app_settings_state.dart';
class AppSettingsCubit extends Cubit<AppSettingsState> {
AppSettingsCubit({
required final bool isDarkModeOn,
required final bool isAutoDarkModeOn,
required final bool isOnboardingShowing,
}) : super(
AppSettingsState(
isDarkModeOn: isDarkModeOn,
isAutoDarkModeOn: isAutoDarkModeOn,
isOnboardingShowing: isOnboardingShowing,
),
);
@ -27,10 +29,12 @@ class AppSettingsCubit extends Cubit<AppSettingsState> {
void load() async {
final bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
final bool? isAutoDarkModeOn = box.get(BNames.isAutoDarkModeOn);
final bool? isOnboardingShowing = box.get(BNames.isOnboardingShowing);
emit(
state.copyWith(
isDarkModeOn: isDarkModeOn,
isAutoDarkModeOn: isAutoDarkModeOn,
isOnboardingShowing: isOnboardingShowing,
),
);
@ -49,9 +53,14 @@ class AppSettingsCubit extends Cubit<AppSettingsState> {
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
}
void turnOffOnboarding() {
box.put(BNames.isOnboardingShowing, false);
void updateAutoDarkMode({required final bool isAutoDarkModeOn}) {
box.put(BNames.isAutoDarkModeOn, isAutoDarkModeOn);
emit(state.copyWith(isAutoDarkModeOn: isAutoDarkModeOn));
}
emit(state.copyWith(isOnboardingShowing: false));
void turnOffOnboarding({final bool isOnboardingShowing = false}) {
box.put(BNames.isOnboardingShowing, isOnboardingShowing);
emit(state.copyWith(isOnboardingShowing: isOnboardingShowing));
}
}

View File

@ -3,21 +3,25 @@ part of 'app_settings_cubit.dart';
class AppSettingsState extends Equatable {
const AppSettingsState({
required this.isDarkModeOn,
required this.isAutoDarkModeOn,
required this.isOnboardingShowing,
this.corePalette,
});
final bool isDarkModeOn;
final bool isAutoDarkModeOn;
final bool isOnboardingShowing;
final color_utils.CorePalette? corePalette;
AppSettingsState copyWith({
final bool? isDarkModeOn,
final bool? isAutoDarkModeOn,
final bool? isOnboardingShowing,
final color_utils.CorePalette? corePalette,
}) =>
AppSettingsState(
isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn,
isAutoDarkModeOn: isAutoDarkModeOn ?? this.isAutoDarkModeOn,
isOnboardingShowing: isOnboardingShowing ?? this.isOnboardingShowing,
corePalette: corePalette ?? this.corePalette,
);
@ -26,5 +30,6 @@ class AppSettingsState extends Equatable {
corePalette ?? color_utils.CorePalette.of(BrandColors.primary.value);
@override
List<dynamic> get props => [isDarkModeOn, isOnboardingShowing, corePalette];
List<dynamic> get props =>
[isDarkModeOn, isAutoDarkModeOn, isOnboardingShowing, corePalette];
}

View File

@ -15,9 +15,9 @@ class ApiDevicesCubit
@override
void load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
_refetch();
}
// if (serverInstallationCubit.state is ServerInstallationFinished) {
_refetch();
// }
}
Future<void> refresh() async {

View File

@ -36,10 +36,6 @@ class UserFormCubit extends FormCubit {
@override
FutureOr<void> onSubmit() {
print('onSubmit');
print('initialUser: $initialUser');
print('login: ${login.state.value}');
print('password: ${password.state.value}');
if (initialUser == null) {
final User user = User(
login: login.state.value,

View File

@ -14,21 +14,21 @@ class RecoveryKeyCubit
@override
void load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
final RecoveryKeyStatus? status = await _getRecoveryKeyStatus();
if (status == null) {
emit(state.copyWith(loadingStatus: LoadingStatus.error));
} else {
emit(
state.copyWith(
status: status,
loadingStatus: LoadingStatus.success,
),
);
}
// if (serverInstallationCubit.state is ServerInstallationFinished) {
final RecoveryKeyStatus? status = await _getRecoveryKeyStatus();
if (status == null) {
emit(state.copyWith(loadingStatus: LoadingStatus.error));
} else {
emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized));
emit(
state.copyWith(
status: status,
loadingStatus: LoadingStatus.success,
),
);
}
// } else {
// emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized));
// }
}
Future<RecoveryKeyStatus?> _getRecoveryKeyStatus() async {

View File

@ -0,0 +1,19 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
part 'support_system_state.dart';
class SupportSystemCubit extends Cubit<SupportSystemState> {
SupportSystemCubit() : super(const SupportSystemState('about'));
void showArticle({
required final String article,
final BuildContext? context,
}) {
emit(SupportSystemState(article));
if (context != null) {
Scaffold.of(context).openEndDrawer();
}
}
}

View File

@ -0,0 +1,12 @@
part of 'support_system_cubit.dart';
class SupportSystemState extends Equatable {
const SupportSystemState(
this.currentArticle,
);
final String currentArticle;
@override
List<Object> get props => [currentArticle];
}

View File

@ -1,16 +1,19 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/text_themes.dart';
class NavigationService {
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>();
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
NavigatorState? get navigator => navigatorKey.currentState;
void showPopUpDialog(final AlertDialog dialog) {
final BuildContext context = navigatorKey.currentState!.overlay!.context;
final BuildContext? context = navigatorKey.currentContext;
if (context == null) {
showSnackBar(
'Could not show dialog. This should not happen, please report this.',
);
return;
}
showDialog(
context: context,
@ -21,8 +24,7 @@ class NavigationService {
void showSnackBar(final String text) {
final ScaffoldMessengerState state = scaffoldMessengerKey.currentState!;
final SnackBar snack = SnackBar(
backgroundColor: BrandColors.black.withOpacity(0.8),
content: Text(text, style: buttonTitleText),
content: Text(text),
duration: const Duration(seconds: 2),
);
state.showSnackBar(snack);

View File

@ -1,20 +1,74 @@
import 'package:graphql/client.dart';
import 'package:intl/intl.dart';
final DateFormat formatter = DateFormat('hh:mm');
class Message {
Message({this.text, this.type = MessageType.normal}) : time = DateTime.now();
Message({this.text, this.severity = MessageSeverity.normal})
: time = DateTime.now();
Message.warn({this.text})
: type = MessageType.warning,
: severity = MessageSeverity.warning,
time = DateTime.now();
final String? text;
final DateTime time;
final MessageType type;
final MessageSeverity severity;
String get timeString => formatter.format(time);
}
enum MessageType {
enum MessageSeverity {
normal,
warning,
}
class RestApiRequestMessage extends Message {
RestApiRequestMessage({
this.method,
this.uri,
this.data,
this.headers,
}) : super(text: 'request-uri: $uri\nheaders: $headers\ndata: $data');
final String? method;
final Uri? uri;
final String? data;
final Map<String, dynamic>? headers;
}
class RestApiResponseMessage extends Message {
RestApiResponseMessage({
this.method,
this.uri,
this.statusCode,
this.data,
}) : super(text: 'response-uri: $uri\ncode: $statusCode\ndata: $data');
final String? method;
final Uri? uri;
final int? statusCode;
final String? data;
}
class GraphQlResponseMessage extends Message {
GraphQlResponseMessage({
this.data,
this.errors,
this.context,
}) : super(text: 'GraphQL Response\ndata: $data');
final Map<String, dynamic>? data;
final List<GraphQLError>? errors;
final Context? context;
}
class GraphQlRequestMessage extends Message {
GraphQlRequestMessage({
this.operation,
this.variables,
this.context,
}) : super(text: 'GraphQL Request\noperation: $operation');
final Operation? operation;
final Map<String, dynamic>? variables;
final Context? context;
}

View File

@ -5,9 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:wakelock/wakelock.dart';
import 'package:timezone/data/latest.dart' as tz;
@ -20,7 +18,7 @@ import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await HiveConfig.init();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
try {
/// Wakelock support for Linux
@ -43,21 +41,20 @@ void main() async {
fallbackColor: BrandColors.primary,
);
BlocOverrides.runZoned(
() => runApp(
Localization(
child: MyApp(
lightThemeData: lightThemeData,
darkThemeData: darkThemeData,
),
Bloc.observer = SimpleBlocObserver();
runApp(
Localization(
child: SelfprivacyApp(
lightThemeData: lightThemeData,
darkThemeData: darkThemeData,
),
),
blocObserver: SimpleBlocObserver(),
);
}
class MyApp extends StatelessWidget {
const MyApp({
class SelfprivacyApp extends StatelessWidget {
SelfprivacyApp({
required this.lightThemeData,
required this.darkThemeData,
super.key,
@ -66,42 +63,42 @@ class MyApp extends StatelessWidget {
final ThemeData lightThemeData;
final ThemeData darkThemeData;
final _appRouter = RootRouter(getIt.get<NavigationService>().navigatorKey);
@override
Widget build(final BuildContext context) => Localization(
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light, // Manually changing appbar color
child: BlocAndProviderConfig(
child: BlocBuilder<AppSettingsCubit, AppSettingsState>(
builder: (
final BuildContext context,
final AppSettingsState appSettings,
) =>
MaterialApp(
scaffoldMessengerKey:
getIt.get<NavigationService>().scaffoldMessengerKey,
navigatorKey: getIt.get<NavigationService>().navigatorKey,
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
title: 'SelfPrivacy',
theme: lightThemeData,
darkTheme: darkThemeData,
themeMode:
appSettings.isDarkModeOn ? ThemeMode.dark : ThemeMode.light,
home: appSettings.isOnboardingShowing
? const OnboardingPage(nextPage: InitializingPage())
: const RootPage(),
builder: (final BuildContext context, final Widget? widget) {
Widget error = const Text('...rendering error...');
if (widget is Scaffold || widget is Navigator) {
error = Scaffold(body: Center(child: error));
}
ErrorWidget.builder =
(final FlutterErrorDetails errorDetails) => error;
return widget!;
},
),
child: BlocAndProviderConfig(
child: BlocBuilder<AppSettingsCubit, AppSettingsState>(
builder: (
final BuildContext context,
final AppSettingsState appSettings,
) =>
MaterialApp.router(
routeInformationParser: _appRouter.defaultRouteParser(),
routerDelegate: _appRouter.delegate(),
scaffoldMessengerKey:
getIt.get<NavigationService>().scaffoldMessengerKey,
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
title: 'SelfPrivacy',
theme: lightThemeData,
darkTheme: darkThemeData,
themeMode: appSettings.isAutoDarkModeOn
? ThemeMode.system
: appSettings.isDarkModeOn
? ThemeMode.dark
: ThemeMode.light,
builder: (final BuildContext context, final Widget? widget) {
Widget error = const Text('...rendering error...');
if (widget is Scaffold || widget is Navigator) {
error = Scaffold(body: Center(child: error));
}
ErrorWidget.builder =
(final FlutterErrorDetails errorDetails) => error;
return widget!;
},
),
),
),

View File

@ -1,13 +0,0 @@
import 'package:flutter/material.dart';
class BrandAlert extends AlertDialog {
BrandAlert({
super.key,
final String? title,
final String? contentText,
super.actions,
}) : super(
title: title != null ? Text(title) : null,
content: title != null ? Text(contentText!) : null,
);
}

View File

@ -1,56 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
class BrandBottomSheet extends StatelessWidget {
const BrandBottomSheet({
required this.child,
super.key,
this.isExpended = false,
});
final Widget child;
final bool isExpended;
@override
Widget build(final BuildContext context) {
final double mainHeight = MediaQuery.of(context).size.height -
MediaQuery.of(context).padding.top -
300;
late Widget innerWidget;
if (isExpended) {
innerWidget = Scaffold(
body: child,
);
} else {
final ThemeData themeData = Theme.of(context);
innerWidget = Material(
color: themeData.scaffoldBackgroundColor,
child: IntrinsicHeight(child: child),
);
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Center(
child: Container(
height: 4,
width: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: BrandColors.gray4,
),
),
),
const SizedBox(height: 6),
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: mainHeight),
child: innerWidget,
),
),
],
);
}
}

View File

@ -1,55 +0,0 @@
import 'package:flutter/material.dart';
class BrandCards {
static Widget big({required final Widget child}) => _BrandCard(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 15,
),
shadow: bigShadow,
borderRadius: BorderRadius.circular(20),
child: child,
);
static Widget small({required final Widget child}) => _BrandCard(
padding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 10,
),
shadow: bigShadow,
borderRadius: BorderRadius.circular(10),
child: child,
);
}
class _BrandCard extends StatelessWidget {
const _BrandCard({
required this.child,
required this.padding,
required this.shadow,
required this.borderRadius,
});
final Widget child;
final EdgeInsets padding;
final List<BoxShadow> shadow;
final BorderRadius borderRadius;
@override
Widget build(final BuildContext context) => Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: borderRadius,
boxShadow: shadow,
),
padding: padding,
child: child,
);
}
final List<BoxShadow> bigShadow = [
BoxShadow(
offset: const Offset(0, 4),
blurRadius: 8,
color: Colors.black.withOpacity(.08),
)
];

View File

@ -25,5 +25,8 @@ class BrandHeader extends StatelessWidget {
onBackButtonPressed ?? () => Navigator.of(context).pop(),
)
: null,
actions: const [
SizedBox.shrink(),
],
);
}

View File

@ -1,147 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
import 'package:selfprivacy/ui/helpers/widget_size.dart';
class BrandHeroScreen extends StatelessWidget {
const BrandHeroScreen({
required this.children,
super.key,
this.hasBackButton = true,
this.hasFlashButton = true,
this.heroIcon,
this.heroIconWidget,
this.heroTitle = '',
this.heroSubtitle,
this.onBackButtonPressed,
});
final List<Widget> children;
final bool hasBackButton;
final bool hasFlashButton;
final IconData? heroIcon;
final Widget? heroIconWidget;
final String heroTitle;
final String? heroSubtitle;
final VoidCallback? onBackButtonPressed;
@override
Widget build(final BuildContext context) {
final Widget heroIconWidget = this.heroIconWidget ??
Icon(
heroIcon ?? Icons.help,
size: 48.0,
color: Theme.of(context).colorScheme.onBackground,
);
final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null;
return Scaffold(
floatingActionButton: hasFlashButton ? const BrandFab() : null,
body: CustomScrollView(
slivers: [
HeroSliverAppBar(
heroTitle: heroTitle,
hasHeroIcon: hasHeroIcon,
hasBackButton: hasBackButton,
onBackButtonPressed: onBackButtonPressed,
heroIconWidget: heroIconWidget,
),
if (heroSubtitle != null)
SliverPadding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 4.0,
),
sliver: SliverList(
delegate: SliverChildListDelegate([
Text(
heroSubtitle!,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
textAlign: hasHeroIcon ? TextAlign.center : TextAlign.start,
),
]),
),
),
SliverPadding(
padding: const EdgeInsets.all(16.0),
sliver: SliverList(
delegate: SliverChildListDelegate(children),
),
),
],
),
);
}
}
class HeroSliverAppBar extends StatefulWidget {
const HeroSliverAppBar({
required this.heroTitle,
required this.hasHeroIcon,
required this.hasBackButton,
required this.onBackButtonPressed,
required this.heroIconWidget,
super.key,
});
final String heroTitle;
final bool hasHeroIcon;
final bool hasBackButton;
final VoidCallback? onBackButtonPressed;
final Widget heroIconWidget;
@override
State<HeroSliverAppBar> createState() => _HeroSliverAppBarState();
}
class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
Size _size = Size.zero;
@override
Widget build(final BuildContext context) => SliverAppBar(
expandedHeight:
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,
primary: true,
pinned: true,
stretch: true,
leading: widget.hasBackButton
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: widget.onBackButtonPressed ??
() => Navigator.of(context).pop(),
)
: null,
flexibleSpace: FlexibleSpaceBar(
title: LayoutBuilder(
builder: (final context, final constraints) => SizedBox(
width: constraints.maxWidth - 72.0,
child: WidgetSize(
onChange: (final Size size) => setState(() => _size = size),
child: Text(
widget.heroTitle,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface,
),
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
),
),
),
),
expandedTitleScale: 1.2,
centerTitle: true,
collapseMode: CollapseMode.pin,
titlePadding: const EdgeInsets.only(
bottom: 12.0,
top: 16.0,
),
background: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 72.0),
if (widget.hasHeroIcon) widget.heroIconWidget,
],
),
),
);
}

View File

@ -27,14 +27,14 @@ class BrandLinearIndicator extends StatelessWidget {
alignment: Alignment.centerLeft,
child: AnimatedSlide(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
curve: Curves.easeInOutCubicEmphasized,
offset: Offset(
-(1 - value),
0,
),
child: AnimatedContainer(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
curve: Curves.easeInOutCubicEmphasized,
width: constraints.maxWidth,
decoration: BoxDecoration(
color: color,

View File

@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:url_launcher/url_launcher_string.dart';
class BrandMarkdown extends StatefulWidget {
@ -37,24 +35,7 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
@override
Widget build(final BuildContext context) {
final bool isDark = Theme.of(context).brightness == Brightness.dark;
final MarkdownStyleSheet markdown = MarkdownStyleSheet(
p: defaultTextStyle.copyWith(
color: isDark ? BrandColors.white : null,
),
h1: headline1Style.copyWith(
color: isDark ? BrandColors.white : null,
),
h2: headline2Style.copyWith(
color: isDark ? BrandColors.white : null,
),
h3: headline3Style.copyWith(
color: isDark ? BrandColors.white : null,
),
h4: headline4Style.copyWith(
color: isDark ? BrandColors.white : null,
),
);
final MarkdownStyleSheet markdown = MarkdownStyleSheet();
return MarkdownBody(
shrinkWrap: true,
styleSheet: markdown,

View File

@ -1,40 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
// TODO: Delete this file.
class BrandRadio extends StatelessWidget {
const BrandRadio({
required this.isChecked,
super.key,
});
final bool isChecked;
@override
Widget build(final BuildContext context) => Container(
height: 20,
width: 20,
alignment: Alignment.center,
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: _getBorder(),
),
child: isChecked
? Container(
height: 10,
width: 10,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: BrandColors.primary,
),
)
: null,
);
BoxBorder? _getBorder() => Border.all(
color: isChecked ? BrandColors.primary : BrandColors.gray1,
width: 2,
);
}

View File

@ -1,19 +0,0 @@
import 'package:flutter/material.dart';
class BrandSwitch extends StatelessWidget {
const BrandSwitch({
required this.onChanged,
required this.value,
super.key,
});
final ValueChanged<bool> onChanged;
final bool value;
@override
Widget build(final BuildContext context) => Switch(
activeColor: Theme.of(context).colorScheme.primary,
value: value,
onChanged: onChanged,
);
}

View File

@ -1,60 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
class BrandTabBar extends StatefulWidget {
const BrandTabBar({super.key, this.controller});
final TabController? controller;
@override
State<BrandTabBar> createState() => _BrandTabBarState();
}
class _BrandTabBarState extends State<BrandTabBar> {
int? currentIndex;
@override
void initState() {
currentIndex = widget.controller!.index;
widget.controller!.addListener(_listener);
super.initState();
}
void _listener() {
if (currentIndex != widget.controller!.index) {
setState(() {
currentIndex = widget.controller!.index;
});
}
}
@override
void dispose() {
widget.controller ?? widget.controller!.removeListener(_listener);
super.dispose();
}
@override
Widget build(final BuildContext context) => NavigationBar(
destinations: [
_getIconButton('basis.providers'.tr(), BrandIcons.server, 0),
_getIconButton('basis.services'.tr(), BrandIcons.box, 1),
_getIconButton('basis.users'.tr(), BrandIcons.users, 2),
_getIconButton('basis.more'.tr(), Icons.menu_rounded, 3),
],
onDestinationSelected: (final index) {
widget.controller!.animateTo(index);
},
selectedIndex: currentIndex ?? 0,
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
);
NavigationDestination _getIconButton(
final String label,
final IconData iconData,
final int index,
) =>
NavigationDestination(
icon: Icon(iconData),
label: label,
);
}

View File

@ -1,238 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/text_themes.dart';
export 'package:selfprivacy/utils/extensions/text_extensions.dart';
// TODO: Delete this file
enum TextType {
h1, // right now only at onboarding and opened providers
h2, // cards titles
h3, // titles in about page
h4, // caption
h5, // Table data
body1, // normal
body2, // with opacity
medium,
small,
onboardingTitle,
buttonTitleText, // risen button title text,
h4Underlined,
}
class BrandText extends StatelessWidget {
factory BrandText.h4(
final String? text, {
final TextStyle? style,
final TextAlign? textAlign,
}) =>
BrandText(
text,
type: TextType.h4,
style: style,
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: textAlign,
);
factory BrandText.onboardingTitle(
final String text, {
final TextStyle? style,
}) =>
BrandText(
text,
type: TextType.onboardingTitle,
style: style,
);
factory BrandText.h3(
final String text, {
final TextStyle? style,
final TextAlign? textAlign,
}) =>
BrandText(
text,
type: TextType.h3,
style: style,
textAlign: textAlign,
overflow: TextOverflow.ellipsis,
);
factory BrandText.h4Underlined(
final String? text, {
final TextStyle? style,
final TextAlign? textAlign,
}) =>
BrandText(
text,
type: TextType.h4Underlined,
style: style,
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: textAlign,
);
factory BrandText.h1(
final String? text, {
final TextStyle? style,
final TextOverflow? overflow,
final bool? softWrap,
}) =>
BrandText(
text,
type: TextType.h1,
style: style,
);
factory BrandText.h2(
final String? text, {
final TextStyle? style,
final TextAlign? textAlign,
}) =>
BrandText(
text,
type: TextType.h2,
style: style,
textAlign: textAlign,
);
factory BrandText.body1(final String? text, {final TextStyle? style}) =>
BrandText(
text,
type: TextType.body1,
style: style,
);
factory BrandText.small(final String text, {final TextStyle? style}) =>
BrandText(
text,
type: TextType.small,
style: style,
);
factory BrandText.body2(final String? text, {final TextStyle? style}) =>
BrandText(
text,
type: TextType.body2,
style: style,
);
factory BrandText.buttonTitleText(
final String? text, {
final TextStyle? style,
}) =>
BrandText(
text,
type: TextType.buttonTitleText,
style: style,
);
factory BrandText.h5(
final String? text, {
final TextStyle? style,
final TextAlign? textAlign,
}) =>
BrandText(
text,
type: TextType.h5,
style: style,
textAlign: textAlign,
);
factory BrandText.medium(
final String? text, {
final TextStyle? style,
final TextAlign? textAlign,
}) =>
BrandText(
text,
type: TextType.medium,
style: style,
textAlign: textAlign,
);
const BrandText(
this.text, {
required this.type,
super.key,
this.style,
this.overflow,
this.softWrap,
this.textAlign,
this.maxLines,
});
final String? text;
final TextStyle? style;
final TextType type;
final TextOverflow? overflow;
final bool? softWrap;
final TextAlign? textAlign;
final int? maxLines;
@override
Text build(final BuildContext context) {
TextStyle style;
final bool isDark = Theme.of(context).brightness == Brightness.dark;
switch (type) {
case TextType.h1:
style = isDark
? headline1Style.copyWith(color: Colors.white)
: headline1Style;
break;
case TextType.h2:
style = isDark
? headline2Style.copyWith(color: Colors.white)
: headline2Style;
break;
case TextType.h3:
style = isDark
? headline3Style.copyWith(color: Colors.white)
: headline3Style;
break;
case TextType.h4:
style = isDark
? headline4Style.copyWith(color: Colors.white)
: headline4Style;
break;
case TextType.h4Underlined:
style = isDark
? headline4UnderlinedStyle.copyWith(color: Colors.white)
: headline4UnderlinedStyle;
break;
case TextType.h5:
style = isDark
? headline5Style.copyWith(color: Colors.white)
: headline5Style;
break;
case TextType.body1:
style = isDark ? body1Style.copyWith(color: Colors.white) : body1Style;
break;
case TextType.body2:
style = isDark
? body2Style.copyWith(color: Colors.white.withOpacity(0.6))
: body2Style;
break;
case TextType.small:
style = isDark ? smallStyle.copyWith(color: Colors.white) : smallStyle;
break;
case TextType.onboardingTitle:
style = isDark
? onboardingTitle.copyWith(color: Colors.white)
: onboardingTitle;
break;
case TextType.medium:
style =
isDark ? mediumStyle.copyWith(color: Colors.white) : mediumStyle;
break;
case TextType.buttonTitleText:
style = !isDark
? buttonTitleText.copyWith(color: Colors.white)
: buttonTitleText;
break;
}
if (this.style != null) {
style = style.merge(this.style);
}
return Text(
text!,
style: style,
maxLines: maxLines,
overflow: overflow,
softWrap: softWrap,
textAlign: textAlign,
);
}
}

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/utils/named_font_weight.dart';
class BrandTimer extends StatefulWidget {
@ -52,11 +51,12 @@ class _BrandTimerState extends State<BrandTimer> {
}
@override
Widget build(final BuildContext context) => BrandText.medium(
_timeString,
style: const TextStyle(
fontWeight: NamedFontWeight.demiBold,
),
Widget build(final BuildContext context) => Text(
_timeString ?? '',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: NamedFontWeight.demiBold,
color: Theme.of(context).colorScheme.onSurface,
),
);
void _getTime() {

View File

@ -1,7 +1,4 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
enum BrandButtonTypes { rised, text, iconText }
class BrandButton {
static ConstrainedBox rised({
@ -58,53 +55,4 @@ class BrandButton {
),
child: TextButton(onPressed: onPressed, child: Text(title)),
);
static IconTextButton emptyWithIconText({
required final VoidCallback onPressed,
required final String title,
required final Icon icon,
final Key? key,
}) =>
IconTextButton(
key: key,
title: title,
onPressed: onPressed,
icon: icon,
);
}
class IconTextButton extends StatelessWidget {
const IconTextButton({
super.key,
this.onPressed,
this.title,
this.icon,
});
final VoidCallback? onPressed;
final String? title;
final Icon? icon;
@override
Widget build(final BuildContext context) => Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
child: Container(
height: 48,
width: double.infinity,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BrandText.body1(title),
Padding(
padding: const EdgeInsets.all(12.0),
child: icon,
)
],
),
),
),
);
}

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
class ActionButton extends StatelessWidget {
const ActionButton({
/// Basically a [TextButton] to be used in dialogs
class DialogActionButton extends StatelessWidget {
const DialogActionButton({
super.key,
this.text,
this.onPressed,

View File

@ -1,6 +1,16 @@
import 'package:flutter/material.dart';
/// For some reason original [SegmentedButton] does not have animations.
///
/// The [SegmentedButtons] was written for SelfPrivacy before [SegmentedButton] was introduced.
/// While it doesn't have that much options to pass, it has cute little animation.
/// It is based on [ToggleButtons].
class SegmentedButtons extends StatelessWidget {
/// Creates a segmented buttons widget. This is a SelfPrivacy implementation.
///
/// Provide the button titles in [titles] as a [List<String>].
/// Current selection is provided in [isSelected] as a [List<bool>].
/// This widget will call [onPressed] with the index of the button that was pressed.
const SegmentedButtons({
required this.isSelected,
required this.onPressed,
@ -8,15 +18,24 @@ class SegmentedButtons extends StatelessWidget {
super.key,
});
/// The current selection state of the buttons.
///
/// The length of this list must be equal to the length of [titles].
/// Several buttons can be selected at the same time.
final List<bool> isSelected;
/// The callback that is called when a button is pressed.
/// It will be called with the index of the button that was pressed.
final Function(int)? onPressed;
/// The titles of the buttons.
final List<String> titles;
@override
Widget build(final BuildContext context) => LayoutBuilder(
builder: (final context, final constraints) => ToggleButtons(
constraints: BoxConstraints(
minWidth: (constraints.maxWidth - 8) / 3,
minWidth: (constraints.maxWidth - 8) / titles.length,
minHeight: 40 + Theme.of(context).visualDensity.vertical * 4,
),
borderRadius: BorderRadius.circular(48),
@ -38,7 +57,7 @@ class SegmentedButtons extends StatelessWidget {
opacity: isSelected[index] ? 1 : 0,
child: AnimatedScale(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
curve: Curves.easeInOutCubicEmphasized,
alignment: Alignment.centerLeft,
scale: isSelected[index] ? 1 : 0,
child: Icon(
@ -53,7 +72,7 @@ class SegmentedButtons extends StatelessWidget {
? const EdgeInsets.only(left: 24)
: EdgeInsets.zero,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
curve: Curves.easeInOutCubicEmphasized,
child: Text(
title,
style: Theme.of(context).textTheme.labelLarge,

View File

@ -0,0 +1,113 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class ProgressDrawer extends StatelessWidget {
/// A [Drawer] that displays a list of steps and the current step.
/// Used in setup wizards. The [trailing] widget is displayed at the bottom.
/// The [steps] are translated using [EasyLocalization].
const ProgressDrawer({
required this.steps,
required this.currentStep,
required this.constraints,
required this.trailing,
required this.title,
super.key,
});
final List<String> steps;
final int currentStep;
final Widget trailing;
final BoxConstraints constraints;
final String title;
@override
Widget build(final BuildContext context) => SizedBox(
width: 300,
height: constraints.maxHeight,
child: Drawer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
title,
style: Theme.of(context).textTheme.titleLarge,
),
),
Flexible(
fit: FlexFit.tight,
child: SingleChildScrollView(
child: Column(
children: [
...steps.map((final step) {
final index = steps.indexOf(step);
return _StepIndicator(
title: step.tr(),
isCurrent: index == currentStep,
isCompleted: index < currentStep,
);
}),
],
),
),
),
// const Spacer(),
Padding(
padding: const EdgeInsets.all(16.0),
child: trailing,
),
],
),
),
);
}
class _StepIndicator extends StatelessWidget {
const _StepIndicator({
required this.title,
required this.isCompleted,
required this.isCurrent,
});
final String title;
final bool isCompleted;
final bool isCurrent;
@override
Widget build(final BuildContext context) => ListTile(
selected: isCurrent,
leading: isCurrent
? const _StepCurrentIcon()
: isCompleted
? const _StepCompletedIcon()
: const _StepPendingIcon(),
title: Text(
title,
),
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
);
}
class _StepCompletedIcon extends StatelessWidget {
const _StepCompletedIcon();
@override
Widget build(final BuildContext context) => const Icon(Icons.check_circle);
}
class _StepPendingIcon extends StatelessWidget {
const _StepPendingIcon();
@override
Widget build(final BuildContext context) => const Icon(Icons.circle_outlined);
}
class _StepCurrentIcon extends StatelessWidget {
const _StepCurrentIcon();
@override
Widget build(final BuildContext context) =>
const Icon(Icons.build_circle_outlined);
}

View File

@ -0,0 +1,52 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
class SupportDrawer extends StatelessWidget {
const SupportDrawer({
super.key,
});
@override
Widget build(final BuildContext context) {
final currentArticle =
context.watch<SupportSystemCubit>().state.currentArticle;
return Drawer(
width: 440,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
const SizedBox(width: 8),
const Icon(Icons.help_outline),
const SizedBox(width: 16),
Text(
'support.title'.tr(),
style: Theme.of(context).textTheme.titleLarge,
),
const Spacer(),
IconButton(
onPressed: () => Scaffold.of(context).closeEndDrawer(),
icon: const Icon(Icons.chevron_right_outlined),
),
],
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.all(8.0),
child: BrandMarkdown(
fileName: currentArticle,
),
),
],
),
),
),
);
}
}

View File

@ -1,26 +0,0 @@
import 'package:flutter/material.dart';
class BrandError extends StatelessWidget {
const BrandError({super.key, this.error, this.stackTrace});
final Object? error;
final StackTrace? stackTrace;
@override
Widget build(final BuildContext context) => SafeArea(
child: Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(error.toString()),
const Text('stackTrace: '),
Text(stackTrace.toString()),
],
),
),
),
),
);
}

View File

@ -6,10 +6,8 @@ import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
import 'package:selfprivacy/logic/models/json/server_job.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/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart';
import 'package:selfprivacy/ui/helpers/modals.dart';
@ -32,7 +30,12 @@ class JobsContent extends StatelessWidget {
if (state is JobsStateEmpty) {
widgets = [
const SizedBox(height: 80),
Center(child: BrandText.body1('jobs.empty'.tr())),
Center(
child: Text(
'jobs.empty'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
),
];
if (installationState is ServerInstallationFinished) {
@ -65,38 +68,49 @@ class JobsContent extends StatelessWidget {
];
} else if (state is JobsStateWithJobs) {
widgets = [
...state.clientJobList
.map(
(final j) => Row(
children: [
Expanded(
child: BrandCards.small(
child: Text(j.title),
...state.clientJobList.map(
(final j) => Row(
children: [
Expanded(
child: Card(
color: Theme.of(context).colorScheme.surfaceVariant,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 10,
),
),
const SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.errorContainer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
onPressed: () =>
context.read<JobsCubit>().removeJob(j.id),
child: Text(
'basis.remove'.tr(),
style: TextStyle(
color:
Theme.of(context).colorScheme.onErrorContainer,
),
j.title,
style:
Theme.of(context).textTheme.labelLarge?.copyWith(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
),
),
),
],
),
),
)
.toList(),
const SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.errorContainer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
onPressed: () => context.read<JobsCubit>().removeJob(j.id),
child: Text(
'basis.remove'.tr(),
style: TextStyle(
color: Theme.of(context).colorScheme.onErrorContainer,
),
),
),
],
),
),
const SizedBox(height: 20),
BrandButton.rised(
onPressed: () => context.read<JobsCubit>().applyAll(),
@ -109,8 +123,9 @@ class JobsContent extends StatelessWidget {
children: [
const SizedBox(height: 15),
Center(
child: BrandText.h2(
child: Text(
'jobs.title'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
),
const SizedBox(height: 20),

View File

@ -0,0 +1,300 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:selfprivacy/logic/models/message.dart';
class LogListItem extends StatelessWidget {
const LogListItem({
required this.message,
super.key,
});
final Message message;
@override
Widget build(final BuildContext context) {
final messageItem = message;
if (messageItem is RestApiRequestMessage) {
return _RestApiRequestMessageItem(message: messageItem);
} else if (messageItem is RestApiResponseMessage) {
return _RestApiResponseMessageItem(message: messageItem);
} else if (messageItem is GraphQlResponseMessage) {
return _GraphQlResponseMessageItem(message: messageItem);
} else if (messageItem is GraphQlRequestMessage) {
return _GraphQlRequestMessageItem(message: messageItem);
} else {
return _DefaultMessageItem(message: messageItem);
}
}
}
class _RestApiRequestMessageItem extends StatelessWidget {
const _RestApiRequestMessageItem({required this.message});
final RestApiRequestMessage message;
@override
Widget build(final BuildContext context) => ListTile(
title: Text(
'${message.method}\n${message.uri}',
),
subtitle: Text(message.timeString),
leading: const Icon(Icons.upload_outlined),
iconColor: Theme.of(context).colorScheme.secondary,
onTap: () => showDialog(
context: context,
builder: (final BuildContext context) => AlertDialog(
scrollable: true,
title: Text(
'${message.method}\n${message.uri}',
),
content: Column(
children: [
Text(message.timeString),
const SizedBox(height: 16),
// Headers is a map of key-value pairs
if (message.headers != null) const Text('Headers'),
if (message.headers != null)
Text(
message.headers!.entries
.map((final entry) => '${entry.key}: ${entry.value}')
.join('\n'),
),
if (message.data != null && message.data != 'null')
const Text('Data'),
if (message.data != null && message.data != 'null')
Text(message.data!),
],
),
actions: [
// A button to copy the request to the clipboard
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: message.text));
},
child: Text('console_page.copy'.tr()),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('basis.close'.tr()),
),
],
),
),
);
}
class _RestApiResponseMessageItem extends StatelessWidget {
const _RestApiResponseMessageItem({required this.message});
final RestApiResponseMessage message;
@override
Widget build(final BuildContext context) => ListTile(
title: Text(
'${message.statusCode} ${message.method}\n${message.uri}',
),
subtitle: Text(message.timeString),
leading: const Icon(Icons.download_outlined),
iconColor: Theme.of(context).colorScheme.primary,
onTap: () => showDialog(
context: context,
builder: (final BuildContext context) => AlertDialog(
scrollable: true,
title: Text(
'${message.statusCode} ${message.method}\n${message.uri}',
),
content: Column(
children: [
Text(message.timeString),
const SizedBox(height: 16),
// Headers is a map of key-value pairs
if (message.data != null && message.data != 'null')
const Text('Data'),
if (message.data != null && message.data != 'null')
Text(message.data!),
],
),
actions: [
// A button to copy the request to the clipboard
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: message.text));
},
child: Text('console_page.copy'.tr()),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('basis.close'.tr()),
),
],
),
),
);
}
class _GraphQlResponseMessageItem extends StatelessWidget {
const _GraphQlResponseMessageItem({required this.message});
final GraphQlResponseMessage message;
@override
Widget build(final BuildContext context) => ListTile(
title: Text(
'GraphQL Response at ${message.timeString}',
),
subtitle: Text(
message.data.toString(),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
leading: const Icon(Icons.arrow_circle_down_outlined),
iconColor: Theme.of(context).colorScheme.tertiary,
onTap: () => showDialog(
context: context,
builder: (final BuildContext context) => AlertDialog(
scrollable: true,
title: Text(
'GraphQL Response at ${message.timeString}',
),
content: Column(
children: [
Text(message.timeString),
const Divider(),
if (message.data != null) const Text('Data'),
// Data is a map of key-value pairs
if (message.data != null)
Text(
message.data!.entries
.map((final entry) => '${entry.key}: ${entry.value}')
.join('\n'),
),
const Divider(),
if (message.errors != null) const Text('Errors'),
if (message.errors != null)
Text(
message.errors!
.map(
(final entry) =>
'${entry.message} at ${entry.locations}',
)
.join('\n'),
),
const Divider(),
if (message.context != null) const Text('Context'),
if (message.context != null)
Text(
message.context!.toString(),
),
],
),
actions: [
// A button to copy the request to the clipboard
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: message.text));
},
child: Text('console_page.copy'.tr()),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('basis.close'.tr()),
),
],
),
),
);
}
class _GraphQlRequestMessageItem extends StatelessWidget {
const _GraphQlRequestMessageItem({required this.message});
final GraphQlRequestMessage message;
@override
Widget build(final BuildContext context) => ListTile(
title: Text(
'GraphQL Request at ${message.timeString}',
),
subtitle: Text(
message.operation.toString(),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
leading: const Icon(Icons.arrow_circle_up_outlined),
iconColor: Theme.of(context).colorScheme.secondary,
onTap: () => showDialog(
context: context,
builder: (final BuildContext context) => AlertDialog(
scrollable: true,
title: Text(
'GraphQL Response at ${message.timeString}',
),
content: Column(
children: [
Text(message.timeString),
const Divider(),
if (message.operation != null) const Text('Operation'),
// Data is a map of key-value pairs
if (message.operation != null)
Text(
message.operation!.toString(),
),
const Divider(),
if (message.variables != null) const Text('Variables'),
if (message.variables != null)
Text(
message.variables!.entries
.map((final entry) => '${entry.key}: ${entry.value}')
.join('\n'),
),
const Divider(),
if (message.context != null) const Text('Context'),
if (message.context != null)
Text(
message.context!.toString(),
),
],
),
actions: [
// A button to copy the request to the clipboard
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: message.text));
},
child: Text('console_page.copy'.tr()),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('basis.close'.tr()),
),
],
),
),
);
}
class _DefaultMessageItem extends StatelessWidget {
const _DefaultMessageItem({required this.message});
final Message message;
@override
Widget build(final BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: '${message.timeString}: \n',
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
TextSpan(text: message.text),
],
),
),
);
}

View File

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:easy_localization/easy_localization.dart';
class NotReadyCard extends StatelessWidget {
@ -13,11 +13,7 @@ class NotReadyCard extends StatelessWidget {
child: ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
onTap: () => Navigator.of(context).push(
materialRoute(
const InitializingPage(),
),
),
onTap: () => context.pushRoute(const InitializingRoute()),
title: Text(
'not_ready_card.in_menu'.tr(),
style: Theme.of(context).textTheme.titleSmall?.copyWith(

View File

@ -1,13 +1,17 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ionicons/ionicons.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
import 'package:selfprivacy/ui/helpers/modals.dart';
class BrandFab extends StatefulWidget {
const BrandFab({super.key});
const BrandFab({
this.extended = false,
super.key,
});
final bool extended;
@override
State<BrandFab> createState() => _BrandFabState();
@ -56,28 +60,40 @@ class _BrandFabState extends State<BrandFab>
child: FloatingActionButton(
onPressed: () {
// TODO: Make a hero animation to the screen
showBrandBottomSheet(
showModalBottomSheet(
context: context,
builder: (final BuildContext context) => const BrandBottomSheet(
isExpended: true,
child: JobsContent(),
),
builder: (final BuildContext context) => const JobsContent(),
);
},
child: AnimatedBuilder(
animation: _colorTween,
builder: (final BuildContext context, final Widget? child) {
final double v = _animationController.value;
final IconData icon =
v > 0.5 ? Ionicons.flash : Ionicons.flash_outline;
return Transform.scale(
scale: 1 + (v < 0.5 ? v : 1 - v) * 2,
child: Icon(
icon,
color: _colorTween.value,
isExtended: widget.extended,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedBuilder(
animation: _colorTween,
builder: (final BuildContext context, final Widget? child) {
final double v = _animationController.value;
final IconData icon =
v > 0.5 ? Ionicons.flash : Ionicons.flash_outline;
return Transform.scale(
scale: 1 + (v < 0.5 ? v : 1 - v) * 2,
child: Icon(
icon,
color: _colorTween.value,
),
);
},
),
if (widget.extended)
const SizedBox(
width: 8,
),
);
},
if (widget.extended)
Text(
'jobs.title'.tr(),
),
],
),
),
);

View File

@ -1,6 +1,4 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/text_themes.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
class ProgressBar extends StatefulWidget {
@ -65,7 +63,7 @@ class _ProgressBarState extends State<ProgressBar> {
Container(
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: BrandColors.gray4,
color: const Color(0xFFDDDDDD),
borderRadius: BorderRadius.circular(5),
),
child: LayoutBuilder(
@ -119,3 +117,13 @@ class _ProgressBarState extends State<ProgressBar> {
);
}
}
const TextStyle progressTextStyleLight = TextStyle(
fontSize: 11,
color: Colors.black,
height: 1.7,
);
final TextStyle progressTextStyleDark = progressTextStyleLight.copyWith(
color: Colors.white,
);

View File

@ -1,36 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
// TODO: Delete this file.
class SwitcherBlock extends StatelessWidget {
const SwitcherBlock({
required this.child,
required this.isActive,
required this.onChange,
super.key,
});
final Widget child;
final bool isActive;
final ValueChanged<bool> onChange;
@override
Widget build(final BuildContext context) => Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: child),
const SizedBox(width: 5),
Switch(
activeColor: BrandColors.green1,
activeTrackColor: BrandColors.green2,
onChanged: onChange,
value: isActive,
),
],
),
);
}

View File

@ -1,21 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
Future<T?> showBrandBottomSheet<T>({
required final BuildContext context,
required final WidgetBuilder builder,
}) =>
showCupertinoModalBottomSheet<T>(
builder: builder,
barrierColor: Colors.black45,
context: context,
shadow: const BoxShadow(color: Colors.transparent),
backgroundColor: Colors.transparent,
);
import 'package:selfprivacy/ui/components/buttons/dialog_action_button.dart';
void showPopUpAlert({
required final String description,
@ -26,16 +12,16 @@ void showPopUpAlert({
final String? cancelButtonTitle,
}) {
getIt.get<NavigationService>().showPopUpDialog(
BrandAlert(
title: alertTitle ?? 'basis.alert'.tr(),
contentText: description,
AlertDialog(
title: Text(alertTitle ?? 'basis.alert'.tr()),
content: Text(description),
actions: [
ActionButton(
DialogActionButton(
text: actionButtonTitle,
isRed: true,
onPressed: actionButtonOnPressed,
),
ActionButton(
DialogActionButton(
text: cancelButtonTitle ?? 'basis.cancel'.tr(),
onPressed: cancelButtonOnPressed,
),

View File

@ -1,14 +1,26 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
/// A helper widget that calls a callback when its size changes.
///
/// This is useful when you want to know the size of a widget, and use it in
/// another leaf of the tree.
///
/// The [onChange] callback is called after the widget is rendered, and the
/// size of the widget is different from the previous render.
class WidgetSize extends StatefulWidget {
/// Creates a helper widget that calls a callback when its size changes.
const WidgetSize({
required this.onChange,
required this.child,
super.key,
});
/// The child widget, the size of which is to be measured.
final Widget child;
final Function onChange;
/// The callback to be called when the size of the widget changes.
final Function(Size) onChange;
@override
State<WidgetSize> createState() => _WidgetSizeState();
@ -34,6 +46,11 @@ class _WidgetSizeState extends State<WidgetSize> {
}
final newSize = context.size;
if (newSize == null) {
return;
}
if (oldSize == newSize) {
return;
}

View File

@ -0,0 +1,195 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:ionicons/ionicons.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
import 'package:selfprivacy/ui/components/drawers/support_drawer.dart';
import 'package:selfprivacy/ui/helpers/widget_size.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
class BrandHeroScreen extends StatelessWidget {
const BrandHeroScreen({
required this.children,
super.key,
this.hasBackButton = true,
this.hasFlashButton = false,
this.heroIcon,
this.heroIconWidget,
this.heroTitle = '',
this.heroSubtitle,
this.onBackButtonPressed,
this.bodyPadding = const EdgeInsets.all(16.0),
this.ignoreBreakpoints = false,
this.hasSupportDrawer = false,
});
final List<Widget> children;
final bool hasBackButton;
final bool hasFlashButton;
final IconData? heroIcon;
final Widget? heroIconWidget;
final String heroTitle;
final String? heroSubtitle;
final VoidCallback? onBackButtonPressed;
final EdgeInsetsGeometry bodyPadding;
/// On non-mobile screens the buttons of the app bar are hidden.
/// This is because this widget implies that it is nested inside a bigger layout.
/// If it is not nested, set this to true.
final bool ignoreBreakpoints;
/// Usually support drawer is provided by the parent layout.
/// If it is not provided, set this to true.
final bool hasSupportDrawer;
@override
Widget build(final BuildContext context) {
final Widget heroIconWidget = this.heroIconWidget ??
Icon(
heroIcon ?? Icons.help,
size: 48.0,
color: Theme.of(context).colorScheme.onBackground,
);
final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null;
return Scaffold(
endDrawerEnableOpenDragGesture: false,
endDrawer: hasSupportDrawer ? const SupportDrawer() : null,
body: CustomScrollView(
slivers: [
HeroSliverAppBar(
heroTitle: heroTitle,
hasHeroIcon: hasHeroIcon,
hasBackButton: hasBackButton,
onBackButtonPressed: onBackButtonPressed,
heroIconWidget: heroIconWidget,
hasFlashButton: hasFlashButton,
ignoreBreakpoints: ignoreBreakpoints,
),
if (heroSubtitle != null)
SliverPadding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 4.0,
),
sliver: SliverList(
delegate: SliverChildListDelegate([
Text(
heroSubtitle!,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onBackground,
),
textAlign: hasHeroIcon ? TextAlign.center : TextAlign.start,
),
]),
),
),
SliverPadding(
padding: bodyPadding,
sliver: SliverList(
delegate: SliverChildListDelegate(children),
),
),
],
),
);
}
}
class HeroSliverAppBar extends StatefulWidget {
const HeroSliverAppBar({
required this.heroTitle,
required this.hasHeroIcon,
required this.hasBackButton,
required this.onBackButtonPressed,
required this.heroIconWidget,
required this.hasFlashButton,
required this.ignoreBreakpoints,
super.key,
});
final String heroTitle;
final bool hasHeroIcon;
final bool hasBackButton;
final bool hasFlashButton;
final VoidCallback? onBackButtonPressed;
final Widget heroIconWidget;
final bool ignoreBreakpoints;
@override
State<HeroSliverAppBar> createState() => _HeroSliverAppBarState();
}
class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
Size _size = Size.zero;
@override
Widget build(final BuildContext context) {
final isMobile =
widget.ignoreBreakpoints ? true : Breakpoints.small.isActive(context);
final isJobsListEmpty = context.watch<JobsCubit>().state is JobsStateEmpty;
return SliverAppBar(
expandedHeight:
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,
primary: true,
pinned: isMobile,
stretch: true,
surfaceTintColor: isMobile ? null : Colors.transparent,
leading: (widget.hasBackButton && isMobile)
? const AutoLeadingButton()
: const SizedBox.shrink(),
actions: [
if (widget.hasFlashButton && isMobile)
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (final BuildContext context) => const JobsContent(),
);
},
icon: Icon(
isJobsListEmpty ? Ionicons.flash_outline : Ionicons.flash,
),
color: isJobsListEmpty
? Theme.of(context).colorScheme.onBackground
: Theme.of(context).colorScheme.primary,
),
),
const SizedBox.shrink(),
],
flexibleSpace: FlexibleSpaceBar(
title: LayoutBuilder(
builder: (final context, final constraints) => SizedBox(
width: constraints.maxWidth - 72.0,
child: WidgetSize(
onChange: (final Size size) => setState(() => _size = size),
child: Text(
widget.heroTitle,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface,
),
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
),
),
),
),
expandedTitleScale: 1.2,
centerTitle: true,
collapseMode: CollapseMode.pin,
titlePadding: const EdgeInsets.only(
bottom: 12.0,
top: 16.0,
),
background: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 72.0),
if (widget.hasHeroIcon) widget.heroIconWidget,
],
),
),
);
}
}

View File

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
class ResponsiveLayoutWithInfobox extends StatelessWidget {
const ResponsiveLayoutWithInfobox({
required this.primaryColumn,
this.topChild,
this.secondaryColumn,
super.key,
});
final Widget? topChild;
final Widget primaryColumn;
final Widget? secondaryColumn;
@override
Widget build(final BuildContext context) {
final hasSecondaryColumn = secondaryColumn != null;
final hasTopChild = topChild != null;
if (Breakpoints.large.isActive(context)) {
return LayoutBuilder(
builder: (final context, final constraints) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (hasTopChild)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: constraints.maxWidth * 0.9,
child: topChild,
),
],
),
if (hasTopChild) const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: hasSecondaryColumn
? constraints.maxWidth * 0.7
: constraints.maxWidth * 0.9,
child: primaryColumn,
),
if (hasSecondaryColumn) const SizedBox(width: 16),
if (hasSecondaryColumn)
SizedBox(
width: constraints.maxWidth * 0.2,
child: secondaryColumn,
),
],
),
],
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (hasTopChild) topChild!,
const SizedBox(height: 16),
primaryColumn,
const SizedBox(height: 32),
if (hasSecondaryColumn) secondaryColumn!,
],
);
}
}

View File

@ -0,0 +1,277 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
import 'package:selfprivacy/ui/components/drawers/support_drawer.dart';
import 'package:selfprivacy/ui/router/root_destinations.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
class RootScaffoldWithNavigation extends StatelessWidget {
const RootScaffoldWithNavigation({
required this.child,
required this.title,
required this.destinations,
this.showBottomBar = true,
this.showFab = true,
super.key,
});
final Widget child;
final String title;
final bool showBottomBar;
final List<RouteDestination> destinations;
final bool showFab;
@override
// ignore: prefer_expression_function_bodies
Widget build(final BuildContext context) {
return Scaffold(
appBar: Breakpoints.mediumAndUp.isActive(context)
? PreferredSize(
preferredSize: const Size.fromHeight(52),
child: _RootAppBar(title: title),
)
: null,
endDrawer: const SupportDrawer(),
endDrawerEnableOpenDragGesture: false,
body: Row(
children: [
if (Breakpoints.medium.isActive(context))
_MainScreenNavigationRail(
destinations: destinations,
showFab: showFab,
),
if (Breakpoints.large.isActive(context))
_MainScreenNavigationDrawer(
destinations: destinations,
showFab: showFab,
),
Expanded(child: child),
],
),
bottomNavigationBar: _BottomBar(
destinations: destinations,
hidden: !(Breakpoints.small.isActive(context) && showBottomBar),
key: const Key('bottomBar'),
),
floatingActionButton:
showFab && Breakpoints.small.isActive(context) && showBottomBar
? const BrandFab()
: null,
);
}
}
class _RootAppBar extends StatelessWidget {
const _RootAppBar({
required this.title,
});
final String title;
@override
Widget build(final BuildContext context) => AppBar(
title: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder:
(final Widget child, final Animation<double> animation) =>
SlideTransition(
position: animation.drive(
Tween<Offset>(
begin: const Offset(0.0, 0.2),
end: Offset.zero,
),
),
child: FadeTransition(
opacity: animation,
child: child,
),
),
child: SizedBox(
key: ValueKey<String>(title),
width: double.infinity,
child: Text(
title,
),
),
),
leading: context.router.pageCount > 1
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => context.router.pop(),
)
: null,
actions: const [
SizedBox.shrink(),
],
);
}
class _MainScreenNavigationRail extends StatelessWidget {
const _MainScreenNavigationRail({
required this.destinations,
this.showFab = true,
});
final List<RouteDestination> destinations;
final bool showFab;
@override
Widget build(final BuildContext context) {
int? activeIndex = destinations.indexWhere(
(final destination) =>
context.router.isRouteActive(destination.route.routeName),
);
final prevActiveIndex = destinations.indexWhere(
(final destination) => context.router.stack
.any((final route) => route.name == destination.route.routeName),
);
if (activeIndex == -1) {
if (prevActiveIndex != -1) {
activeIndex = prevActiveIndex;
} else {
activeIndex = 0;
}
}
final isExtended = Breakpoints.large.isActive(context);
return LayoutBuilder(
builder: (final context, final constraints) => SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: IntrinsicHeight(
child: NavigationRail(
backgroundColor: Colors.transparent,
labelType: isExtended
? NavigationRailLabelType.none
: NavigationRailLabelType.all,
extended: isExtended,
leading: showFab
? const BrandFab(
extended: false,
)
: null,
groupAlignment: 0.0,
destinations: destinations
.map(
(final destination) => NavigationRailDestination(
icon: Icon(destination.icon),
label: Text(destination.label),
),
)
.toList(),
selectedIndex: activeIndex,
onDestinationSelected: (final index) {
context.router.replaceAll([destinations[index].route]);
},
),
),
),
),
);
}
}
class _BottomBar extends StatelessWidget {
const _BottomBar({
required this.destinations,
required this.hidden,
super.key,
});
final List<RouteDestination> destinations;
final bool hidden;
@override
Widget build(final BuildContext context) {
final prevActiveIndex = destinations.indexWhere(
(final destination) => context.router.stack
.any((final route) => route.name == destination.route.routeName),
);
return AnimatedContainer(
duration: const Duration(milliseconds: 500),
height: hidden ? 0 : 80,
curve: Curves.easeInOutCubicEmphasized,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
),
child: NavigationBar(
selectedIndex: prevActiveIndex == -1 ? 0 : prevActiveIndex,
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
onDestinationSelected: (final index) {
context.router.replaceAll([destinations[index].route]);
},
destinations: destinations
.map(
(final destination) => NavigationDestination(
icon: Icon(destination.icon),
label: destination.label,
),
)
.toList(),
),
);
}
}
class _MainScreenNavigationDrawer extends StatelessWidget {
const _MainScreenNavigationDrawer({
required this.destinations,
this.showFab = true,
});
final List<RouteDestination> destinations;
final bool showFab;
@override
Widget build(final BuildContext context) {
int? activeIndex = destinations.indexWhere(
(final destination) =>
context.router.isRouteActive(destination.route.routeName),
);
final prevActiveIndex = destinations.indexWhere(
(final destination) => context.router.stack
.any((final route) => route.name == destination.route.routeName),
);
if (activeIndex == -1) {
if (prevActiveIndex != -1) {
activeIndex = prevActiveIndex;
} else {
activeIndex = 0;
}
}
return SizedBox(
height: MediaQuery.of(context).size.height,
width: 296,
child: NavigationDrawer(
key: const Key('PrimaryNavigationDrawer'),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
surfaceTintColor: Colors.transparent,
selectedIndex: activeIndex,
onDestinationSelected: (final index) {
context.router.replaceAll([destinations[index].route]);
},
children: [
const Padding(
padding: EdgeInsets.all(16.0),
child: BrandFab(extended: true),
),
const SizedBox(height: 16),
...destinations.map(
(final destination) => NavigationDrawerDestination(
icon: Icon(destination.icon),
label: Text(destination.label),
),
),
],
),
);
}
}

View File

@ -1,26 +1,27 @@
import 'package:auto_route/auto_route.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/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/models/json/backup.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/cards/outlined_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/helpers/modals.dart';
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class BackupDetails extends StatefulWidget {
const BackupDetails({super.key});
@RoutePage()
class BackupDetailsPage extends StatefulWidget {
const BackupDetailsPage({super.key});
@override
State<BackupDetails> createState() => _BackupDetailsState();
State<BackupDetailsPage> createState() => _BackupDetailsPageState();
}
class _BackupDetailsState extends State<BackupDetails>
class _BackupDetailsPageState extends State<BackupDetailsPage>
with SingleTickerProviderStateMixin {
@override
Widget build(final BuildContext context) {
@ -57,7 +58,10 @@ class _BackupDetailsState extends State<BackupDetails>
text: 'backup.initialize'.tr(),
),
if (backupStatus == BackupStatusEnum.initializing)
BrandText.body1('backup.waiting_for_rebuild'.tr()),
Text(
'backup.waiting_for_rebuild'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (backupStatus != BackupStatusEnum.initializing &&
backupStatus != BackupStatusEnum.noKey)
OutlinedCard(
@ -227,7 +231,10 @@ class _BackupDetailsState extends State<BackupDetails>
),
),
if (backupStatus == BackupStatusEnum.error)
BrandText.body1(backupError.toString()),
Text(
backupError.toString(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
);
}

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -5,11 +6,12 @@ import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/json/api_token.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/ui/pages/devices/new_device.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@RoutePage()
class DevicesScreen extends StatefulWidget {
const DevicesScreen({super.key});
@ -25,7 +27,7 @@ class _DevicesScreenState extends State<DevicesScreen> {
return RefreshIndicator(
onRefresh: () async {
context.read<ApiDevicesCubit>().refresh();
await context.read<ApiDevicesCubit>().refresh();
},
child: BrandHeroScreen(
heroTitle: 'devices.main_screen.header'.tr(),
@ -90,8 +92,7 @@ class _DevicesInfo extends StatelessWidget {
),
),
...devicesStatus.otherDevices
.map((final device) => _DeviceTile(device: device))
.toList(),
.map((final device) => _DeviceTile(device: device)),
],
);
}

View File

@ -3,8 +3,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
class NewDeviceScreen extends StatelessWidget {
const NewDeviceScreen({super.key});

View File

@ -1,13 +1,15 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/utils/network_utils.dart';
@RoutePage()
class DnsDetailsPage extends StatefulWidget {
const DnsDetailsPage({super.key});
@ -155,8 +157,7 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
),
],
),
)
.toList(),
),
const SizedBox(height: 16.0),
ListTile(
title: Text(
@ -197,8 +198,7 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
),
],
),
)
.toList(),
),
],
);
}

View File

@ -1,67 +1,73 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
import 'package:package_info/package_info.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:url_launcher/url_launcher.dart';
@RoutePage()
class AboutApplicationPage extends StatelessWidget {
const AboutApplicationPage({super.key});
@override
Widget build(final BuildContext context) => SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'about_application_page.title'.tr(),
hasBackButton: true,
Widget build(final BuildContext context) {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
return BrandHeroScreen(
hasBackButton: true,
hasFlashButton: false,
heroTitle: 'about_application_page.title'.tr(),
children: [
FutureBuilder(
future: _packageVersion(),
builder: (final context, final snapshot) => Text(
'about_application_page.application_version_text'
.tr(args: [snapshot.data.toString()]),
style: Theme.of(context).textTheme.bodyLarge,
),
),
if (isReady)
FutureBuilder(
future: _apiVersion(),
builder: (final context, final snapshot) => Text(
'about_application_page.api_version_text'
.tr(args: [snapshot.data.toString()]),
style: Theme.of(context).textTheme.bodyLarge,
),
),
body: ListView(
padding: paddingH15V0,
const SizedBox(height: 10),
// Button to call showAboutDialog
TextButton(
onPressed: () => showAboutDialog(
context: context,
applicationName: 'SelfPrivacy',
applicationLegalese: '© 2022 SelfPrivacy',
// Link to privacy policy
children: [
const SizedBox(height: 10),
FutureBuilder(
future: _packageVersion(),
builder: (final context, final snapshot) => BrandText.body1(
'about_application_page.application_version_text'
.tr(args: [snapshot.data.toString()]),
),
),
FutureBuilder(
future: _apiVersion(),
builder: (final context, final snapshot) => BrandText.body1(
'about_application_page.api_version_text'
.tr(args: [snapshot.data.toString()]),
),
),
const SizedBox(height: 10),
// Button to call showAboutDialog
TextButton(
onPressed: () => showAboutDialog(
context: context,
applicationName: 'SelfPrivacy',
applicationLegalese: '© 2022 SelfPrivacy',
// Link to privacy policy
children: [
TextButton(
onPressed: () => launchUrl(
Uri.parse('https://selfprivacy.ru/privacy-policy'),
mode: LaunchMode.externalApplication,
),
child: Text('about_application_page.privacy_policy'.tr()),
),
],
onPressed: () => launchUrl(
Uri.parse('https://selfprivacy.ru/privacy-policy'),
mode: LaunchMode.externalApplication,
),
child: const Text('Show about dialog'),
child: Text('about_application_page.privacy_policy'.tr()),
),
],
),
child: const Text('Show about dialog'),
),
);
const SizedBox(height: 8),
const Divider(height: 0),
const SizedBox(height: 8),
const BrandMarkdown(
fileName: 'about',
),
],
);
}
Future<String> _packageVersion() async {
String packageVersion = 'unknown';

View File

@ -1,229 +0,0 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/utils/named_font_weight.dart';
import 'package:easy_localization/easy_localization.dart';
class AppSettingsPage extends StatefulWidget {
const AppSettingsPage({super.key});
@override
State<AppSettingsPage> createState() => _AppSettingsPageState();
}
class _AppSettingsPageState extends State<AppSettingsPage> {
@override
Widget build(final BuildContext context) {
final bool isDarkModeOn =
context.watch<AppSettingsCubit>().state.isDarkModeOn;
return SafeArea(
child: Builder(
builder: (final context) => Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'application_settings.title'.tr(),
hasBackButton: true,
),
),
body: ListView(
padding: paddingH15V0,
children: [
const Divider(height: 1),
Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: _TextColumn(
title: 'application_settings.dark_theme_title'.tr(),
value:
'application_settings.dark_theme_description'.tr(),
hasWarning: false,
),
),
const SizedBox(width: 5),
BrandSwitch(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (final value) => context
.read<AppSettingsCubit>()
.updateDarkMode(isDarkModeOn: !isDarkModeOn),
),
],
),
),
const Divider(height: 0),
Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: _TextColumn(
title: 'application_settings.reset_config_title'.tr(),
value: 'application_settings.reset_config_description'
.tr(),
hasWarning: false,
),
),
const SizedBox(width: 5),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: BrandColors.red1,
),
child: Text(
'basis.reset'.tr(),
style: const TextStyle(
color: BrandColors.white,
fontWeight: NamedFontWeight.demiBold,
),
),
onPressed: () {
showDialog(
context: context,
builder: (final _) => BrandAlert(
title: 'modals.are_you_sure'.tr(),
contentText: 'modals.purge_all_keys'.tr(),
actions: [
ActionButton(
text: 'modals.purge_all_keys_confirm'.tr(),
isRed: true,
onPressed: () {
context
.read<ServerInstallationCubit>()
.clearAppConfig();
Navigator.of(context).pop();
},
),
ActionButton(
text: 'basis.cancel'.tr(),
),
],
),
);
},
),
],
),
),
const Divider(height: 0),
_deleteServer(context)
],
),
),
),
);
}
Widget _deleteServer(final BuildContext context) {
final bool isDisabled =
context.watch<ServerInstallationCubit>().state.serverDetails == null;
return Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: _TextColumn(
title: 'application_settings.delete_server_title'.tr(),
value: 'application_settings.delete_server_description'.tr(),
hasWarning: false,
),
),
const SizedBox(width: 5),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: BrandColors.red1,
),
onPressed: isDisabled
? null
: () {
showDialog(
context: context,
builder: (final _) => BrandAlert(
title: 'modals.are_you_sure'.tr(),
contentText: 'modals.delete_server_volume'.tr(),
actions: [
ActionButton(
text: 'modals.yes'.tr(),
isRed: true,
onPressed: () async {
showDialog(
context: context,
builder: (final context) => Container(
alignment: Alignment.center,
child: const CircularProgressIndicator(),
),
);
await context
.read<ServerInstallationCubit>()
.serverDelete();
if (!mounted) {
return;
}
Navigator.of(context).pop();
},
),
ActionButton(
text: 'basis.cancel'.tr(),
),
],
),
);
},
child: Text(
'basis.delete'.tr(),
style: const TextStyle(
color: BrandColors.white,
fontWeight: NamedFontWeight.demiBold,
),
),
),
],
),
);
}
}
class _TextColumn extends StatelessWidget {
const _TextColumn({
required this.title,
required this.value,
this.hasWarning = false,
});
final String title;
final String value;
final bool hasWarning;
@override
Widget build(final BuildContext context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BrandText.body1(
title,
style: TextStyle(color: hasWarning ? BrandColors.warning : null),
),
const SizedBox(height: 5),
BrandText.body1(
value,
style: const TextStyle(
fontSize: 13,
height: 1.53,
color: BrandColors.gray1,
).merge(TextStyle(color: hasWarning ? BrandColors.warning : null)),
),
],
);
}

View File

@ -0,0 +1,151 @@
import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/buttons/dialog_action_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:easy_localization/easy_localization.dart';
@RoutePage()
class AppSettingsPage extends StatefulWidget {
const AppSettingsPage({super.key});
@override
State<AppSettingsPage> createState() => _AppSettingsPageState();
}
class _AppSettingsPageState extends State<AppSettingsPage> {
@override
Widget build(final BuildContext context) {
final bool isDarkModeOn =
context.watch<AppSettingsCubit>().state.isDarkModeOn;
final bool isSystemDarkModeOn =
context.watch<AppSettingsCubit>().state.isAutoDarkModeOn;
return BrandHeroScreen(
hasBackButton: true,
hasFlashButton: false,
bodyPadding: const EdgeInsets.symmetric(vertical: 16),
heroTitle: 'application_settings.title'.tr(),
children: [
SwitchListTile(
title: Text('application_settings.system_dark_theme_title'.tr()),
subtitle:
Text('application_settings.system_dark_theme_description'.tr()),
value: isSystemDarkModeOn,
onChanged: (final value) => context
.read<AppSettingsCubit>()
.updateAutoDarkMode(isAutoDarkModeOn: !isSystemDarkModeOn),
),
SwitchListTile(
title: Text('application_settings.dark_theme_title'.tr()),
subtitle: Text('application_settings.dark_theme_description'.tr()),
value: Theme.of(context).brightness == Brightness.dark,
onChanged: isSystemDarkModeOn
? null
: (final value) => context
.read<AppSettingsCubit>()
.updateDarkMode(isDarkModeOn: !isDarkModeOn),
),
const Divider(height: 0),
Padding(
padding: const EdgeInsets.all(16),
child: Text(
'application_settings.dangerous_settings'.tr(),
style: Theme.of(context).textTheme.labelLarge!.copyWith(
color: Theme.of(context).colorScheme.error,
),
),
),
const _ResetAppTile(),
// const Divider(height: 0),
_deleteServer(context)
],
);
}
Widget _deleteServer(final BuildContext context) {
final bool isDisabled =
context.watch<ServerInstallationCubit>().state.serverDetails == null;
return ListTile(
title: Text('application_settings.delete_server_title'.tr()),
subtitle: Text('application_settings.delete_server_description'.tr()),
textColor: isDisabled
? Theme.of(context).colorScheme.onBackground.withOpacity(0.5)
: Theme.of(context).colorScheme.onBackground,
onTap: isDisabled
? null
: () {
showDialog(
context: context,
builder: (final _) => AlertDialog(
title: Text('modals.are_you_sure'.tr()),
content: Text('modals.delete_server_volume'.tr()),
actions: [
DialogActionButton(
text: 'modals.yes'.tr(),
isRed: true,
onPressed: () async {
unawaited(
showDialog(
context: context,
builder: (final context) => Container(
alignment: Alignment.center,
child: const CircularProgressIndicator(),
),
),
);
await context
.read<ServerInstallationCubit>()
.serverDelete();
if (!mounted) {
return;
}
Navigator.of(context).pop();
},
),
DialogActionButton(
text: 'basis.cancel'.tr(),
),
],
),
);
},
);
}
}
class _ResetAppTile extends StatelessWidget {
const _ResetAppTile();
@override
Widget build(final BuildContext context) => ListTile(
title: Text('application_settings.reset_config_title'.tr()),
subtitle: Text('application_settings.reset_config_description'.tr()),
onTap: () {
showDialog(
context: context,
builder: (final _) => AlertDialog(
title: Text('modals.are_you_sure'.tr()),
content: Text('modals.purge_all_keys'.tr()),
actions: [
DialogActionButton(
text: 'modals.purge_all_keys_confirm'.tr(),
isRed: true,
onPressed: () {
context.read<ServerInstallationCubit>().clearAppConfig();
Navigator.of(context).pop();
},
),
DialogActionButton(
text: 'basis.cancel'.tr(),
),
],
),
);
},
);
}

View File

@ -0,0 +1,85 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/api_maps/staging_options.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:easy_localization/easy_localization.dart';
@RoutePage()
class DeveloperSettingsPage extends StatefulWidget {
const DeveloperSettingsPage({super.key});
@override
State<DeveloperSettingsPage> createState() => _DeveloperSettingsPageState();
}
class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
@override
Widget build(final BuildContext context) => BrandHeroScreen(
hasBackButton: true,
hasFlashButton: false,
bodyPadding: const EdgeInsets.symmetric(vertical: 16),
heroTitle: 'developer_settings.title'.tr(),
heroSubtitle: 'developer_settings.subtitle'.tr(),
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(
'developer_settings.server_setup'.tr(),
style: Theme.of(context).textTheme.labelLarge!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
),
),
SwitchListTile(
title: Text('developer_settings.use_staging_acme'.tr()),
subtitle:
Text('developer_settings.use_staging_acme_description'.tr()),
value: StagingOptions.stagingAcme,
onChanged: null,
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(
'developer_settings.routing'.tr(),
style: Theme.of(context).textTheme.labelLarge!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
),
),
ListTile(
title: Text('developer_settings.reset_onboarding'.tr()),
subtitle:
Text('developer_settings.reset_onboarding_description'.tr()),
enabled:
!context.watch<AppSettingsCubit>().state.isOnboardingShowing,
onTap: () => context
.read<AppSettingsCubit>()
.turnOffOnboarding(isOnboardingShowing: true),
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(
'developer_settings.cubit_statuses'.tr(),
style: Theme.of(context).textTheme.labelLarge!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
),
),
ListTile(
title: const Text('ApiDevicesCubit'),
subtitle: Text(
context.watch<ApiDevicesCubit>().state.status.toString(),
),
),
ListTile(
title: const Text('RecoveryKeyCubit'),
subtitle: Text(
context.watch<RecoveryKeyCubit>().state.loadingStatus.toString(),
),
),
],
);
}

View File

@ -1,20 +1,21 @@
import 'package:auto_route/auto_route.dart';
import 'dart:collection';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/models/message.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/list_tiles/log_list_tile.dart';
class Console extends StatefulWidget {
const Console({super.key});
@RoutePage()
class ConsolePage extends StatefulWidget {
const ConsolePage({super.key});
@override
State<Console> createState() => _ConsoleState();
State<ConsolePage> createState() => _ConsolePageState();
}
class _ConsoleState extends State<Console> {
class _ConsolePageState extends State<ConsolePage> {
@override
void initState() {
getIt.get<ConsoleModel>().addListener(update);
@ -28,21 +29,30 @@ class _ConsoleState extends State<Console> {
super.dispose();
}
void update() => setState(() => {});
bool paused = false;
void update() {
if (!paused) {
setState(() => {});
}
}
@override
Widget build(final BuildContext context) => SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(53),
child: Column(
children: [
BrandHeader(
title: 'console_page.title'.tr(),
hasBackButton: true,
),
],
appBar: AppBar(
title: Text('console_page.title'.tr()),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
actions: [
IconButton(
icon: Icon(
paused ? Icons.play_arrow_outlined : Icons.pause_outlined),
onPressed: () => setState(() => paused = !paused),
),
],
),
body: FutureBuilder(
future: getIt.allReady(),
@ -61,30 +71,7 @@ class _ConsoleState extends State<Console> {
const SizedBox(height: 20),
...UnmodifiableListView(
messages
.map((final message) {
final bool isError =
message.type == MessageType.warning;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text:
'${message.timeString}${isError ? '(Error)' : ''}: \n',
style: TextStyle(
fontWeight: FontWeight.bold,
color:
isError ? BrandColors.red1 : null,
),
),
TextSpan(text: message.text),
],
),
),
);
})
.map((final message) => LogListItem(message: message))
.toList()
.reversed,
),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:ionicons/ionicons.dart';
@ -5,23 +6,13 @@ import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/pages/devices/devices.dart';
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart';
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart';
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/ui/pages/users/users.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/pages/more/about_us.dart';
import 'package:selfprivacy/ui/pages/more/app_settings/app_setting.dart';
import 'package:selfprivacy/ui/pages/more/console.dart';
import 'package:selfprivacy/ui/pages/more/about_application.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
import 'package:selfprivacy/ui/router/router.dart';
@RoutePage()
class MorePage extends StatelessWidget {
const MorePage({super.key});
@ -34,12 +25,14 @@ class MorePage extends StatelessWidget {
context.watch<ApiServerVolumeCubit>().state.usesBinds;
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'basis.more'.tr(),
),
),
appBar: Breakpoints.small.isActive(context)
? PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'basis.more'.tr(),
),
)
: null,
body: ListView(
children: [
Padding(
@ -50,7 +43,7 @@ class MorePage extends StatelessWidget {
_MoreMenuItem(
title: 'storage.start_migration_button'.tr(),
iconData: Icons.drive_file_move_outline,
goTo: ServicesMigrationPage(
goTo: () => ServicesMigrationRoute(
diskStatus: context
.watch<ApiServerVolumeCubit>()
.state
@ -77,7 +70,7 @@ class MorePage extends StatelessWidget {
_MoreMenuItem(
title: 'more_page.configuration_wizard'.tr(),
iconData: Icons.change_history_outlined,
goTo: const InitializingPage(),
goTo: () => const InitializingRoute(),
subtitle: 'not_ready_card.in_menu'.tr(),
accent: true,
),
@ -85,47 +78,43 @@ class MorePage extends StatelessWidget {
_MoreMenuItem(
title: 'more_page.create_ssh_key'.tr(),
iconData: Ionicons.key_outline,
goTo: const UserDetails(
goTo: () => UserDetailsRoute(
login: 'root',
),
),
if (isReady)
_MoreMenuItem(
iconData: Icons.password_outlined,
goTo: const RecoveryKey(),
goTo: () => const RecoveryKeyRoute(),
title: 'recovery_key.key_main_header'.tr(),
),
if (isReady)
_MoreMenuItem(
iconData: Icons.devices_outlined,
goTo: const DevicesScreen(),
goTo: () => const DevicesRoute(),
title: 'devices.main_screen.header'.tr(),
),
_MoreMenuItem(
title: 'more_page.application_settings'.tr(),
iconData: Icons.settings_outlined,
goTo: const AppSettingsPage(),
),
_MoreMenuItem(
title: 'more_page.about_project'.tr(),
iconData: BrandIcons.engineer,
goTo: const AboutUsPage(),
goTo: () => const AppSettingsRoute(),
),
_MoreMenuItem(
title: 'more_page.about_application'.tr(),
iconData: BrandIcons.fire,
goTo: const AboutApplicationPage(),
goTo: () => const AboutApplicationRoute(),
longGoTo: const DeveloperSettingsRoute(),
),
if (!isReady)
_MoreMenuItem(
title: 'more_page.onboarding'.tr(),
iconData: BrandIcons.start,
goTo: const OnboardingPage(nextPage: RootPage()),
goTo: () => const OnboardingRoute(),
),
_MoreMenuItem(
title: 'more_page.console'.tr(),
iconData: BrandIcons.terminal,
goTo: const Console(),
goTo: () => const ConsoleRoute(),
),
],
),
@ -140,14 +129,16 @@ class _MoreMenuItem extends StatelessWidget {
const _MoreMenuItem({
required this.iconData,
required this.title,
required this.goTo,
this.subtitle,
this.goTo,
this.longGoTo,
this.accent = false,
});
final IconData iconData;
final String title;
final Widget? goTo;
final PageRouteInfo Function() goTo;
final PageRouteInfo? longGoTo;
final String? subtitle;
final bool accent;
@ -160,9 +151,9 @@ class _MoreMenuItem extends StatelessWidget {
tertiary: accent,
child: ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
onTap: goTo != null
? () => Navigator.of(context).push(materialRoute(goTo!))
: null,
onTap: () => context.pushRoute(goTo()),
onLongPress:
longGoTo != null ? () => context.pushRoute(longGoTo!) : null,
leading: Icon(
iconData,
size: 24,

View File

@ -1,13 +1,14 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:easy_localization/easy_localization.dart';
@RoutePage()
class OnboardingPage extends StatefulWidget {
const OnboardingPage({required this.nextPage, super.key});
const OnboardingPage({super.key});
final Widget nextPage;
@override
State<OnboardingPage> createState() => _OnboardingPageState();
}
@ -22,14 +23,14 @@ class _OnboardingPageState extends State<OnboardingPage> {
@override
Widget build(final BuildContext context) => Scaffold(
body: PageView(
controller: pageController,
children: [
_withPadding(firstPage()),
_withPadding(secondPage()),
],
),
);
body: PageView(
controller: pageController,
children: [
_withPadding(firstPage()),
_withPadding(secondPage()),
],
),
);
Widget _withPadding(final Widget child) => Padding(
padding: const EdgeInsets.symmetric(
@ -76,7 +77,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
pageController.animateToPage(
1,
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
curve: Curves.easeInOutCubicEmphasized,
);
},
text: 'basis.next'.tr(),
@ -142,10 +143,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
BrandButton.rised(
onPressed: () {
context.read<AppSettingsCubit>().turnOffOnboarding();
Navigator.of(context).pushAndRemoveUntil(
materialRoute(widget.nextPage),
(final route) => false,
);
context.router.replaceAll([
const RootRoute(),
const InitializingRoute(),
]);
},
text: 'basis.got_it'.tr(),
),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
@ -10,13 +11,12 @@ import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart';
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@RoutePage()
class ProvidersPage extends StatefulWidget {
const ProvidersPage({super.key});
@ -61,12 +61,14 @@ class _ProvidersPageState extends State<ProvidersPage> {
}
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'basis.providers_title'.tr(),
),
),
appBar: Breakpoints.small.isActive(context)
? PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'basis.providers_title'.tr(),
),
)
: null,
body: ListView(
padding: paddingH15V0,
children: [
@ -81,8 +83,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
subtitle: diskStatus.isDiskOkay
? 'storage.status_ok'.tr()
: 'storage.status_error'.tr(),
onTap: () => Navigator.of(context)
.push(materialRoute(const ServerDetailsScreen())),
onTap: () => context.pushRoute(const ServerDetailsRoute()),
),
const SizedBox(height: 16),
_Card(
@ -92,11 +93,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
subtitle: appConfig.isDomainSelected
? appConfig.serverDomain!.domainName
: '',
onTap: () => Navigator.of(context).push(
materialRoute(
const DnsDetailsPage(),
),
),
onTap: () => context.pushRoute(const DnsDetailsRoute()),
),
const SizedBox(height: 16),
// TODO: When backups are fixed, show this card
@ -108,8 +105,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
icon: BrandIcons.save,
title: 'backup.card_title'.tr(),
subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '',
onTap: () => Navigator.of(context)
.push(materialRoute(const BackupDetails())),
onTap: () => context.pushRoute(const BackupDetailsRoute()),
),
],
),

View File

@ -1,4 +1,4 @@
import 'package:cubit_form/cubit_form.dart';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -6,20 +6,21 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.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/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key_receiving.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class RecoveryKey extends StatefulWidget {
const RecoveryKey({super.key});
@RoutePage()
class RecoveryKeyPage extends StatefulWidget {
const RecoveryKeyPage({super.key});
@override
State<RecoveryKey> createState() => _RecoveryKeyState();
State<RecoveryKeyPage> createState() => _RecoveryKeyPageState();
}
class _RecoveryKeyState extends State<RecoveryKey> {
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
@override
void initState() {
super.initState();
@ -250,7 +251,7 @@ class _RecoveryKeyConfigurationState extends State<RecoveryKeyConfiguration> {
setState(() {
_isLoading = false;
});
Navigator.of(context).push(
await Navigator.of(context).push(
materialRoute(
RecoveryKeyReceiving(recoveryKey: token), // TO DO
),

View File

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
class RecoveryKeyReceiving extends StatelessWidget {

View File

@ -1,89 +1,153 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart';
import 'package:selfprivacy/ui/pages/more/more.dart';
import 'package:selfprivacy/ui/pages/providers/providers.dart';
import 'package:selfprivacy/ui/pages/services/services.dart';
import 'package:selfprivacy/ui/pages/users/users.dart';
import 'package:selfprivacy/ui/layouts/root_scaffold_with_navigation.dart';
import 'package:selfprivacy/ui/router/root_destinations.dart';
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
import 'package:selfprivacy/ui/router/router.dart';
class RootPage extends StatefulWidget {
@RoutePage()
class RootPage extends StatefulWidget implements AutoRouteWrapper {
const RootPage({super.key});
@override
State<RootPage> createState() => _RootPageState();
@override
Widget wrappedRoute(final BuildContext context) => this;
}
class _RootPageState extends State<RootPage> with TickerProviderStateMixin {
late TabController tabController;
bool shouldUseSplitView() => false;
late final AnimationController _controller = AnimationController(
duration: const Duration(milliseconds: 400),
vsync: this,
);
late final Animation<double> _animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
@override
void initState() {
tabController = TabController(length: 4, vsync: this);
tabController.addListener(() {
setState(() {
tabController.index == 2
? _controller.forward()
: _controller.reverse();
});
});
super.initState();
}
@override
void dispose() {
tabController.dispose();
_controller.dispose();
super.dispose();
}
final destinations = rootDestinations;
@override
Widget build(final BuildContext context) {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
return Provider<ChangeTab>(
create: (final _) => ChangeTab(tabController.animateTo),
child: Scaffold(
body: TabBarView(
controller: tabController,
children: const [
ProvidersPage(),
ServicesPage(),
UsersPage(),
MorePage(),
if (context.read<AppSettingsCubit>().state.isOnboardingShowing) {
context.router.replace(const OnboardingRoute());
}
return AutoRouter(
builder: (final context, final child) {
final currentDestinationIndex = destinations.indexWhere(
(final destination) =>
context.router.isRouteActive(destination.route.routeName),
);
final isOtherRouterActive =
context.router.root.current.name != RootRoute.name;
final routeName = getRouteTitle(context.router.current.name).tr();
return RootScaffoldWithNavigation(
title: routeName,
destinations: destinations,
showBottomBar:
!(currentDestinationIndex == -1 && !isOtherRouterActive),
showFab: isReady,
child: child,
);
},
);
}
}
class MainScreenNavigationRail extends StatelessWidget {
const MainScreenNavigationRail({
required this.destinations,
super.key,
});
final List<RouteDestination> destinations;
@override
Widget build(final BuildContext context) {
int? activeIndex = destinations.indexWhere(
(final destination) =>
context.router.isRouteActive(destination.route.routeName),
);
if (activeIndex == -1) {
activeIndex = null;
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: 72,
child: LayoutBuilder(
builder: (final context, final constraints) => SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: IntrinsicHeight(
child: NavigationRail(
backgroundColor: Colors.transparent,
labelType: NavigationRailLabelType.all,
destinations: destinations
.map(
(final destination) => NavigationRailDestination(
icon: Icon(destination.icon),
label: Text(destination.label),
),
)
.toList(),
selectedIndex: activeIndex,
onDestinationSelected: (final index) {
context.router.replaceAll([destinations[index].route]);
},
),
),
),
),
),
),
);
}
}
class MainScreenNavigationDrawer extends StatelessWidget {
const MainScreenNavigationDrawer({
required this.destinations,
super.key,
});
final List<RouteDestination> destinations;
@override
Widget build(final BuildContext context) {
int? activeIndex = destinations.indexWhere(
(final destination) =>
context.router.isRouteActive(destination.route.routeName),
);
if (activeIndex == -1) {
activeIndex = null;
}
return SizedBox(
height: MediaQuery.of(context).size.height,
width: 296,
child: LayoutBuilder(
builder: (final context, final constraints) => NavigationDrawer(
// backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
// surfaceTintColor: Colors.transparent,
key: const Key('PrimaryNavigationDrawer'),
selectedIndex: activeIndex,
onDestinationSelected: (final index) {
context.router.replaceAll([destinations[index].route]);
},
children: [
const SizedBox(height: 18),
...destinations.map(
(final destination) => NavigationDrawerDestination(
icon: Icon(destination.icon),
label: Text(destination.label),
),
),
],
),
bottomNavigationBar: BrandTabBar(
controller: tabController,
),
floatingActionButton: isReady
? SizedBox(
height: 104 + 16,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
ScaleTransition(
scale: _animation,
child: const AddUserFab(),
),
const SizedBox(height: 16),
const BrandFab(),
],
),
)
: null,
),
);
}

View File

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
@ -10,18 +10,17 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/ui/components/brand_button/segmented_buttons.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/segmented_buttons.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart';
import 'package:selfprivacy/ui/pages/server_details/charts/cpu_chart.dart';
import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart';
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
import 'package:selfprivacy/utils/extensions/duration.dart';
import 'package:selfprivacy/utils/named_font_weight.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:timezone/timezone.dart';
@ -32,6 +31,7 @@ part 'time_zone/time_zone.dart';
var navigatorKey = GlobalKey<NavigatorState>();
@RoutePage()
class ServerDetailsScreen extends StatefulWidget {
const ServerDetailsScreen({super.key});
@ -75,6 +75,7 @@ class _ServerDetailsScreenState extends State<ServerDetailsScreen>
return BlocProvider(
create: (final context) => context.read<ServerDetailsCubit>()..check(),
child: BrandHeroScreen(
hasFlashButton: true,
heroIcon: BrandIcons.server,
heroTitle: 'server.card_title'.tr(),
heroSubtitle: 'server.description'.tr(),

View File

@ -23,15 +23,13 @@ class _TextDetails extends StatelessWidget {
),
),
),
...details.metadata
.map(
(final metadata) => ListTileOnSurfaceVariant(
leadingIcon: metadata.type.icon,
title: metadata.name,
subtitle: metadata.value,
),
)
.toList(),
...details.metadata.map(
(final metadata) => ListTileOnSurfaceVariant(
leadingIcon: metadata.type.icon,
title: metadata.name,
subtitle: metadata.value,
),
),
],
),
);
@ -39,24 +37,6 @@ class _TextDetails extends StatelessWidget {
throw Exception('wrong state');
}
}
Widget getRowTitle(final String title) => Padding(
padding: const EdgeInsets.only(right: 10),
child: BrandText.h5(
title,
textAlign: TextAlign.right,
),
);
Widget getRowValue(final String title, {final bool isBold = false}) =>
BrandText.body1(
title,
style: isBold
? const TextStyle(
fontWeight: NamedFontWeight.demiBold,
)
: null,
);
}
class _TempMessage extends StatelessWidget {
@ -69,7 +49,10 @@ class _TempMessage extends StatelessWidget {
Widget build(final BuildContext context) => SizedBox(
height: MediaQuery.of(context).size.height - 100,
child: Center(
child: BrandText.body2(message),
child: Text(
message,
style: Theme.of(context).textTheme.bodyMedium,
),
),
);
}

View File

@ -57,66 +57,72 @@ class _SelectTimezoneState extends State<SelectTimezone> {
}
@override
Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(
title: isSearching
? TextField(
readOnly: false,
textAlign: TextAlign.start,
textInputAction: TextInputAction.next,
enabled: true,
controller: searchController,
decoration: InputDecoration(
errorText: null,
hintText: 'server.timezone_search_bar'.tr(),
),
)
: Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text('server.select_timezone'.tr()),
Widget build(final BuildContext context) {
final isDesktop = Breakpoints.mediumAndUp.isActive(context);
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: (isDesktop || isSearching)
? TextField(
readOnly: false,
textAlign: TextAlign.start,
textInputAction: TextInputAction.next,
enabled: true,
controller: searchController,
decoration: InputDecoration(
errorText: null,
hintText: 'server.timezone_search_bar'.tr(),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: isSearching
? () => setState(() => isSearching = false)
: () => Navigator.of(context).pop(),
),
actions: [
if (!isSearching)
IconButton(
icon: const Icon(Icons.search),
onPressed: () => setState(() => isSearching = true),
)
: Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text('server.select_timezone'.tr()),
),
],
leading: !isDesktop
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: isSearching
? () => setState(() => isSearching = false)
: () => Navigator.of(context).pop(),
)
: null,
actions: [
if (!isSearching && !isDesktop)
IconButton(
icon: const Icon(Icons.search),
onPressed: () => setState(() => isSearching = true),
),
],
),
body: SafeArea(
child: ListView(
controller: scrollController,
children: locations
.where(
(final Location location) => timezoneFilterValue == null
? true
: location.name
.toLowerCase()
.contains(timezoneFilterValue!) ||
Duration(
milliseconds: location.currentTimeZone.offset,
)
.toDayHourMinuteFormat()
.contains(timezoneFilterValue!),
)
.toList()
.asMap()
.map(
(final key, final value) => locationToListTile(key, value),
)
.values
.toList(),
),
body: SafeArea(
child: ListView(
controller: scrollController,
children: locations
.where(
(final Location location) => timezoneFilterValue == null
? true
: location.name
.toLowerCase()
.contains(timezoneFilterValue!) ||
Duration(
milliseconds: location.currentTimeZone.offset,
)
.toDayHourMinuteFormat()
.contains(timezoneFilterValue!),
)
.toList()
.asMap()
.map(
(final key, final value) => locationToListTile(key, value),
)
.values
.toList(),
),
),
);
),
);
}
MapEntry<int, Container> locationToListTile(
MapEntry<int, ListTile> locationToListTile(
final int key,
final Location location,
) {
@ -126,46 +132,19 @@ class _SelectTimezoneState extends State<SelectTimezone> {
return MapEntry(
key,
Container(
height: 75,
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: BrandColors.dividerColor,
),
),
ListTile(
title: Text(
location.name,
),
child: InkWell(
onTap: () {
context.read<ServerDetailsCubit>().repository.setTimezone(
location.name,
);
Navigator.of(context).pop();
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
BrandText.body1(
location.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
BrandText.small(
'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}',
style: const TextStyle(
fontSize: 13,
),
),
],
),
),
subtitle: Text(
'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}',
),
onTap: () {
context.read<ServerDetailsCubit>().repository.setTimezone(
location.name,
);
Navigator.of(context).pop();
},
),
);
}

View File

@ -2,8 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart';
import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';

View File

@ -1,21 +1,19 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/service.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/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart';
import 'package:selfprivacy/ui/components/storage_list_items/service_migration_list_item.dart';
import 'package:selfprivacy/ui/helpers/modals.dart';
import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@RoutePage()
class ServicesMigrationPage extends StatefulWidget {
const ServicesMigrationPage({
required this.services,
@ -110,22 +108,20 @@ class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
),
child: Column(
children: [
...widget.diskStatus.diskVolumes
.map(
(final volume) => Column(
children: [
ServerStorageListItem(
volume: recalculatedDiskUsages(
volume,
widget.services,
),
dense: true,
),
const SizedBox(height: headerVerticalPadding),
],
...widget.diskStatus.diskVolumes.map(
(final volume) => Column(
children: [
ServerStorageListItem(
volume: recalculatedDiskUsages(
volume,
widget.services,
),
dense: true,
),
)
.toList(),
const SizedBox(height: headerVerticalPadding),
],
),
),
],
),
),
@ -138,23 +134,21 @@ class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
children: <Widget>[
if (widget.services.isEmpty)
const Center(child: CircularProgressIndicator()),
...widget.services
.map(
(final service) => Column(
children: [
const SizedBox(height: 8),
ServiceMigrationListItem(
service: service,
diskStatus: widget.diskStatus,
selectedVolume: serviceToDisk[service.id]!,
onChange: onChange,
),
const SizedBox(height: 4),
const Divider(),
],
...widget.services.map(
(final service) => Column(
children: [
const SizedBox(height: 8),
ServiceMigrationListItem(
service: service,
diskStatus: widget.diskStatus,
selectedVolume: serviceToDisk[service.id]!,
onChange: onChange,
),
)
.toList(),
const SizedBox(height: 4),
const Divider(),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InfoBox(
@ -180,17 +174,10 @@ class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
}
}
}
Navigator.of(context).pushAndRemoveUntil(
materialRoute(const RootPage()),
(final predicate) => false,
);
showBrandBottomSheet(
context.router.popUntilRoot();
showModalBottomSheet(
context: context,
builder: (final BuildContext context) =>
const BrandBottomSheet(
isExpended: true,
child: JobsContent(),
),
builder: (final BuildContext context) => const JobsContent(),
);
},
),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
@ -5,12 +6,11 @@ import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.d
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/price.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@RoutePage()
class ExtendingVolumePage extends StatefulWidget {
const ExtendingVolumePage({
required this.diskVolumeToResize,
@ -155,10 +155,7 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
DiskSize.fromGibibyte(_currentSliderGbValue),
context.read<ApiServerVolumeCubit>().reload,
);
Navigator.of(context).pushAndRemoveUntil(
materialRoute(const RootPage()),
(final predicate) => false,
);
context.router.popUntilRoot();
},
child: Text('storage.extend_volume_button.title'.tr()),
),

View File

@ -1,16 +1,17 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/outlined_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/extending_volume.dart';
import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/router/router.dart';
@RoutePage()
class ServerStoragePage extends StatefulWidget {
const ServerStoragePage({
required this.diskStatus,
@ -45,28 +46,26 @@ class _ServerStoragePageState extends State<ServerStoragePage> {
heroTitle: 'storage.card_title'.tr(),
children: [
// ...sections,
...widget.diskStatus.diskVolumes
.map(
(final volume) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ServerStorageSection(
volume: volume,
diskStatus: widget.diskStatus,
services: services
.where(
(final service) =>
service.storageUsage.volume == volume.name,
)
.toList(),
),
const SizedBox(height: 16),
const Divider(),
const SizedBox(height: 16),
],
...widget.diskStatus.diskVolumes.map(
(final volume) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ServerStorageSection(
volume: volume,
diskStatus: widget.diskStatus,
services: services
.where(
(final service) =>
service.storageUsage.volume == volume.name,
)
.toList(),
),
)
.toList(),
const SizedBox(height: 16),
const Divider(),
const SizedBox(height: 16),
],
),
),
const SizedBox(height: 8),
],
);
@ -93,24 +92,20 @@ class ServerStorageSection extends StatelessWidget {
volume: volume,
),
const SizedBox(height: 16),
...services
.map(
(final service) => ServerConsumptionListTile(
service: service,
volume: volume,
),
)
.toList(),
...services.map(
(final service) => ServerConsumptionListTile(
service: service,
volume: volume,
),
),
if (volume.isResizable) ...[
const SizedBox(height: 16),
BrandOutlinedButton(
title: 'storage.extend_volume_button.title'.tr(),
onPressed: () => Navigator.of(context).push(
materialRoute(
ExtendingVolumePage(
diskVolumeToResize: volume,
diskStatus: diskStatus,
),
onPressed: () => context.pushRoute(
ExtendingVolumeRoute(
diskVolumeToResize: volume,
diskStatus: diskStatus,
),
),
),

View File

@ -1,12 +1,12 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/server_storage.dart';
import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/router/router.dart';
class StorageCard extends StatelessWidget {
const StorageCard({
@ -45,13 +45,8 @@ class StorageCard extends StatelessWidget {
clipBehavior: Clip.antiAlias,
child: InkResponse(
highlightShape: BoxShape.rectangle,
onTap: () => Navigator.of(context).push(
materialRoute(
ServerStoragePage(
diskStatus: diskStatus,
),
),
),
onTap: () =>
context.pushRoute(ServerStorageRoute(diskStatus: diskStatus)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
@ -6,12 +7,12 @@ import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/launch_url.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@RoutePage()
class ServicePage extends StatefulWidget {
const ServicePage({required this.serviceId, super.key});
@ -46,11 +47,15 @@ class _ServicePageState extends State<ServicePage> {
return BrandHeroScreen(
hasBackButton: true,
hasFlashButton: true,
heroIconWidget: SvgPicture.string(
service.svgIcon,
width: 48.0,
height: 48.0,
color: Theme.of(context).colorScheme.onBackground,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.onBackground,
BlendMode.srcIn,
),
),
heroTitle: service.displayName,
children: [
@ -108,14 +113,12 @@ class _ServicePageState extends State<ServicePage> {
ListTile(
iconColor: Theme.of(context).colorScheme.onBackground,
// Open page ServicesMigrationPage
onTap: () => Navigator.of(context).push(
materialRoute(
ServicesMigrationPage(
services: [service],
diskStatus:
context.read<ApiServerVolumeCubit>().state.diskStatus,
isMigration: false,
),
onTap: () => context.pushRoute(
ServicesMigrationRoute(
services: [service],
diskStatus:
context.read<ApiServerVolumeCubit>().state.diskStatus,
isMigration: false,
),
),
leading: const Icon(Icons.drive_file_move_outlined),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/brand_theme.dart';
@ -5,17 +6,16 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/ui/pages/services/service_page.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
import 'package:selfprivacy/utils/launch_url.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/utils/ui_helpers.dart';
@RoutePage()
class ServicesPage extends StatefulWidget {
const ServicesPage({super.key});
@ -34,32 +34,35 @@ class _ServicesPageState extends State<ServicesPage> {
.sort((final a, final b) => a.status.index.compareTo(b.status.index));
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'basis.services'.tr(),
),
),
appBar: Breakpoints.small.isActive(context)
? PreferredSize(
preferredSize: const Size.fromHeight(52),
child: BrandHeader(
title: 'basis.services'.tr(),
),
)
: null,
body: RefreshIndicator(
onRefresh: () async {
context.read<ServicesCubit>().reload();
await context.read<ServicesCubit>().reload();
},
child: ListView(
padding: paddingH15V0,
children: [
BrandText.body1('basis.services_title'.tr()),
Text(
'basis.services_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 24),
if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)],
...services
.map(
(final service) => Padding(
padding: const EdgeInsets.only(
bottom: 30,
),
child: _Card(service: service),
),
)
.toList()
...services.map(
(final service) => Padding(
padding: const EdgeInsets.only(
bottom: 30,
),
child: _Card(service: service),
),
)
],
),
),
@ -98,81 +101,106 @@ class _Card extends StatelessWidget {
}
}
return GestureDetector(
onTap: isReady
? () => Navigator.of(context)
.push(materialRoute(ServicePage(serviceId: service.id)))
: null,
child: BrandCards.big(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconStatusMask(
status: getStatus(service.status),
icon: SvgPicture.string(
service.svgIcon,
width: 30.0,
height: 30.0,
color: Theme.of(context).colorScheme.onBackground,
),
),
],
),
ClipRect(
child: Stack(
return Card(
clipBehavior: Clip.antiAlias,
child: InkResponse(
highlightShape: BoxShape.rectangle,
onTap: isReady
? () => context.pushRoute(
ServiceRoute(serviceId: service.id),
)
: null,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
BrandText.h2(service.displayName),
const SizedBox(height: 10),
if (service.url != '' && service.url != null)
Column(
children: [
GestureDetector(
onTap: () => launchURL(
service.url,
),
child: Text(
'${service.url}',
style: TextStyle(
color:
Theme.of(context).colorScheme.secondary,
decoration: TextDecoration.underline,
),
),
),
const SizedBox(height: 10),
],
),
if (service.id == 'mailserver')
Column(
children: [
Text(
domainName,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
const SizedBox(height: 10),
],
),
BrandText.body2(service.loginInfo),
const SizedBox(height: 10),
BrandText.body2(service.description),
const SizedBox(height: 10),
],
IconStatusMask(
status: getStatus(service.status),
icon: SvgPicture.string(
service.svgIcon,
width: 30.0,
height: 30.0,
colorFilter: const ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
),
],
),
)
],
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text(
service.displayName,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 8),
if (service.url != '' && service.url != null)
Column(
children: [
_ServiceLink(
url: service.url ?? '',
),
const SizedBox(height: 10),
],
),
if (service.id == 'mailserver')
Column(
children: [
_ServiceLink(
url: domainName,
isActive: false,
),
const SizedBox(height: 10),
],
),
Text(
service.loginInfo,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 10),
Text(
service.description,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 10),
],
)
],
),
),
),
);
}
}
class _ServiceLink extends StatelessWidget {
const _ServiceLink({
required this.url,
this.isActive = true,
});
final String url;
final bool isActive;
@override
Widget build(final BuildContext context) => GestureDetector(
onTap: isActive
? () => launchURL(
url,
)
: null,
child: Text(
url,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
);
}

View File

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
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/forms/setup/initializing/provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
@ -9,18 +9,21 @@ import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_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_md/brand_md.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/buttons/outlined_button.dart';
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart';
import 'package:selfprivacy/ui/components/drawers/progress_drawer.dart';
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
import 'package:selfprivacy/ui/pages/root_route.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/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';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
@RoutePage()
class InitializingPage extends StatelessWidget {
const InitializingPage({super.key});
@ -48,99 +51,155 @@ class InitializingPage extends StatelessWidget {
][cubit.state.progress.index]();
}
const steps = [
'initializing.steps.hosting',
'initializing.steps.server_type',
'initializing.steps.dns_provider',
'initializing.steps.backups_provider',
'initializing.steps.domain',
'initializing.steps.master_account',
'initializing.steps.server',
'initializing.steps.dns_setup',
'initializing.steps.nixos_installation',
'initializing.steps.server_reboot',
'initializing.steps.final_checks',
];
return BlocListener<ServerInstallationCubit, ServerInstallationState>(
listener: (final context, final state) {
if (cubit.state is ServerInstallationFinished) {
Navigator.of(context)
.pushReplacement(materialRoute(const RootPage()));
context.router.popUntilRoot();
}
},
child: Scaffold(
appBar: AppBar(
actions: [
if (cubit.state is ServerInstallationFinished)
IconButton(
icon: const Icon(Icons.check),
onPressed: () {
Navigator.of(context)
.pushReplacement(materialRoute(const RootPage()));
},
)
],
title: Text(
'more_page.configuration_wizard'.tr(),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(28),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: ProgressBar(
steps: const [
'Hosting',
'Server Type',
'CloudFlare',
'Backblaze',
'Domain',
'User',
'Server',
'Installation',
],
activeIndex: cubit.state.porgressBar,
),
),
),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0.0),
child: 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()),
(final predicate) => false,
);
},
),
endDrawer: const SupportDrawer(),
endDrawerEnableOpenDragGesture: false,
appBar: Breakpoints.large.isActive(context)
? null
: AppBar(
actions: [
if (cubit.state is ServerInstallationFinished)
IconButton(
icon: const Icon(Icons.check),
onPressed: () {
context.router.popUntilRoot();
},
),
if (cubit.state is ServerInstallationEmpty ||
cubit.state is ServerInstallationNotFinished)
Container(
alignment: Alignment.center,
child: BrandButton.text(
title: 'basis.connect_to_existing'.tr(),
const SizedBox.shrink(),
],
title: Text(
'more_page.configuration_wizard'.tr(),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(28),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: ProgressBar(
steps: const [
'Hosting',
'Server Type',
'CloudFlare',
'Backblaze',
'Domain',
'User',
'Server',
'Installation',
],
activeIndex: cubit.state.porgressBar,
),
),
),
),
body: LayoutBuilder(
builder: (final context, final constraints) => Row(
children: [
if (Breakpoints.large.isActive(context))
ProgressDrawer(
steps: steps,
currentStep: cubit.state.progress.index,
title: 'more_page.configuration_wizard'.tr(),
constraints: constraints,
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (cubit.state is ServerInstallationEmpty ||
cubit.state is ServerInstallationNotFinished)
Container(
alignment: Alignment.center,
child: BrandButton.filled(
text: 'basis.connect_to_existing'.tr(),
onPressed: () {
context.router.replace(const RecoveryRoute());
},
),
),
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: double.infinity,
),
child: OutlinedButton(
child: Text(
cubit.state is ServerInstallationFinished
? 'basis.close'.tr()
: 'basis.later'.tr(),
),
onPressed: () {
Navigator.of(context).push(
materialRoute(
const RecoveryRouting(),
),
);
context.router.popUntilRoot();
},
),
)
],
),
],
),
),
SizedBox(
width: constraints.maxWidth -
(Breakpoints.large.isActive(context) ? 300 : 0),
height: constraints.maxHeight,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: Breakpoints.large.isActive(context)
? const EdgeInsets.all(16.0)
: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0.0),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: actualInitializingPage,
),
),
if (!Breakpoints.large.isActive(context))
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
child: BrandButton.text(
title:
cubit.state is ServerInstallationFinished
? 'basis.close'.tr()
: 'basis.later'.tr(),
onPressed: () {
context.router.popUntilRoot();
},
),
),
if (cubit.state is ServerInstallationEmpty ||
cubit.state is ServerInstallationNotFinished)
Container(
alignment: Alignment.center,
child: BrandButton.text(
title: 'basis.connect_to_existing'.tr(),
onPressed: () {
context.router
.replace(const RecoveryRoute());
},
),
)
],
),
],
),
),
),
],
@ -179,57 +238,55 @@ class InitializingPage extends StatelessWidget {
),
);
void _showModal(final BuildContext context, final Widget widget) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (final BuildContext context) => widget,
);
}
Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) =>
BlocProvider(
create: (final context) => DnsProviderFormCubit(initializingCubit),
child: Builder(
builder: (final context) => 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,
),
const SizedBox(height: 32),
CubitFormTextField(
formFieldCubit: context.read<DnsProviderFormCubit>().apiKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'initializing.cloudflare_api_token'.tr(),
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: 32),
BrandButton.rised(
onPressed: () =>
context.read<DnsProviderFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
const SizedBox(height: 10),
BrandButton.text(
onPressed: () => _showModal(
context,
const _HowTo(
fileName: 'how_cloudflare',
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<DnsProviderFormCubit>().apiKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'initializing.cloudflare_api_token'.tr(),
),
),
title: 'initializing.how'.tr(),
),
],
const SizedBox(height: 32),
BrandButton.filled(
onPressed: () =>
context.read<DnsProviderFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
const SizedBox(height: 10),
BrandOutlinedButton(
onPressed: () {
context.read<SupportSystemCubit>().showArticle(
article: 'how_cloudflare',
context: context,
);
Scaffold.of(context).openEndDrawer();
},
title: 'initializing.how'.tr(),
),
],
),
),
),
);
@ -240,50 +297,57 @@ class InitializingPage extends StatelessWidget {
child: Builder(
builder: (final context) {
final formCubitState = context.watch<BackblazeFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'initializing.connect_to_server_provider'.tr()}Backblaze',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 32),
CubitFormTextField(
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'KeyID',
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'initializing.connect_to_server_provider'.tr()}Backblaze',
style: Theme.of(context).textTheme.headlineSmall,
),
),
const SizedBox(height: 16),
CubitFormTextField(
formFieldCubit:
context.read<BackblazeFormCubit>().applicationKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'Master Application Key',
),
),
const SizedBox(height: 32),
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',
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CubitFormTextField(
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'KeyID',
),
),
title: 'initializing.how'.tr(),
),
],
const SizedBox(height: 16),
CubitFormTextField(
formFieldCubit:
context.read<BackblazeFormCubit>().applicationKey,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration(
hintText: 'Master Application Key',
),
),
const SizedBox(height: 32),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<BackblazeFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
const SizedBox(height: 10),
BrandButton.text(
onPressed: () {
context.read<SupportSystemCubit>().showArticle(
article: 'how_backblaze',
context: context,
);
Scaffold.of(context).openEndDrawer();
},
title: 'initializing.how'.tr(),
),
],
),
);
},
),
@ -296,9 +360,8 @@ class InitializingPage extends StatelessWidget {
builder: (final context) {
final DomainSetupState state =
context.watch<DomainSetupCubit>().state;
return SizedBox(
width: double.infinity,
child: Column(
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
@ -310,7 +373,11 @@ class InitializingPage extends StatelessWidget {
'initializing.use_this_domain_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 32),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (state is Empty)
Text(
'initializing.no_connected_domains'.tr(),
@ -350,7 +417,7 @@ class InitializingPage extends StatelessWidget {
],
if (state is Empty) ...[
const SizedBox(height: 30),
BrandButton.rised(
BrandButton.filled(
onPressed: () => context.read<DomainSetupCubit>().load(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
@ -360,14 +427,17 @@ class InitializingPage extends StatelessWidget {
color: Colors.white,
),
const SizedBox(width: 10),
BrandText.buttonTitleText('domain.update_list'.tr()),
Text(
'domain.update_list'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
],
if (state is Loaded) ...[
const SizedBox(height: 32),
BrandButton.rised(
BrandButton.filled(
onPressed: () =>
context.read<DomainSetupCubit>().saveDomain(),
text: 'initializing.save_domain'.tr(),
@ -388,74 +458,83 @@ class InitializingPage extends StatelessWidget {
builder: (final context) {
final formCubitState = context.watch<RootUserFormCubit>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.create_master_account'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.enter_username_and_password'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
if (formCubitState.isErrorShown) const SizedBox(height: 16),
if (formCubitState.isErrorShown)
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'users.username_rule'.tr(),
style: TextStyle(
color: Theme.of(context).colorScheme.error,
'initializing.create_master_account'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.enter_username_and_password'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (formCubitState.isErrorShown) const SizedBox(height: 16),
if (formCubitState.isErrorShown)
Text(
'users.username_rule'.tr(),
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
const SizedBox(height: 32),
CubitFormTextField(
formFieldCubit: context.read<RootUserFormCubit>().userName,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'basis.username'.tr(),
),
),
const SizedBox(height: 32),
CubitFormTextField(
formFieldCubit: context.read<RootUserFormCubit>().userName,
textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration(
hintText: 'basis.username'.tr(),
),
),
const SizedBox(height: 16),
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
bloc: context.read<RootUserFormCubit>().isVisible,
builder: (final context, final state) {
final bool 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,
const SizedBox(height: 16),
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
bloc: context.read<RootUserFormCubit>().isVisible,
builder: (final context, final state) {
final bool 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),
),
onPressed: () => context
.read<RootUserFormCubit>()
.isVisible
.setValue(!isVisible),
suffixIconConstraints:
const BoxConstraints(minWidth: 60),
prefixIconConstraints:
const BoxConstraints(maxWidth: 60),
prefixIcon: Container(),
),
suffixIconConstraints:
const BoxConstraints(minWidth: 60),
prefixIconConstraints:
const BoxConstraints(maxWidth: 60),
prefixIcon: Container(),
),
);
},
),
const SizedBox(height: 32),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<RootUserFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
],
);
},
),
const SizedBox(height: 32),
BrandButton.filled(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<RootUserFormCubit>().trySubmit(),
text: 'basis.connect'.tr(),
),
],
),
);
},
),
@ -465,27 +544,28 @@ class InitializingPage extends StatelessWidget {
final bool isLoading =
(appConfigCubit.state as ServerInstallationNotFinished).isLoading;
return Builder(
builder: (final context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.final'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.create_server'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 128),
BrandButton.rised(
onPressed:
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
text: isLoading
? 'basis.loading'.tr()
: 'initializing.create_server'.tr(),
),
],
builder: (final context) => ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.final'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.create_server'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: BrandButton.filled(
onPressed:
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
text: isLoading
? 'basis.loading'.tr()
: 'initializing.create_server'.tr(),
),
),
);
}
@ -514,84 +594,67 @@ class InitializingPage extends StatelessWidget {
return Builder(
builder: (final context) => SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.checks'.tr(args: [doneCount.toString(), '4']),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
if (text != null)
child: ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
text,
style: Theme.of(context).textTheme.bodyMedium,
'initializing.checks'.tr(args: [doneCount.toString(), '4']),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 128),
const SizedBox(height: 10),
if (doneCount == 0 && state.dnsMatches != null)
Column(
children: state.dnsMatches!.entries.map((final entry) {
final String domain = entry.key;
final bool 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: [
Text(
'initializing.until_the_next_check'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
BrandTimer(
startDateTime: state.timerStart!,
duration: state.duration!,
)
],
),
if (state.isLoading)
Text(
'initializing.check'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
const SizedBox(height: 16),
if (text != null)
Text(
text,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 128),
const SizedBox(height: 10),
if (doneCount == 0 && state.dnsMatches != null)
Column(
children: state.dnsMatches!.entries.map((final entry) {
final String domain = entry.key;
final bool 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: [
Text(
'initializing.until_the_next_check'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
BrandTimer(
startDateTime: state.timerStart!,
duration: state.duration!,
)
],
),
if (state.isLoading)
Text(
'initializing.check'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
);
}
}
class _HowTo extends StatelessWidget {
const _HowTo({
required this.fileName,
});
final String fileName;
@override
Widget build(final BuildContext context) => BrandBottomSheet(
isExpended: true,
child: Padding(
padding: paddingH15V0,
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: [
BrandMarkdown(
fileName: fileName,
),
],
),
),
);
}

View File

@ -2,16 +2,15 @@ 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/provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_details.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_button/outlined_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.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/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
import 'package:selfprivacy/utils/launch_url.dart';
class ServerProviderPicker extends StatefulWidget {
@ -98,56 +97,49 @@ class ProviderInputDataPage extends StatelessWidget {
final ProviderFormCubit providerCubit;
@override
Widget build(final BuildContext context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${'initializing.connect_to_server_provider'.tr()}${providerInfo.providerType.displayName}",
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',
Widget build(final BuildContext context) => ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${'initializing.connect_to_server_provider'.tr()}${providerInfo.providerType.displayName}",
style: Theme.of(context).textTheme.headlineSmall,
),
),
const SizedBox(height: 32),
BrandButton.filled(
child: Text('basis.connect'.tr()),
onPressed: () => providerCubit.trySubmit(),
),
const SizedBox(height: 10),
BrandOutlinedButton(
child: Text('initializing.how'.tr()),
onPressed: () => showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (final BuildContext context) => BrandBottomSheet(
isExpended: true,
child: Padding(
padding: paddingH15V0,
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: [
BrandMarkdown(
fileName: providerInfo.pathToHow,
),
],
),
),
const SizedBox(height: 16),
Text(
'initializing.connect_to_server_provider_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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.filled(
child: Text('basis.connect'.tr()),
onPressed: () => providerCubit.trySubmit(),
),
const SizedBox(height: 10),
BrandOutlinedButton(
child: Text('initializing.how'.tr()),
onPressed: () {
context.read<SupportSystemCubit>().showArticle(
article: providerInfo.pathToHow,
context: context,
);
},
),
],
),
);
}
@ -164,175 +156,182 @@ class ProviderSelectionPage extends StatelessWidget {
@override
Widget build(final BuildContext context) => SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.connect_to_server'.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(0xFFD50C2D),
child: ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.connect_to_server'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 10),
Text(
'initializing.select_provider'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
children: [
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(0xFFD50C2D),
),
child: SvgPicture.asset(
'assets/images/logos/hetzner.svg',
),
),
child: SvgPicture.asset(
'assets/images/logos/hetzner.svg',
const SizedBox(width: 16),
Text(
'Hetzner Cloud',
style: Theme.of(context).textTheme.titleMedium,
),
),
const SizedBox(width: 16),
Text(
'Hetzner Cloud',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_countries_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_countries_text_hetzner'
.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_price_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_price_text_hetzner'.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_hetzner'.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_email_notice'.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
BrandButton.filled(
child: Text('basis.select'.tr()),
onPressed: () {
serverInstallationCubit
.setServerProviderType(ServerProvider.hetzner);
callback(ServerProvider.hetzner);
},
),
// Outlined button that will open website
BrandOutlinedButton(
onPressed: () =>
launchURL('https://www.hetzner.com/cloud'),
title: 'initializing.select_provider_site_button'.tr(),
),
],
],
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_countries_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_countries_text_hetzner'
.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_price_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_price_text_hetzner'.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_hetzner'
.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_email_notice'.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
BrandButton.filled(
child: Text('basis.select'.tr()),
onPressed: () {
serverInstallationCubit
.setServerProviderType(ServerProvider.hetzner);
callback(ServerProvider.hetzner);
},
),
// Outlined button that will open website
BrandOutlinedButton(
onPressed: () =>
launchURL('https://www.hetzner.com/cloud'),
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(0xFF0080FF),
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(0xFF0080FF),
),
child: SvgPicture.asset(
'assets/images/logos/digital_ocean.svg',
),
),
child: SvgPicture.asset(
'assets/images/logos/digital_ocean.svg',
const SizedBox(width: 16),
Text(
'Digital Ocean',
style: Theme.of(context).textTheme.titleMedium,
),
),
const SizedBox(width: 16),
Text(
'Digital Ocean',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_countries_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_countries_text_do'.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_price_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_price_text_do'.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.filled(
child: Text('basis.select'.tr()),
onPressed: () {
serverInstallationCubit
.setServerProviderType(ServerProvider.digitalOcean);
callback(ServerProvider.digitalOcean);
},
),
// Outlined button that will open website
BrandOutlinedButton(
onPressed: () =>
launchURL('https://www.digitalocean.com'),
title: 'initializing.select_provider_site_button'.tr(),
),
],
],
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_countries_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_countries_text_do'.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
Text(
'initializing.select_provider_price_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
'initializing.select_provider_price_text_do'.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.filled(
child: Text('basis.select'.tr()),
onPressed: () {
serverInstallationCubit.setServerProviderType(
ServerProvider.digitalOcean,
);
callback(ServerProvider.digitalOcean);
},
),
// Outlined button that will open website
BrandOutlinedButton(
onPressed: () =>
launchURL('https://www.digitalocean.com'),
title: 'initializing.select_provider_site_button'.tr(),
),
],
),
),
),
),
const SizedBox(height: 16),
InfoBox(text: 'initializing.select_provider_notice'.tr()),
],
],
),
secondaryColumn:
InfoBox(text: 'initializing.select_provider_notice'.tr()),
),
);
}

View File

@ -5,8 +5,9 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/models/server_provider_location.dart';
import 'package:selfprivacy/logic/models/server_type.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
class ServerTypePicker extends StatefulWidget {
const ServerTypePicker({
@ -70,50 +71,67 @@ class SelectLocationPage extends StatelessWidget {
if ((snapshot.data as List<ServerProviderLocation>).isEmpty) {
return Text('initializing.no_locations_found'.tr());
}
return Column(
children: [
Text(
'initializing.choose_location_type'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.choose_location_type_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 16),
...(snapshot.data! as List<ServerProviderLocation>).map(
(final location) => SizedBox(
width: double.infinity,
child: InkWell(
onTap: () {
callback(location);
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${location.flag ?? ''} ${location.title}',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
if (location.description != null)
Text(
location.description!,
style: Theme.of(context).textTheme.bodyMedium,
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.choose_location_type'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.choose_location_type_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...(snapshot.data! as List<ServerProviderLocation>).map(
(final location) => Column(
children: [
SizedBox(
width: double.infinity,
child: Card(
clipBehavior: Clip.antiAlias,
child: InkResponse(
highlightShape: BoxShape.rectangle,
onTap: () {
callback(location);
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${location.flag ?? ''} ${location.title}',
style: Theme.of(context)
.textTheme
.titleMedium,
),
const SizedBox(height: 8),
if (location.description != null)
Text(
location.description!,
style: Theme.of(context)
.textTheme
.bodyMedium,
),
],
),
],
),
),
),
),
),
const SizedBox(height: 8),
],
),
),
),
const SizedBox(height: 24),
],
],
),
);
} else {
return const Center(child: CircularProgressIndicator());
@ -180,121 +198,145 @@ class SelectTypePage extends StatelessWidget {
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.choose_server_type'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.choose_server_type_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 16),
...(snapshot.data! as List<ServerType>).map(
(final type) => SizedBox(
width: double.infinity,
child: InkWell(
onTap: () {
serverInstallationCubit.setServerType(type);
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
type.title,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.memory_outlined,
color:
Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 8),
Text(
'server.core_count'.plural(type.cores),
style:
Theme.of(context).textTheme.bodyMedium,
),
],
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.memory_outlined,
color:
Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 8),
Text(
'initializing.choose_server_type_ram'
.tr(args: [type.ram.toString()]),
style:
Theme.of(context).textTheme.bodyMedium,
),
],
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.sd_card_outlined,
color:
Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 8),
Text(
'initializing.choose_server_type_storage'
.tr(
args: [type.disk.gibibyte.toString()],
return ResponsiveLayoutWithInfobox(
topChild: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'initializing.choose_server_type'.tr(),
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
Text(
'initializing.choose_server_type_text'.tr(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
primaryColumn: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...(snapshot.data! as List<ServerType>).map(
(final type) => Column(
children: [
SizedBox(
width: double.infinity,
child: InkWell(
onTap: () {
serverInstallationCubit.setServerType(type);
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
type.title,
style: Theme.of(context)
.textTheme
.titleMedium,
),
style:
Theme.of(context).textTheme.bodyMedium,
),
],
),
const SizedBox(height: 8),
const Divider(height: 8),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.payments_outlined,
color:
Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 8),
Text(
'initializing.choose_server_type_payment_per_month'
.tr(
args: [
'${type.price.value.toString()} ${type.price.currency}'
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.memory_outlined,
color: Theme.of(context)
.colorScheme
.onSurface,
),
const SizedBox(width: 8),
Text(
'server.core_count'
.plural(type.cores),
style: Theme.of(context)
.textTheme
.bodyMedium,
),
],
),
style:
Theme.of(context).textTheme.bodyLarge,
),
],
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.memory_outlined,
color: Theme.of(context)
.colorScheme
.onSurface,
),
const SizedBox(width: 8),
Text(
'initializing.choose_server_type_ram'
.tr(args: [type.ram.toString()]),
style: Theme.of(context)
.textTheme
.bodyMedium,
),
],
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.sd_card_outlined,
color: Theme.of(context)
.colorScheme
.onSurface,
),
const SizedBox(width: 8),
Text(
'initializing.choose_server_type_storage'
.tr(
args: [
type.disk.gibibyte.toString()
],
),
style: Theme.of(context)
.textTheme
.bodyMedium,
),
],
),
const SizedBox(height: 8),
const Divider(height: 8),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.payments_outlined,
color: Theme.of(context)
.colorScheme
.onSurface,
),
const SizedBox(width: 8),
Text(
'initializing.choose_server_type_payment_per_month'
.tr(
args: [
'${type.price.value.toString()} ${type.price.currency}'
],
),
style: Theme.of(context)
.textTheme
.bodyLarge,
),
],
),
],
),
),
],
),
),
),
),
const SizedBox(height: 8),
],
),
),
),
const SizedBox(height: 16),
InfoBox(text: 'initializing.choose_server_type_notice'.tr()),
],
],
),
secondaryColumn:
InfoBox(text: 'initializing.choose_server_type_notice'.tr()),
);
} else {
return const Center(child: CircularProgressIndicator());

View File

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
@ -17,6 +17,7 @@ class RecoverByNewDeviceKeyInstruction extends StatelessWidget {
heroSubtitle: 'recovering.method_device_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
onBackButtonPressed:
context.read<ServerInstallationCubit>().revertRecoveryStep,
children: [
@ -61,6 +62,7 @@ class RecoverByNewDeviceKeyInput extends StatelessWidget {
heroSubtitle: 'recovering.method_device_input_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
children: [
CubitFormTextField(
formFieldCubit:

View File

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
@ -28,6 +28,7 @@ class RecoverByOldTokenInstruction extends StatelessWidget {
heroTitle: 'recovering.recovery_main_header'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
onBackButtonPressed:
context.read<ServerInstallationCubit>().revertRecoveryStep,
children: [
@ -72,6 +73,7 @@ class RecoverByOldToken extends StatelessWidget {
heroSubtitle: 'recovering.method_device_input_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
children: [
CubitFormTextField(
formFieldCubit:

View File

@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
class RecoverByRecoveryKey extends StatelessWidget {
const RecoverByRecoveryKey({super.key});
@ -31,6 +31,7 @@ class RecoverByRecoveryKey extends StatelessWidget {
heroSubtitle: 'recovering.method_recovery_input_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
onBackButtonPressed:
context.read<ServerInstallationCubit>().revertRecoveryStep,
children: [

View File

@ -1,13 +1,11 @@
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/forms/setup/initializing/backblaze_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_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_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
class RecoveryConfirmBackblaze extends StatelessWidget {
const RecoveryConfirmBackblaze({super.key});
@ -28,6 +26,8 @@ class RecoveryConfirmBackblaze extends StatelessWidget {
heroTitle: 'recovering.confirm_backblaze'.tr(),
heroSubtitle: 'recovering.confirm_backblaze_description'.tr(),
hasBackButton: true,
ignoreBreakpoints: true,
hasSupportDrawer: true,
onBackButtonPressed: () {
Navigator.of(context).popUntil((final route) => route.isFirst);
},
@ -57,27 +57,15 @@ class RecoveryConfirmBackblaze extends StatelessWidget {
text: 'basis.connect'.tr(),
),
const SizedBox(height: 16),
BrandButton.text(
onPressed: () => showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (final BuildContext context) => BrandBottomSheet(
isExpended: true,
child: Padding(
padding: paddingH15V0,
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: const [
BrandMarkdown(
fileName: 'how_backblaze',
Builder(
builder: (final context) => BrandButton.text(
onPressed: () =>
context.read<SupportSystemCubit>().showArticle(
article: 'how_backblaze',
context: context,
),
],
),
),
),
title: 'initializing.how'.tr(),
),
title: 'initializing.how'.tr(),
),
],
);

View File

@ -1,13 +1,11 @@
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/forms/setup/initializing/dns_provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_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_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
class RecoveryConfirmCloudflare extends StatelessWidget {
const RecoveryConfirmCloudflare({super.key});
@ -31,6 +29,8 @@ class RecoveryConfirmCloudflare extends StatelessWidget {
),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
hasSupportDrawer: true,
onBackButtonPressed:
context.read<ServerInstallationCubit>().revertRecoveryStep,
children: [
@ -49,27 +49,15 @@ class RecoveryConfirmCloudflare extends StatelessWidget {
text: 'basis.connect'.tr(),
),
const SizedBox(height: 16),
BrandButton.text(
onPressed: () => showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (final BuildContext context) => BrandBottomSheet(
isExpended: true,
child: Padding(
padding: paddingH15V0,
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: const [
BrandMarkdown(
fileName: 'how_cloudflare',
Builder(
builder: (final context) => BrandButton.text(
onPressed: () =>
context.read<SupportSystemCubit>().showArticle(
article: 'how_cloudflare',
context: context,
),
],
),
),
),
title: 'initializing.how'.tr(),
),
title: 'initializing.how'.tr(),
),
],
);

View File

@ -2,9 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
class RecoveryConfirmServer extends StatefulWidget {
const RecoveryConfirmServer({super.key});
@ -38,6 +38,7 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
? 'recovering.choose_server_description'.tr()
: 'recovering.confirm_server_description'.tr(),
hasBackButton: true,
ignoreBreakpoints: true,
onBackButtonPressed: () {
Navigator.of(context).popUntil((final route) => route.isFirst);
},

View File

@ -2,9 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/components/cards/outlined_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@ -17,6 +17,7 @@ class RecoveryMethodSelect extends StatelessWidget {
heroSubtitle: 'recovering.method_select_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
onBackButtonPressed:
context.read<ServerInstallationCubit>().revertRecoveryStep,
children: [
@ -74,6 +75,7 @@ class RecoveryFallbackMethodSelect extends StatelessWidget {
heroSubtitle: 'recovering.fallback_select_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
children: [
OutlinedCard(
child: ListTile(

View File

@ -1,11 +1,12 @@
import 'package:auto_route/auto_route.dart';
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/factories/field_cubit_factory.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/root_route.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';
@ -17,6 +18,7 @@ import 'package:selfprivacy/ui/pages/setup/recovering/recovery_server_provider_c
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
@RoutePage()
class RecoveryRouting extends StatelessWidget {
const RecoveryRouting({super.key});
@ -110,6 +112,7 @@ class SelectDomainToRecover extends StatelessWidget {
heroSubtitle: 'recovering.domain_recovery_description'.tr(),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
onBackButtonPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(const RootPage()),

View File

@ -1,13 +1,11 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_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_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
class RecoveryServerProviderConnected extends StatelessWidget {
const RecoveryServerProviderConnected({super.key});
@ -31,6 +29,8 @@ class RecoveryServerProviderConnected extends StatelessWidget {
),
hasBackButton: true,
hasFlashButton: false,
ignoreBreakpoints: true,
hasSupportDrawer: true,
onBackButtonPressed: () {
Navigator.of(context).popUntil((final route) => route.isFirst);
},
@ -51,26 +51,14 @@ class RecoveryServerProviderConnected extends StatelessWidget {
child: Text('basis.continue'.tr()),
),
const SizedBox(height: 16),
BrandButton.text(
title: 'initializing.how'.tr(),
onPressed: () => showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (final BuildContext context) => BrandBottomSheet(
isExpended: true,
child: Padding(
padding: paddingH15V0,
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: const [
BrandMarkdown(
fileName: 'how_hetzner',
Builder(
builder: (final context) => BrandButton.text(
title: 'initializing.how'.tr(),
onPressed: () =>
context.read<SupportSystemCubit>().showArticle(
article: 'how_hetzner',
context: context,
),
],
),
),
),
),
),
],

Some files were not shown because too many files have changed in this diff Show More