feature/service-configurations
kherel 2022-01-25 18:00:47 +01:00
parent d79b41a3fe
commit 9566a6ad10
30 changed files with 461 additions and 329 deletions

View File

@ -61,4 +61,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.10.1
COCOAPODS: 1.11.2

View File

@ -157,7 +157,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -2,7 +2,6 @@ import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:selfprivacy/logic/models/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/backblaze_credential.dart';

View File

@ -6,7 +6,6 @@ import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/get_it/console.dart';
import 'package:selfprivacy/logic/models/message.dart';
abstract class ApiMap {

View File

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
import 'package:easy_localization/easy_localization.dart';
class CloudFlareFormCubit extends FormCubit {
@ -16,7 +15,11 @@ class CloudFlareFormCubit extends FormCubit {
ValidationModel<String>(
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
LegnthStringValidationWithLenghShowing(
40, 'validations.length'.tr(args: ["40"]))
40,
'validations.length'.tr(
args: ["40"],
),
)
],
);

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:easy_localization/easy_localization.dart';

View File

@ -37,7 +37,7 @@ class JobsCubit extends Cubit<JobsState> {
emit(newState);
}
void createOrRemoveServiceToggleJob(ServiceToggleJob job) {
void createOrRemoveServiceToggleJob(ToggleJob job) {
var newJobsList = <Job>[];
if (state is JobsStateWithJobs) {
newJobsList.addAll((state as JobsStateWithJobs).jobList);

View File

@ -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';

View File

@ -6,46 +6,16 @@ part of 'backup.dart';
// JsonSerializableGenerator
// **************************************************************************
Backup _$BackupFromJson(Map<String, dynamic> json) {
return Backup(
time: DateTime.parse(json['time'] as String),
id: json['short_id'] as String,
);
}
BackupStatus _$BackupStatusFromJson(Map<String, dynamic> json) {
return BackupStatus(
status: _$enumDecode(_$BackupStatusEnumEnumMap, json['status']),
progress: (json['progress'] as num).toDouble(),
errorMessage: json['error_message'] as String?,
);
}
K _$enumDecode<K, V>(
Map<K, V> enumValues,
Object? source, {
K? unknownValue,
}) {
if (source == null) {
throw ArgumentError(
'A value must be provided. Supported values: '
'${enumValues.values.join(', ')}',
Backup _$BackupFromJson(Map<String, dynamic> 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<String, dynamic> 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',

View File

@ -6,42 +6,16 @@ part of 'hetzner_server_info.dart';
// JsonSerializableGenerator
// **************************************************************************
HetznerServerInfo _$HetznerServerInfoFromJson(Map<String, dynamic> 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<String, dynamic>),
HetznerServerInfo.locationFromJson(json['datacenter'] as Map),
);
}
K _$enumDecode<K, V>(
Map<K, V> enumValues,
Object? source, {
K? unknownValue,
}) {
if (source == null) {
throw ArgumentError(
'A value must be provided. Supported values: '
'${enumValues.values.join(', ')}',
HetznerServerInfo _$HetznerServerInfoFromJson(Map<String, dynamic> 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<String, dynamic>),
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<String, dynamic> json) {
return HetznerServerTypeInfo(
json['cores'] as int,
json['memory'] as num,
json['disk'] as int,
(json['prices'] as List<dynamic>)
.map((e) => HetznerPriceInfo.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
Map<String, dynamic> json) =>
HetznerServerTypeInfo(
json['cores'] as int,
json['memory'] as num,
json['disk'] as int,
(json['prices'] as List<dynamic>)
.map((e) => HetznerPriceInfo.fromJson(e as Map<String, dynamic>))
.toList(),
);
HetznerPriceInfo _$HetznerPriceInfoFromJson(Map<String, dynamic> json) {
return HetznerPriceInfo(
HetznerPriceInfo.getPrice(json['price_hourly'] as Map),
HetznerPriceInfo.getPrice(json['price_monthly'] as Map),
);
}
HetznerPriceInfo _$HetznerPriceInfoFromJson(Map<String, dynamic> json) =>
HetznerPriceInfo(
HetznerPriceInfo.getPrice(json['price_hourly'] as Map),
HetznerPriceInfo.getPrice(json['price_monthly'] as Map),
);
HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) {
return HetznerLocation(
json['country'] as String,
json['city'] as String,
json['description'] as String,
json['network_zone'] as String,
);
}
HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) =>
HetznerLocation(
json['country'] as String,
json['city'] as String,
json['description'] as String,
json['network_zone'] as String,
);

View File

@ -42,19 +42,29 @@ class DeleteUserJob extends Job {
List<Object> 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<Object> get props => [id, title, type, needToTurnOn];
List<Object> 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 {

View File

@ -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<String, dynamic> json) =>
_$AutoUpgradeConfigurationsFromJson(json);
Map<String, dynamic> toJson() => _$AutoUpgradeConfigurationsToJson(this);
@override
List<Object?> get props => [enable, allowReboot];
}

View File

@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_configurations.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
AutoUpgradeConfigurations _$AutoUpgradeConfigurationsFromJson(
Map<String, dynamic> json) =>
AutoUpgradeConfigurations(
enable: json['enable'] as bool,
allowReboot: json['allowReboot'] as bool,
);
Map<String, dynamic> _$AutoUpgradeConfigurationsToJson(
AutoUpgradeConfigurations instance) =>
<String, dynamic>{
'enable': instance.enable,
'allowReboot': instance.allowReboot,
};

View File

@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
import 'package:timezone/timezone.dart';
class ServerTimezone extends Equatable {
final Location timezone;
const ServerTimezone({required this.timezone});
factory ServerTimezone.fromJson(Map<String, dynamic> json) {
var timezone = getLocation(json['timezone']);
return ServerTimezone(timezone: timezone);
}
Map<String, dynamic> toJson() => {'timezone': timezone.name};
@override
List<Object?> get props => [timezone.name];
}

View File

@ -7,6 +7,7 @@ import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
import 'package:easy_localization/easy_localization.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 {

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';

View File

@ -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';

View File

@ -1,7 +1,5 @@
import 'package:cubit_form/cubit_form.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/forms/initializing/backblaze_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart';
@ -9,7 +7,6 @@ import 'package:selfprivacy/logic/cubit/forms/initializing/domain_cloudflare.dar
import 'package:selfprivacy/logic/cubit/forms/initializing/hetzner_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/root_user_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_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_cards/brand_cards.dart';

View File

@ -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';

View File

@ -2,10 +2,8 @@ import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/models/provider.dart';
import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';

View File

@ -1,122 +1,116 @@
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';
// 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';
// import 'package:selfprivacy/ui/pages/providers/settings/time_zone.dart';
// import 'package:selfprivacy/utils/route_transitions/basic.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({Key? key}) : super(key: key);
// 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',
),
)
],
);
}
}
// @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: () {
// print('aaa');
// Navigator.of(context).push(
// materialRoute(
// SelectTimezone(),
// ),
// );
// },
// child: _TextColumn(
// title: 'Server Timezone',
// value: 'Europe/Kyssiv',
// ),
// ),
// ],
// );
// }
// }
class _Button extends StatelessWidget {
const _Button({
Key? key,
required this.onTap,
required this.child,
}) : super(key: key);
// class _Button extends StatelessWidget {
// const _Button({
// Key? key,
// required this.onTap,
// required this.child,
// }) : super(key: key);
final Widget child;
final VoidCallback onTap;
// 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,
),
);
}
}
// @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);
// 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,
),
),
],
);
}
}
// 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,
// ),
// ),
// ],
// );
// }
// }

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart';

