mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-20 13:59:12 +00:00
Created block time schedule settings
This commit is contained in:
parent
5b2523158b
commit
d73ad93180
10 changed files with 560 additions and 8 deletions
|
@ -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)
|
- [flutter reorderable list](https://pub.dev/packages/flutter_reorderable_list)
|
||||||
- [pie chart](https://pub.dev/packages/pie_chart)
|
- [pie chart](https://pub.dev/packages/pie_chart)
|
||||||
- [segmented button slide](https://pub.dev/packages/segmented_button_slide)
|
- [segmented button slide](https://pub.dev/packages/segmented_button_slide)
|
||||||
|
- [timezone](https://pub.dev/packages/timezone)
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
|
@ -719,7 +719,7 @@
|
||||||
"unblockClient": "Unblock client",
|
"unblockClient": "Unblock client",
|
||||||
"blockingClient": "Blocking client...",
|
"blockingClient": "Blocking client...",
|
||||||
"unblockingClient": "Unblocking client...",
|
"unblockingClient": "Unblocking client...",
|
||||||
"upstreamDnsCacheConfiguration": "Configuración de la caché DNS upstream",
|
"upstreamDnsCacheConfiguration": "DNS upstream cache configuration",
|
||||||
"enableDnsCachingClient": "Enable DNS caching for this client",
|
"enableDnsCachingClient": "Enable DNS caching for this client",
|
||||||
"dnsCacheSize": "DNS cache size",
|
"dnsCacheSize": "DNS cache size",
|
||||||
"nameInvalid": "Name is required",
|
"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.",
|
"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",
|
"logsSettingsDescription": "Configure query logs",
|
||||||
"ignoredDomains": "Ignored domains",
|
"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."
|
||||||
}
|
}
|
|
@ -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.",
|
"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",
|
"logsSettingsDescription": "Configura los registros de peticiones",
|
||||||
"ignoredDomains": "Dominios ignorados",
|
"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."
|
||||||
}
|
}
|
|
@ -91,6 +91,7 @@ class Client {
|
||||||
final bool? ignoreStatistics;
|
final bool? ignoreStatistics;
|
||||||
final bool? upstreamsCacheEnabled;
|
final bool? upstreamsCacheEnabled;
|
||||||
final int? upstreamsCacheSize;
|
final int? upstreamsCacheSize;
|
||||||
|
final BlockedServicesSchedule? blockedServicesSchedule;
|
||||||
|
|
||||||
Client({
|
Client({
|
||||||
required this.name,
|
required this.name,
|
||||||
|
@ -108,6 +109,7 @@ class Client {
|
||||||
required this.ignoreStatistics,
|
required this.ignoreStatistics,
|
||||||
required this.upstreamsCacheEnabled,
|
required this.upstreamsCacheEnabled,
|
||||||
required this.upstreamsCacheSize,
|
required this.upstreamsCacheSize,
|
||||||
|
required this.blockedServicesSchedule,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Client.fromJson(Map<String, dynamic> json) => Client(
|
factory Client.fromJson(Map<String, dynamic> json) => Client(
|
||||||
|
@ -127,7 +129,8 @@ class Client {
|
||||||
ignoreQuerylog: json["ignore_querylog"],
|
ignoreQuerylog: json["ignore_querylog"],
|
||||||
ignoreStatistics: json["ignore_statistics"],
|
ignoreStatistics: json["ignore_statistics"],
|
||||||
upstreamsCacheEnabled: json["upstreams_cache_enabled"],
|
upstreamsCacheEnabled: json["upstreams_cache_enabled"],
|
||||||
upstreamsCacheSize: json["upstreams_cache_size"]
|
upstreamsCacheSize: json["upstreams_cache_size"],
|
||||||
|
blockedServicesSchedule: BlockedServicesSchedule.fromJson(json["blocked_services_schedule"])
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
|
@ -145,6 +148,67 @@ class Client {
|
||||||
"ignore_querylog": ignoreQuerylog,
|
"ignore_querylog": ignoreQuerylog,
|
||||||
"ignore_statistics": ignoreStatistics,
|
"ignore_statistics": ignoreStatistics,
|
||||||
"upstreams_cache_enabled": upstreamsCacheEnabled,
|
"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<String, dynamic> 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<String, dynamic> 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<String, dynamic> json) => BlockedServicesScheduleDay(
|
||||||
|
start: json["start"],
|
||||||
|
end: json["end"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"start": start,
|
||||||
|
"end": end,
|
||||||
};
|
};
|
||||||
}
|
}
|
143
lib/screens/clients/client/blocking_schedule.dart
Normal file
143
lib/screens/clients/client/blocking_schedule.dart
Normal file
|
@ -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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
283
lib/screens/clients/client/blocking_schedule_modal.dart
Normal file
283
lib/screens/clients/client/blocking_schedule_modal.dart
Normal file
|
@ -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<BlockingScheduleModal> createState() => _BlockingScheduleModalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BlockingScheduleModalState extends State<BlockingScheduleModal> {
|
||||||
|
String? _timezone;
|
||||||
|
List<String> _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)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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/settings_tile.dart';
|
||||||
import 'package:adguard_home_manager/screens/clients/client/tags_section.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/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_switch_list_tile.dart';
|
||||||
import 'package:adguard_home_manager/widgets/custom_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 TextEditingController dnsCacheField;
|
||||||
final String? dnsCacheError;
|
final String? dnsCacheError;
|
||||||
final void Function(String?) updateDnsCacheError;
|
final void Function(String?) updateDnsCacheError;
|
||||||
|
final BlockedServicesSchedule blockedServicesSchedule;
|
||||||
|
final void Function(BlockedServicesSchedule) setBlockedServicesSchedule;
|
||||||
|
|
||||||
const ClientForm({
|
const ClientForm({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -90,6 +93,8 @@ class ClientForm extends StatelessWidget {
|
||||||
required this.dnsCacheField,
|
required this.dnsCacheField,
|
||||||
required this.dnsCacheError,
|
required this.dnsCacheError,
|
||||||
required this.updateDnsCacheError,
|
required this.updateDnsCacheError,
|
||||||
|
required this.blockedServicesSchedule,
|
||||||
|
required this.setBlockedServicesSchedule,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -282,6 +287,11 @@ class ClientForm extends StatelessWidget {
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
BlockingSchedule(
|
||||||
|
blockedServicesSchedule: blockedServicesSchedule,
|
||||||
|
setBlockedServicesSchedule: setBlockedServicesSchedule,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
final _dnsCacheField = TextEditingController();
|
final _dnsCacheField = TextEditingController();
|
||||||
String? _dnsCacheError;
|
String? _dnsCacheError;
|
||||||
|
|
||||||
|
BlockedServicesSchedule _blockedServicesSchedule = BlockedServicesSchedule();
|
||||||
|
|
||||||
// VALIDATIONS
|
// VALIDATIONS
|
||||||
bool _nameValid = true;
|
bool _nameValid = true;
|
||||||
bool _identifiersValid = true;
|
bool _identifiersValid = true;
|
||||||
|
@ -140,6 +142,9 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
_dnsCacheField.text = widget.client!.upstreamsCacheSize != null
|
_dnsCacheField.text = widget.client!.upstreamsCacheSize != null
|
||||||
? widget.client!.upstreamsCacheSize.toString()
|
? widget.client!.upstreamsCacheSize.toString()
|
||||||
: "";
|
: "";
|
||||||
|
if (widget.client!.blockedServicesSchedule != null) {
|
||||||
|
_blockedServicesSchedule = widget.client!.blockedServicesSchedule!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
@ -166,7 +171,8 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
upstreamsCacheEnabled: _enableDnsCache,
|
upstreamsCacheEnabled: _enableDnsCache,
|
||||||
upstreamsCacheSize: _dnsCacheField.text != ""
|
upstreamsCacheSize: _dnsCacheField.text != ""
|
||||||
? int.parse(_dnsCacheField.text)
|
? int.parse(_dnsCacheField.text)
|
||||||
: null
|
: null,
|
||||||
|
blockedServicesSchedule: _blockedServicesSchedule
|
||||||
);
|
);
|
||||||
widget.onConfirm(client);
|
widget.onConfirm(client);
|
||||||
}
|
}
|
||||||
|
@ -268,7 +274,9 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v),
|
updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v),
|
||||||
dnsCacheField: _dnsCacheField,
|
dnsCacheField: _dnsCacheField,
|
||||||
dnsCacheError: _dnsCacheError,
|
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<ClientScreen> {
|
||||||
updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v),
|
updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v),
|
||||||
dnsCacheField: _dnsCacheField,
|
dnsCacheField: _dnsCacheField,
|
||||||
dnsCacheError: _dnsCacheError,
|
dnsCacheError: _dnsCacheError,
|
||||||
updateDnsCacheError: (v) => setState(() => _dnsCacheError = v)
|
updateDnsCacheError: (v) => setState(() => _dnsCacheError = v),
|
||||||
|
blockedServicesSchedule: _blockedServicesSchedule,
|
||||||
|
setBlockedServicesSchedule: (v) => setState(() => _blockedServicesSchedule = v),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -659,6 +659,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
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:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -76,6 +76,7 @@ dependencies:
|
||||||
pie_chart: ^5.4.0
|
pie_chart: ^5.4.0
|
||||||
segmented_button_slide: ^1.0.4
|
segmented_button_slide: ^1.0.4
|
||||||
http: ^1.1.2
|
http: ^1.1.2
|
||||||
|
timezone: ^0.9.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Add table
Reference in a new issue