Added blocked services modal

This commit is contained in:
Juan Gilsanz Polo 2022-10-10 15:57:47 +02:00
parent 504d3cca56
commit d8a791cadb
7 changed files with 317 additions and 19 deletions

View file

@ -321,5 +321,8 @@
"days7": "7 days", "days7": "7 days",
"changingUpdateFrequency": "Changing...", "changingUpdateFrequency": "Changing...",
"updateFrequencyChanged": "Update frequency changed successfully", "updateFrequencyChanged": "Update frequency changed successfully",
"updateFrequencyNotChanged": "Updare frecuency couldn't be changed" "updateFrequencyNotChanged": "Updare frecuency couldn't be changed",
"updating": "Updating values...",
"blockedServicesUpdated": "Blocked services updated successfully",
"blockedServicesNotUpdated": "Blocked services couldn't be updated"
} }

View file

@ -321,5 +321,8 @@
"days7": "7 días", "days7": "7 días",
"changingUpdateFrequency": "Cambiando...", "changingUpdateFrequency": "Cambiando...",
"updateFrequencyChanged": "Frecuencia de actualización cambiada correctamente", "updateFrequencyChanged": "Frecuencia de actualización cambiada correctamente",
"updateFrequencyNotChanged": "La frecuencia de actualización no pudo ser cambiada" "updateFrequencyNotChanged": "La frecuencia de actualización no pudo ser cambiada",
"updating": "Actualizando valores...",
"blockedServicesUpdated": "Servicios bloqueados actualizados correctamente",
"blockedServicesNotUpdated": "No se pudieron actualizar los servicios bloqueados"
} }

View file

@ -18,6 +18,7 @@ class FilteringData {
final List<Filter> filters; final List<Filter> filters;
final List<Filter> whitelistFilters; final List<Filter> whitelistFilters;
List<String> userRules; List<String> userRules;
List<String> blockedServices;
int interval; int interval;
bool enabled; bool enabled;
@ -25,6 +26,7 @@ class FilteringData {
required this.filters, required this.filters,
required this.whitelistFilters, required this.whitelistFilters,
required this.userRules, required this.userRules,
required this.blockedServices,
required this.interval, required this.interval,
required this.enabled, required this.enabled,
}); });
@ -33,6 +35,7 @@ class FilteringData {
filters: json["filters"] != null ? List<Filter>.from(json["filters"].map((x) => Filter.fromJson(x))) : [], filters: json["filters"] != null ? List<Filter>.from(json["filters"].map((x) => Filter.fromJson(x))) : [],
whitelistFilters: json["whitelist_filters"] != null ? List<Filter>.from(json["whitelist_filters"].map((x) => Filter.fromJson(x))) : [], whitelistFilters: json["whitelist_filters"] != null ? List<Filter>.from(json["whitelist_filters"].map((x) => Filter.fromJson(x))) : [],
userRules: json["user_rules"] != null ? List<String>.from(json["user_rules"].map((x) => x)).where((i) => i != '').toList() : [], userRules: json["user_rules"] != null ? List<String>.from(json["user_rules"].map((x) => x)).where((i) => i != '').toList() : [],
blockedServices: json["blocked_services"] != null ? List<String>.from(json["blocked_services"].map((x) => x)).where((i) => i != '').toList() : [],
interval: json["interval"], interval: json["interval"],
enabled: json["enabled"], enabled: json["enabled"],
); );
@ -41,6 +44,7 @@ class FilteringData {
"filters": List<dynamic>.from(filters.map((x) => x.toJson())), "filters": List<dynamic>.from(filters.map((x) => x.toJson())),
"whitelist_filters": List<dynamic>.from(whitelistFilters.map((x) => x.toJson())), "whitelist_filters": List<dynamic>.from(whitelistFilters.map((x) => x.toJson())),
"user_rules": List<dynamic>.from(userRules.map((x) => x)), "user_rules": List<dynamic>.from(userRules.map((x) => x)),
"blocked_services": List<dynamic>.from(blockedServices.map((x) => x)),
"interval": interval, "interval": interval,
"enabled": enabled, "enabled": enabled,
}; };

View file