View File

@ -8,6 +8,7 @@ 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_radio_tile/brand_radio_tile.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
@ -15,13 +16,17 @@ 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/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.dart';
var navigatorKey = GlobalKey<NavigatorState>();

View File

@ -49,27 +49,14 @@ class _ServerSettings extends StatelessWidget {
isActive: false,
),
_Button(
onTap: () {},
onTap: () {
Navigator.of(context).push(materialRoute(SelectTimezone()));
},
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',
),
)
],
);
}

View File

@ -0,0 +1,92 @@
part of 'server_details.dart';
final List<Location> 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<SelectTimezone> {
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);
if (index >= 0) {
controller.animateTo(40.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+()-]'), '');
return MapEntry(
key,
Container(
height: 40,
padding: EdgeInsets.symmetric(horizontal: 20),
alignment: Alignment.center,
child: Row(
children: [
Expanded(
flex: 1,
child: Text(
'${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}'),
),
Expanded(
flex: 2,
child: Text(value.name),
),
// Text(value.toString()),
],
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: BrandColors.dividerColor,
)),
),
),
);
})
.values
.toList(),
),
);
}
}

View File

@ -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";
}
}

View File

@ -1,4 +1,3 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
extension TextExtension on Text {

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "22.0.0"
version: "31.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.2"
version: "2.8.0"
archive:
dependency: transitive
description:
@ -28,7 +28,7 @@ packages:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "2.0.0"
asn1lib:
dependency: transitive
description:
@ -42,7 +42,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
version: "2.8.2"
auto_size_text:
dependency: "direct main"
description:
@ -56,14 +56,14 @@ packages:
name: basic_utils
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.0"
version: "3.4.0"
bloc:
dependency: transitive
description:
name: bloc
url: "https://pub.dartlang.org"
source: hosted
version: "7.2.1"
version: "8.0.2"
boolean_selector:
dependency: transitive
description:
@ -105,14 +105,14 @@ packages:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.5"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "7.1.0"
version: "7.2.3"
built_collection:
dependency: transitive
description:
@ -133,7 +133,7 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
@ -210,14 +210,14 @@ 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:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.0.2"
dart_style:
dependency: transitive
description:
@ -231,7 +231,7 @@ packages:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "4.0.1"
easy_localization:
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
@ -339,7 +332,7 @@ packages:
name: flutter_markdown
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.5"
version: "0.6.9"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@ -398,7 +391,7 @@ packages:
name: hive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
version: "2.0.5"
hive_flutter:
dependency: "direct main"
description:
@ -412,7 +405,7 @@ packages:
name: hive_generator
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
http:
dependency: transitive
description:
@ -475,14 +468,14 @@ packages:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
version: "4.3.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.4"
version: "6.0.1"
local_auth:
dependency: "direct main"
description:
@ -504,13 +497,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
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:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
meta:
dependency: transitive
description:
@ -566,7 +566,7 @@ packages:
name: package_info
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.0.0"
path:
dependency: transitive
description:
@ -830,7 +830,7 @@ packages:
name: source_helper
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
version: "1.3.1"
source_map_stack_trace:
dependency: transitive
description:
@ -900,21 +900,28 @@ packages:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.17.10"
version: "1.17.12"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
version: "0.4.3"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
version: "0.4.2"
timezone:
dependency: "direct main"
description:
name: timezone
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.0"
timing:
dependency: transitive
description:
@ -942,7 +949,7 @@ packages:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.9"
version: "6.0.2"
url_launcher_linux:
dependency: transitive
description:
@ -984,7 +991,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
vm_service:
dependency: transitive
description:
@ -998,7 +1005,7 @@ packages:
name: wakelock
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.3+3"
version: "0.5.0+2"
wakelock_macos:
dependency: transitive
description:
@ -1077,5 +1084,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.13.4 <3.0.0"
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"

View File

@ -8,46 +8,47 @@ environment:
flutter: ">=2.5.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:
build_runner: 2.1.5
flutter_launcher_icons: 0.9.0
flutter_test:
sdk: flutter
build_runner: ^2.1.1
flutter_launcher_icons: ^0.9.0
hive_generator: ^1.0.0
json_serializable: ^4.0.2
hive_generator: 1.1.1
json_serializable: 6.0.1
flutter_icons:
android: "launcher_icon"