feat(timezone): Implement search bar for 'Select Timezone' page

pull/138/head
NaiJi ✨ 2022-10-06 14:46:29 +00:00 committed by Gitea
parent e1419ce38f
commit e36a94ded5
5 changed files with 123 additions and 75 deletions

View File

@ -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",

View File

@ -101,6 +101,7 @@
"reboot_after_upgrade_hint": "Автоматически перезагружать сервер после применения обновлений",
"server_timezone": "Часовой пояс сервера",
"select_timezone": "Выберите часовой пояс",
"timezone_search_bar": "Имя часового пояса или значение временного сдвига",
"server_id": "ID сервера",
"status": "Статус",
"cpu": "Процессор",

View File

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

View File

@ -14,11 +14,20 @@ class SelectTimezone extends StatefulWidget {
}
class _SelectTimezoneState extends State<SelectTimezone> {
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<SelectTimezone> {
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<SelectTimezone> {
@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<ServerDetailsCubit>()
.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<int, Container> 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<ServerDetailsCubit>().repository.setTimezone(
location.name,
);
Navigator.of(context).pop();
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
BrandText.body1(
location.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
BrandText.small(
'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}',
style: const TextStyle(
fontSize: 13,
),
),
],
),
),
),
),
);
}
}

View File

@ -32,6 +32,13 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
super.initState();
}
@override
void dispose() {
_sizeController.dispose();
_priceController.dispose();
super.dispose();
}
bool _isError = false;
late double _currentSliderGbValue;