@ -129,6 +129,11 @@ class ServersProvider with ChangeNotifier {
_filtering.data!.interval = frequency; _filtering.data!.interval = frequency;
notifyListeners(); notifyListeners();
} }
void setBlockedServices(List<String> blockedServices) {
_filtering.data!.blockedServices = blockedServices;
notifyListeners();
}
Future<bool> createServer(Server server) async { Future<bool> createServer(Server server) async {
final saved = await saveServerIntoDb(server); final saved = await saveServerIntoDb(server);

View file

@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/constants/services.dart';
class BlockedServicesModal extends StatefulWidget {
final ScrollController scrollController;
final List<String> blockedServices;
final void Function(List<String>) onApply;
const BlockedServicesModal({
Key? key,
required this.scrollController,
required this.blockedServices,
required this.onApply,
}) : super(key: key);
@override
State<BlockedServicesModal> createState() => _BlockedServicesModalState();
}
class _BlockedServicesModalState extends State<BlockedServicesModal> {
List<Map<String, dynamic>> values = [];
List<String> convertFinalValues() {
return List<String>.from(
values.map((v) => v['checked'] == true ? v['id'] : '').where((v) => v != '')
);
}
void updateValues(bool value, Map<String, dynamic> item) {
setState(() => values = values.map((v) {
if (v['id'] == item['id']) {
return {
'id': v['id'],
'checked': value
};
}
else {
return v;
}
}).toList());
}
@override
void initState() {
for (var service in services) {
values.add({
"id": service['id'],
"checked": widget.blockedServices.contains(service['id'])
});
}
super.initState();
}
@override
Widget build(BuildContext context) {
final allSelected = convertFinalValues().length == services.length ? true : false;
return Container(
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28)
)
),
child: Column(
children: [
Column(
children: [
const Padding(
padding: EdgeInsets.only(top: 28),
child: Icon(
Icons.block,
size: 26,
),
),
const SizedBox(height: 20),
Text(
AppLocalizations.of(context)!.blockedServices,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 24
),
)
],
),
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
controller: widget.scrollController,
itemCount: services.length,
itemBuilder: (context, index) => Material(
color: Colors.transparent,
child: InkWell(
onTap: () => updateValues(!(values[index]['checked'] as bool), services[index]),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
services[index]['label']!,
style: const TextStyle(
fontSize: 16
),
),
Checkbox(
value: values[index]['checked'],
onChanged: (value) => updateValues(value!, services[index]),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)
),
)
],
),
),
),
)
),
),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () => {
allSelected == true
? setState(() => values = values.map((v) => {
'id': v['id'],
'checked': false
}).toList())
: setState(() => values = values.map((v) => {
'id': v['id'],
'checked': true
}).toList())
},
child: Text(
allSelected == true
? AppLocalizations.of(context)!.unselectAll
: AppLocalizations.of(context)!.selectAll
)
),
Row(
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
),
const SizedBox(width: 20),
TextButton(
onPressed: () {
Navigator.pop(context);
widget.onApply(convertFinalValues());
},
child: Text(AppLocalizations.of(context)!.apply)
),
],
)
],
),
)
],
),
);
}
}

View file

