Added update interval lists modal

This commit is contained in:
Juan Gilsanz Polo 2022-10-10 14:57:42 +02:00
parent 1e07b4e799
commit 78561a68bf
8 changed files with 463 additions and 10 deletions

View file

@ -311,5 +311,15 @@
"enablingFiltering": "Enabling filtering...",
"disablingFiltering": "Disabling filtering...",
"filteringStatusUpdated": "Filtering status updated successfully",
"filteringStatusNotUpdated": "Filtering status couldn't be updated"
"filteringStatusNotUpdated": "Filtering status couldn't be updated",
"updateFrequency": "Update frequency",
"never": "Never",
"hour1": "1 hour",
"hours12": "12 hours",
"hours24": "24 hours",
"days3": "3 days",
"days7": "7 days",
"changingUpdateFrequency": "Changing...",
"updateFrequencyChanged": "Update frequency changed successfully",
"updateFrequencyNotChanged": "Updare frecuency couldn't be changed"
}

View file

@ -311,5 +311,15 @@
"enablingFiltering": "Habilitando filtrado...",
"disablingFiltering": "Deshabilitando filtrado...",
"filteringStatusUpdated": "Estado de filtrado actualizado correctamente",
"filteringStatusNotUpdated": "El estado de filtrado no pudo ser actualizado"
"filteringStatusNotUpdated": "El estado de filtrado no pudo ser actualizado",
"updateFrequency": "Frecuencia de actualización",
"never": "Nunca",
"hour1": "1 hora",
"hours12": "12 horas",
"hours24": "24 horas",
"days3": "3 días",
"days7": "7 días",
"changingUpdateFrequency": "Cambiando...",
"updateFrequencyChanged": "Frecuencia de actualización cambiada correctamente",
"updateFrequencyNotChanged": "La frecuencia de actualización no pudo ser cambiada"
}

View file

