diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 60b94cc..7354d9c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -715,5 +715,8 @@ "blockedResponseTtlDescription": "Specifies for how many seconds the clients should cache a filtered response", "invalidValue": "Invalid value", "noDataChart": "There's no data to display this chart.", - "noData": "No data" + "noData": "No data", + "unblockClient": "Unblock client", + "blockingClient": "Blocking client...", + "unblockingClient": "Unblocking client..." } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index a7a9fd6..78d518f 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -715,5 +715,8 @@ "blockedResponseTtlDescription": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada", "invalidValue": "Valor no válido", "noDataChart": "No hay datos para mostrar este gráfico.", - "noData": "No hay datos" + "noData": "No hay datos", + "unblockClient": "Desbloquear cliente", + "blockingClient": "Bloqueando cliente...", + "unblockingClient": "Desbloqueando cliente..." } \ No newline at end of file diff --git a/lib/models/menu_option.dart b/lib/models/menu_option.dart index b89f8d9..e898514 100644 --- a/lib/models/menu_option.dart +++ b/lib/models/menu_option.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class MenuOption { final IconData? icon; final String title; - final void Function(dynamic) action; + final void Function() action; final bool? disabled; const MenuOption({ diff --git a/lib/providers/clients_provider.dart b/lib/providers/clients_provider.dart index bfcf19d..36185cc 100644 --- a/lib/providers/clients_provider.dart +++ b/lib/providers/clients_provider.dart @@ -190,6 +190,16 @@ class ClientsProvider with ChangeNotifier { "blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [], }; + if (body['allowed_clients']!.contains(item)) { + body['allowed_clients'] = body['allowed_clients']!.where((e) => e != item).toList(); + } + else if (body['disallowed_clients']!.contains(item)) { + body['disallowed_clients'] = body['disallowed_clients']!.where((e) => e != item).toList(); + } + else if (body['blocked_hosts']!.contains(item)) { + body['blocked_hosts'] = body['blocked_hosts']!.where((e) => e != item).toList(); + } + if (type == AccessSettingsList.allowed) { body['allowed_clients']!.add(item); } @@ -223,6 +233,18 @@ class ClientsProvider with ChangeNotifier { } } + AccessSettingsList? checkClientList(String client) { + if (_clients!.clientsAllowedBlocked!.allowedClients.contains(client)) { + return AccessSettingsList.allowed; + } + else if (_clients!.clientsAllowedBlocked!.disallowedClients.contains(client)) { + return AccessSettingsList.disallowed; + } + else { + return null; + } + } + Future removeClientList(String client, AccessSettingsList type) async { Map> body = { "allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [], diff --git a/lib/screens/clients/client/active_client_tile.dart b/lib/screens/clients/client/active_client_tile.dart index 76c928c..7a7a825 100644 --- a/lib/screens/clients/client/active_client_tile.dart +++ b/lib/screens/clients/client/active_client_tile.dart @@ -28,11 +28,11 @@ class ActiveClientTile extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: OptionsMenu( - options: [ + options: (_) => [ MenuOption( icon: Icons.copy_rounded, title: AppLocalizations.of(context)!.copyClipboard, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: client.name != '' ? client.name! : client.ip, @@ -99,11 +99,11 @@ class ActiveClientTile extends StatelessWidget { } else { return OptionsMenu( - options: [ + options: (_) => [ MenuOption( icon: Icons.copy_rounded, title: AppLocalizations.of(context)!.copyClipboard, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: client.name != '' ? client.name! : client.ip, diff --git a/lib/screens/clients/client/added_client_tile.dart b/lib/screens/clients/client/added_client_tile.dart index 4f4037e..8c10fd7 100644 --- a/lib/screens/clients/client/added_client_tile.dart +++ b/lib/screens/clients/client/added_client_tile.dart @@ -46,11 +46,11 @@ class _AddedClientTileState extends State { color: Colors.transparent, borderRadius: BorderRadius.circular(28), child: OptionsMenu( - options: [ + options: (_) => [ MenuOption( icon: Icons.copy_rounded, title: AppLocalizations.of(context)!.copyClipboard, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''), successMessage: AppLocalizations.of(context)!.copiedClipboard, ) @@ -164,16 +164,16 @@ class _AddedClientTileState extends State { } else { return OptionsMenu( - options: [ + options: (_) => [ if (widget.onEdit != null) MenuOption( title: AppLocalizations.of(context)!.seeDetails, icon: Icons.file_open_rounded, - action: (_) => widget.onEdit!(widget.client) + action: () => widget.onEdit!(widget.client) ), MenuOption( icon: Icons.copy_rounded, title: AppLocalizations.of(context)!.copyClipboard, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''), successMessage: AppLocalizations.of(context)!.copiedClipboard, ) diff --git a/lib/screens/clients/search_clients.dart b/lib/screens/clients/search_clients.dart index d8e1104..e5b1451 100644 --- a/lib/screens/clients/search_clients.dart +++ b/lib/screens/clients/search_clients.dart @@ -226,16 +226,16 @@ class _SearchClientsState extends State { itemCount: clientsScreen.length, padding: const EdgeInsets.only(bottom: 0), itemBuilder: (context, index) => OptionsMenu( - options: [ + options: (v) => [ MenuOption( icon: Icons.edit_rounded, title: AppLocalizations.of(context)!.edit, - action: (v) => openClientModal(v) + action: () => openClientModal(v) ), MenuOption( icon: Icons.delete_rounded, title: AppLocalizations.of(context)!.delete, - action: (v) => openDeleteModal(v) + action: () => openDeleteModal(v) ), ], value: clientsScreen[index], diff --git a/lib/screens/filters/filters_triple_column.dart b/lib/screens/filters/filters_triple_column.dart index 3401bac..7d15639 100644 --- a/lib/screens/filters/filters_triple_column.dart +++ b/lib/screens/filters/filters_triple_column.dart @@ -283,11 +283,11 @@ class FiltersTripleColumn extends StatelessWidget { ), ], child: OptionsMenu( - options: [ + options: (_) => [ MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: filteringProvider.filtering!.userRules[index], successMessage: AppLocalizations.of(context)!.copiedClipboard, ) diff --git a/lib/screens/filters/list_options_menu.dart b/lib/screens/filters/list_options_menu.dart index 32e4610..c35d080 100644 --- a/lib/screens/filters/list_options_menu.dart +++ b/lib/screens/filters/list_options_menu.dart @@ -146,7 +146,7 @@ class ListOptionsMenu extends StatelessWidget { color: Colors.transparent, child: InkWell( child: OptionsMenu( - options: [ + options: (_) => [ MenuOption( title: list.enabled == true ? AppLocalizations.of(context)!.disable @@ -154,12 +154,12 @@ class ListOptionsMenu extends StatelessWidget { icon: list.enabled == true ? Icons.gpp_bad_rounded : Icons.verified_user_rounded, - action: (_) => enableDisable() + action: () => enableDisable() ), MenuOption( title: AppLocalizations.of(context)!.copyListUrl, icon: Icons.copy_rounded, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: list.url, successMessage: AppLocalizations.of(context)!.listUrlCopied ) @@ -167,12 +167,12 @@ class ListOptionsMenu extends StatelessWidget { MenuOption( title: AppLocalizations.of(context)!.openListUrl, icon: Icons.open_in_browser_rounded, - action: (_) => openUrl(list.url) + action: () => openUrl(list.url) ), MenuOption( title: AppLocalizations.of(context)!.selectionMode, icon: Icons.check_rounded, - action: (_) => Future.delayed( + action: () => Future.delayed( const Duration(milliseconds: 0), () => openSelectionMode() ) diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index 32cdb89..f34a186 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -14,6 +14,7 @@ import 'package:adguard_home_manager/screens/home/appbar.dart'; import 'package:adguard_home_manager/screens/home/fab.dart'; import 'package:adguard_home_manager/screens/home/chart.dart'; +import 'package:adguard_home_manager/providers/clients_provider.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart'; import 'package:adguard_home_manager/functions/number_format.dart'; import 'package:adguard_home_manager/constants/enums.dart'; @@ -39,6 +40,9 @@ class _HomeState extends State { withLoadingIndicator: statusProvider.serverStatus != null ? false : true ); + final clientsProvider = Provider.of(context, listen: false); + clientsProvider.fetchClients(updateLoading: false); + super.initState(); isVisible = true; diff --git a/lib/screens/home/top_items/row_item.dart b/lib/screens/home/top_items/row_item.dart index 569d30c..b323eb0 100644 --- a/lib/screens/home/top_items/row_item.dart +++ b/lib/screens/home/top_items/row_item.dart @@ -16,7 +16,7 @@ class RowItem extends StatefulWidget { final bool clients; final bool showColor; final String? unit; - final List options; + final List Function(dynamic) options; final void Function(dynamic)? onTapEntry; const RowItem({ diff --git a/lib/screens/home/top_items/top_items_lists.dart b/lib/screens/home/top_items/top_items_lists.dart index 7f230fb..758a0b6 100644 --- a/lib/screens/home/top_items/top_items_lists.dart +++ b/lib/screens/home/top_items/top_items_lists.dart @@ -6,6 +6,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:adguard_home_manager/screens/home/top_items/top_items_section.dart'; +import 'package:adguard_home_manager/providers/clients_provider.dart'; import 'package:adguard_home_manager/functions/number_format.dart'; import 'package:adguard_home_manager/functions/snackbar.dart'; import 'package:adguard_home_manager/classes/process_modal.dart'; @@ -30,6 +31,7 @@ class TopItemsLists extends StatelessWidget { final statusProvider = Provider.of(context); final appConfigProvider = Provider.of(context); final logsProvider = Provider.of(context); + final clientsProvider = Provider.of(context); List bottom = [ Padding( @@ -96,10 +98,53 @@ class TopItemsLists extends StatelessWidget { } } - void copyValueClipboard(value) { + void copyValueClipboard(dynamic value) { copyToClipboard(value: value, successMessage: AppLocalizations.of(context)!.copiedClipboard); } + void blockUnblockClient(dynamic client) async { + final currentList = clientsProvider.checkClientList(client); + final newList = currentList == AccessSettingsList.allowed || currentList == null + ? AccessSettingsList.disallowed + : AccessSettingsList.allowed; + + ProcessModal processModal = ProcessModal(); + processModal.open( + currentList == AccessSettingsList.allowed || currentList == null + ? AppLocalizations.of(context)!.blockingClient + : AppLocalizations.of(context)!.unblockingClient + ); + + final result = await clientsProvider.addClientList(client, newList); + if (!context.mounted) return; + + processModal.close(); + + if (result.successful == true) { + showSnacbkar( + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.clientAddedSuccessfully, + color: Colors.green + ); + } + else if (result.successful == false && result.content == 'client_another_list') { + showSnacbkar( + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.clientAnotherList, + color: Colors.red + ); + } + else { + showSnacbkar( + appConfigProvider: appConfigProvider, + label: newList == AccessSettingsList.allowed || newList == AccessSettingsList.disallowed + ? AppLocalizations.of(context)!.clientNotRemoved + : AppLocalizations.of(context)!.domainNotAdded, + color: Colors.red + ); + } + } + return Column( children: order.asMap().entries.map((item) { switch (item.value) { @@ -113,16 +158,16 @@ class TopItemsLists extends StatelessWidget { withChart: true, withProgressBar: true, buildValue: (v) => v.toString(), - menuOptions: [ + menuOptions: (v) => [ MenuOption( title: AppLocalizations.of(context)!.blockDomain, icon: Icons.block_rounded, - action: (v) => blockUnblock(domain: v.toString(), newStatus: 'block') + action: () => blockUnblock(domain: v.toString(), newStatus: 'block') ), MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: copyValueClipboard + action: () => copyValueClipboard(v) ), ], onTapEntry: (v) => filterDomainLogs(value: v.toString()), @@ -141,16 +186,16 @@ class TopItemsLists extends StatelessWidget { withChart: true, withProgressBar: true, buildValue: (v) => v.toString(), - menuOptions: [ + menuOptions: (v) => [ MenuOption( title: AppLocalizations.of(context)!.unblockDomain, icon: Icons.check_rounded, - action: (v) => blockUnblock(domain: v, newStatus: 'unblock') + action: () => blockUnblock(domain: v, newStatus: 'unblock') ), MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: copyValueClipboard + action: () => copyValueClipboard(v) ) ], onTapEntry: (v) => filterDomainLogs(value: v), @@ -169,12 +214,21 @@ class TopItemsLists extends StatelessWidget { withChart: true, withProgressBar: true, buildValue: (v) => v.toString(), - menuOptions: [ + menuOptions: (v) => [ + if (clientsProvider.clients?.clientsAllowedBlocked != null) MenuOption( + title: clientsProvider.checkClientList(v) == AccessSettingsList.allowed || clientsProvider.checkClientList(v) == null + ? AppLocalizations.of(context)!.blockClient + : AppLocalizations.of(context)!.unblockClient, + icon: clientsProvider.checkClientList(v) == AccessSettingsList.allowed || clientsProvider.checkClientList(v) == null + ? Icons.block_rounded + : Icons.check_rounded, + action: () => blockUnblockClient(v) + ), MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: copyValueClipboard - ) + action: () => copyValueClipboard(v) + ), ], onTapEntry: (v) => filterClientLogs(value: v), ), @@ -193,11 +247,11 @@ class TopItemsLists extends StatelessWidget { withChart: true, withProgressBar: true, buildValue: (v) => v.toString(), - menuOptions: [ + menuOptions: (v) => [ MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: copyValueClipboard + action: () => copyValueClipboard(v) ) ], ), @@ -217,11 +271,11 @@ class TopItemsLists extends StatelessWidget { withChart: false, withProgressBar: false, buildValue: (v) => "${doubleFormat(v*1000, Platform.localeName)} ms", - menuOptions: [ + menuOptions: (v) => [ MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: copyValueClipboard + action: () => copyValueClipboard(v) ) ], ), diff --git a/lib/screens/home/top_items/top_items_screen.dart b/lib/screens/home/top_items/top_items_screen.dart index f2f9b4d..1070953 100644 --- a/lib/screens/home/top_items/top_items_screen.dart +++ b/lib/screens/home/top_items/top_items_screen.dart @@ -24,7 +24,7 @@ class TopItemsScreen extends StatefulWidget { final List> data; final bool withProgressBar; final String Function(dynamic) buildValue; - final List options; + final List Function(dynamic) options; final void Function(dynamic)? onTapEntry; final bool isFullscreen; @@ -263,7 +263,7 @@ class _TopItemsScreenState extends State { class _Content extends StatelessWidget { final List> screenData; final bool? isClient; - final List options; + final List Function(dynamic) options; final bool withProgressBar; final void Function(dynamic)? onTapEntry; final String Function(dynamic) buildValue; diff --git a/lib/screens/home/top_items/top_items_section.dart b/lib/screens/home/top_items/top_items_section.dart index f084b3f..bc021cf 100644 --- a/lib/screens/home/top_items/top_items_section.dart +++ b/lib/screens/home/top_items/top_items_section.dart @@ -21,7 +21,7 @@ class TopItemsSection extends StatefulWidget { final bool withChart; final bool withProgressBar; final String Function(dynamic) buildValue; - final List menuOptions; + final List Function(dynamic) menuOptions; final void Function(dynamic)? onTapEntry; const TopItemsSection({ @@ -350,7 +350,7 @@ class _ItemsList extends StatelessWidget { final HomeTopItems type; final bool showChart; final String Function(dynamic) buildValue; - final List menuOptions; + final List Function(dynamic) menuOptions; final void Function(dynamic)? onTapEntry; const _ItemsList({ diff --git a/lib/screens/logs/log_tile.dart b/lib/screens/logs/log_tile.dart index af130e2..6f4ea6b 100644 --- a/lib/screens/logs/log_tile.dart +++ b/lib/screens/logs/log_tile.dart @@ -128,7 +128,7 @@ class LogTile extends StatelessWidget { child: OptionsMenu( onTap: (_) => onLogTap(log), borderRadius: BorderRadius.circular(28), - options: [ + options: (v) => [ if (log.question.name != null) MenuOption( title: domainBlocked == true ? AppLocalizations.of(context)!.unblockDomain @@ -136,7 +136,7 @@ class LogTile extends StatelessWidget { icon: domainBlocked == true ? Icons.check_rounded : Icons.block_rounded, - action: (_) => blockUnblock( + action: () => blockUnblock( domain: log.question.name!, newStatus: domainBlocked == true ? 'unblock' : 'block' ) @@ -144,7 +144,7 @@ class LogTile extends StatelessWidget { MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: (v) => copyToClipboard(value: v, successMessage: AppLocalizations.of(context)!.copiedClipboard) + action: () => copyToClipboard(value: v, successMessage: AppLocalizations.of(context)!.copiedClipboard) ) ], child: Container( @@ -306,7 +306,7 @@ class LogTile extends StatelessWidget { color: Colors.transparent, child: OptionsMenu( onTap: (_) => onLogTap(log), - options: [ + options: (_) => [ if (log.question.name != null) MenuOption( title: domainBlocked == true ? AppLocalizations.of(context)!.unblockDomain @@ -314,7 +314,7 @@ class LogTile extends StatelessWidget { icon: domainBlocked == true ? Icons.check_rounded : Icons.block_rounded, - action: (_) => blockUnblock( + action: () => blockUnblock( domain: log.question.name!, newStatus: domainBlocked == true ? 'unblock' : 'block' ) @@ -322,7 +322,7 @@ class LogTile extends StatelessWidget { if (log.question.name != null) MenuOption( title: AppLocalizations.of(context)!.copyClipboard, icon: Icons.copy_rounded, - action: (_) => copyToClipboard( + action: () => copyToClipboard( value: log.question.name!, successMessage: AppLocalizations.of(context)!.copiedClipboard ) diff --git a/lib/widgets/options_menu.dart b/lib/widgets/options_menu.dart index 861edab..ad91b33 100644 --- a/lib/widgets/options_menu.dart +++ b/lib/widgets/options_menu.dart @@ -11,7 +11,7 @@ import 'package:adguard_home_manager/models/menu_option.dart'; class OptionsMenu extends StatelessWidget { final Widget child; - final List options; + final List Function(dynamic) options; final dynamic value; final BorderRadius? borderRadius; final void Function(dynamic)? onTap; @@ -40,11 +40,11 @@ class OptionsMenu extends StatelessWidget { return Material( color: Colors.transparent, child: ContextMenuArea( - builder: (context) => options.map((opt) => CustomListTile( + builder: (context) => options(value).map((opt) => CustomListTile( title: opt.title, icon: opt.icon, onTap: () { - opt.action(value); + opt.action(); Navigator.pop(context); }, )).toList(), @@ -64,7 +64,7 @@ class OptionsMenu extends StatelessWidget { } class _OptionsModal extends StatelessWidget { - final List options; + final List Function(dynamic) options; final dynamic value; const _OptionsModal({ @@ -98,12 +98,12 @@ class _OptionsModal extends StatelessWidget { ), child: SingleChildScrollView( child: Wrap( - children: options.map((opt) => CustomListTileDialog( + children: options(value).map((opt) => CustomListTileDialog( title: opt.title, icon: opt.icon, onTap: () { Navigator.pop(context); - opt.action(value); + opt.action(); }, )).toList() ),