Added option to block and unblock clients from home screen

This commit is contained in:
Juan Gilsanz Polo 2023-12-17 22:09:13 +01:00
parent 71b870b42f
commit 569ab7d569
16 changed files with 139 additions and 53 deletions

View file

@ -715,5 +715,8 @@
"blockedResponseTtlDescription": "Specifies for how many seconds the clients should cache a filtered response", "blockedResponseTtlDescription": "Specifies for how many seconds the clients should cache a filtered response",
"invalidValue": "Invalid value", "invalidValue": "Invalid value",
"noDataChart": "There's no data to display this chart.", "noDataChart": "There's no data to display this chart.",
"noData": "No data" "noData": "No data",
"unblockClient": "Unblock client",
"blockingClient": "Blocking client...",
"unblockingClient": "Unblocking client..."
} }

View file

@ -715,5 +715,8 @@
"blockedResponseTtlDescription": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada", "blockedResponseTtlDescription": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
"invalidValue": "Valor no válido", "invalidValue": "Valor no válido",
"noDataChart": "No hay datos para mostrar este gráfico.", "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..."
} }

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
class MenuOption { class MenuOption {
final IconData? icon; final IconData? icon;
final String title; final String title;
final void Function(dynamic) action; final void Function() action;
final bool? disabled; final bool? disabled;
const MenuOption({ const MenuOption({

View file

@ -190,6 +190,16 @@ class ClientsProvider with ChangeNotifier {
"blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [], "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) { if (type == AccessSettingsList.allowed) {
body['allowed_clients']!.add(item); 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<ApiResponse> removeClientList(String client, AccessSettingsList type) async { Future<ApiResponse> removeClientList(String client, AccessSettingsList type) async {
Map<String, List<String>> body = { Map<String, List<String>> body = {
"allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [], "allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [],

View file

@ -28,11 +28,11 @@ class ActiveClientTile extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: OptionsMenu( child: OptionsMenu(
options: [ options: (_) => [
MenuOption( MenuOption(
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: client.name != '' value: client.name != ''
? client.name! ? client.name!
: client.ip, : client.ip,
@ -99,11 +99,11 @@ class ActiveClientTile extends StatelessWidget {
} }
else { else {
return OptionsMenu( return OptionsMenu(
options: [ options: (_) => [
MenuOption( MenuOption(
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: client.name != '' value: client.name != ''
? client.name! ? client.name!
: client.ip, : client.ip,

View file

@ -46,11 +46,11 @@ class _AddedClientTileState extends State<AddedClientTile> {
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(28), borderRadius: BorderRadius.circular(28),
child: OptionsMenu( child: OptionsMenu(
options: [ options: (_) => [
MenuOption( MenuOption(
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''), value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
successMessage: AppLocalizations.of(context)!.copiedClipboard, successMessage: AppLocalizations.of(context)!.copiedClipboard,
) )
@ -164,16 +164,16 @@ class _AddedClientTileState extends State<AddedClientTile> {
} }
else { else {
return OptionsMenu( return OptionsMenu(
options: [ options: (_) => [
if (widget.onEdit != null) MenuOption( if (widget.onEdit != null) MenuOption(
title: AppLocalizations.of(context)!.seeDetails, title: AppLocalizations.of(context)!.seeDetails,
icon: Icons.file_open_rounded, icon: Icons.file_open_rounded,
action: (_) => widget.onEdit!(widget.client) action: () => widget.onEdit!(widget.client)
), ),
MenuOption( MenuOption(
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''), value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
successMessage: AppLocalizations.of(context)!.copiedClipboard, successMessage: AppLocalizations.of(context)!.copiedClipboard,
) )

View file

@ -226,16 +226,16 @@ class _SearchClientsState extends State<SearchClients> {
itemCount: clientsScreen.length, itemCount: clientsScreen.length,
padding: const EdgeInsets.only(bottom: 0), padding: const EdgeInsets.only(bottom: 0),
itemBuilder: (context, index) => OptionsMenu( itemBuilder: (context, index) => OptionsMenu(
options: [ options: (v) => [
MenuOption( MenuOption(
icon: Icons.edit_rounded, icon: Icons.edit_rounded,
title: AppLocalizations.of(context)!.edit, title: AppLocalizations.of(context)!.edit,
action: (v) => openClientModal(v) action: () => openClientModal(v)
), ),
MenuOption( MenuOption(
icon: Icons.delete_rounded, icon: Icons.delete_rounded,
title: AppLocalizations.of(context)!.delete, title: AppLocalizations.of(context)!.delete,
action: (v) => openDeleteModal(v) action: () => openDeleteModal(v)
), ),
], ],
value: clientsScreen[index], value: clientsScreen[index],

View file

@ -283,11 +283,11 @@ class FiltersTripleColumn extends StatelessWidget {
), ),
], ],
child: OptionsMenu( child: OptionsMenu(
options: [ options: (_) => [
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: filteringProvider.filtering!.userRules[index], value: filteringProvider.filtering!.userRules[index],
successMessage: AppLocalizations.of(context)!.copiedClipboard, successMessage: AppLocalizations.of(context)!.copiedClipboard,
) )

View file

@ -146,7 +146,7 @@ class ListOptionsMenu extends StatelessWidget {
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
child: OptionsMenu( child: OptionsMenu(
options: [ options: (_) => [
MenuOption( MenuOption(
title: list.enabled == true title: list.enabled == true
? AppLocalizations.of(context)!.disable ? AppLocalizations.of(context)!.disable
@ -154,12 +154,12 @@ class ListOptionsMenu extends StatelessWidget {
icon: list.enabled == true icon: list.enabled == true
? Icons.gpp_bad_rounded ? Icons.gpp_bad_rounded
: Icons.verified_user_rounded, : Icons.verified_user_rounded,
action: (_) => enableDisable() action: () => enableDisable()
), ),
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyListUrl, title: AppLocalizations.of(context)!.copyListUrl,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: list.url, value: list.url,
successMessage: AppLocalizations.of(context)!.listUrlCopied successMessage: AppLocalizations.of(context)!.listUrlCopied
) )
@ -167,12 +167,12 @@ class ListOptionsMenu extends StatelessWidget {
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.openListUrl, title: AppLocalizations.of(context)!.openListUrl,
icon: Icons.open_in_browser_rounded, icon: Icons.open_in_browser_rounded,
action: (_) => openUrl(list.url) action: () => openUrl(list.url)
), ),
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.selectionMode, title: AppLocalizations.of(context)!.selectionMode,
icon: Icons.check_rounded, icon: Icons.check_rounded,
action: (_) => Future.delayed( action: () => Future.delayed(
const Duration(milliseconds: 0), const Duration(milliseconds: 0),
() => openSelectionMode() () => openSelectionMode()
) )

View file

@ -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/fab.dart';
import 'package:adguard_home_manager/screens/home/chart.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/providers/logs_provider.dart';
import 'package:adguard_home_manager/functions/number_format.dart'; import 'package:adguard_home_manager/functions/number_format.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
@ -39,6 +40,9 @@ class _HomeState extends State<Home> {
withLoadingIndicator: statusProvider.serverStatus != null ? false : true withLoadingIndicator: statusProvider.serverStatus != null ? false : true
); );
final clientsProvider = Provider.of<ClientsProvider>(context, listen: false);
clientsProvider.fetchClients(updateLoading: false);
super.initState(); super.initState();
isVisible = true; isVisible = true;

View file

@ -16,7 +16,7 @@ class RowItem extends StatefulWidget {
final bool clients; final bool clients;
final bool showColor; final bool showColor;
final String? unit; final String? unit;
final List<MenuOption> options; final List<MenuOption> Function(dynamic) options;
final void Function(dynamic)? onTapEntry; final void Function(dynamic)? onTapEntry;
const RowItem({ const RowItem({

View file

@ -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/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/number_format.dart';
import 'package:adguard_home_manager/functions/snackbar.dart'; import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/classes/process_modal.dart'; import 'package:adguard_home_manager/classes/process_modal.dart';
@ -30,6 +31,7 @@ class TopItemsLists extends StatelessWidget {
final statusProvider = Provider.of<StatusProvider>(context); final statusProvider = Provider.of<StatusProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context); final appConfigProvider = Provider.of<AppConfigProvider>(context);
final logsProvider = Provider.of<LogsProvider>(context); final logsProvider = Provider.of<LogsProvider>(context);
final clientsProvider = Provider.of<ClientsProvider>(context);
List<Widget> bottom = [ List<Widget> bottom = [
Padding( Padding(
@ -96,10 +98,53 @@ class TopItemsLists extends StatelessWidget {
} }
} }
void copyValueClipboard(value) { void copyValueClipboard(dynamic value) {
copyToClipboard(value: value, successMessage: AppLocalizations.of(context)!.copiedClipboard); 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( return Column(
children: order.asMap().entries.map((item) { children: order.asMap().entries.map((item) {
switch (item.value) { switch (item.value) {
@ -113,16 +158,16 @@ class TopItemsLists extends StatelessWidget {
withChart: true, withChart: true,
withProgressBar: true, withProgressBar: true,
buildValue: (v) => v.toString(), buildValue: (v) => v.toString(),
menuOptions: [ menuOptions: (v) => [
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.blockDomain, title: AppLocalizations.of(context)!.blockDomain,
icon: Icons.block_rounded, icon: Icons.block_rounded,
action: (v) => blockUnblock(domain: v.toString(), newStatus: 'block') action: () => blockUnblock(domain: v.toString(), newStatus: 'block')
), ),
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: copyValueClipboard action: () => copyValueClipboard(v)
), ),
], ],
onTapEntry: (v) => filterDomainLogs(value: v.toString()), onTapEntry: (v) => filterDomainLogs(value: v.toString()),
@ -141,16 +186,16 @@ class TopItemsLists extends StatelessWidget {
withChart: true, withChart: true,
withProgressBar: true, withProgressBar: true,
buildValue: (v) => v.toString(), buildValue: (v) => v.toString(),
menuOptions: [ menuOptions: (v) => [
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.unblockDomain, title: AppLocalizations.of(context)!.unblockDomain,
icon: Icons.check_rounded, icon: Icons.check_rounded,
action: (v) => blockUnblock(domain: v, newStatus: 'unblock') action: () => blockUnblock(domain: v, newStatus: 'unblock')
), ),
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: copyValueClipboard action: () => copyValueClipboard(v)
) )
], ],
onTapEntry: (v) => filterDomainLogs(value: v), onTapEntry: (v) => filterDomainLogs(value: v),
@ -169,12 +214,21 @@ class TopItemsLists extends StatelessWidget {
withChart: true, withChart: true,
withProgressBar: true, withProgressBar: true,
buildValue: (v) => v.toString(), 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( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: copyValueClipboard action: () => copyValueClipboard(v)
) ),
], ],
onTapEntry: (v) => filterClientLogs(value: v), onTapEntry: (v) => filterClientLogs(value: v),
), ),
@ -193,11 +247,11 @@ class TopItemsLists extends StatelessWidget {
withChart: true, withChart: true,
withProgressBar: true, withProgressBar: true,
buildValue: (v) => v.toString(), buildValue: (v) => v.toString(),
menuOptions: [ menuOptions: (v) => [
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: copyValueClipboard action: () => copyValueClipboard(v)
) )
], ],
), ),
@ -217,11 +271,11 @@ class TopItemsLists extends StatelessWidget {
withChart: false, withChart: false,
withProgressBar: false, withProgressBar: false,
buildValue: (v) => "${doubleFormat(v*1000, Platform.localeName)} ms", buildValue: (v) => "${doubleFormat(v*1000, Platform.localeName)} ms",
menuOptions: [ menuOptions: (v) => [
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: copyValueClipboard action: () => copyValueClipboard(v)
) )
], ],
), ),

View file

@ -24,7 +24,7 @@ class TopItemsScreen extends StatefulWidget {
final List<Map<String, dynamic>> data; final List<Map<String, dynamic>> data;
final bool withProgressBar; final bool withProgressBar;
final String Function(dynamic) buildValue; final String Function(dynamic) buildValue;
final List<MenuOption> options; final List<MenuOption> Function(dynamic) options;
final void Function(dynamic)? onTapEntry; final void Function(dynamic)? onTapEntry;
final bool isFullscreen; final bool isFullscreen;
@ -263,7 +263,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
class _Content extends StatelessWidget { class _Content extends StatelessWidget {
final List<Map<String, dynamic>> screenData; final List<Map<String, dynamic>> screenData;
final bool? isClient; final bool? isClient;
final List<MenuOption> options; final List<MenuOption> Function(dynamic) options;
final bool withProgressBar; final bool withProgressBar;
final void Function(dynamic)? onTapEntry; final void Function(dynamic)? onTapEntry;
final String Function(dynamic) buildValue; final String Function(dynamic) buildValue;

View file

@ -21,7 +21,7 @@ class TopItemsSection extends StatefulWidget {
final bool withChart; final bool withChart;
final bool withProgressBar; final bool withProgressBar;
final String Function(dynamic) buildValue; final String Function(dynamic) buildValue;
final List<MenuOption> menuOptions; final List<MenuOption> Function(dynamic) menuOptions;
final void Function(dynamic)? onTapEntry; final void Function(dynamic)? onTapEntry;
const TopItemsSection({ const TopItemsSection({
@ -350,7 +350,7 @@ class _ItemsList extends StatelessWidget {
final HomeTopItems type; final HomeTopItems type;
final bool showChart; final bool showChart;
final String Function(dynamic) buildValue; final String Function(dynamic) buildValue;
final List<MenuOption> menuOptions; final List<MenuOption> Function(dynamic) menuOptions;
final void Function(dynamic)? onTapEntry; final void Function(dynamic)? onTapEntry;
const _ItemsList({ const _ItemsList({

View file

@ -128,7 +128,7 @@ class LogTile extends StatelessWidget {
child: OptionsMenu( child: OptionsMenu(
onTap: (_) => onLogTap(log), onTap: (_) => onLogTap(log),
borderRadius: BorderRadius.circular(28), borderRadius: BorderRadius.circular(28),
options: [ options: (v) => [
if (log.question.name != null) MenuOption( if (log.question.name != null) MenuOption(
title: domainBlocked == true title: domainBlocked == true
? AppLocalizations.of(context)!.unblockDomain ? AppLocalizations.of(context)!.unblockDomain
@ -136,7 +136,7 @@ class LogTile extends StatelessWidget {
icon: domainBlocked == true icon: domainBlocked == true
? Icons.check_rounded ? Icons.check_rounded
: Icons.block_rounded, : Icons.block_rounded,
action: (_) => blockUnblock( action: () => blockUnblock(
domain: log.question.name!, domain: log.question.name!,
newStatus: domainBlocked == true ? 'unblock' : 'block' newStatus: domainBlocked == true ? 'unblock' : 'block'
) )
@ -144,7 +144,7 @@ class LogTile extends StatelessWidget {
MenuOption( MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, 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( child: Container(
@ -306,7 +306,7 @@ class LogTile extends StatelessWidget {
color: Colors.transparent, color: Colors.transparent,
child: OptionsMenu( child: OptionsMenu(
onTap: (_) => onLogTap(log), onTap: (_) => onLogTap(log),
options: [ options: (_) => [
if (log.question.name != null) MenuOption( if (log.question.name != null) MenuOption(
title: domainBlocked == true title: domainBlocked == true
? AppLocalizations.of(context)!.unblockDomain ? AppLocalizations.of(context)!.unblockDomain
@ -314,7 +314,7 @@ class LogTile extends StatelessWidget {
icon: domainBlocked == true icon: domainBlocked == true
? Icons.check_rounded ? Icons.check_rounded
: Icons.block_rounded, : Icons.block_rounded,
action: (_) => blockUnblock( action: () => blockUnblock(
domain: log.question.name!, domain: log.question.name!,
newStatus: domainBlocked == true ? 'unblock' : 'block' newStatus: domainBlocked == true ? 'unblock' : 'block'
) )
@ -322,7 +322,7 @@ class LogTile extends StatelessWidget {
if (log.question.name != null) MenuOption( if (log.question.name != null) MenuOption(
title: AppLocalizations.of(context)!.copyClipboard, title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded, icon: Icons.copy_rounded,
action: (_) => copyToClipboard( action: () => copyToClipboard(
value: log.question.name!, value: log.question.name!,
successMessage: AppLocalizations.of(context)!.copiedClipboard successMessage: AppLocalizations.of(context)!.copiedClipboard
) )

View file

@ -11,7 +11,7 @@ import 'package:adguard_home_manager/models/menu_option.dart';
class OptionsMenu extends StatelessWidget { class OptionsMenu extends StatelessWidget {
final Widget child; final Widget child;
final List<MenuOption> options; final List<MenuOption> Function(dynamic) options;
final dynamic value; final dynamic value;
final BorderRadius? borderRadius; final BorderRadius? borderRadius;
final void Function(dynamic)? onTap; final void Function(dynamic)? onTap;
@ -40,11 +40,11 @@ class OptionsMenu extends StatelessWidget {
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
child: ContextMenuArea( child: ContextMenuArea(
builder: (context) => options.map((opt) => CustomListTile( builder: (context) => options(value).map((opt) => CustomListTile(
title: opt.title, title: opt.title,
icon: opt.icon, icon: opt.icon,
onTap: () { onTap: () {
opt.action(value); opt.action();
Navigator.pop(context); Navigator.pop(context);
}, },
)).toList(), )).toList(),
@ -64,7 +64,7 @@ class OptionsMenu extends StatelessWidget {
} }
class _OptionsModal extends StatelessWidget { class _OptionsModal extends StatelessWidget {
final List<MenuOption> options; final List<MenuOption> Function(dynamic) options;
final dynamic value; final dynamic value;
const _OptionsModal({ const _OptionsModal({
@ -98,12 +98,12 @@ class _OptionsModal extends StatelessWidget {
), ),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Wrap( child: Wrap(
children: options.map((opt) => CustomListTileDialog( children: options(value).map((opt) => CustomListTileDialog(
title: opt.title, title: opt.title,
icon: opt.icon, icon: opt.icon,
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
opt.action(value); opt.action();
}, },
)).toList() )).toList()
), ),