Added some context menus on desktop

This commit is contained in:
Juan Gilsanz Polo 2023-05-04 22:38:37 +02:00
parent d628e56248
commit 27ffa75d63
19 changed files with 864 additions and 623 deletions

View file

@ -126,4 +126,43 @@ Map<String, dynamic> getFilteredStatus(BuildContext context, AppConfigProvider a
default: default:
return {'filtered': null, 'label': 'Unknown'}; return {'filtered': null, 'label': 'Unknown'};
} }
}
bool isDomainBlocked(String filterKey) {
switch (filterKey) {
case 'NotFilteredNotFound':
return false;
case 'NotFilteredWhiteList':
return false;
case 'NotFilteredError':
return false;
case 'FilteredBlackList':
return true;
case 'FilteredSafeBrowsing':
return true;
case 'FilteredParental':
return true;
case 'FilteredInvalid':
return true;
case 'FilteredSafeSearch':
return true;
case 'FilteredBlockedService':
return true;
case 'Rewrite':
case 'RewriteEtcHosts':
case 'RewriteRule':
return false;
default:
return false;
}
} }

View file

@ -612,5 +612,7 @@
"selectClientLeftColumn": "Select a client of the left column", "selectClientLeftColumn": "Select a client of the left column",
"disableList": "Disable list", "disableList": "Disable list",
"enableList": "Enable list", "enableList": "Enable list",
"screens": "Screens" "screens": "Screens",
"copiedClipboard": "Copied to clipboard",
"seeDetails": "See details"
} }

View file

@ -612,5 +612,7 @@
"selectClientLeftColumn": "Selecciona un cliente de la columna de la izquierda", "selectClientLeftColumn": "Selecciona un cliente de la columna de la izquierda",
"disableList": "Deshabilitar lista", "disableList": "Deshabilitar lista",
"enableList": "Habilitar lista", "enableList": "Habilitar lista",
"screens": "Pantallas" "screens": "Pantallas",
"copiedClipboard": "Copiado al portapapeles",
"seeDetails": "Ver los detalles"
} }

View file

@ -140,7 +140,11 @@ class _MainState extends State<Main> {
], ],
builder: (context, child) { builder: (context, child) {
return MediaQuery( return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), data: MediaQuery.of(context).copyWith(
textScaleFactor: !(Platform.isAndroid || Platform.isIOS)
? 0.9
: 1.0
),
child: child!, child: child!,
); );
}, },

View file

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
class MenuOption {
final IconData? icon;
final String title;
final void Function() action;
final bool? disabled;
const MenuOption({
required this.title,
required this.action,
this.icon,
this.disabled
});
}

View file

@ -1,7 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:contextmenu/contextmenu.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/models/clients.dart'; import 'package:adguard_home_manager/models/clients.dart';
class ActiveClientTile extends StatelessWidget { class ActiveClientTile extends StatelessWidget {
@ -23,77 +26,113 @@ class ActiveClientTile extends StatelessWidget {
if (splitView == true) { if (splitView == true) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material( child: ContextMenuArea(
color: Colors.transparent, builder: (context) => [
borderRadius: BorderRadius.circular(28), CustomListTile(
child: InkWell( title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded,
onTap: () {
copyToClipboard(
context: context,
value: client.name != ''
? client.name!
: client.ip,
successMessage: AppLocalizations.of(context)!.copiedClipboard,
);
Navigator.pop(context);
},
)
],
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(28), borderRadius: BorderRadius.circular(28),
onTap: () => onTap(client), child: InkWell(
child: Container( borderRadius: BorderRadius.circular(28),
width: double.maxFinite, onTap: () => onTap(client),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Container(
decoration: BoxDecoration( width: double.maxFinite,
borderRadius: BorderRadius.circular(28), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
color: client == selectedClient decoration: BoxDecoration(
? Theme.of(context).colorScheme.primaryContainer borderRadius: BorderRadius.circular(28),
: null color: client == selectedClient
), ? Theme.of(context).colorScheme.primaryContainer
child: Row( : null
mainAxisAlignment: MainAxisAlignment.spaceBetween, ),
children: [ child: Row(
Flexible( mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Row( children: [
mainAxisSize: MainAxisSize.min, Flexible(
children: [ child: Row(
Flexible( mainAxisSize: MainAxisSize.min,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Flexible(
children: [ child: Column(
Text( crossAxisAlignment: CrossAxisAlignment.start,
client.name != '' children: [
? client.name! Text(
: client.ip, client.name != ''
style: TextStyle( ? client.name!
fontSize: 16, : client.ip,
fontWeight: FontWeight.w400, style: TextStyle(
color: Theme.of(context).colorScheme.onSurface, fontSize: 16,
fontWeight: FontWeight.w400,
color: Theme.of(context).colorScheme.onSurface,
),
), ),
), if (client.name != '') Text(client.ip)
if (client.name != '') Text(client.ip) ],
], ),
), )
) ],
], ),
), ),
), Text(
Text( client.source,
client.source, style: TextStyle(
style: TextStyle( color: Theme.of(context).colorScheme.onSurface
color: Theme.of(context).colorScheme.onSurface ),
), ),
), ],
], )
) ),
), ),
), ),
), ),
); );
} }
else { else {
return CustomListTile( return ContextMenuArea(
title: client.name != '' builder: (context) => [
? client.name! CustomListTile(
: client.ip, title: AppLocalizations.of(context)!.copyClipboard,
subtitle: client.name != '' icon: Icons.copy_rounded,
? client.ip onTap: () {
: null, copyToClipboard(
trailing: Text( context: context,
client.source, value: client.name != ''
style: TextStyle( ? client.name!
color: Theme.of(context).colorScheme.onSurface : client.ip,
successMessage: AppLocalizations.of(context)!.copiedClipboard,
);
Navigator.pop(context);
},
)
],
child: CustomListTile(
title: client.name != ''
? client.name!
: client.ip,
subtitle: client.name != ''
? client.ip
: null,
trailing: Text(
client.source,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface
),
), ),
onTap: () => onTap(client),
), ),
onTap: () => onTap(client),
); );
} }
} }

View file

@ -1,14 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:contextmenu/contextmenu.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart'; import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/models/clients.dart'; import 'package:adguard_home_manager/models/clients.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart';
class AddedClientTile extends StatefulWidget { class AddedClientTile extends StatelessWidget {
final Client client; final Client client;
final void Function(Client) onTap; final void Function(Client) onTap;
final void Function(Client) onLongPress; final void Function(Client) onLongPress;
@ -26,224 +29,254 @@ class AddedClientTile extends StatefulWidget {
required this.splitView required this.splitView
}) : super(key: key); }) : super(key: key);
@override
State<AddedClientTile> createState() => _AddedClientTileState();
}
class _AddedClientTileState extends State<AddedClientTile> {
bool hover = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context); final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context); final appConfigProvider = Provider.of<AppConfigProvider>(context);
if (widget.splitView == true) { if (splitView == true) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(28), borderRadius: BorderRadius.circular(28),
child: InkWell( child: ContextMenuArea(
borderRadius: BorderRadius.circular(28), builder: (context) => [
onTap: () => widget.onTap(widget.client), CustomListTile(
onHover: (v) => setState(() => hover = v), title: AppLocalizations.of(context)!.seeDetails,
child: Container( icon: Icons.file_open_rounded,
width: double.maxFinite, onTap: () {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), Navigator.pop(context);
decoration: BoxDecoration( onEdit(client);
borderRadius: BorderRadius.circular(28), }
color: widget.client == widget.selectedClient
? Theme.of(context).colorScheme.primaryContainer
: null
), ),
child: Row( CustomListTile(
mainAxisAlignment: MainAxisAlignment.spaceBetween, title: AppLocalizations.of(context)!.copyClipboard,
children: [ icon: Icons.copy_rounded,
Flexible( onTap: () {
child: Row( copyToClipboard(
mainAxisSize: MainAxisSize.min, context: context,
children: [ value: client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
Flexible( successMessage: AppLocalizations.of(context)!.copiedClipboard,
child: Column( );
crossAxisAlignment: CrossAxisAlignment.start, Navigator.pop(context);
children: [ }
Text( ),
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''), ],
style: TextStyle( child: InkWell(
fontSize: 16, borderRadius: BorderRadius.circular(28),
fontWeight: FontWeight.w400, onTap: () => onTap(client),
color: Theme.of(context).colorScheme.onSurface, onLongPress: () => onLongPress(client),
child: Container(
width: double.maxFinite,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
color: client == selectedClient
? Theme.of(context).colorScheme.primaryContainer
: null
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Theme.of(context).colorScheme.onSurface,
),
), ),
), const SizedBox(height: 8),
const SizedBox(height: 8), Row(
Row( children: [
children: [ Icon(
Icon( Icons.filter_list_rounded,
Icons.filter_list_rounded, size: 19,
size: 19, color: client.filteringEnabled == true
color: widget.client.filteringEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.vpn_lock_rounded,
size: 18,
color: widget.client.safebrowsingEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.block,
size: 18,
color: widget.client.parentalEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.search_rounded,
size: 19,
color: serverVersionIsAhead(
currentVersion: serversProvider.serverStatus.data!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red
: widget.client.safesearchEnabled == true
? appConfigProvider.useThemeColorForStatus == true ? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Colors.green : Colors.green
: appConfigProvider.useThemeColorForStatus == true : appConfigProvider.useThemeColorForStatus == true
? Colors.grey ? Colors.grey
: Colors.red, : Colors.red,
) ),
], const SizedBox(width: 10),
) Icon(
], Icons.vpn_lock_rounded,
), size: 18,
) color: client.safebrowsingEnabled == true
], ? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.block,
size: 18,
color: client.parentalEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.search_rounded,
size: 19,
color: serverVersionIsAhead(
currentVersion: serversProvider.serverStatus.data!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? client.safeSearch != null && client.safeSearch!.enabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red
: client.safesearchEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
)
],
)
],
),
)
],
),
), ),
), ],
if (hover == true) IconButton( )
onPressed: () => widget.onEdit(widget.client), ),
icon: const Icon(Icons.edit_rounded)
)
],
)
), ),
), ),
), ),
); );
} }
else { else {
return CustomListTile( return ContextMenuArea(
onLongPress: () => widget.onLongPress(widget.client), builder: (context) => [
onTap: () => widget.onTap(widget.client), CustomListTile(
onHover: (v) => setState(() => hover = v), title: AppLocalizations.of(context)!.seeDetails,
title: widget.client.name, icon: Icons.file_open_rounded,
subtitleWidget: Column( onTap: () {
crossAxisAlignment: CrossAxisAlignment.start, Navigator.pop(context);
children: [ onEdit(client);
Text( }
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''), ),
style: TextStyle( CustomListTile(
color: Theme.of(context).listTileTheme.textColor title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded,
onTap: () {
copyToClipboard(
context: context,
value: client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
successMessage: AppLocalizations.of(context)!.copiedClipboard,
);
Navigator.pop(context);
}
),
],
child: CustomListTile(
onLongPress: () => onLongPress(client),
onTap: () => onTap(client),
title: client.name,
subtitleWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
style: TextStyle(
color: Theme.of(context).listTileTheme.textColor
),
), ),
), const SizedBox(height: 8),
const SizedBox(height: 8), Row(
Row( children: [
children: [ Icon(
Icon( Icons.filter_list_rounded,
Icons.filter_list_rounded, size: 19,
size: 19, color: client.filteringEnabled == true
color: widget.client.filteringEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.vpn_lock_rounded,
size: 18,
color: widget.client.safebrowsingEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.block,
size: 18,
color: widget.client.parentalEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.search_rounded,
size: 19,
color: serverVersionIsAhead(
currentVersion: serversProvider.serverStatus.data!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red
: widget.client.safesearchEnabled == true
? appConfigProvider.useThemeColorForStatus == true ? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Colors.green : Colors.green
: appConfigProvider.useThemeColorForStatus == true : appConfigProvider.useThemeColorForStatus == true
? Colors.grey ? Colors.grey
: Colors.red, : Colors.red,
) ),
], const SizedBox(width: 10),
) Icon(
], Icons.vpn_lock_rounded,
size: 18,
color: client.safebrowsingEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.block,
size: 18,
color: client.parentalEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
),
const SizedBox(width: 10),
Icon(
Icons.search_rounded,
size: 19,
color: serverVersionIsAhead(
currentVersion: serversProvider.serverStatus.data!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? client.safeSearch != null && client.safeSearch!.enabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red
: client.safesearchEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
: appConfigProvider.useThemeColorForStatus == true
? Colors.grey
: Colors.red,
)
],
)
],
),
), ),
trailing: hover == true
? IconButton(
onPressed: () => widget.onEdit(widget.client),
icon: const Icon(Icons.edit_rounded)
)
: null,
); );
} }
} }

View file

@ -144,53 +144,67 @@ class _LogsListClientState extends State<LogsListClient> {
); );
case 1: case 1:
return RefreshIndicator( if (logsData!.data.isNotEmpty) {
onRefresh: fetchLogs, return RefreshIndicator(
child: ListView.builder( onRefresh: fetchLogs,
controller: scrollController, child: ListView.builder(
padding: const EdgeInsets.only(top: 0), controller: scrollController,
itemCount: isLoadingMore == true padding: const EdgeInsets.only(top: 0),
? logsData!.data.length+1 itemCount: isLoadingMore == true
: logsData!.data.length, ? logsData!.data.length+1
itemBuilder: (context, index) { : logsData!.data.length,
if (isLoadingMore == true && index == logsData!.data.length) { itemBuilder: (context, index) {
return const Padding( if (isLoadingMore == true && index == logsData!.data.length) {
padding: EdgeInsets.symmetric(vertical: 20), return const Padding(
child: Center( padding: EdgeInsets.symmetric(vertical: 20),
child: CircularProgressIndicator(), child: Center(
), child: CircularProgressIndicator(),
); ),
} );
else { }
return LogTile( else {
log: logsData!.data[index], return LogTile(
index: index, log: logsData!.data[index],
length: logsData!.data.length, index: index,
useAlwaysNormalTile: true, length: logsData!.data.length,
onLogTap: (log) => { useAlwaysNormalTile: true,
if (width > 700) { onLogTap: (log) => {
showDialog( if (width > 700) {
context: context, showDialog(
builder: (context) => LogDetailsScreen( context: context,
log: log, builder: (context) => LogDetailsScreen(
dialog: true log: log,
dialog: true
)
) )
) }
else {
Navigator.push(context, MaterialPageRoute(
builder: (context) => LogDetailsScreen(
log: log,
dialog: false
)
))
}
} }
else { );
Navigator.push(context, MaterialPageRoute( }
builder: (context) => LogDetailsScreen(
log: log,
dialog: false
)
))
}
}
);
} }
} ),
), );
); }
else {
return Center(
child: Text(
AppLocalizations.of(context)!.noLogsDisplay,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
);
}
case 2: case 2:
return SizedBox( return SizedBox(

View file

@ -1,13 +1,17 @@
import 'dart:io'; import 'dart:io';
import 'package:adguard_home_manager/screens/filters/add_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:contextmenu/contextmenu.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/filters/add_button.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/widgets/options_modal.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/models/menu_option.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/models/filtering.dart'; import 'package:adguard_home_manager/models/filtering.dart';
import 'package:adguard_home_manager/functions/number_format.dart'; import 'package:adguard_home_manager/functions/number_format.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart';
@ -235,20 +239,44 @@ class FiltersTripleColumn extends StatelessWidget {
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: serversProvider.filtering.data!.userRules.length, itemCount: serversProvider.filtering.data!.userRules.length,
itemBuilder: (context, index) => ListTile( itemBuilder: (context, index) => ContextMenuArea(
title: Text( builder: (context) => [
serversProvider.filtering.data!.userRules[index], CustomListTile(
style: TextStyle( title: AppLocalizations.of(context)!.copyClipboard,
color: checkIfComment(serversProvider.filtering.data!.userRules[index]) == true icon: Icons.copy_rounded,
? Theme.of(context).colorScheme.onSurface.withOpacity(0.6) onTap: () {
: Theme.of(context).colorScheme.onSurface, copyToClipboard(
fontWeight: FontWeight.normal, context: context,
value: serversProvider.filtering.data!.userRules[index],
successMessage: AppLocalizations.of(context)!.copiedClipboard,
);
Navigator.pop(context);
}
),
],
child: CustomListTile(
onLongPress: () => showDialog(
context: context,
builder: (context) => OptionsModal(
options: [
MenuOption(
title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded,
action: () => copyToClipboard(
context: context,
value: serversProvider.filtering.data!.userRules[index],
successMessage: AppLocalizations.of(context)!.copiedClipboard,
)
)
]
)
),
title: serversProvider.filtering.data!.userRules[index],
subtitleWidget: generateSubtitle(serversProvider.filtering.data!.userRules[index]),
trailing: IconButton(
onPressed: () => onRemoveCustomRule(serversProvider.filtering.data!.userRules[index]),
icon: const Icon(Icons.delete)
), ),
),
subtitle: generateSubtitle(serversProvider.filtering.data!.userRules[index]),
trailing: IconButton(
onPressed: () => onRemoveCustomRule(serversProvider.filtering.data!.userRules[index]),
icon: const Icon(Icons.delete)
), ),
), ),
), ),

View file

@ -216,9 +216,9 @@ class _HomeState extends State<Home> {
maxWidth: 500 maxWidth: 500
), ),
child: TopItems( child: TopItems(
label: AppLocalizations.of(context)!.topBlockedDomains, label: AppLocalizations.of(context)!.topClients,
data: serversProvider.serverStatus.data!.stats.topBlockedDomains, data: serversProvider.serverStatus.data!.stats.topClients,
type: 'topBlockedDomains', type: 'topClients',
), ),
), ),
), ),

View file

@ -2,17 +2,18 @@
import 'dart:io'; import 'dart:io';
import 'package:adguard_home_manager/screens/top_items/top_items_modal.dart';
import 'package:animations/animations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.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/home/top_items_options_modal.dart'; import 'package:adguard_home_manager/widgets/domain_options.dart';
import 'package:adguard_home_manager/screens/top_items/top_items_modal.dart';
import 'package:adguard_home_manager/widgets/options_modal.dart';
import 'package:adguard_home_manager/screens/top_items/top_items.dart'; import 'package:adguard_home_manager/screens/top_items/top_items.dart';
import 'package:adguard_home_manager/models/applied_filters.dart'; import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/models/menu_option.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart';
import 'package:adguard_home_manager/classes/process_modal.dart'; import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/models/filtering_status.dart'; import 'package:adguard_home_manager/models/filtering_status.dart';
@ -119,14 +120,32 @@ class TopItems extends StatelessWidget {
); );
} }
List<MenuOption> generateOptions(String domain) {
final isBlocked = getIsBlocked();
return [
if (isBlocked == true) MenuOption(
title: AppLocalizations.of(context)!.unblock,
icon: Icons.check,
action: () => blockUnblock(domain, 'unblock')
),
if (isBlocked == false) MenuOption(
title: AppLocalizations.of(context)!.block,
icon: Icons.check,
action: () => blockUnblock(domain, 'block')
),
MenuOption(
title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.check,
action: () => copyDomainClipboard(domain)
),
];
}
void openOptionsModal(String domain, String type) { void openOptionsModal(String domain, String type) {
showDialog( showDialog(
context: context, context: context,
builder: (context) => TopItemsOptionsModal( builder: (context) => OptionsModal(
isBlocked: getIsBlocked(), options: generateOptions(domain),
changeStatus: (String status) => blockUnblock(domain, status),
copyToClipboard: () => copyDomainClipboard(domain),
type: type,
) )
); );
} }
@ -143,7 +162,10 @@ class TopItems extends StatelessWidget {
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: DomainOptions(
item: item.keys.toList()[0],
isClient: type == 'topClients',
isBlocked: type == 'topBlockedDomains',
onTap: () { onTap: () {
if (type == 'topQueriedDomains' || type == 'topBlockedDomains') { if (type == 'topQueriedDomains' || type == 'topBlockedDomains') {
logsProvider.setSearchText(item.keys.toList()[0]); logsProvider.setSearchText(item.keys.toList()[0]);
@ -167,10 +189,8 @@ class TopItems extends StatelessWidget {
clients: [item.keys.toList()[0]] clients: [item.keys.toList()[0]]
) )
); );
appConfigProvider.setSelectedScreen(2);
} }
}, },
onLongPress: () => openOptionsModal(item.keys.toList()[0], type),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 20, horizontal: 20,

