From d73ad93180fb5191d2d4270279d06e6526a9a1cc Mon Sep 17 00:00:00 2001 From: Juan Gilsanz Polo Date: Thu, 25 Jan 2024 22:34:30 +0100 Subject: [PATCH] Created block time schedule settings --- README.md | 1 + lib/l10n/app_en.arb | 20 +- lib/l10n/app_es.arb | 18 +- lib/models/clients.dart | 68 ++++- .../clients/client/blocking_schedule.dart | 143 +++++++++ .../client/blocking_schedule_modal.dart | 283 ++++++++++++++++++ lib/screens/clients/client/client_form.dart | 10 + lib/screens/clients/client/client_screen.dart | 16 +- pubspec.lock | 8 + pubspec.yaml | 1 + 10 files changed, 560 insertions(+), 8 deletions(-) create mode 100644 lib/screens/clients/client/blocking_schedule.dart create mode 100644 lib/screens/clients/client/blocking_schedule_modal.dart diff --git a/README.md b/README.md index 930d090..fb9505d 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ On [this repository](https://github.com/JuanRodenas/Pihole_list) you can find a - [flutter reorderable list](https://pub.dev/packages/flutter_reorderable_list) - [pie chart](https://pub.dev/packages/pie_chart) - [segmented button slide](https://pub.dev/packages/segmented_button_slide) +- [timezone](https://pub.dev/packages/timezone)
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b7e9999..6e281d4 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -719,7 +719,7 @@ "unblockClient": "Unblock client", "blockingClient": "Blocking client...", "unblockingClient": "Unblocking client...", - "upstreamDnsCacheConfiguration": "Configuración de la caché DNS upstream", + "upstreamDnsCacheConfiguration": "DNS upstream cache configuration", "enableDnsCachingClient": "Enable DNS caching for this client", "dnsCacheSize": "DNS cache size", "nameInvalid": "Name is required", @@ -729,5 +729,21 @@ "redirectHttpsWarning": "If you have enabled \"Redirect to HTTPS automatically\" on your AdGuard Home server, you must select an HTTPS connection and use the HTTPS port of your server.", "logsSettingsDescription": "Configure query logs", "ignoredDomains": "Ignored domains", - "noIgnoredDomainsAdded": "No domains to ignore added" + "noIgnoredDomainsAdded": "No domains to ignore added", + "pauseServiceBlocking": "Pause service blocking", + "newSchedule": "New schedule", + "editSchedule": "Edit schedule", + "timezone": "Timezone", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday", + "sunday": "Sunday", + "from": "From: {from}", + "to": "To: {to}", + "selectStartTime": "Select start time", + "selectEndTime": "Select end time", + "startTimeBeforeEndTime": "Start time must be before end time." } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 0ea54a0..55f8237 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -729,5 +729,21 @@ "redirectHttpsWarning": "Si tienes activado \"Redireccionar a HTTPS automáticamente\" en tu servidor AdGuard Home, debes seleccionar una conexión HTTPS y utilizar el puerto de HTTPS de tu servidor.", "logsSettingsDescription": "Configura los registros de peticiones", "ignoredDomains": "Dominios ignorados", - "noIgnoredDomainsAdded": "No hay añadidos dominios para ignorar" + "noIgnoredDomainsAdded": "No hay añadidos dominios para ignorar", + "pauseServiceBlocking": "Pausa del servicio de bloqueo", + "newSchedule": "Nueva programación", + "editSchedule": "Editar programación", + "timezone": "Zona horaria", + "monday": "Lunes", + "tuesday": "Martes", + "wednesday": "Miércoles", + "thursday": "Jueves", + "friday": "Viernes", + "saturday": "Sábado", + "sunday": "Domingo", + "from": "Desde: {from}", + "to": "Hasta: {to}", + "selectStartTime": "Seleccionar hora de inicio", + "selectEndTime": "Seleccionar hora de fin", + "startTimeBeforeEndTime": "La hora de inicio debe ser anterior a la hora de fin." } \ No newline at end of file diff --git a/lib/models/clients.dart b/lib/models/clients.dart index 67740bd..56a967e 100644 --- a/lib/models/clients.dart +++ b/lib/models/clients.dart @@ -91,6 +91,7 @@ class Client { final bool? ignoreStatistics; final bool? upstreamsCacheEnabled; final int? upstreamsCacheSize; + final BlockedServicesSchedule? blockedServicesSchedule; Client({ required this.name, @@ -108,6 +109,7 @@ class Client { required this.ignoreStatistics, required this.upstreamsCacheEnabled, required this.upstreamsCacheSize, + required this.blockedServicesSchedule, }); factory Client.fromJson(Map json) => Client( @@ -127,7 +129,8 @@ class Client { ignoreQuerylog: json["ignore_querylog"], ignoreStatistics: json["ignore_statistics"], upstreamsCacheEnabled: json["upstreams_cache_enabled"], - upstreamsCacheSize: json["upstreams_cache_size"] + upstreamsCacheSize: json["upstreams_cache_size"], + blockedServicesSchedule: BlockedServicesSchedule.fromJson(json["blocked_services_schedule"]) ); Map toJson() => { @@ -145,6 +148,67 @@ class Client { "ignore_querylog": ignoreQuerylog, "ignore_statistics": ignoreStatistics, "upstreams_cache_enabled": upstreamsCacheEnabled, - "upstreams_cache_size": upstreamsCacheSize + "upstreams_cache_size": upstreamsCacheSize, + "blocked_services_schedule":blockedServicesSchedule?.toJson() + }; +} + +class BlockedServicesSchedule { + final String? timeZone; + final BlockedServicesScheduleDay? mon; + final BlockedServicesScheduleDay? tue; + final BlockedServicesScheduleDay? wed; + final BlockedServicesScheduleDay? thu; + final BlockedServicesScheduleDay? fri; + final BlockedServicesScheduleDay? sat; + final BlockedServicesScheduleDay? sun; + + BlockedServicesSchedule({ + this.timeZone, + this.mon, + this.tue, + this.wed, + this.thu, + this.fri, + this.sat, + this.sun + }); + + factory BlockedServicesSchedule.fromJson(Map json) => BlockedServicesSchedule( + timeZone: json["time_zone"], + mon: json["mon"] == null ? null : BlockedServicesScheduleDay.fromJson(json["mon"]), + tue: json["tue"] == null ? null : BlockedServicesScheduleDay.fromJson(json["tue"]), + wed: json["wed"] == null ? null : BlockedServicesScheduleDay.fromJson(json["wed"]), + thu: json["thu"] == null ? null : BlockedServicesScheduleDay.fromJson(json["thu"]), + fri: json["fri"] == null ? null : BlockedServicesScheduleDay.fromJson(json["fri"]), + sat: json["sat"] == null ? null : BlockedServicesScheduleDay.fromJson(json["sat"]), + sun: json["sun"] == null ? null : BlockedServicesScheduleDay.fromJson(json["sun"]), + ); + + Map toJson() => { + "time_zone": timeZone, + "tue": tue?.toJson(), + "wed": wed?.toJson(), + "thu": thu?.toJson(), + }; +} + +class BlockedServicesScheduleDay { + final int? start; + final int? end; + + BlockedServicesScheduleDay({ + this.start, + this.end, + }); + + factory BlockedServicesScheduleDay.fromJson(Map json) => BlockedServicesScheduleDay( + start: json["start"], + end: json["end"], + ); + + Map toJson() => { + "start": start, + "end": end, }; } \ No newline at end of file diff --git a/lib/screens/clients/client/blocking_schedule.dart b/lib/screens/clients/client/blocking_schedule.dart new file mode 100644 index 0000000..0ae7abb --- /dev/null +++ b/lib/screens/clients/client/blocking_schedule.dart @@ -0,0 +1,143 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/screens/clients/client/blocking_schedule_modal.dart'; +import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; +import 'package:adguard_home_manager/widgets/section_label.dart'; + +import 'package:adguard_home_manager/models/clients.dart'; + +class BlockingSchedule extends StatelessWidget { + final BlockedServicesSchedule blockedServicesSchedule; + final void Function(BlockedServicesSchedule) setBlockedServicesSchedule; + + const BlockingSchedule({ + super.key, + required this.blockedServicesSchedule, + required this.setBlockedServicesSchedule, + }); + + @override + Widget build(BuildContext context) { + void openAddScheduleModal() { + showDialog( + context: context, + builder: (context) => BlockingScheduleModal( + onConfirm: (v) => {}, + ), + ); + } + + String formatTime(int time) { + final formatted = Duration(milliseconds: time); + final hours = formatted.inHours; + final minutes = formatted.inMinutes - hours*60; + return "${hours.toString().padLeft(2 , '0')}:${minutes.toString().padLeft(2, '0')}"; + } + + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SectionLabel(label: AppLocalizations.of(context)!.pauseServiceBlocking), + Padding( + padding: const EdgeInsets.only(right: 12), + child: IconButton( + onPressed: openAddScheduleModal, + icon: const Icon(Icons.add) + ), + ) + ], + ), + const SizedBox(height: 2), + if (blockedServicesSchedule.mon != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.monday, + schedule: "${formatTime(blockedServicesSchedule.mon!.start!)} - ${formatTime(blockedServicesSchedule.mon!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + if (blockedServicesSchedule.tue != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.tuesday, + schedule: "${formatTime(blockedServicesSchedule.tue!.start!)} - ${formatTime(blockedServicesSchedule.tue!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + if (blockedServicesSchedule.wed != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.wednesday, + schedule: "${formatTime(blockedServicesSchedule.wed!.start!)} - ${formatTime(blockedServicesSchedule.wed!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + if (blockedServicesSchedule.thu != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.thursday, + schedule: "${formatTime(blockedServicesSchedule.thu!.start!)} - ${formatTime(blockedServicesSchedule.thu!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + if (blockedServicesSchedule.fri != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.friday, + schedule: "${formatTime(blockedServicesSchedule.fri!.start!)} - ${formatTime(blockedServicesSchedule.fri!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + if (blockedServicesSchedule.sat != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.saturday, + schedule: "${formatTime(blockedServicesSchedule.sat!.start!)} - ${formatTime(blockedServicesSchedule.sat!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + if (blockedServicesSchedule.sun != null) _ScheduleTile( + weekday: AppLocalizations.of(context)!.sunday, + schedule: "${formatTime(blockedServicesSchedule.sun!.start!)} - ${formatTime(blockedServicesSchedule.sun!.end!)}", + onEdit: () => {}, + onDelete: () => {} + ), + ], + ); + } +} + +class _ScheduleTile extends StatelessWidget { + final String weekday; + final String schedule; + final void Function() onEdit; + final void Function() onDelete; + + const _ScheduleTile({ + required this.weekday, + required this.schedule, + required this.onEdit, + required this.onDelete, + }); + + @override + Widget build(BuildContext context) { + return CustomListTile( + title: weekday, + subtitle: schedule, + trailing: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + onPressed: onEdit, + icon: const Icon(Icons.edit_rounded), + tooltip: AppLocalizations.of(context)!.edit, + ), + const SizedBox(width: 4), + IconButton( + onPressed: onDelete, + icon: const Icon(Icons.delete_rounded), + tooltip: AppLocalizations.of(context)!.delete, + ), + ], + ), + padding: const EdgeInsets.only( + left: 16, + top: 6, + right: 12, + bottom: 6 + ) + ); + } +} \ No newline at end of file diff --git a/lib/screens/clients/client/blocking_schedule_modal.dart b/lib/screens/clients/client/blocking_schedule_modal.dart new file mode 100644 index 0000000..f4e18ef --- /dev/null +++ b/lib/screens/clients/client/blocking_schedule_modal.dart @@ -0,0 +1,283 @@ +import 'package:flutter/material.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/models/clients.dart'; + +class BlockingScheduleModal extends StatefulWidget { + final void Function(BlockedServicesSchedule) onConfirm; + + const BlockingScheduleModal({ + super.key, + required this.onConfirm, + }); + + @override + State createState() => _BlockingScheduleModalState(); +} + +class _BlockingScheduleModalState extends State { + String? _timezone; + List _weekdays = []; + TimeOfDay? _from; + TimeOfDay? _to; + + bool _compareTimes(TimeOfDay startTime, TimeOfDay endTime) { + bool result = false; + int startTimeInt = (startTime.hour * 60 + startTime.minute) * 60; + int endTimeInt = (endTime.hour * 60 + endTime.minute) * 60; + + if (endTimeInt > startTimeInt) { + result = true; + } else { + result = false; + } + return result; + } + + bool _validate() { + return _timezone != null && + _weekdays.isNotEmpty && + _from != null && + _to != null && + _compareTimes(_from!, _to!); + } + + int _timeOfDayToInt(TimeOfDay timeOfDay) { + return Duration( + days: 0, + hours: timeOfDay.hour, + minutes: timeOfDay.minute, + seconds: 0 + ).inMinutes; + } + + @override + void initState() { + tz.initializeTimeZones(); + _timezone = tz.local.name; + super.initState(); + } + + @override + Widget build(BuildContext context) { + void onSelectWeekday(bool newStatus, String day) { + if (newStatus == true && !_weekdays.contains(day)) { + setState(() => _weekdays.add(day)); + } + else if (newStatus == false) { + setState(() => _weekdays = _weekdays.where((e) => e != day).toList()); + } + } + + void onConfirm() { + widget.onConfirm( + BlockedServicesSchedule( + timeZone: _timezone, + mon: _weekdays.contains("mon") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + tue: _weekdays.contains("tue") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + wed: _weekdays.contains("wed") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + thu: _weekdays.contains("thu") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + fri: _weekdays.contains("fri") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + sat: _weekdays.contains("sat") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + sun: _weekdays.contains("sun") ? BlockedServicesScheduleDay(start: _timeOfDayToInt(_from!), end: _timeOfDayToInt(_to!)) : null, + ) + ); + } + + final valid = _validate(); + final validTimes = _from != null && _to != null + ? _compareTimes(_from!, _to!) + : null; + + return Dialog( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.schedule_rounded, + size: 24, + color: Theme.of(context).listTileTheme.iconColor + ), + const SizedBox(height: 16), + Text( + AppLocalizations.of(context)!.newSchedule, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 24, + ), + ), + const SizedBox(height: 30), + LayoutBuilder( + builder: (context, constraints) => DropdownButtonFormField( + items: tz.timeZoneDatabase.locations.keys.map((item) => DropdownMenuItem( + value: item, + child: SizedBox( + width: constraints.maxWidth-48, + child: Text( + item, + overflow: TextOverflow.ellipsis, + ), + ), + )).toList(), + value: _timezone, + onChanged: (v) => setState(() => _timezone = v), + decoration: InputDecoration( + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + label: Text(AppLocalizations.of(context)!.timezone) + ), + borderRadius: BorderRadius.circular(20), + ), + ), + const SizedBox(height: 16), + SizedBox( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + FilterChip( + label: Text(AppLocalizations.of(context)!.monday), + selected: _weekdays.contains("mon"), + onSelected: (value) => onSelectWeekday(value, "mon") + ), + const SizedBox(width: 8), + FilterChip( + label: Text(AppLocalizations.of(context)!.tuesday), + selected: _weekdays.contains("tue"), + onSelected: (value) => onSelectWeekday(value, "tue") + ), + const SizedBox(width: 8), + FilterChip( + label: Text(AppLocalizations.of(context)!.wednesday), + selected: _weekdays.contains("wed"), + onSelected: (value) => onSelectWeekday(value, "wed") + ), + const SizedBox(width: 8), + FilterChip( + label: Text(AppLocalizations.of(context)!.thursday), + selected: _weekdays.contains("thu"), + onSelected: (value) => onSelectWeekday(value, "thu") + ), + const SizedBox(width: 8), + FilterChip( + label: Text(AppLocalizations.of(context)!.friday), + selected: _weekdays.contains("fri"), + onSelected: (value) => onSelectWeekday(value, "fri") + ), + const SizedBox(width: 8), + FilterChip( + label: Text(AppLocalizations.of(context)!.saturday), + selected: _weekdays.contains("sat"), + onSelected: (value) => onSelectWeekday(value, "sat") + ), + const SizedBox(width: 8), + FilterChip( + label: Text(AppLocalizations.of(context)!.sunday), + selected: _weekdays.contains("sun"), + onSelected: (value) => onSelectWeekday(value, "sun") + ), + ], + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + final selected = await showTimePicker( + context: context, + initialTime: _from ?? const TimeOfDay(hour: 0, minute: 0), + helpText: AppLocalizations.of(context)!.selectStartTime, + confirmText: AppLocalizations.of(context)!.confirm, + ); + setState(() => _from = selected); + }, + child: Text( + AppLocalizations.of(context)!.from( + _from != null ? "${_from!.hour.toString().padLeft(2, '0')}:${_from!.minute.toString().padLeft(2, '0')}" : "--:--" + ) + ) + ), + ElevatedButton( + onPressed: () async { + final selected = await showTimePicker( + context: context, + initialTime: _to ?? const TimeOfDay(hour: 23, minute: 59), + helpText: AppLocalizations.of(context)!.selectEndTime, + confirmText: AppLocalizations.of(context)!.confirm + ); + setState(() => _to = selected); + }, + child: Text( + AppLocalizations.of(context)!.to( + _to != null ? "${_to!.hour.toString().padLeft(2, '0')}:${_to!.minute.toString().padLeft(2, '0')}" : "--:--" + ) + ) + ), + ], + ), + if (validTimes == false) Padding( + padding: const EdgeInsets.only(top: 16), + child: Card( + color: const Color.fromARGB(255, 255, 182, 175), + elevation: 0, + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Icon( + Icons.error_rounded, + color: Theme.of(context).colorScheme.onSurfaceVariant + ), + const SizedBox(width: 16), + Expanded( + child: Text( + AppLocalizations.of(context)!.startTimeBeforeEndTime, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface + ), + ), + ), + ], + ), + ), + ) + ) + ], + ), + ), + ), + const SizedBox(height: 24), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(AppLocalizations.of(context)!.close) + ), + const SizedBox(width: 8), + TextButton( + onPressed: valid ? () => onConfirm() : null, + child: Text(AppLocalizations.of(context)!.confirm) + ), + ], + ) + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/clients/client/client_form.dart b/lib/screens/clients/client/client_form.dart index e127466..2d22dd7 100644 --- a/lib/screens/clients/client/client_form.dart +++ b/lib/screens/clients/client/client_form.dart @@ -8,6 +8,7 @@ import 'package:adguard_home_manager/screens/clients/client/identifiers_section. import 'package:adguard_home_manager/screens/clients/client/settings_tile.dart'; import 'package:adguard_home_manager/screens/clients/client/tags_section.dart'; import 'package:adguard_home_manager/screens/clients/client/upstream_servers_section.dart'; +import 'package:adguard_home_manager/screens/clients/client/blocking_schedule.dart'; import 'package:adguard_home_manager/widgets/custom_switch_list_tile.dart'; import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; @@ -52,6 +53,8 @@ class ClientForm extends StatelessWidget { final TextEditingController dnsCacheField; final String? dnsCacheError; final void Function(String?) updateDnsCacheError; + final BlockedServicesSchedule blockedServicesSchedule; + final void Function(BlockedServicesSchedule) setBlockedServicesSchedule; const ClientForm({ super.key, @@ -90,6 +93,8 @@ class ClientForm extends StatelessWidget { required this.dnsCacheField, required this.dnsCacheError, required this.updateDnsCacheError, + required this.blockedServicesSchedule, + required this.setBlockedServicesSchedule, }); @override @@ -282,6 +287,11 @@ class ClientForm extends StatelessWidget { keyboardType: TextInputType.number, ), ), + BlockingSchedule( + blockedServicesSchedule: blockedServicesSchedule, + setBlockedServicesSchedule: setBlockedServicesSchedule, + ), + const SizedBox(height: 16), ], ); } diff --git a/lib/screens/clients/client/client_screen.dart b/lib/screens/clients/client/client_screen.dart index f41de69..225b5eb 100644 --- a/lib/screens/clients/client/client_screen.dart +++ b/lib/screens/clients/client/client_screen.dart @@ -81,6 +81,8 @@ class _ClientScreenState extends State { bool _enableDnsCache = false; final _dnsCacheField = TextEditingController(); String? _dnsCacheError; + + BlockedServicesSchedule _blockedServicesSchedule = BlockedServicesSchedule(); // VALIDATIONS bool _nameValid = true; @@ -140,6 +142,9 @@ class _ClientScreenState extends State { _dnsCacheField.text = widget.client!.upstreamsCacheSize != null ? widget.client!.upstreamsCacheSize.toString() : ""; + if (widget.client!.blockedServicesSchedule != null) { + _blockedServicesSchedule = widget.client!.blockedServicesSchedule!; + } } super.initState(); } @@ -166,7 +171,8 @@ class _ClientScreenState extends State { upstreamsCacheEnabled: _enableDnsCache, upstreamsCacheSize: _dnsCacheField.text != "" ? int.parse(_dnsCacheField.text) - : null + : null, + blockedServicesSchedule: _blockedServicesSchedule ); widget.onConfirm(client); } @@ -268,7 +274,9 @@ class _ClientScreenState extends State { updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v), dnsCacheField: _dnsCacheField, dnsCacheError: _dnsCacheError, - updateDnsCacheError: (v) => setState(() => _dnsCacheError = v) + updateDnsCacheError: (v) => setState(() => _dnsCacheError = v), + blockedServicesSchedule: _blockedServicesSchedule, + setBlockedServicesSchedule: (v) => setState(() => _blockedServicesSchedule = v), ), ], ), @@ -353,7 +361,9 @@ class _ClientScreenState extends State { updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v), dnsCacheField: _dnsCacheField, dnsCacheError: _dnsCacheError, - updateDnsCacheError: (v) => setState(() => _dnsCacheError = v) + updateDnsCacheError: (v) => setState(() => _dnsCacheError = v), + blockedServicesSchedule: _blockedServicesSchedule, + setBlockedServicesSchedule: (v) => setState(() => _blockedServicesSchedule = v), ), ], ), diff --git a/pubspec.lock b/pubspec.lock index cf61106..1cb21ba 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -659,6 +659,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + timezone: + dependency: "direct main" + description: + name: timezone + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" + source: hosted + version: "0.9.2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e4dbaa8..a488bef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -76,6 +76,7 @@ dependencies: pie_chart: ^5.4.0 segmented_button_slide: ^1.0.4 http: ^1.1.2 + timezone: ^0.9.2 dev_dependencies: flutter_test: