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

@ -127,3 +127,42 @@ Map<String, dynamic> getFilteredStatus(BuildContext context, AppConfigProvider a
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",
"disableList": "Disable 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",
"disableList": "Deshabilitar 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) {
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!,
);
},

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: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/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/models/clients.dart';
class ActiveClientTile extends StatelessWidget {
@ -23,6 +26,23 @@ class ActiveClientTile extends StatelessWidget {
if (splitView == true) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: ContextMenuArea(
builder: (context) => [
CustomListTile(
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),
@ -77,10 +97,28 @@ class ActiveClientTile extends StatelessWidget {
),
),
),
),
);
}
else {
return CustomListTile(
return ContextMenuArea(
builder: (context) => [
CustomListTile(
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: CustomListTile(
title: client.name != ''
? client.name!
: client.ip,
@ -94,6 +132,7 @@ class ActiveClientTile extends StatelessWidget {
),
),
onTap: () => onTap(client),
),
);
}
}

View file

@ -1,14 +1,17 @@
import 'package:flutter/material.dart';
import 'package:contextmenu/contextmenu.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/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/providers/servers_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
class AddedClientTile extends StatefulWidget {
class AddedClientTile extends StatelessWidget {
final Client client;
final void Function(Client) onTap;
final void Function(Client) onLongPress;
@ -26,41 +29,57 @@ class AddedClientTile extends StatefulWidget {
required this.splitView
}) : super(key: key);
@override
State<AddedClientTile> createState() => _AddedClientTileState();
}
class _AddedClientTileState extends State<AddedClientTile> {
bool hover = false;
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
if (widget.splitView == true) {
if (splitView == true) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(28),
child: ContextMenuArea(
builder: (context) => [
CustomListTile(
title: AppLocalizations.of(context)!.seeDetails,
icon: Icons.file_open_rounded,
onTap: () {
Navigator.pop(context);
onEdit(client);
}
),
CustomListTile(
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: InkWell(
borderRadius: BorderRadius.circular(28),
onTap: () => widget.onTap(widget.client),
onHover: (v) => setState(() => hover = v),
onTap: () => onTap(client),
onLongPress: () => onLongPress(client),
child: Container(
width: double.maxFinite,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(28),
color: widget.client == widget.selectedClient
color: client == selectedClient
? Theme.of(context).colorScheme.primaryContainer
: null
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
Expanded(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -69,7 +88,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
@ -82,7 +101,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
Icon(
Icons.filter_list_rounded,
size: 19,
color: widget.client.filteringEnabled == true
color: client.filteringEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -94,7 +113,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
Icon(
Icons.vpn_lock_rounded,
size: 18,
color: widget.client.safebrowsingEnabled == true
color: client.safebrowsingEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -106,7 +125,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
Icon(
Icons.block,
size: 18,
color: widget.client.parentalEnabled == true
color: client.parentalEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -123,14 +142,14 @@ class _AddedClientTileState extends State<AddedClientTile> {
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == 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
: widget.client.safesearchEnabled == true
: client.safesearchEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -146,28 +165,47 @@ class _AddedClientTileState extends State<AddedClientTile> {
],
),
),
if (hover == true) IconButton(
onPressed: () => widget.onEdit(widget.client),
icon: const Icon(Icons.edit_rounded)
)
],
)
),
),
),
),
);
}
else {
return CustomListTile(
onLongPress: () => widget.onLongPress(widget.client),
onTap: () => widget.onTap(widget.client),
onHover: (v) => setState(() => hover = v),
title: widget.client.name,
return ContextMenuArea(
builder: (context) => [
CustomListTile(
title: AppLocalizations.of(context)!.seeDetails,
icon: Icons.file_open_rounded,
onTap: () {
Navigator.pop(context);
onEdit(client);
}
),
CustomListTile(
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(
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
style: TextStyle(
color: Theme.of(context).listTileTheme.textColor
),
@ -178,7 +216,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
Icon(
Icons.filter_list_rounded,
size: 19,
color: widget.client.filteringEnabled == true
color: client.filteringEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -190,7 +228,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
Icon(
Icons.vpn_lock_rounded,
size: 18,
color: widget.client.safebrowsingEnabled == true
color: client.safebrowsingEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -202,7 +240,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
Icon(
Icons.block,
size: 18,
color: widget.client.parentalEnabled == true
color: client.parentalEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -219,14 +257,14 @@ class _AddedClientTileState extends State<AddedClientTile> {
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == 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
: widget.client.safesearchEnabled == true
: client.safesearchEnabled == true
? appConfigProvider.useThemeColorForStatus == true
? Theme.of(context).colorScheme.primary
: Colors.green
@ -238,12 +276,7 @@ class _AddedClientTileState extends State<AddedClientTile> {
)
],
),
trailing: hover == true
? IconButton(
onPressed: () => widget.onEdit(widget.client),
icon: const Icon(Icons.edit_rounded)
)
: null,
),
);
}
}

View file

@ -144,6 +144,7 @@ class _LogsListClientState extends State<LogsListClient> {
);
case 1:
if (logsData!.data.isNotEmpty) {
return RefreshIndicator(
onRefresh: fetchLogs,
child: ListView.builder(
@ -191,6 +192,19 @@ class _LogsListClientState extends State<LogsListClient> {
}
),
);
}
else {
return Center(
child: Text(
AppLocalizations.of(context)!.noLogsDisplay,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
);
}
case 2:
return SizedBox(

View file

@ -1,13 +1,17 @@
import 'dart:io';
import 'package:adguard_home_manager/screens/filters/add_button.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: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/options_modal.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/functions/number_format.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
@ -235,23 +239,47 @@ class FiltersTripleColumn extends StatelessWidget {
Expanded(
child: ListView.builder(
itemCount: serversProvider.filtering.data!.userRules.length,
itemBuilder: (context, index) => ListTile(
title: Text(
serversProvider.filtering.data!.userRules[index],
style: TextStyle(
color: checkIfComment(serversProvider.filtering.data!.userRules[index]) == true
? Theme.of(context).colorScheme.onSurface.withOpacity(0.6)
: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.normal,
itemBuilder: (context, index) => ContextMenuArea(
builder: (context) => [
CustomListTile(
title: AppLocalizations.of(context)!.copyClipboard,
icon: Icons.copy_rounded,
onTap: () {
copyToClipboard(
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,
)
)
]
)
),
subtitle: generateSubtitle(serversProvider.filtering.data!.userRules[index]),
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)
),
),
),
),
)
],
),

View file

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

View file

@ -2,17 +2,18 @@
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/services.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/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/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/classes/process_modal.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) {
showDialog(
context: context,
builder: (context) => TopItemsOptionsModal(
isBlocked: getIsBlocked(),
changeStatus: (String status) => blockUnblock(domain, status),
copyToClipboard: () => copyDomainClipboard(domain),
type: type,
builder: (context) => OptionsModal(
options: generateOptions(domain),
)
);
}
@ -143,7 +162,10 @@ class TopItems extends StatelessWidget {
return Material(
color: Colors.transparent,
child: InkWell(
child: DomainOptions(
item: item.keys.toList()[0],
isClient: type == 'topClients',
isBlocked: type == 'topBlockedDomains',
onTap: () {
if (type == 'topQueriedDomains' || type == 'topBlockedDomains') {
logsProvider.setSearchText(item.keys.toList()[0]);
@ -167,10 +189,8 @@ class TopItems extends StatelessWidget {
clients: [item.keys.toList()[0]]
)
);
appConfigProvider.setSelectedScreen(2);
}
},
onLongPress: () => openOptionsModal(item.keys.toList()[0], type),
child: Padding(
padding: const EdgeInsets.symmetric(
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: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/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/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)) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(28),
child: InkWell(
borderRadius: BorderRadius.circular(28),
child: DomainOptions(
onTap: () => onLogTap(log),
borderRadius: BorderRadius.circular(28),
item: log.question.name,
isBlocked: isDomainBlocked(log.reason),
child: Container(
width: double.maxFinite,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
@ -291,9 +261,10 @@ class LogTile extends StatelessWidget {
else {
return Material(
color: Colors.transparent,
child: InkWell(
child: DomainOptions(
onTap: () => onLogTap(log),
onLongPress: () => openOptionsModal(log),
item: log.question.name,
isBlocked: isDomainBlocked(log.reason),
child: Container(
width: double.maxFinite,
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: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/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/functions/snackbar.dart';
import 'package:adguard_home_manager/functions/number_format.dart';
@ -68,44 +67,6 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
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(
appBar: AppBar(
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: () {
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
logsProvider.setSearchText(screenData[index].keys.toList()[0]);
@ -226,10 +190,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
Navigator.pop(context);
}
},
onLongPress: () => openOptionsModal(
screenData[index].keys.toList()[0],
widget.type
),
child: CustomListTile(
title: screenData[index].keys.toList()[0],
trailing: Text(
screenData[index].values.toList()[0].toString(),
@ -279,6 +240,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
),
],
)
),
);
}
)

View file

@ -7,11 +7,10 @@ import 'package:percent_indicator/percent_indicator.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/widgets/custom_list_tile.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/functions/snackbar.dart';
import 'package:adguard_home_manager/functions/number_format.dart';
@ -67,44 +66,6 @@ class _TopItemsModalState extends State<TopItemsModal> {
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(
child: ConstrainedBox(
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: () {
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
logsProvider.setSearchText(screenData[index].keys.toList()[0]);
@ -198,10 +162,7 @@ class _TopItemsModalState extends State<TopItemsModal> {
Navigator.pop(context);
}
},
onLongPress: () => openOptionsModal(
screenData[index].keys.toList()[0],
widget.type
),
child: CustomListTile(
title: screenData[index].keys.toList()[0],
trailing: Text(
screenData[index].values.toList()[0].toString(),
@ -251,6 +212,7 @@ class _TopItemsModalState extends State<TopItemsModal> {
),
],
)
),
);
}
),

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

View file

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