View file

@ -1,84 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile_dialog.dart';
class TopItemsOptionsModal extends StatelessWidget {
final bool? isBlocked;
final void Function(String status)? changeStatus;
final void Function() copyToClipboard;
final String type;
const TopItemsOptionsModal({
Key? key,
this.isBlocked,
this.changeStatus,
required this.copyToClipboard,
required this.type
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.symmetric(vertical: 16),
title: Column(
children: [
Icon(
Icons.more_horiz,
size: 24,
color: Theme.of(context).listTileTheme.iconColor
),
const SizedBox(height: 16),
Text(
AppLocalizations.of(context)!.options,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface
),
)
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (type == 'topQueriedDomains' || type == 'topBlockedDomains') ...[
if (isBlocked == true && changeStatus != null) CustomListTileDialog(
title: AppLocalizations.of(context)!.unblock,
icon: Icons.check,
onTap: () {
Navigator.pop(context);
changeStatus!('unblock');
},
),
if (isBlocked == false && changeStatus != null) CustomListTileDialog(
title: AppLocalizations.of(context)!.block,
icon: Icons.block,
onTap: () {
Navigator.pop(context);
changeStatus!('block');
},
),
],
CustomListTileDialog(
title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy,
onTap: () {
Navigator.pop(context);
copyToClipboard();
}
),
],
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
)
],
)
],
);
}
}

View file

