diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7eefa660..5db2ddac 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -61,4 +61,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 94528fe7..b22b290f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -157,7 +157,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cf..3db53b6e 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ getAutoUpgradeSettings() async { + var client = await getClient(); + Response response = await client.get('/system/configuration/autoUpgrade'); + client.close(); + return AutoUpgradeSettings.fromJson(response.data); + } + + Future updateAutoUpgradeSettings(AutoUpgradeSettings settings) async { + var client = await getClient(); + await client.put( + '/system/configuration/autoUpgrade', + data: settings.toJson(), + ); + client.close(); + } + + Future getServerTimezone() async { + var client = await getClient(); + Response response = await client.get('/system/configuration/timezone'); + client.close(); + + return TimeZoneSettings.fromString(response.data); + } + + Future updateServerTimezone(TimeZoneSettings settings) async { + var client = await getClient(); + await client.put( + '/system/configuration/timezone', + data: settings.toJson(), + ); + client.close(); + } + Future getDkim() async { var client = await getClient(); Response response = await client.get('/services/mailserver/dkim'); diff --git a/lib/logic/cubit/backups/backups_cubit.dart b/lib/logic/cubit/backups/backups_cubit.dart index 5550eb73..e79978c2 100644 --- a/lib/logic/cubit/backups/backups_cubit.dart +++ b/lib/logic/cubit/backups/backups_cubit.dart @@ -1,12 +1,12 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/backblaze.dart'; +import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/backup.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; -import 'package:selfprivacy/logic/api_maps/backblaze.dart'; -import 'package:easy_localization/easy_localization.dart'; part 'backups_state.dart'; diff --git a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart index 02f0f643..7ee8c8fa 100644 --- a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart @@ -17,7 +17,11 @@ class CloudFlareFormCubit extends FormCubit { ValidationModel( (s) => regExp.hasMatch(s), 'validations.key_format'.tr()), LengthStringValidationWithLengthShowing( - 40, 'validations.length'.tr(args: ["40"])) + 40, + 'validations.length'.tr( + args: ["40"], + ), + ) ], ); diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index a2c1b414..b093ba08 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -37,7 +37,7 @@ class JobsCubit extends Cubit { emit(newState); } - void createOrRemoveServiceToggleJob(ServiceToggleJob job) { + void createOrRemoveServiceToggleJob(ToggleJob job) { var newJobsList = []; if (state is JobsStateWithJobs) { newJobsList.addAll((state as JobsStateWithJobs).jobList); diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart index 3d46142d..b4969037 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -2,7 +2,9 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/timezone_settings.dart'; part 'server_detailed_info_state.dart'; @@ -16,7 +18,12 @@ class ServerDetailsCubit extends Cubit { if (isReadyToCheck) { emit(ServerDetailsLoading()); var data = await repository.load(); - emit(Loaded(serverInfo: data, checkTime: DateTime.now())); + emit(Loaded( + serverInfo: data.hetznerServerInfo, + autoUpgradeSettings: data.autoUpgradeSettings, + serverTimezone: data.serverTimezone, + checkTime: DateTime.now(), + )); } else { emit(ServerDetailsNotReady()); } diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart index bcd34625..a5d6c07e 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart @@ -1,9 +1,33 @@ import 'package:selfprivacy/logic/api_maps/hetzner.dart'; +import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/timezone_settings.dart'; class ServerDetailsRepository { - Future load() async { - var client = HetznerApi(); - return await client.getInfo(); + var hetznerAPi = HetznerApi(); + var selfprivacyServer = ServerApi(); + + Future<_ServerDetailsRepositoryDto> load() async { + print('load'); + return _ServerDetailsRepositoryDto( + autoUpgradeSettings: await selfprivacyServer.getAutoUpgradeSettings(), + hetznerServerInfo: await hetznerAPi.getInfo(), + serverTimezone: await selfprivacyServer.getServerTimezone(), + ); } } + +class _ServerDetailsRepositoryDto { + final HetznerServerInfo hetznerServerInfo; + + final TimeZoneSettings serverTimezone; + + final AutoUpgradeSettings autoUpgradeSettings; + + _ServerDetailsRepositoryDto({ + required this.hetznerServerInfo, + required this.serverTimezone, + required this.autoUpgradeSettings, + }); +} diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart index cf017658..b4524751 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart @@ -17,13 +17,24 @@ class Loading extends ServerDetailsState {} class Loaded extends ServerDetailsState { final HetznerServerInfo serverInfo; + + final TimeZoneSettings serverTimezone; + + final AutoUpgradeSettings autoUpgradeSettings; final DateTime checkTime; Loaded({ required this.serverInfo, + required this.serverTimezone, + required this.autoUpgradeSettings, required this.checkTime, }); @override - List get props => [serverInfo, checkTime]; + List get props => [ + serverInfo, + serverTimezone, + autoUpgradeSettings, + checkTime, + ]; } diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart index 5bbfcecc..6d5dfef7 100644 --- a/lib/logic/cubit/services/services_cubit.dart +++ b/lib/logic/cubit/services/services_cubit.dart @@ -2,7 +2,6 @@ import 'package:hive/hive.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; part 'services_state.dart'; diff --git a/lib/logic/models/auto_upgrade_settings.dart b/lib/logic/models/auto_upgrade_settings.dart new file mode 100644 index 00000000..6007e622 --- /dev/null +++ b/lib/logic/models/auto_upgrade_settings.dart @@ -0,0 +1,22 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'auto_upgrade_settings.g.dart'; + +@JsonSerializable(createToJson: true) +class AutoUpgradeSettings extends Equatable { + final bool enable; + final bool allowReboot; + + AutoUpgradeSettings({ + required this.enable, + required this.allowReboot, + }); + + @override + List get props => [enable, allowReboot]; + factory AutoUpgradeSettings.fromJson(Map json) => + _$AutoUpgradeSettingsFromJson(json); + + Map toJson() => _$AutoUpgradeSettingsToJson(this); +} diff --git a/lib/logic/models/auto_upgrade_settings.g.dart b/lib/logic/models/auto_upgrade_settings.g.dart new file mode 100644 index 00000000..e6accc2f --- /dev/null +++ b/lib/logic/models/auto_upgrade_settings.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'auto_upgrade_settings.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AutoUpgradeSettings _$AutoUpgradeSettingsFromJson(Map json) => + AutoUpgradeSettings( + enable: json['enable'] as bool, + allowReboot: json['allowReboot'] as bool, + ); + +Map _$AutoUpgradeSettingsToJson( + AutoUpgradeSettings instance) => + { + 'enable': instance.enable, + 'allowReboot': instance.allowReboot, + }; diff --git a/lib/logic/models/backup.g.dart b/lib/logic/models/backup.g.dart index c1b50d03..c784abe1 100644 --- a/lib/logic/models/backup.g.dart +++ b/lib/logic/models/backup.g.dart @@ -6,46 +6,16 @@ part of 'backup.dart'; // JsonSerializableGenerator // ************************************************************************** -Backup _$BackupFromJson(Map json) { - return Backup( - time: DateTime.parse(json['time'] as String), - id: json['short_id'] as String, - ); -} - -BackupStatus _$BackupStatusFromJson(Map json) { - return BackupStatus( - status: _$enumDecode(_$BackupStatusEnumEnumMap, json['status']), - progress: (json['progress'] as num).toDouble(), - errorMessage: json['error_message'] as String?, - ); -} - -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', +Backup _$BackupFromJson(Map json) => Backup( + time: DateTime.parse(json['time'] as String), + id: json['short_id'] as String, ); - } - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} +BackupStatus _$BackupStatusFromJson(Map json) => BackupStatus( + status: $enumDecode(_$BackupStatusEnumEnumMap, json['status']), + progress: (json['progress'] as num).toDouble(), + errorMessage: json['error_message'] as String?, + ); const _$BackupStatusEnumEnumMap = { BackupStatusEnum.noKey: 'NO_KEY', diff --git a/lib/logic/models/hetzner_server_info.g.dart b/lib/logic/models/hetzner_server_info.g.dart index 40055d19..73e6be68 100644 --- a/lib/logic/models/hetzner_server_info.g.dart +++ b/lib/logic/models/hetzner_server_info.g.dart @@ -6,42 +6,16 @@ part of 'hetzner_server_info.dart'; // JsonSerializableGenerator // ************************************************************************** -HetznerServerInfo _$HetznerServerInfoFromJson(Map json) { - return HetznerServerInfo( - json['id'] as int, - json['name'] as String, - _$enumDecode(_$ServerStatusEnumMap, json['status']), - DateTime.parse(json['created'] as String), - HetznerServerTypeInfo.fromJson(json['server_type'] as Map), - HetznerServerInfo.locationFromJson(json['datacenter'] as Map), - ); -} - -K _$enumDecode( - Map enumValues, - Object? source, { - K? unknownValue, -}) { - if (source == null) { - throw ArgumentError( - 'A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}', +HetznerServerInfo _$HetznerServerInfoFromJson(Map json) => + HetznerServerInfo( + json['id'] as int, + json['name'] as String, + $enumDecode(_$ServerStatusEnumMap, json['status']), + DateTime.parse(json['created'] as String), + HetznerServerTypeInfo.fromJson( + json['server_type'] as Map), + HetznerServerInfo.locationFromJson(json['datacenter'] as Map), ); - } - - return enumValues.entries.singleWhere( - (e) => e.value == source, - orElse: () { - if (unknownValue == null) { - throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}', - ); - } - return MapEntry(unknownValue, enumValues.values.first); - }, - ).key; -} const _$ServerStatusEnumMap = { ServerStatus.running: 'running', @@ -56,29 +30,26 @@ const _$ServerStatusEnumMap = { }; HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( - Map json) { - return HetznerServerTypeInfo( - json['cores'] as int, - json['memory'] as num, - json['disk'] as int, - (json['prices'] as List) - .map((e) => HetznerPriceInfo.fromJson(e as Map)) - .toList(), - ); -} + Map json) => + HetznerServerTypeInfo( + json['cores'] as int, + json['memory'] as num, + json['disk'] as int, + (json['prices'] as List) + .map((e) => HetznerPriceInfo.fromJson(e as Map)) + .toList(), + ); -HetznerPriceInfo _$HetznerPriceInfoFromJson(Map json) { - return HetznerPriceInfo( - HetznerPriceInfo.getPrice(json['price_hourly'] as Map), - HetznerPriceInfo.getPrice(json['price_monthly'] as Map), - ); -} +HetznerPriceInfo _$HetznerPriceInfoFromJson(Map json) => + HetznerPriceInfo( + HetznerPriceInfo.getPrice(json['price_hourly'] as Map), + HetznerPriceInfo.getPrice(json['price_monthly'] as Map), + ); -HetznerLocation _$HetznerLocationFromJson(Map json) { - return HetznerLocation( - json['country'] as String, - json['city'] as String, - json['description'] as String, - json['network_zone'] as String, - ); -} +HetznerLocation _$HetznerLocationFromJson(Map json) => + HetznerLocation( + json['country'] as String, + json['city'] as String, + json['description'] as String, + json['network_zone'] as String, + ); diff --git a/lib/logic/models/job.dart b/lib/logic/models/job.dart index e9767803..70d238a9 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/job.dart @@ -42,19 +42,29 @@ class DeleteUserJob extends Job { List get props => [id, title, user]; } -class ServiceToggleJob extends Job { - ServiceToggleJob({ +class ToggleJob extends Job { + ToggleJob({ required this.type, - required this.needToTurnOn, - }) : super( - title: - '${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}'); + required String title, + }) : super(title: title); - final ServiceTypes type; - final bool needToTurnOn; + final dynamic type; @override - List get props => [id, title, type, needToTurnOn]; + List get props => [...super.props, type]; +} + +class ServiceToggleJob extends ToggleJob { + ServiceToggleJob({ + required ServiceTypes type, + required this.needToTurnOn, + }) : super( + title: + '${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}', + type: type, + ); + + final bool needToTurnOn; } class CreateSSHKeyJob extends Job { diff --git a/lib/logic/models/server_configurations.dart b/lib/logic/models/server_configurations.dart new file mode 100644 index 00000000..73915566 --- /dev/null +++ b/lib/logic/models/server_configurations.dart @@ -0,0 +1,22 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'server_configurations.g.dart'; + +@JsonSerializable(createToJson: true) +class AutoUpgradeConfigurations extends Equatable { + const AutoUpgradeConfigurations({ + required this.enable, + required this.allowReboot, + }); + + final bool enable; + final bool allowReboot; + + factory AutoUpgradeConfigurations.fromJson(Map json) => + _$AutoUpgradeConfigurationsFromJson(json); + Map toJson() => _$AutoUpgradeConfigurationsToJson(this); + + @override + List get props => [enable, allowReboot]; +} diff --git a/lib/logic/models/server_configurations.g.dart b/lib/logic/models/server_configurations.g.dart new file mode 100644 index 00000000..a198ae2e --- /dev/null +++ b/lib/logic/models/server_configurations.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_configurations.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AutoUpgradeConfigurations _$AutoUpgradeConfigurationsFromJson( + Map json) => + AutoUpgradeConfigurations( + enable: json['enable'] as bool, + allowReboot: json['allowReboot'] as bool, + ); + +Map _$AutoUpgradeConfigurationsToJson( + AutoUpgradeConfigurations instance) => + { + 'enable': instance.enable, + 'allowReboot': instance.allowReboot, + }; diff --git a/lib/logic/models/timezone_settings.dart b/lib/logic/models/timezone_settings.dart new file mode 100644 index 00000000..76d28aff --- /dev/null +++ b/lib/logic/models/timezone_settings.dart @@ -0,0 +1,18 @@ +import 'package:timezone/timezone.dart'; + +class TimeZoneSettings { + final Location timezone; + + TimeZoneSettings(this.timezone); + + Map toJson() { + return { + 'timezone': timezone.name, + }; + } + + factory TimeZoneSettings.fromString(String string) { + var location = timeZoneDatabase.locations[string]!; + return TimeZoneSettings(location); + } +} diff --git a/lib/main.dart b/lib/main.dart index c9bc9d26..15c7a8a6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:selfprivacy/ui/pages/initializing/initializing.dart'; import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:wakelock/wakelock.dart'; +import 'package:timezone/data/latest.dart' as tz; import 'config/bloc_config.dart'; import 'config/bloc_observer.dart'; @@ -19,12 +20,15 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); await HiveConfig.init(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - Bloc.observer = SimpleBlocObserver(); Wakelock.enable(); await getItSetup(); await EasyLocalization.ensureInitialized(); + tz.initializeTimeZones(); - runApp(MyApp()); + BlocOverrides.runZoned( + () => runApp(Localization(child: MyApp())), + blocObserver: SimpleBlocObserver(), + ); } class MyApp extends StatelessWidget { diff --git a/lib/ui/components/brand_loader/brand_loader.dart b/lib/ui/components/brand_loader/brand_loader.dart index 52b1b820..9cd5b571 100644 --- a/lib/ui/components/brand_loader/brand_loader.dart +++ b/lib/ui/components/brand_loader/brand_loader.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/lib/ui/components/jobs_content/jobs_content.dart b/lib/ui/components/jobs_content/jobs_content.dart index 8f766355..92298a06 100644 --- a/lib/ui/components/jobs_content/jobs_content.dart +++ b/lib/ui/components/jobs_content/jobs_content.dart @@ -5,7 +5,6 @@ import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; -import 'package:selfprivacy/logic/cubit/users/users_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_button/brand_button.dart'; diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index acc9132c..7ec44f08 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -1,8 +1,6 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/backblaze_form_cubit.dart'; diff --git a/lib/ui/pages/more/console/console.dart b/lib/ui/pages/more/console/console.dart index 5b89ee87..2e129f79 100644 --- a/lib/ui/pages/more/console/console.dart +++ b/lib/ui/pages/more/console/console.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; diff --git a/lib/ui/pages/providers/settings/settings.dart b/lib/ui/pages/providers/settings/settings.dart deleted file mode 100644 index 04a71ab1..00000000 --- a/lib/ui/pages/providers/settings/settings.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.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/switch_block/switch_bloc.dart'; -import 'package:easy_localization/easy_localization.dart'; - -class SettingsPage extends StatelessWidget { - const SettingsPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ListView( - padding: paddingH15V0, - children: [ - SizedBox(height: 10), - BrandHeader(title: 'basis.settings'.tr(), hasBackButton: true), - BrandDivider(), - SwitcherBlock( - onChange: (_) {}, - child: _TextColumn( - title: 'Allow Auto-upgrade', - value: 'Wether to allow automatic packages upgrades', - ), - isActive: true, - ), - SwitcherBlock( - onChange: (_) {}, - child: _TextColumn( - title: 'Reboot after upgrade', - value: 'Reboot without prompt after applying updates', - ), - isActive: false, - ), - _Button( - onTap: () {}, - child: _TextColumn( - title: 'Server Timezone', - value: 'Europe/Kyiv', - ), - ), - _Button( - onTap: () {}, - child: _TextColumn( - title: 'Server Locale', - value: 'Default', - ), - ), - _Button( - onTap: () {}, - child: _TextColumn( - hasWarning: true, - title: 'Factory Reset', - value: 'Restore default settings on your server', - ), - ) - ], - ); - } -} - -class _Button extends StatelessWidget { - const _Button({ - Key? key, - required this.onTap, - required this.child, - }) : super(key: key); - - final Widget child; - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - child: Container( - padding: EdgeInsets.only(top: 20, bottom: 5), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide(width: 1, color: BrandColors.dividerColor), - )), - child: child, - ), - ); - } -} - -class _TextColumn extends StatelessWidget { - const _TextColumn({ - Key? key, - required this.title, - required this.value, - this.hasWarning = false, - }) : super(key: key); - - final String title; - final String value; - final bool hasWarning; - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BrandText.body1( - title, - style: TextStyle(color: hasWarning ? BrandColors.warning : null), - ), - SizedBox(height: 5), - BrandText.body1( - value, - style: TextStyle( - fontSize: 13, - height: 1.53, - color: hasWarning ? BrandColors.warning : BrandColors.gray1, - ), - ), - ], - ); - } -} diff --git a/lib/ui/pages/rootRoute.dart b/lib/ui/pages/rootRoute.dart index 426c3627..74a28880 100644 --- a/lib/ui/pages/rootRoute.dart +++ b/lib/ui/pages/rootRoute.dart @@ -1,6 +1,6 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:selfprivacy/logic/api_maps/server.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'; @@ -30,6 +30,8 @@ class _RootPageState extends State tabController.dispose(); } + var selfprivacyServer = ServerApi(); + @override Widget build(BuildContext context) { return SafeArea( diff --git a/lib/ui/pages/server_details/server_details.dart b/lib/ui/pages/server_details/server_details.dart index 06d6d376..5368df30 100644 --- a/lib/ui/pages/server_details/server_details.dart +++ b/lib/ui/pages/server_details/server_details.dart @@ -8,20 +8,27 @@ import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.da import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.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/components/brand_loader/brand_loader.dart'; import 'package:selfprivacy/ui/components/brand_radio_tile/brand_radio_tile.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:easy_localization/easy_localization.dart'; import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart'; +import 'package:selfprivacy/ui/pages/server_details/time_zone/lang.dart'; import 'package:selfprivacy/utils/named_font_weight.dart'; +import 'package:selfprivacy/utils/route_transitions/basic.dart'; +import 'package:timezone/timezone.dart'; import 'cpu_chart.dart'; import 'network_charts.dart'; +import 'package:selfprivacy/utils/extensions/duration.dart'; part 'server_settings.dart'; part 'text_details.dart'; part 'chart.dart'; part 'header.dart'; +part 'time_zone/time_zone.dart'; var navigatorKey = GlobalKey(); @@ -56,57 +63,57 @@ class _ServerDetailsState extends State var isReady = context.watch().state is AppConfigFinished; var providerState = isReady ? StateType.stable : StateType.uninitialized; - return Scaffold( - appBar: PreferredSize( - child: Column( + return BlocProvider( + create: (context) => ServerDetailsCubit()..check(), + child: Scaffold( + appBar: PreferredSize( + child: Column( + children: [ + Container( + height: 51, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 15), + child: BrandText.h4('basis.details'.tr()), + ), + BrandDivider(), + ], + ), + preferredSize: Size.fromHeight(52), + ), + body: TabBarView( + physics: NeverScrollableScrollPhysics(), + controller: tabController, children: [ - Container( - height: 51, - alignment: Alignment.center, - padding: EdgeInsets.symmetric(horizontal: 15), - child: BrandText.h4('basis.details'.tr()), + SingleChildScrollView( + physics: ClampingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: paddingH15V0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _Header( + providerState: providerState, + tabController: tabController), + BrandText.body1('providers.server.bottom_sheet.1'.tr()), + ], + ), + ), + SizedBox(height: 10), + BlocProvider( + create: (context) => HetznerMetricsCubit()..restart(), + child: _Chart(), + ), + SizedBox(height: 20), + _TextDetails(), + ], + ), ), - BrandDivider(), + _ServerSettings(tabController: tabController), ], ), - preferredSize: Size.fromHeight(52), - ), - body: TabBarView( - physics: NeverScrollableScrollPhysics(), - controller: tabController, - children: [ - SingleChildScrollView( - physics: ClampingScrollPhysics(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: paddingH15V0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _Header( - providerState: providerState, - tabController: tabController), - BrandText.body1('providers.server.bottom_sheet.1'.tr()), - ], - ), - ), - SizedBox(height: 10), - BlocProvider( - create: (context) => HetznerMetricsCubit()..restart(), - child: _Chart(), - ), - SizedBox(height: 20), - BlocProvider( - create: (context) => ServerDetailsCubit()..check(), - child: _TextDetails(), - ), - ], - ), - ), - _ServerSettings(tabController: tabController), - ], ), ); } diff --git a/lib/ui/pages/server_details/server_settings.dart b/lib/ui/pages/server_details/server_settings.dart index 6e2ed6b1..c4d6ed02 100644 --- a/lib/ui/pages/server_details/server_settings.dart +++ b/lib/ui/pages/server_details/server_settings.dart @@ -10,6 +10,12 @@ class _ServerSettings extends StatelessWidget { @override Widget build(BuildContext context) { + var serverDetailsState = context.watch().state; + if (serverDetailsState is ServerDetailsNotReady) { + return Text('not ready'); + } else if (serverDetailsState is! Loaded) { + return BrandLoader.horizontal(); + } return ListView( padding: paddingH15V0, children: [ @@ -38,7 +44,7 @@ class _ServerSettings extends StatelessWidget { title: 'Allow Auto-upgrade', value: 'Wether to allow automatic packages upgrades', ), - isActive: true, + isActive: serverDetailsState.autoUpgradeSettings.enable, ), SwitcherBlock( onChange: (_) {}, @@ -46,30 +52,17 @@ class _ServerSettings extends StatelessWidget { title: 'Reboot after upgrade', value: 'Reboot without prompt after applying updates', ), - isActive: false, + isActive: serverDetailsState.autoUpgradeSettings.allowReboot, ), _Button( - onTap: () {}, + onTap: () { + Navigator.of(context).push(materialRoute(SelectTimezone())); + }, child: _TextColumn( title: 'Server Timezone', - value: 'Europe/Kyiv', + value: serverDetailsState.serverTimezone.timezone.name, ), ), - _Button( - onTap: () {}, - child: _TextColumn( - title: 'Server Locale', - value: 'Default', - ), - ), - _Button( - onTap: () {}, - child: _TextColumn( - hasWarning: true, - title: 'Factory Reset', - value: 'Restore default settings on your server', - ), - ) ], ); } diff --git a/lib/ui/pages/server_details/time_zone/lang.dart b/lib/ui/pages/server_details/time_zone/lang.dart new file mode 100644 index 00000000..4ea55019 --- /dev/null +++ b/lib/ui/pages/server_details/time_zone/lang.dart @@ -0,0 +1,431 @@ +final russian = { + "Pacific/Midway": "Мидуэй", + "Pacific/Niue": "Ниуэ", + "Pacific/Pago_Pago": "Паго-Паго", + "America/Adak": "Адак", + "Pacific/Honolulu": "Гонолулу", + "Pacific/Johnston": "Джонстон", + "Pacific/Rarotonga": "Раротонга", + "Pacific/Tahiti": "Таити", + "US/Hawaii": "Гавайи", + "Pacific/Marquesas": "Маркизские острова", + "America/Sitka": "Ситка", + "America/Anchorage": "Анкоридж", + "America/Metlakatla": "Метлакатла", + "America/Juneau": "Джуно", + "US/Alaska": "Аляска", + "America/Nome": "Ном", + "America/Yakutat": "Якутат", + "Pacific/Gambier": "Гамбье", + "America/Tijuana": "Тихуана", + "Pacific/Pitcairn": "Питкэрн", + "US/Pacific": "США/Тихий океан", + "Canada/Pacific": "США/Тихий океан", + "America/Los_Angeles": "Лос-Анджелес", + "America/Vancouver": "Ванкувер", + "America/Santa_Isabel": "Санта-Изабель", + "America/Chihuahua": "Чихуахуа", + "America/Cambridge_Bay": "Кембридж-Бэй", + "America/Inuvik": "Инувик", + "America/Boise": "Бойсе", + "America/Dawson": "Доусон", + "America/Mazatlan": "Масатлан", + "America/Dawson_Creek": "Доусон-Крик", + "US/Arizona": "Аризона", + "America/Denver": "Денвер", + "US/Mountain": "гора", + "America/Edmonton": "Эдмонтон", + "America/Yellowknife": "Йеллоунайф", + "America/Ojinaga": "Охинага", + "America/Phoenix": "Феникс", + "America/Whitehorse": "Белая лошадь", + "Canada/Mountain": "гора", + "America/Hermosillo": "Эрмосильо", + "America/Creston": "Крестон", + "America/Swift_Current": "Свифт Керрент", + "America/Tegucigalpa": "Тегусигальпа", + "America/Regina": "Регина", + "America/Rankin_Inlet": "Ранкин-Инлет", + "America/Rainy_River": "Райни-Ривер", + "America/Winnipeg": "Виннипег", + "America/North_Dakota/Center": "Северная Дакота/Центр", + "America/North_Dakota/Beulah": "Северная Дакота/Беула", + "America/Monterrey": "Монтеррей", + "America/Mexico_City": "Мехико", + "US/Central": "Центральный", + "America/Merida": "Мерида", + "America/Menominee": "Меномини", + "America/Matamoros": "Матаморос", + "America/Managua": "Манагуа", + "America/North_Dakota/New_Salem": "Северная Дакота/Нью-Салем", + "Pacific/Galapagos": "Галапагосские острова", + "America/Indiana/Tell_City": "Индиана/Телл-Сити", + "America/Indiana/Knox": "Индиана/Нокс", + "Canada/Central": "Центральный", + "America/Guatemala": "Гватемала", + "America/El_Salvador": "Сальвадор", + "America/Costa_Rica": "Коста-Рика", + "America/Chicago": "Чикаго", + "America/Belize": "Белиз", + "America/Bahia_Banderas": "Баия де Бандерас", + "America/Resolute": "Резольют", + "America/Atikokan": "Атикокан", + "America/Lima": "Лима", + "America/Bogota": "Богота", + "America/Cancun": "Канкун", + "America/Cayman": "Кайман", + "America/Detroit": "Детройт", + "America/Indiana/Indianapolis": "Индиана/Индианаполис", + "America/Eirunepe": "Эйрунепе", + "America/Grand_Turk": "Гранд-Терк", + "America/Guayaquil": "Гуаякиль", + "America/Havana": "Гавана", + "America/Indiana/Marengo": "Индиана/Маренго", + "America/Indiana/Petersburg": "Индиана/Петербург", + "America/Indiana/Vevay": "Индиана/Вева", + "America/Indiana/Vincennes": "Индиана/Винсеннес", + "America/Indiana/Winamac": "Индиана/Винамак", + "America/Iqaluit": "Икалуит", + "America/Jamaica": "Ямайка", + "America/Kentucky/Louisville": "Кентукки/Луисвилл", + "America/Nassau": "Нассау", + "America/Toronto": "Торонто", + "America/Montreal": "Монреаль", + "America/Pangnirtung": "Пангниртунг", + "America/Port-au-Prince": "Порт-о-Пренс", + "America/Kentucky/Monticello": "Кентукки/Монтичелло", + "Canada/Eastern": "Канада/Восточное", + "US/Eastern": "США/Восточное", + "America/Thunder_Bay": "Тандер-Бей", + "Pacific/Easter": "Пасха", + "America/Panama": "Панама", + "America/Nipigon": "Нипигон", + "America/Rio_Branco": "Рио-Бранко", + "America/New_York": "Нью-Йорк", + "Canada/Atlantic": "Атлантика", + "America/Kralendijk": "Кралендейк", + "America/La_Paz": "Ла-Пас", + "America/Halifax": "Галифакс", + "America/Lower_Princes": "Лоуэр-Принс-Куотер", + "America/Manaus": "Манаус", + "America/Marigot": "Мариго", + "America/Martinique": "Мартиника", + "America/Moncton": "Монктон", + "America/Guyana": "Гайана", + "America/Montserrat": "Монтсеррат", + "America/Guadeloupe": "Гваделупа", + "America/Grenada": "Гренада", + "America/Goose_Bay": "Гуз-Бей", + "America/Glace_Bay": "Глас Бэй", + "America/Curacao": "Кюрасао", + "America/Cuiaba": "Куяба", + "America/Port_of_Spain": "Порт-оф-Спейн", + "America/Porto_Velho": "Порту-Велью", + "America/Puerto_Rico": "Пуэрто-Рико", + "America/Caracas": "Каракас", + "America/Santo_Domingo": "Санто-Доминго", + "America/St_Barthelemy": "Святой Бартелеми", + "Atlantic/Bermuda": "Бермуды", + "America/St_Kitts": "Сент-Китс", + "America/St_Lucia": "Святая Люсия", + "America/St_Thomas": "Сент-Томас", + "America/St_Vincent": "Сент-Винсент", + "America/Thule": "Туле", + "America/Campo_Grande": "Кампу-Гранди", + "America/Boa_Vista": "Боа-Виста", + "America/Tortola": "Тортола", + "America/Aruba": "Аруба", + "America/Blanc-Sablon": "Блан-Саблон", + "America/Barbados": "Барбадос", + "America/Anguilla": "Ангилья", + "America/Antigua": "Антигуа", + "America/Dominica": "Доминика", + "Canada/Newfoundland": "Ньюфаундленд", + "America/St_Johns": "Сент-Джонс", + "America/Sao_Paulo": "Сан-Паулу", + "Atlantic/Stanley": "Стэнли", + "America/Miquelon": "Микелон", + "America/Argentina/Salta": "Аргентина/Сальта", + "America/Montevideo": "Монтевидео", + "America/Argentina/Rio_Gallegos": "Аргентина/Рио-Гальегос", + "America/Argentina/Mendoza": "Аргентина/Мендоса", + "America/Argentina/La_Rioja": "Аргентина/Ла-Риоха", + "America/Argentina/Jujuy": "Аргентина/Жужуй", + "Antarctica/Rothera": "Ротера", + "America/Argentina/Cordoba": "Аргентина/Кордова", + "America/Argentina/Catamarca": "Аргентина/Катамарка", + "America/Argentina/Ushuaia": "Аргентина/Ушуая", + "America/Argentina/Tucuman": "Аргентина/Тукуман", + "America/Paramaribo": "Парамарибо", + "America/Argentina/San_Luis": "Аргентина/Сан-Луис", + "America/Recife": "Ресифи", + "America/Argentina/Buenos_Aires": "Аргентина/Буэнос-Айрес", + "America/Asuncion": "Асунсьон", + "America/Maceio": "Масейо", + "America/Santarem": "Сантарен", + "America/Santiago": "Сантьяго", + "Antarctica/Palmer": "Палмер", + "America/Argentina/San_Juan": "Аргентина/Сан-Хуан", + "America/Fortaleza": "Форталеза", + "America/Cayenne": "Кайенна", + "America/Godthab": "Годтаб", + "America/Belem": "Белен", + "America/Araguaina": "Арагуайна", + "America/Bahia": "Баия", + "Atlantic/South_Georgia": "Южная_Грузия", + "America/Noronha": "Норонья", + "Atlantic/Azores": "Азорские острова", + "Atlantic/Cape_Verde": "Кабо-Верде", + "America/Scoresbysund": "Скорсбисунд", + "Africa/Accra": "Аккра", + "Atlantic/Faroe": "Фарерские острова", + "Europe/Guernsey": "Гернси", + "Africa/Dakar": "Дакар", + "Europe/Isle_of_Man": "Остров Мэн", + "Africa/Conakry": "Конакри", + "Africa/Abidjan": "Абиджан", + "Atlantic/Canary": "канарейка", + "Africa/Banjul": "Банжул", + "Europe/Jersey": "Джерси", + "Atlantic/St_Helena": "Остров Святой Елены", + "Africa/Bissau": "Бисау", + "Europe/London": "Лондон", + "Africa/Nouakchott": "Нуакшот", + "Africa/Lome": "Ломе", + "America/Danmarkshavn": "Данмарксхавн", + "Africa/Ouagadougou": "Уагадугу", + "Europe/Lisbon": "Лиссабон", + "Africa/Sao_Tome": "Сан-Томе", + "Africa/Monrovia": "Монровия", + "Atlantic/Reykjavik": "Рейкьявик", + "Antarctica/Troll": "Тролль", + "Atlantic/Madeira": "Мадейра", + "Africa/Bamako": "Бамако", + "Europe/Dublin": "Дублин", + "Africa/Freetown": "Фритаун", + "Europe/Monaco": "Монако", + "Europe/Skopje": "Скопье", + "Europe/Amsterdam": "Амстердам", + "Africa/Tunis": "Тунис", + "Arctic/Longyearbyen": "Лонгйир", + "Africa/Bangui": "Банги", + "Africa/Lagos": "Лагос", + "Africa/Douala": "Дуала", + "Africa/Libreville": "Либревиль", + "Europe/Belgrade": "Белград", + "Europe/Stockholm": "Стокгольм", + "Europe/Berlin": "Берлин", + "Europe/Zurich": "Цюрих", + "Europe/Zagreb": "Загреб", + "Europe/Warsaw": "Варшава", + "Africa/Luanda": "Луанда", + "Africa/Porto-Novo": "Порто-Ново", + "Africa/Brazzaville": "Браззавиль", + "Europe/Vienna": "Вена", + "Europe/Vatican": "Ватикан", + "Europe/Vaduz": "Вадуц", + "Europe/Tirane": "Тиран", + "Europe/Bratislava": "Братислава", + "Europe/Brussels": "Брюссель", + "Europe/Paris": "Париж", + "Europe/Sarajevo": "Сараево", + "Europe/San_Marino": "Сан-Марино", + "Europe/Rome": "Рим", + "Africa/El_Aaiun": "Эль-Аайун", + "Africa/Casablanca": "Касабланка", + "Europe/Malta": "Мальта", + "Africa/Ceuta": "Сеута", + "Europe/Gibraltar": "Гибралтар", + "Africa/Malabo": "Малабо", + "Europe/Busingen": "Бузинген", + "Africa/Ndjamena": "Нджамена", + "Europe/Andorra": "Андорра", + "Europe/Oslo": "Осло", + "Europe/Luxembourg": "Люксембург", + "Africa/Niamey": "Ниамей", + "Europe/Copenhagen": "Копенгаген", + "Europe/Madrid": "Мадрид", + "Europe/Budapest": "Будапешт", + "Africa/Algiers": "Алжир", + "Europe/Ljubljana": "Любляна", + "Europe/Podgorica": "Подгорица", + "Africa/Kinshasa": "Киншаса", + "Europe/Prague": "Прага", + "Europe/Riga": "Рига", + "Africa/Bujumbura": "Бужумбура", + "Africa/Lubumbashi": "Лубумбаши", + "Europe/Bucharest": "Бухарест", + "Africa/Blantyre": "Блантайр", + "Asia/Nicosia": "Никосия", + "Europe/Sofia": "София", + "Asia/Jerusalem": "Иерусалим", + "Europe/Tallinn": "Таллинн", + "Europe/Uzhgorod": "Ужгород", + "Africa/Lusaka": "Лусака", + "Europe/Mariehamn": "Мариехамн", + "Asia/Hebron": "Хеврон", + "Asia/Gaza": "Газа", + "Asia/Damascus": "Дамаск", + "Europe/Zaporozhye": "Запорожье", + "Asia/Beirut": "Бейрут", + "Africa/Juba": "Джуба", + "Africa/Harare": "Хараре", + "Europe/Athens": "Афины", + "Europe/Kiev": "Киев", + "Europe/Kaliningrad": "Калининград", + "Africa/Khartoum": "Хартум", + "Africa/Cairo": "Каир", + "Africa/Kigali": "Кигали", + "Asia/Amman": "Амман", + "Africa/Maputo": "Мапуту", + "Africa/Gaborone": "Габороне", + "Africa/Tripoli": "Триполи", + "Africa/Maseru": "Масеру", + "Africa/Windhoek": "Виндхук", + "Africa/Johannesburg": "Йоханнесбург", + "Europe/Chisinau": "Кишинев", + "Africa/Mbabane": "Мбабане", + "Europe/Vilnius": "Вильнюс", + "Europe/Helsinki": "Хельсинки", + "Europe/Moscow": "Москва", + "Africa/Kampala": "Кампала", + "Africa/Nairobi": "Найроби", + "Africa/Asmara": "Асмэра", + "Europe/Istanbul": "Стамбул", + "Asia/Riyadh": "Эр-Рияд", + "Asia/Qatar": "Катар", + "Europe/Minsk": "Минск", + "Indian/Comoro": "Коморо", + "Asia/Kuwait": "Кувейт", + "Africa/Addis_Ababa": "Аддис-Абеба", + "Africa/Dar_es_Salaam": "Дар-эс-Салам", + "Europe/Volgograd": "Волгоград", + "Indian/Antananarivo": "Антананариву", + "Asia/Bahrain": "Бахрейн", + "Asia/Baghdad": "Багдад", + "Indian/Mayotte": "Майотта", + "Africa/Djibouti": "Джибути", + "Europe/Simferopol": "Симферополь", + "Asia/Aden": "Аден", + "Antarctica/Syowa": "Сёва", + "Africa/Mogadishu": "Могадишо", + "Asia/Tehran": "Тегеран", + "Asia/Yerevan": "Ереван", + "Asia/Tbilisi": "Тбилиси", + "Asia/Muscat": "Мускат", + "Europe/Samara": "Самара", + "Indian/Mahe": "Маэ", + "Asia/Baku": "Баку", + "Indian/Mauritius": "Маврикий", + "Indian/Reunion": "Воссоединение", + "Asia/Dubai": "Дубай", + "Asia/Kabul": "Кабул", + "Asia/Ashgabat": "Ашхабад", + "Antarctica/Mawson": "Моусон", + "Asia/Aqtau": "Актау", + "Asia/Yekaterinburg": "Екатеринбург", + "Asia/Aqtobe": "Актобе", + "Asia/Dushanbe": "Душанбе", + "Asia/Tashkent": "Ташкент", + "Asia/Samarkand": "Самарканд", + "Asia/Qyzylorda": "Кызылорда", + "Asia/Oral": "Оральный", + "Asia/Karachi": "Карачи", + "Indian/Kerguelen": "Кергелен", + "Indian/Maldives": "Мальдивы", + "Asia/Kolkata": "Калькутта", + "Asia/Colombo": "Коломбо", + "Asia/Kathmandu": "Катманду", + "Antarctica/Vostok": "Восток", + "Asia/Almaty": "Алматы", + "Asia/Urumqi": "Урумчи", + "Asia/Thimphu": "Тхимпху", + "Asia/Omsk": "Омск", + "Asia/Dhaka": "Дакка", + "Indian/Chagos": "Чагос", + "Asia/Bishkek": "Бишкек", + "Asia/Rangoon": "Рангун", + "Indian/Cocos": "кокосы", + "Asia/Bangkok": "Бангкок", + "Asia/Hovd": "Ховд", + "Asia/Novokuznetsk": "Новокузнецк", + "Asia/Vientiane": "Вьентьян", + "Asia/Krasnoyarsk": "Красноярск", + "Antarctica/Davis": "Дэвис", + "Asia/Novosibirsk": "Новосибирск", + "Asia/Phnom_Penh": "Пномпень", + "Asia/Pontianak": "Понтианак", + "Asia/Jakarta": "Джакарта", + "Asia/Ho_Chi_Minh": "Хо Ши Мин", + "Indian/Christmas": "Рождество", + "Asia/Manila": "Манила", + "Asia/Makassar": "Макассар", + "Asia/Macau": "Макао", + "Asia/Kuala_Lumpur": "Куала-Лумпур", + "Asia/Singapore": "Сингапур", + "Asia/Shanghai": "Шанхай", + "Asia/Irkutsk": "Иркутск", + "Asia/Kuching": "Кучинг", + "Asia/Hong_Kong": "Гонконг", + "Australia/Perth": "Перт", + "Asia/Taipei": "Тайбэй", + "Asia/Brunei": "Бруней", + "Asia/Choibalsan": "Чойбалсан", + "Asia/Ulaanbaatar": "Улан-Батор", + "Australia/Eucla": "Евкла", + "Asia/Yakutsk": "Якутск", + "Asia/Dili": "Дили", + "Pacific/Palau": "Палау", + "Asia/Jayapura": "Джаяпура", + "Asia/Seoul": "Сеул", + "Asia/Pyongyang": "Пхеньян", + "Asia/Khandyga": "Хандыга", + "Asia/Chita": "Чита", + "Asia/Tokyo": "Токио", + "Australia/Darwin": "Дарвин", + "Pacific/Saipan": "Сайпан", + "Australia/Brisbane": "Брисбен", + "Pacific/Port_Moresby": "Порт-Морсби", + "Pacific/Chuuk": "Чуук", + "Antarctica/DumontDUrville": "Дюмон-д'Юрвиль", + "Pacific/Guam": "Гуам", + "Australia/Lindeman": "Линдеман", + "Asia/Ust-Nera": "Усть-Нера", + "Asia/Vladivostok": "Владивосток", + "Australia/Broken_Hill": "Брокен-Хилл", + "Australia/Adelaide": "Аделаида", + "Asia/Sakhalin": "Сахалин", + "Pacific/Guadalcanal": "Гуадалканал", + "Pacific/Efate": "Эфате", + "Antarctica/Casey": "Кейси", + "Antarctica/Macquarie": "Маккуори", + "Pacific/Kosrae": "Косрае", + "Australia/Sydney": "Сидней", + "Pacific/Noumea": "Нумеа", + "Australia/Melbourne": "Мельбурн", + "Australia/Lord_Howe": "Остров Лорд-Хау", + "Australia/Hobart": "Хобарт", + "Pacific/Pohnpei": "Понпеи", + "Australia/Currie": "Карри", + "Asia/Srednekolymsk": "Среднеколымск", + "Asia/Magadan": "Магадан", + "Pacific/Kwajalein": "Кваджалейн", + "Pacific/Majuro": "Маджуро", + "Pacific/Funafuti": "Фунафути", + "Asia/Anadyr": "Анадырь", + "Pacific/Nauru": "Науру", + "Asia/Kamchatka": "Камчатка", + "Pacific/Fiji": "Фиджи", + "Pacific/Norfolk": "Норфолк", + "Pacific/Tarawa": "Тарава", + "Pacific/Wallis": "Уоллис", + "Pacific/Wake": "Будить", + "Pacific/Tongatapu": "Тонгатапу", + "Antarctica/McMurdo": "МакМердо", + "Pacific/Enderbury": "Эндербери", + "Pacific/Fakaofo": "Факаофо", + "Pacific/Auckland": "Окленд", + "Pacific/Chatham": "Чатем", + "Pacific/Kiritimati": "Киритимати", + "Pacific/Apia": "Апиа", +}; diff --git a/lib/ui/pages/server_details/time_zone/time_zone.dart b/lib/ui/pages/server_details/time_zone/time_zone.dart new file mode 100644 index 00000000..04f192c0 --- /dev/null +++ b/lib/ui/pages/server_details/time_zone/time_zone.dart @@ -0,0 +1,107 @@ +part of '../server_details.dart'; + +final List locations = timeZoneDatabase.locations.values.toList() + ..sort((l1, l2) => + l1.currentTimeZone.offset.compareTo(l2.currentTimeZone.offset)); + +class SelectTimezone extends StatefulWidget { + SelectTimezone({Key? key}) : super(key: key); + + @override + _SelectTimezoneState createState() => _SelectTimezoneState(); +} + +class _SelectTimezoneState extends State { + final ScrollController controller = ScrollController(); + + @override + void initState() { + WidgetsBinding.instance!.addPostFrameCallback(_afterLayout); + super.initState(); + } + + void _afterLayout(_) { + var t = DateTime.now().timeZoneOffset; + var index = locations.indexWhere((element) => + Duration(milliseconds: element.currentTimeZone.offset) == t); + print(t); + + if (index >= 0) { + controller.animateTo(60.0 * index, + duration: Duration(milliseconds: 300), curve: Curves.easeIn); + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + child: BrandHeader( + title: 'select timezone', + hasBackButton: true, + ), + preferredSize: Size.fromHeight(52), + ), + body: ListView( + controller: controller, + children: locations + .asMap() + .map((key, value) { + var duration = + Duration(milliseconds: value.currentTimeZone.offset); + var area = value.currentTimeZone.abbreviation + .replaceAll(RegExp(r'[\d+()-]'), ''); + + String timezoneName = value.name; + if (context.locale.toString() == 'ru') { + timezoneName = russian[value.name] ?? + () { + var arr = value.name.split('/')..removeAt(0); + return arr.join('/'); + }(); + } + + return MapEntry( + key, + Container( + height: 60, + padding: EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + BrandText.body1( + timezoneName, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + BrandText.small( + 'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}', + style: TextStyle( + fontSize: 13, + )), + ], + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: BrandColors.dividerColor, + )), + ), + ), + ); + }) + .values + .toList(), + ), + ); + } +} diff --git a/lib/utils/extensions/duration.dart b/lib/utils/extensions/duration.dart new file mode 100644 index 00000000..a81627c0 --- /dev/null +++ b/lib/utils/extensions/duration.dart @@ -0,0 +1,41 @@ +// ignore_for_file: unnecessary_this + +extension DurationFormatter on Duration { + String toDayHourMinuteSecondFormat() { + return [ + this.inHours.remainder(24), + this.inMinutes.remainder(60), + this.inSeconds.remainder(60) + ].map((seg) { + return seg.toString().padLeft(2, '0'); + }).join(':'); + } + + String toDayHourMinuteFormat() { + var designator = this >= Duration.zero ? '+' : '-'; + + var segments = [ + this.inHours.remainder(24).abs(), + this.inMinutes.remainder(60).abs(), + ].map((seg) { + return seg.toString().padLeft(2, '0'); + }); + + return '$designator${segments.first}:${segments.last}'; + } + + String toHoursMinutesSecondsFormat() { + // WAT: https://flutterigniter.com/how-to-format-duration/ + return this.toString().split('.').first.padLeft(8, "0"); + } + + String toDayHourMinuteFormat2() { + var segments = [ + this.inHours.remainder(24), + this.inMinutes.remainder(60), + ].map((seg) { + return seg.toString().padLeft(2, '0'); + }); + return segments.first + " h" + " " + segments.last + " min"; + } +} diff --git a/lib/utils/extensions/text_extensions.dart b/lib/utils/extensions/text_extensions.dart index 7e378d0c..26932a11 100644 --- a/lib/utils/extensions/text_extensions.dart +++ b/lib/utils/extensions/text_extensions.dart @@ -1,4 +1,3 @@ -import 'dart:ui'; import 'package:flutter/cupertino.dart'; extension TextExtension on Text { diff --git a/pubspec.lock b/pubspec.lock index 11edf7d2..a08f1ac6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -63,7 +63,7 @@ packages: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "7.2.1" + version: "8.0.2" boolean_selector: dependency: transitive description: @@ -210,7 +210,7 @@ packages: name: cubit_form url: "https://pub.dartlang.org" source: hosted - version: "1.0.18" + version: "2.0.1" cupertino_icons: dependency: "direct main" description: @@ -267,13 +267,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.3" - extended_masked_text: - dependency: transitive - description: - name: extended_masked_text - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.1" fake_async: dependency: transitive description: @@ -320,14 +313,14 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.3.3" + version: "8.0.1" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.9.2" + version: "0.9.0" flutter_localizations: dependency: transitive description: flutter @@ -504,6 +497,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.1" + mask_text_input_formatter: + dependency: transitive + description: + name: mask_text_input_formatter + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" matcher: dependency: transitive description: @@ -943,6 +943,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.9" + timezone: + dependency: "direct main" + description: + name: timezone + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.0" timing: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2ad267ae..9eb7cfee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,38 +8,39 @@ environment: flutter: ">=2.10.0" dependencies: + auto_size_text: 3.0.0-nullsafety.0 + basic_utils: 3.4.0 + crypt: 4.0.1 + cubit_form: 2.0.1 + cupertino_icons: 1.0.2 + dio: 4.0.1 + easy_localization: 3.0.0 + either_option: 2.0.1-dev.1 + equatable: 2.0.3 + fl_chart: 0.40.0 flutter: sdk: flutter - crypt: ^4.0.1 - cubit_form: ^1.0.0-nullsafety.0 - cupertino_icons: ^1.0.2 - dio: ^4.0.0-beta7 - easy_localization: ^3.0.0 - either_option: ^2.0.1-dev.1 - equatable: ^2.0.3 - fl_chart: ^0.40.0 - flutter_bloc: ^7.3.3 - flutter_markdown: ^0.6.0 - flutter_secure_storage: ^4.1.0 - get_it: ^7.2.0 - hive: ^2.0.0 - hive_flutter: ^1.0.0 - json_annotation: ^4.0.0 - modal_bottom_sheet: ^2.0.0 - nanoid: ^1.0.0 - package_info: ^2.0.0 - pretty_dio_logger: ^1.1.1 - provider: ^6.0.0 - share_plus: ^2.1.4 - url_launcher: ^6.0.2 - wakelock: ^0.5.0+2 - basic_utils: ^3.4.0 - ionicons: ^0.1.2 - pointycastle: ^3.3.2 - rsa_encrypt: ^2.0.0 - ssh_key: ^0.7.0 - local_auth: ^1.1.7 - auto_size_text: ^3.0.0-nullsafety.0 + flutter_bloc: 8.0.1 + flutter_markdown: 0.6.9 + flutter_secure_storage: 4.2.1 + get_it: 7.2.0 + hive: 2.0.5 + hive_flutter: 1.1.0 + ionicons: 0.1.2 + json_annotation: 4.3.0 + local_auth: 1.1.7 + modal_bottom_sheet: 2.0.0 + nanoid: 1.0.0 + package_info: 2.0.0 + pointycastle: 3.3.2 + pretty_dio_logger: 1.2.0-beta-1 + provider: 6.0.0 + rsa_encrypt: 2.0.0 + share_plus: 2.1.4 + ssh_key: 0.7.0 + timezone: ^0.8.0 + url_launcher: 6.0.2 + wakelock: 0.5.0+2 dev_dependencies: flutter_test: