From e36a94ded5dfce30a09995564b6f615adb03a01a Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 6 Oct 2022 14:46:29 +0000 Subject: [PATCH 1/4] feat(timezone): Implement search bar for 'Select Timezone' page --- assets/translations/en.json | 1 + assets/translations/ru.json | 1 + .../server_details/server_details_screen.dart | 1 + .../server_details/time_zone/time_zone.dart | 188 +++++++++++------- .../server_storage/extending_volume.dart | 7 + 5 files changed, 123 insertions(+), 75 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 032bd0da..6920d6c2 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -101,6 +101,7 @@ "reboot_after_upgrade_hint": "Reboot without prompt after applying changes on server", "server_timezone": "Server timezone", "select_timezone": "Select timezone", + "timezone_search_bar": "Timezone name or time shift value", "server_id": "Server ID", "status": "Status", "cpu": "CPU", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 7b60e4e9..9d9b47e4 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -101,6 +101,7 @@ "reboot_after_upgrade_hint": "Автоматически перезагружать сервер после применения обновлений", "server_timezone": "Часовой пояс сервера", "select_timezone": "Выберите часовой пояс", + "timezone_search_bar": "Имя часового пояса или значение временного сдвига", "server_id": "ID сервера", "status": "Статус", "cpu": "Процессор", diff --git a/lib/ui/pages/server_details/server_details_screen.dart b/lib/ui/pages/server_details/server_details_screen.dart index 0b4ac005..d1eada36 100644 --- a/lib/ui/pages/server_details/server_details_screen.dart +++ b/lib/ui/pages/server_details/server_details_screen.dart @@ -12,6 +12,7 @@ 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_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart'; diff --git a/lib/ui/pages/server_details/time_zone/time_zone.dart b/lib/ui/pages/server_details/time_zone/time_zone.dart index 6a7349c9..2073d305 100644 --- a/lib/ui/pages/server_details/time_zone/time_zone.dart +++ b/lib/ui/pages/server_details/time_zone/time_zone.dart @@ -14,11 +14,20 @@ class SelectTimezone extends StatefulWidget { } class _SelectTimezoneState extends State { - final ScrollController controller = ScrollController(); + final ScrollController scrollController = ScrollController(); + final TextEditingController searchController = TextEditingController(); + + String? timezoneFilterValue; @override void initState() { WidgetsBinding.instance.addPostFrameCallback(_afterLayout); + searchController.addListener(() { + setState(() { + timezoneFilterValue = + searchController.text.isNotEmpty ? searchController.text : null; + }); + }); super.initState(); } @@ -31,7 +40,7 @@ class _SelectTimezoneState extends State { print(t); if (index >= 0) { - controller.animateTo( + scrollController.animateTo( 60.0 * index, duration: const Duration(milliseconds: 300), curve: Curves.easeIn, @@ -41,93 +50,122 @@ class _SelectTimezoneState extends State { @override void dispose() { - controller.dispose(); + scrollController.dispose(); + searchController.dispose(); super.dispose(); } @override Widget build(final BuildContext context) => Scaffold( - appBar: AppBar( - title: Padding( - padding: const EdgeInsets.only(top: 4.0), - child: Text('server.select_timezone'.tr()), - ), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.of(context).pop(), + appBar: PreferredSize( + preferredSize: const Size.fromHeight(156), + child: Column( + children: [ + BrandHeader( + title: 'server.select_timezone'.tr(), + ), + Row( + children: [ + const SizedBox(width: 16), + SizedBox( + height: 52, + child: TextField( + readOnly: false, + textAlign: TextAlign.start, + textInputAction: TextInputAction.next, + enabled: true, + controller: searchController, + decoration: InputDecoration( + border: const OutlineInputBorder(), + errorText: null, + labelText: 'server.timezone_search_bar'.tr(), + ), + ), + ), + const SizedBox(width: 16), + const Icon(Icons.search_outlined), + const SizedBox(width: 16), + ], + ), + ], ), ), body: SafeArea( child: ListView( - controller: controller, + 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) { - final duration = - Duration(milliseconds: value.currentTimeZone.offset); - final area = value.currentTimeZone.abbreviation - .replaceAll(RegExp(r'[\d+()-]'), ''); - - String timezoneName = value.name; - if (context.locale.toString() == 'ru') { - timezoneName = russian[value.name] ?? - () { - final arr = value.name.split('/')..removeAt(0); - return arr.join('/'); - }(); - } - - return MapEntry( - key, - Container( - height: 75, - padding: const EdgeInsets.symmetric(horizontal: 20), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: BrandColors.dividerColor, - ), - ), - ), - child: InkWell( - onTap: () { - context - .read() - .repository - .setTimezone( - timezoneName, - ); - Navigator.of(context).pop(); - }, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - BrandText.body1( - timezoneName, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - BrandText.small( - 'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}', - style: const TextStyle( - fontSize: 13, - ), - ), - ], - ), - ), - ), - ), - ); - }) + .map( + (final key, final value) => locationToListTile(key, value), + ) .values .toList(), ), ), ); + + MapEntry locationToListTile( + final int key, final Location location) { + final duration = Duration(milliseconds: location.currentTimeZone.offset); + final area = location.currentTimeZone.abbreviation + .replaceAll(RegExp(r'[\d+()-]'), ''); + + return MapEntry( + key, + Container( + height: 75, + padding: const EdgeInsets.symmetric(horizontal: 20), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: BrandColors.dividerColor, + ), + ), + ), + child: InkWell( + onTap: () { + context.read().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, + ), + ), + ], + ), + ), + ), + ), + ); + } } diff --git a/lib/ui/pages/server_storage/extending_volume.dart b/lib/ui/pages/server_storage/extending_volume.dart index e465c2b2..fd4491f7 100644 --- a/lib/ui/pages/server_storage/extending_volume.dart +++ b/lib/ui/pages/server_storage/extending_volume.dart @@ -32,6 +32,13 @@ class _ExtendingVolumePageState extends State { super.initState(); } + @override + void dispose() { + _sizeController.dispose(); + _priceController.dispose(); + super.dispose(); + } + bool _isError = false; late double _currentSliderGbValue; From e7bb1dc16ee5ffbc6f05d2203f26b37be562e2f0 Mon Sep 17 00:00:00 2001 From: inexcode Date: Thu, 20 Oct 2022 22:23:55 +0300 Subject: [PATCH 2/4] feat(timezone): Design the search bar for 'Select Timezone' page --- .../server_details/server_details_screen.dart | 7 +-- .../server_details/time_zone/time_zone.dart | 59 +++++++++---------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/lib/ui/pages/server_details/server_details_screen.dart b/lib/ui/pages/server_details/server_details_screen.dart index d1eada36..894c9a28 100644 --- a/lib/ui/pages/server_details/server_details_screen.dart +++ b/lib/ui/pages/server_details/server_details_screen.dart @@ -12,23 +12,20 @@ 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_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/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/ui/pages/server_details/time_zone/lang.dart'; import 'package:selfprivacy/utils/extensions/duration.dart'; import 'package:selfprivacy/utils/extensions/string_extensions.dart'; import 'package:selfprivacy/utils/named_font_weight.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:timezone/timezone.dart'; -import 'package:selfprivacy/ui/pages/server_details/charts/cpu_chart.dart'; -import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart'; - part 'charts/chart.dart'; part 'server_settings.dart'; part 'text_details.dart'; diff --git a/lib/ui/pages/server_details/time_zone/time_zone.dart b/lib/ui/pages/server_details/time_zone/time_zone.dart index 2073d305..28407d7d 100644 --- a/lib/ui/pages/server_details/time_zone/time_zone.dart +++ b/lib/ui/pages/server_details/time_zone/time_zone.dart @@ -18,6 +18,7 @@ class _SelectTimezoneState extends State { final TextEditingController searchController = TextEditingController(); String? timezoneFilterValue; + bool isSearching = false; @override void initState() { @@ -57,38 +58,36 @@ class _SelectTimezoneState extends State { @override Widget build(final BuildContext context) => Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(156), - child: Column( - children: [ - BrandHeader( - title: 'server.select_timezone'.tr(), - ), - Row( - children: [ - const SizedBox(width: 16), - SizedBox( - height: 52, - child: TextField( - readOnly: false, - textAlign: TextAlign.start, - textInputAction: TextInputAction.next, - enabled: true, - controller: searchController, - decoration: InputDecoration( - border: const OutlineInputBorder(), - errorText: null, - labelText: 'server.timezone_search_bar'.tr(), - ), - ), + 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(), ), - const SizedBox(width: 16), - const Icon(Icons.search_outlined), - const SizedBox(width: 16), - ], - ), - ], + ) + : Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Text('server.select_timezone'.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), + ), + ], ), body: SafeArea( child: ListView( From e956a2c9a1f2733bba9e4f1e6d9c32fcd919de19 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 21 Oct 2022 06:54:55 +0400 Subject: [PATCH 3/4] fix(assets): Fix broken string for bytes on disk size --- assets/translations/en.json | 1 + assets/translations/ru.json | 1 + lib/logic/models/disk_size.dart | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 032bd0da..4a36b391 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -171,6 +171,7 @@ "gb": "{} GB", "mb": "{} MB", "kb": "{} KB", + "bytes": "Bytes", "extend_volume_button": "Extend volume", "extending_volume_title": "Extending volume", "extending_volume_description": "Resizing volume will allow you to store more data on your server without extending the server itself. Volume can only be extended: shrinking is not possible.", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 7b60e4e9..fa8737ab 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -171,6 +171,7 @@ "gb": "{} GB", "mb": "{} MB", "kb": "{} KB", + "bytes": "байт", "extend_volume_button": "Расширить хранилище", "extending_volume_title": "Расширение хранилища", "extending_volume_description": "Изменение размера хранилища позволит вам держать больше данных на вашем сервере без расширения самого сервера. Объем можно только увеличить: уменьшить нельзя.", diff --git a/lib/logic/models/disk_size.dart b/lib/logic/models/disk_size.dart index 44c6bc35..c6f55873 100644 --- a/lib/logic/models/disk_size.dart +++ b/lib/logic/models/disk_size.dart @@ -26,7 +26,7 @@ class DiskSize { @override String toString() { if (byte < 1024) { - return '${byte.toStringAsFixed(0)} ${tr('bytes')}'; + return '${byte.toStringAsFixed(0)} ${tr('storage.bytes')}'; } else if (byte < 1024 * 1024) { return 'storage.kb'.tr(args: [kibibyte.toStringAsFixed(1)]); } else if (byte < 1024 * 1024 * 1024) { From 20e4d9565bb2860c4dcd8508264cc7abdf7d3289 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 24 Oct 2022 16:19:39 +0400 Subject: [PATCH 4/4] fix(assets): Fix broken strings for network domain elements --- .../server_installation_repository.dart | 2 +- lib/ui/pages/dns_details/dns_details.dart | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index a83e9deb..e81190b0 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -353,7 +353,7 @@ class ServerInstallationRepository { BrandAlert( title: e.response!.data['errors'][0]['code'] == 1038 ? 'modals.you_cant_use_this_api'.tr() - : 'domain.states.error'.tr(), + : 'domain.error'.tr(), contentText: 'modals.delete_server_volume'.tr(), actions: [ ActionButton( diff --git a/lib/ui/pages/dns_details/dns_details.dart b/lib/ui/pages/dns_details/dns_details.dart index 3308a068..a1d6c29c 100644 --- a/lib/ui/pages/dns_details/dns_details.dart +++ b/lib/ui/pages/dns_details/dns_details.dart @@ -28,7 +28,7 @@ class _DnsDetailsPageState extends State { bool isError = false; switch (dnsState) { case DnsRecordsStatus.uninitialized: - description = 'domain.states.uninitialized'.tr(); + description = 'domain.uninitialized'.tr(); icon = const Icon( Icons.refresh, size: 24.0, @@ -36,7 +36,7 @@ class _DnsDetailsPageState extends State { isError = false; break; case DnsRecordsStatus.refreshing: - description = 'domain.states.refreshing'.tr(); + description = 'domain.refreshing'.tr(); icon = const Icon( Icons.refresh, size: 24.0, @@ -44,7 +44,7 @@ class _DnsDetailsPageState extends State { isError = false; break; case DnsRecordsStatus.good: - description = 'domain.states.ok'.tr(); + description = 'domain.ok'.tr(); icon = const Icon( Icons.check_circle_outline, size: 24.0, @@ -52,8 +52,8 @@ class _DnsDetailsPageState extends State { isError = false; break; case DnsRecordsStatus.error: - description = 'domain.states.error'.tr(); - subtitle = 'domain.states.error_subtitle'.tr(); + description = 'domain.error'.tr(); + subtitle = 'domain.error_subtitle'.tr(); icon = const Icon( Icons.error_outline, size: 24.0,