@ -2,15 +2,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/home/top_items_options_modal.dart'; import 'package:adguard_home_manager/widgets/domain_options.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/functions/block_unblock_domain.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/functions/get_filtered_status.dart'; import 'package:adguard_home_manager/functions/get_filtered_status.dart';
import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/models/logs.dart'; import 'package:adguard_home_manager/models/logs.dart';
import 'package:adguard_home_manager/functions/format_time.dart'; import 'package:adguard_home_manager/functions/format_time.dart';
@ -75,42 +71,16 @@ class LogTile extends StatelessWidget {
); );
} }
void changeBlockStatus(String status) async {
final result = await blockUnblock(context, log.question.name, status);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: result['message'],
color: result['success'] == true ? Colors.green : Colors.red
);
}
void openOptionsModal(Log log) {
showDialog(
context: context,
builder: (context) => TopItemsOptionsModal(
isBlocked: getFilteredStatus(context, appConfigProvider, log.reason, false)['color'] == Colors.red
? true : false,
changeStatus: changeBlockStatus,
copyToClipboard: () => copyToClipboard(
context: context,
value: log.question.name,
successMessage: AppLocalizations.of(context)!.domainCopiedClipboard
),
type: 'topQueriedDomains', // topQueriedDomains can also be used here. It's the same
)
);
}
if (width > 1100 && !(useAlwaysNormalTile == true)) { if (width > 1100 && !(useAlwaysNormalTile == true)) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material( child: InkWell(
color: Colors.transparent,
borderRadius: BorderRadius.circular(28), borderRadius: BorderRadius.circular(28),
child: InkWell( child: DomainOptions(
borderRadius: BorderRadius.circular(28),
onTap: () => onLogTap(log), onTap: () => onLogTap(log),
borderRadius: BorderRadius.circular(28),
item: log.question.name,
isBlocked: isDomainBlocked(log.reason),
child: Container( child: Container(
width: double.maxFinite, width: double.maxFinite,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
@ -291,9 +261,10 @@ class LogTile extends StatelessWidget {
else { else {
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: DomainOptions(
onTap: () => onLogTap(log), onTap: () => onLogTap(log),
onLongPress: () => openOptionsModal(log), item: log.question.name,
isBlocked: isDomainBlocked(log.reason),
child: Container( child: Container(
width: double.maxFinite, width: double.maxFinite,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),

View file

@ -7,11 +7,10 @@ import 'package:percent_indicator/percent_indicator.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.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/home/top_items_options_modal.dart'; import 'package:adguard_home_manager/widgets/domain_options.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/models/applied_filters.dart'; import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.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/snackbar.dart'; import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/functions/number_format.dart'; import 'package:adguard_home_manager/functions/number_format.dart';
@ -67,45 +66,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
for (var element in data) { for (var element in data) {
total = total + int.parse(element.values.toList()[0].toString()); total = total + int.parse(element.values.toList()[0].toString());
} }
bool? getIsBlocked() {
if (widget.type == 'topBlockedDomains') {
return true;
}
else if (widget.type == 'topQueriedDomains') {
return false;
}
else {
return null;
}
}
void changeBlockStatus(String status, String domain) async {
final result = await blockUnblock(context, domain, status);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: result['message'],
color: result['success'] == true ? Colors.green : Colors.red
);
}
void openOptionsModal(String domain, String type) {
showDialog(
context: context,
builder: (context) => TopItemsOptionsModal(
isBlocked: getIsBlocked(),
changeStatus: (String status) => changeBlockStatus(status, domain),
copyToClipboard: () => copyToClipboard(
context: context,
value: domain,
successMessage: AppLocalizations.of(context)!.domainCopiedClipboard
),
type: type,
)
);
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: searchActive == true title: searchActive == true
@ -197,7 +158,10 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
} }
} }
return CustomListTile( return DomainOptions(
item: screenData[index].keys.toList()[0],
isBlocked: widget.type == 'topBlockedDomains',
isClient: widget.type == 'topClients',
onTap: () { onTap: () {
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') { if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
logsProvider.setSearchText(screenData[index].keys.toList()[0]); logsProvider.setSearchText(screenData[index].keys.toList()[0]);
@ -226,59 +190,57 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
Navigator.pop(context); Navigator.pop(context);
} }
}, },
onLongPress: () => openOptionsModal( child: CustomListTile(
screenData[index].keys.toList()[0], title: screenData[index].keys.toList()[0],
widget.type trailing: Text(
), screenData[index].values.toList()[0].toString(),
title: screenData[index].keys.toList()[0], style: TextStyle(
trailing: Text( color: Theme.of(context).colorScheme.onSurfaceVariant
screenData[index].values.toList()[0].toString(), ),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant
), ),
), subtitleWidget: Column(
subtitleWidget: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ if (name != null) ...[
if (name != null) ...[ Text(
Text( name,
name, style: TextStyle(
style: TextStyle( fontSize: 14,
fontSize: 14, color: Theme.of(context).colorScheme.onSurface
color: Theme.of(context).colorScheme.onSurface ),
), ),
), const SizedBox(height: 5),
const SizedBox(height: 5), ],
], Row(
Row( children: [
children: [ SizedBox(
SizedBox( width: 50,
width: 50, child: Text(
child: Text( "${doubleFormat((screenData[index].values.toList()[0]/total*100), Platform.localeName)}%",
"${doubleFormat((screenData[index].values.toList()[0]/total*100), Platform.localeName)}%", style: TextStyle(
style: TextStyle( color: Theme.of(context).listTileTheme.textColor
color: Theme.of(context).listTileTheme.textColor ),
), ),
), ),
), const SizedBox(width: 10),
const SizedBox(width: 10), Flexible(
Flexible( child: LinearPercentIndicator(
child: LinearPercentIndicator( animation: true,
animation: true, lineHeight: 4,
lineHeight: 4, animationDuration: 500,
animationDuration: 500, curve: Curves.easeOut,
curve: Curves.easeOut, percent: screenData[index].values.toList()[0]/total,
percent: screenData[index].values.toList()[0]/total, barRadius: const Radius.circular(5),
barRadius: const Radius.circular(5), progressColor: Theme.of(context).colorScheme.primary,
progressColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant, ),
), ),
), const SizedBox(width: 10),
const SizedBox(width: 10), ],
], ),
), ],
], )
) ),
); );
} }
) )