@ -18,8 +18,8 @@ class FilteringData {
final List<Filter> filters;
final List<Filter> whitelistFilters;
List<String> userRules;
final int interval;
final bool enabled;
int interval;
bool enabled;
FilteringData({
required this.filters,

View file

@ -121,6 +121,12 @@ class ServersProvider with ChangeNotifier {
void setFilteringProtectionStatus(bool status) {
_serverStatus.data!.filteringEnabled = status;
_filtering.data!.enabled = status;
notifyListeners();
}
void setFiltersUpdateFrequency(int frequency) {
_filtering.data!.interval = frequency;
notifyListeners();
}

View file

@ -7,6 +7,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/filters/filters_list.dart';
import 'package:adguard_home_manager/screens/filters/check_host_modal.dart';
import 'package:adguard_home_manager/screens/filters/custom_rules_list.dart';
import 'package:adguard_home_manager/screens/filters/update_interval_lists_modal.dart';
import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
@ -179,6 +180,37 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
}
}
void setUpdateFrequency(int value) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.changingUpdateFrequency);
final result = await requestChangeUpdateFrequency(server: serversProvider.selectedServer!, data: {
"enabled": serversProvider.filtering.data!.enabled,
"interval": value
});
processModal.close();
if (result['result'] == 'success') {
serversProvider.setFiltersUpdateFrequency(value);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.updateFrequencyChanged,
color: Colors.green
);
}
else {
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.updateFrequencyNotChanged,
color: Colors.red
);
}
}
return DefaultTabController(
length: 3,
child: NestedScrollView(
@ -195,9 +227,9 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
floating: true,
forceElevated: innerBoxIsScrolled,
actions: [
IconButton(
if (serversProvider.filtering.loadStatus == 1) IconButton(
onPressed: enableDisableFiltering,
tooltip: serversProvider.serverStatus.data!.filteringEnabled == true
tooltip: serversProvider.filtering.data!.enabled == true
? AppLocalizations.of(context)!.disableFiltering
: AppLocalizations.of(context)!.enableFiltering,
icon: Stack(
@ -214,11 +246,11 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
color: Colors.white
),
child: Icon(
serversProvider.serverStatus.data!.filteringEnabled == true
serversProvider.filtering.data!.enabled == true
? Icons.check_circle_rounded
: Icons.cancel,
size: 12,
color: serversProvider.serverStatus.data!.filteringEnabled == true
color: serversProvider.filtering.data!.enabled == true
? Colors.green
: Colors.red,
),
@ -229,13 +261,27 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
],
)
),
if (serversProvider.filtering.loadStatus == 1) IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => UpdateIntervalListsModal(
interval: serversProvider.filtering.data!.interval,
onChange: setUpdateFrequency
),
backgroundColor: Colors.transparent,
isScrollControlled: true
);
},
icon: const Icon(Icons.update_rounded)
),
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
onTap: fetchUpdateLists,
child: Row(
children: [
const Icon(Icons.update),
const Icon(Icons.sync_rounded),
const SizedBox(width: 10),
Text(AppLocalizations.of(context)!.updateLists)
],
@ -253,7 +299,7 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
),
]
),
const SizedBox(width: 5),
const SizedBox(width: 10),
],
bottom: TabBar(
controller: tabController,

View file

@ -0,0 +1,302 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/option_box.dart';
class UpdateIntervalListsModal extends StatefulWidget {
final int interval;
final void Function(int) onChange;
const UpdateIntervalListsModal({
Key? key,
required this.interval,
required this.onChange,
}) : super(key: key);
@override
State<UpdateIntervalListsModal> createState() => _UpdateIntervalListsModalState();
}
class _UpdateIntervalListsModalState extends State<UpdateIntervalListsModal> {
int? selectedOption;
void _updateRadioValue(value) {
setState(() {
selectedOption = value;
});
}
@override
void initState() {
selectedOption = widget.interval;
super.initState();
}
@override
Widget build(BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return Padding(
padding: mediaQueryData.viewInsets,
child: Container(
height: mediaQueryData.size.height > (Platform.isIOS ? 296 : 410)
? (Platform.isIOS ? 396 : 410)
: mediaQueryData.size.height-25,
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28)
),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Padding(
padding: EdgeInsets.only(top: 24),
child: Icon(
Icons.update_rounded,
size: 26,
),
),
Container(
padding: const EdgeInsets.all(24),
width: double.maxFinite,
child: Text(
AppLocalizations.of(context)!.updateFrequency,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 24,
),
),
),
SizedBox(
width: double.maxFinite,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: (mediaQueryData.size.width-70)/2,
margin: const EdgeInsets.only(
top: 10,
right: 5,
bottom: 5
),
child: OptionBox(
optionsValue: selectedOption,
itemValue: 0,
onTap: _updateRadioValue,
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedOption == 0
? Theme.of(context).primaryColor
: Theme.of(context).textTheme.bodyText1!.color
),
child: Text(AppLocalizations.of(context)!.never),
),
),
),
),
Container(
width: (mediaQueryData.size.width-70)/2,
margin: const EdgeInsets.only(
top: 10,
left: 5,
bottom: 5
),
child: OptionBox(
optionsValue: selectedOption,
itemValue: 1,
onTap: _updateRadioValue,
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedOption == 1
? Theme.of(context).primaryColor
: Theme.of(context).textTheme.bodyText1!.color
),
child: Text(AppLocalizations.of(context)!.hour1),
),
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: (mediaQueryData.size.width-70)/2,
margin: const EdgeInsets.only(
top: 5,
right: 5,
bottom: 5
),
child: OptionBox(
optionsValue: selectedOption,
itemValue: 12,
onTap: _updateRadioValue,
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedOption == 12
? Theme.of(context).primaryColor
: Theme.of(context).textTheme.bodyText1!.color
),
child: Text(AppLocalizations.of(context)!.hours12),
),
),
),
),
Container(
width: (mediaQueryData.size.width-70)/2,
margin: const EdgeInsets.only(
top: 5,
left: 5,
bottom: 5
),
child: OptionBox(
optionsValue: selectedOption,
itemValue: 24,
onTap: _updateRadioValue,
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedOption == 24
? Theme.of(context).primaryColor
: Theme.of(context).textTheme.bodyText1!.color
),
child: Text(AppLocalizations.of(context)!.hours24),
),
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: (mediaQueryData.size.width-70)/2,
margin: const EdgeInsets.only(
top: 5,
right: 5,
bottom: 10
),
child: OptionBox(
optionsValue: selectedOption,
itemValue: 72,
onTap: _updateRadioValue,
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedOption == 72
? Theme.of(context).primaryColor
: Theme.of(context).textTheme.bodyText1!.color
),
child: Text(AppLocalizations.of(context)!.days3),
),
),
),
),
Container(
width: (mediaQueryData.size.width-70)/2,
margin: const EdgeInsets.only(
top: 5,
left: 5,
bottom: 10
),
child: OptionBox(
optionsValue: selectedOption,
itemValue: 168,
onTap: _updateRadioValue,
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 250),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedOption == 168
? Theme.of(context).primaryColor
: Theme.of(context).textTheme.bodyText1!.color
),
child: Text(AppLocalizations.of(context)!.days7),
),
),
),
),
],
),
],
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel),
),
const SizedBox(width: 20),
TextButton(
onPressed: selectedOption != null
? () {
Navigator.pop(context);
widget.onChange(selectedOption!);
}
: null,
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(
Theme.of(context).primaryColor.withOpacity(0.1)
),
foregroundColor: MaterialStateProperty.all(
selectedOption != null
? Theme.of(context).primaryColor
: Colors.grey,
),
),
child: Text(AppLocalizations.of(context)!.confirm),
),
],
),
),
],
)
],
),
),
),
);
}
}

View file

@ -1006,3 +1006,37 @@ Future checkHostFiltered({
return result;
}
}
Future requestChangeUpdateFrequency({
required Server server,
required Map<String, dynamic> data,
}) async {
final result = await apiRequest(
urlPath: '/filtering/config',
method: 'post',
server: server,
body: data,
type: 'change_update_frequency'
);
if (result['hasResponse'] == true) {
if (result['statusCode'] == 200) {
return {'result': 'success'};
}
else {
return {
'result': 'error',
'log': AppLog(
type: 'change_update_frequency',
dateTime: DateTime.now(),
message: 'error_code_not_expected',
statusCode: result['statusCode'],
resBody: result['body'],
)
};
}
}
else {
return result;
}
}

View file

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
class OptionBox extends StatelessWidget {
final Widget child;
final dynamic optionsValue;
final dynamic itemValue;
final void Function(dynamic) onTap;
const OptionBox({
Key? key,
required this.child,
required this.optionsValue,
required this.itemValue,
required this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(50),
child: InkWell(
borderRadius: BorderRadius.circular(50),
onTap: () => onTap(itemValue),
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(
color: optionsValue == itemValue
? Theme.of(context).primaryColor
: Colors.grey
),
color: optionsValue == itemValue
? Theme.of(context).primaryColor.withOpacity(0.1)
: Colors.transparent,
),
child: child,
),
),
);
}
}