@ -2,10 +2,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:bottom_sheet/bottom_sheet.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 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/filters_list.dart';
import 'package:adguard_home_manager/screens/filters/check_host_modal.dart'; import 'package:adguard_home_manager/screens/filters/check_host_modal.dart';
import 'package:adguard_home_manager/screens/filters/blocked_services_modal.dart';
import 'package:adguard_home_manager/screens/filters/custom_rules_list.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/screens/filters/update_interval_lists_modal.dart';
@ -211,6 +213,54 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
} }
} }
void updateBlockedServices(List<String> values) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.updating);
final result = await setBlockedServices(server: serversProvider.selectedServer!, data: values);
processModal.close();
if (result['result'] == 'success') {
serversProvider.setBlockedServices(values);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.blockedServicesUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.blockedServicesNotUpdated,
color: Colors.red
);
}
}
void openBlockedServicesModal() {
Future.delayed(const Duration(seconds: 0), () {
showFlexibleBottomSheet(
minHeight: 0.6,
initHeight: 0.6,
maxHeight: 0.95,
isCollapsible: true,
duration: const Duration(milliseconds: 250),
anchors: [0.95],
context: context,
builder: (ctx, controller, offset) => BlockedServicesModal(
scrollController: controller,
blockedServices: serversProvider.filtering.data!.blockedServices,
onApply: updateBlockedServices,
),
bottomSheetColor: Colors.transparent
);
});
}
return DefaultTabController( return DefaultTabController(
length: 3, length: 3,
child: NestedScrollView( child: NestedScrollView(
@ -226,8 +276,8 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
pinned: true, pinned: true,
floating: true, floating: true,
forceElevated: innerBoxIsScrolled, forceElevated: innerBoxIsScrolled,
actions: [ actions: serversProvider.filtering.loadStatus == 1 ? [
if (serversProvider.filtering.loadStatus == 1) IconButton( IconButton(
onPressed: enableDisableFiltering, onPressed: enableDisableFiltering,
tooltip: serversProvider.filtering.data!.enabled == true tooltip: serversProvider.filtering.data!.enabled == true
? AppLocalizations.of(context)!.disableFiltering ? AppLocalizations.of(context)!.disableFiltering
@ -261,7 +311,7 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
], ],
) )
), ),
if (serversProvider.filtering.loadStatus == 1) IconButton( IconButton(
onPressed: () { onPressed: () {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -287,6 +337,16 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
], ],
) )
), ),
PopupMenuItem(
onTap: openBlockedServicesModal,
child: Row(
children: [
const Icon(Icons.block),
const SizedBox(width: 10),
Text(AppLocalizations.of(context)!.blockedServices)
],
)
),
PopupMenuItem( PopupMenuItem(
onTap: showCheckHostModal, onTap: showCheckHostModal,
child: Row( child: Row(
@ -300,7 +360,7 @@ class _FiltersWidgetState extends State<FiltersWidget> with TickerProviderStateM
] ]
), ),
const SizedBox(width: 5), const SizedBox(width: 5),
], ] : [],
bottom: TabBar( bottom: TabBar(
controller: tabController, controller: tabController,
tabs: [ tabs: [

View file

@ -19,7 +19,7 @@ Future<Map<String, dynamic>> apiRequest({
required Server server, required Server server,
required String method, required String method,
required String urlPath, required String urlPath,
Map<String, dynamic>? body, dynamic body,
required String type, required String type,
}) async { }) async {
try { try {
@ -710,18 +710,29 @@ Future postDeleteClient({
Future getFiltering({ Future getFiltering({
required Server server, required Server server,
}) async { }) async {
final result = await apiRequest( final result = await Future.wait([
urlPath: '/filtering/status', apiRequest(
method: 'get', urlPath: '/filtering/status',
server: server, method: 'get',
type: 'get_filtering_status' server: server,
); type: 'get_filtering_status'
),
apiRequest(
urlPath: '/blocked_services/list',
method: 'get',
server: server,
type: 'get_filtering_status'
),
]);
if (result['hasResponse'] == true) { if (result[0]['hasResponse'] == true && result[0]['hasResponse'] == true) {
if (result['statusCode'] == 200) { if (result[0]['statusCode'] == 200 && result[0]['statusCode'] == 200) {
return { return {
'result': 'success', 'result': 'success',
'data': FilteringData.fromJson(jsonDecode(result['body'])) 'data': FilteringData.fromJson({
...jsonDecode(result[0]['body']),
"blocked_services": jsonDecode(result[1]['body']),
})
}; };
} }
else { else {
@ -731,14 +742,23 @@ Future getFiltering({
type: 'get_filtering_status', type: 'get_filtering_status',
dateTime: DateTime.now(), dateTime: DateTime.now(),
message: 'error_code_not_expected', message: 'error_code_not_expected',
statusCode: result['statusCode'].toString(), statusCode: result.map((res) => res['statusCode'] ?? 'null').toString(),
resBody: result['body'] resBody: result.map((res) => res['body'] ?? 'null').toString(),
) )
}; };
} }
} }
else { else {
return result; return {
'result': 'error',
'log': AppLog(
type: 'get_filtering_status',
dateTime: DateTime.now(),
message: 'no_response',
statusCode: result.map((res) => res['statusCode'] ?? 'null').toString(),
resBody: result.map((res) => res['body'] ?? 'null').toString(),
)
};
} }
} }
@ -1039,4 +1059,38 @@ Future requestChangeUpdateFrequency({
else { else {
return result; return result;
} }
}
Future setBlockedServices({
required Server server,
required List<String> data,
}) async {
final result = await apiRequest(
urlPath: '/blocked_services/set',
method: 'post',
server: server,
body: data,
type: 'update_blocked_services'
);
if (result['hasResponse'] == true) {
if (result['statusCode'] == 200) {
return {'result': 'success'};
}
else {
return {
'result': 'error',
'log': AppLog(
type: 'update_blocked_services',
dateTime: DateTime.now(),
message: 'error_code_not_expected',
statusCode: result['statusCode'],
resBody: result['body'],
)
};
}
}
else {
return result;
}
} }