View file

@ -7,11 +7,10 @@ import 'package:percent_indicator/percent_indicator.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.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/home/top_items_options_modal.dart'; import 'package:adguard_home_manager/widgets/domain_options.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart'; import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/models/applied_filters.dart'; import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.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/snackbar.dart'; import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/functions/number_format.dart'; import 'package:adguard_home_manager/functions/number_format.dart';
@ -66,45 +65,7 @@ class _TopItemsModalState extends State<TopItemsModal> {
for (var element in data) { for (var element in data) {
total = total + int.parse(element.values.toList()[0].toString()); total = total + int.parse(element.values.toList()[0].toString());
} }
bool? getIsBlocked() {
if (widget.type == 'topBlockedDomains') {
return true;
}
else if (widget.type == 'topQueriedDomains') {
return false;
}
else {
return null;
}
}
void changeBlockStatus(String status, String domain) async {
final result = await blockUnblock(context, domain, status);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: result['message'],
color: result['success'] == true ? Colors.green : Colors.red
);
}
void openOptionsModal(String domain, String type) {
showDialog(
context: context,
builder: (context) => TopItemsOptionsModal(
isBlocked: getIsBlocked(),
changeStatus: (String status) => changeBlockStatus(status, domain),
copyToClipboard: () => copyToClipboard(
context: context,
value: domain,
successMessage: AppLocalizations.of(context)!.domainCopiedClipboard
),
type: type,
)
);
}
return Dialog( return Dialog(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints( constraints: const BoxConstraints(
@ -169,7 +130,10 @@ class _TopItemsModalState extends State<TopItemsModal> {
} }
} }
return CustomListTile( return DomainOptions(
isBlocked: widget.type == 'topBlockedDomains',
isClient: widget.type == 'topClients',
item: screenData[index].keys.toList()[0],
onTap: () { onTap: () {
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') { if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
logsProvider.setSearchText(screenData[index].keys.toList()[0]); logsProvider.setSearchText(screenData[index].keys.toList()[0]);
@ -198,59 +162,57 @@ class _TopItemsModalState extends State<TopItemsModal> {
Navigator.pop(context); Navigator.pop(context);
} }
}, },
onLongPress: () => openOptionsModal( child: CustomListTile(
screenData[index].keys.toList()[0], title: screenData[index].keys.toList()[0],
widget.type trailing: Text(
), screenData[index].values.toList()[0].toString(),
title: screenData[index].keys.toList()[0], style: TextStyle(
trailing: Text( color: Theme.of(context).colorScheme.onSurfaceVariant
screenData[index].values.toList()[0].toString(), ),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant
), ),
), subtitleWidget: Column(
subtitleWidget: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ if (name != null) ...[
if (name != null) ...[ Text(
Text( name,
name, style: TextStyle(
style: TextStyle( fontSize: 14,
fontSize: 14, color: Theme.of(context).colorScheme.onSurface
color: Theme.of(context).colorScheme.onSurface ),
), ),
), const SizedBox(height: 5),
const SizedBox(height: 5), ],
], Row(
Row( children: [
children: [ SizedBox(
SizedBox( width: 50,
width: 50, child: Text(
child: Text( "${doubleFormat((screenData[index].values.toList()[0]/total*100), Platform.localeName)}%",
"${doubleFormat((screenData[index].values.toList()[0]/total*100), Platform.localeName)}%", style: TextStyle(
style: TextStyle( color: Theme.of(context).listTileTheme.textColor
color: Theme.of(context).listTileTheme.textColor ),
), ),
), ),
), const SizedBox(width: 10),
const SizedBox(width: 10), Flexible(
Flexible( child: LinearPercentIndicator(
child: LinearPercentIndicator( animation: true,
animation: true, lineHeight: 4,
lineHeight: 4, animationDuration: 500,
animationDuration: 500, curve: Curves.easeOut,
curve: Curves.easeOut, percent: screenData[index].values.toList()[0]/total,
percent: screenData[index].values.toList()[0]/total, barRadius: const Radius.circular(5),
barRadius: const Radius.circular(5), progressColor: Theme.of(context).colorScheme.primary,
progressColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant, ),
), ),
), const SizedBox(width: 10),
const SizedBox(width: 10), ],
], ),
), ],
], )
) ),
); );
} }
), ),

View file

@ -0,0 +1,158 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:contextmenu/contextmenu.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/options_modal.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/models/filtering_status.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/models/menu_option.dart';
class DomainOptions extends StatelessWidget {
final bool isBlocked;
final bool? isClient;
final String item;
final Widget child;
final void Function() onTap;
final BorderRadius? borderRadius;
const DomainOptions({
Key? key,
required this.isBlocked,
this.isClient,
required this.item,
required this.child,
required this.onTap,
this.borderRadius
}) : super(key: key);
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
void blockUnblock(String domain, String newStatus) async {
final ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.savingUserFilters);
final rules = await getFilteringRules(server: serversProvider.selectedServer!);
if (rules['result'] == 'success') {
FilteringStatus oldStatus = serversProvider.serverStatus.data!.filteringStatus;
List<String> newRules = rules['data'].userRules.where((d) => !d.contains(domain)).toList();
if (newStatus == 'block') {
newRules.add("||$domain^");
}
else if (newStatus == 'unblock') {
newRules.add("@@||$domain^");
}
FilteringStatus newObj = serversProvider.serverStatus.data!.filteringStatus;
newObj.userRules = newRules;
serversProvider.setFilteringStatus(newObj);
final result = await postFilteringRules(server: serversProvider.selectedServer!, data: {'rules': newRules});
processModal.close();
if (result['result'] == 'success') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.userFilteringRulesUpdated),
backgroundColor: Colors.green,
)
);
}
else {
appConfigProvider.addLog(result['log']);
serversProvider.setFilteringStatus(oldStatus);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.userFilteringRulesNotUpdated),
backgroundColor: Colors.red,
)
);
}
}
else {
appConfigProvider.addLog(rules['log']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.userFilteringRulesNotUpdated),
backgroundColor: Colors.red,
)
);
}
}
void copyDomainClipboard(String domain) async {
await Clipboard.setData(
ClipboardData(text: domain)
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.domainCopiedClipboard),
backgroundColor: Colors.green,
)
);
}
List<MenuOption> generateOptions() {
return [
if (isClient != true && isBlocked == true) MenuOption(
title: AppLocalizations.of(context)!.unblock,
icon: Icons.check,
action: () => blockUnblock(item, 'unblock')
),
if (isClient != true && isBlocked == false) MenuOption(
title: AppLocalizations.of(context)!.block,
icon: Icons.block,
action: () => blockUnblock(item, 'block')
),
MenuOption(
title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy,
action: () => copyDomainClipboard(item)
),
];
}
void openOptionsModal() {
showDialog(
context: context,
builder: (context) => OptionsModal(
options: generateOptions(),
)
);
}
return Material(
color: Colors.transparent,
borderRadius: borderRadius,
child: ContextMenuArea(
builder: (context) => generateOptions().map((opt) => CustomListTile(
title: opt.title,
icon: opt.icon,
onTap: () {
opt.action();
Navigator.pop(context);
},
)).toList(),
child: InkWell(
onTap: onTap,
onLongPress: () => openOptionsModal(),
borderRadius: borderRadius,
child: child,
),
),
);
}
}

View file

@ -0,0 +1,59 @@
import 'package:adguard_home_manager/models/menu_option.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile_dialog.dart';
class OptionsModal extends StatelessWidget {
final List<MenuOption> options;
const OptionsModal({
Key? key,
required this.options,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.symmetric(vertical: 16),
title: Column(
children: [
Icon(
Icons.more_horiz,
size: 24,
color: Theme.of(context).listTileTheme.iconColor
),
const SizedBox(height: 16),
Text(
AppLocalizations.of(context)!.options,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface
),
)
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: options.map((opt) => CustomListTileDialog(
title: opt.title,
icon: opt.icon,
onTap: () {
Navigator.pop(context);
opt.action();
},
)).toList()
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
)
],
)
],
);
}
}

View file

@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
after_layout:
dependency: transitive
description:
name: after_layout
sha256: "95a1cb2ca1464f44f14769329fbf15987d20ab6c88f8fc5d359bd362be625f29"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
animations: animations:
dependency: "direct main" dependency: "direct main"
description: description:
@ -81,6 +89,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.0" version: "1.17.0"
contextmenu:
dependency: "direct main"
description:
name: contextmenu
sha256: e0c7d60e2fc9f316f5b03f5fe2c0f977d65125345d1a1f77eea02be612e32d0c
url: "https://pub.dev"
source: hosted
version: "3.0.0"
convert: convert:
dependency: transitive dependency: transitive
description: description:

View file

@ -68,6 +68,7 @@ dependencies:
url: https://github.com/JGeek00/flutter_split_view url: https://github.com/JGeek00/flutter_split_view
ref: master-alt ref: master-alt
url_launcher: ^6.1.10 url_launcher: ^6.1.10
contextmenu: ^3.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: