diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 2b66b5e..45b4526 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -45,10 +45,10 @@ "save": "Save", "serverStatus": "Server status", "connectionNotUpdated": "Connection not updated", - "ruleFilteringWidget": "Rule\nfiltering", - "safeBrowsingWidget": "Safe\nbrowsing", - "parentalFilteringWidget": "Parental\nfiltering", - "safeSearchWidget": "Safe\nsearch", + "ruleFilteringWidget": "Rule filtering", + "safeBrowsingWidget": "Safe browsing", + "parentalFilteringWidget": "Parental filtering", + "safeSearchWidget": "Safe search", "ruleFiltering": "Rule filtering", "safeBrowsing": "Safe browsing", "parentalFiltering": "Parental filtering", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 76cff1b..97967c8 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -45,10 +45,10 @@ "save": "Guardar", "connectionNotUpdated": "Conexión no actualizada", "serverStatus": "Estado del servidor", - "ruleFilteringWidget": "Bloqueo por\nfiltros", - "safeBrowsingWidget": "Navegación\nsegura", - "parentalFilteringWidget": "Control\nparental", - "safeSearchWidget": "Búsqueda\nsegura", + "ruleFilteringWidget": "Bloqueo por filtros", + "safeBrowsingWidget": "Navegación segura", + "parentalFilteringWidget": "Control parental", + "safeSearchWidget": "Búsqueda segura", "ruleFiltering": "Bloqueo por filtros", "safeBrowsing": "Navegación segura", "parentalFiltering": "Control parental", diff --git a/lib/screens/home/chart.dart b/lib/screens/home/chart.dart index 315d421..2ea4535 100644 --- a/lib/screens/home/chart.dart +++ b/lib/screens/home/chart.dart @@ -48,12 +48,15 @@ class HomeChart extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - label, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.onSurface + Flexible( + child: Text( + label, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.onSurface + ), ), ), !isEmpty diff --git a/lib/screens/home/fab.dart b/lib/screens/home/fab.dart index ce5f8a9..1369535 100644 --- a/lib/screens/home/fab.dart +++ b/lib/screens/home/fab.dart @@ -12,13 +12,27 @@ class HomeFab extends StatelessWidget { Widget build(BuildContext context) { final serversProvider = Provider.of(context); + final width = MediaQuery.of(context).size.width; + void openManagementBottomSheet() { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (context) => const ManagementModal(), - backgroundColor: Colors.transparent, - ); + if (width > 700) { + showDialog( + context: context, + builder: (context) => const ManagementModal( + dialog: true, + ), + ); + } + else { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => const ManagementModal( + dialog: false, + ), + backgroundColor: Colors.transparent, + ); + } } return serversProvider.serverStatus.loadStatus == 1 diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index 932ddfe..541f732 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -55,6 +55,8 @@ class _HomeState extends State { final serversProvider = Provider.of(context); final appConfigProvider = Provider.of(context); + final width = MediaQuery.of(context).size.width; + Widget status() { switch (serversProvider.serverStatus.loadStatus) { case 0: @@ -92,72 +94,138 @@ class _HomeState extends State { ), const SizedBox(height: 20), - HomeChart( - data: serversProvider.serverStatus.data!.stats.dnsQueries, - label: AppLocalizations.of(context)!.dnsQueries, - primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numDnsQueries, Platform.localeName), - secondaryValue: "${doubleFormat(serversProvider.serverStatus.data!.stats.avgProcessingTime*1000, Platform.localeName)} ms", - color: Colors.blue, - ), - - HomeChart( - data: serversProvider.serverStatus.data!.stats.blockedFiltering, - label: AppLocalizations.of(context)!.blockedFilters, - primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numBlockedFiltering, Platform.localeName), - secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numBlockedFiltering/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%", - color: Colors.red, + Wrap( + children: [ + FractionallySizedBox( + widthFactor: width > 700 ? 0.5 : 1, + child: HomeChart( + data: serversProvider.serverStatus.data!.stats.dnsQueries, + label: AppLocalizations.of(context)!.dnsQueries, + primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numDnsQueries, Platform.localeName), + secondaryValue: "${doubleFormat(serversProvider.serverStatus.data!.stats.avgProcessingTime*1000, Platform.localeName)} ms", + color: Colors.blue, + ), + ), + FractionallySizedBox( + widthFactor: width > 700 ? 0.5 : 1, + child: HomeChart( + data: serversProvider.serverStatus.data!.stats.blockedFiltering, + label: AppLocalizations.of(context)!.blockedFilters, + primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numBlockedFiltering, Platform.localeName), + secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numBlockedFiltering/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%", + color: Colors.red, + ), + ), + FractionallySizedBox( + widthFactor: width > 700 ? 0.5 : 1, + child: HomeChart( + data: serversProvider.serverStatus.data!.stats.replacedSafebrowsing, + label: AppLocalizations.of(context)!.malwarePhisingBlocked, + primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing, Platform.localeName), + secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%", + color: Colors.green, + ), + ), + FractionallySizedBox( + widthFactor: width > 700 ? 0.5 : 1, + child: HomeChart( + data: serversProvider.serverStatus.data!.stats.replacedParental, + label: AppLocalizations.of(context)!.blockedAdultWebsites, + primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedParental, Platform.localeName), + secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedParental/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%", + color: Colors.orange, + ), + ), + + ], ), - HomeChart( - data: serversProvider.serverStatus.data!.stats.replacedSafebrowsing, - label: AppLocalizations.of(context)!.malwarePhisingBlocked, - primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing, Platform.localeName), - secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%", - color: Colors.green, - ), - - HomeChart( - data: serversProvider.serverStatus.data!.stats.replacedParental, - label: AppLocalizations.of(context)!.blockedAdultWebsites, - primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedParental, Platform.localeName), - secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedParental/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%", - color: Colors.orange, - ), - - TopItems( - label: AppLocalizations.of(context)!.topQueriedDomains, - data: serversProvider.serverStatus.data!.stats.topQueriedDomains, - type: 'topQueriedDomains', - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Divider( - thickness: 1, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2), + if (width <= 700) ...[ + TopItems( + label: AppLocalizations.of(context)!.topQueriedDomains, + data: serversProvider.serverStatus.data!.stats.topQueriedDomains, + type: 'topQueriedDomains', ), - ), - const SizedBox(height: 20), - - TopItems( - label: AppLocalizations.of(context)!.topBlockedDomains, - data: serversProvider.serverStatus.data!.stats.topBlockedDomains, - type: 'topBlockedDomains', - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Divider( - thickness: 1, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Divider( + thickness: 1, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2), + ), ), - ), - const SizedBox(height: 20), - TopItems( - label: AppLocalizations.of(context)!.topClients, - data: serversProvider.serverStatus.data!.stats.topClients, - type: 'topClients', - clients: true, - ), + const SizedBox(height: 20), + + TopItems( + label: AppLocalizations.of(context)!.topBlockedDomains, + data: serversProvider.serverStatus.data!.stats.topBlockedDomains, + type: 'topBlockedDomains', + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Divider( + thickness: 1, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2), + ), + ), + const SizedBox(height: 20), + + TopItems( + label: AppLocalizations.of(context)!.topClients, + data: serversProvider.serverStatus.data!.stats.topClients, + type: 'topClients', + clients: true, + ), + ], + if (width > 700) Column( + children: [ + const SizedBox(height: 16), + Wrap( + alignment: WrapAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 500 + ), + child: TopItems( + label: AppLocalizations.of(context)!.topQueriedDomains, + data: serversProvider.serverStatus.data!.stats.topQueriedDomains, + type: 'topQueriedDomains', + ), + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 500 + ), + child: TopItems( + label: AppLocalizations.of(context)!.topBlockedDomains, + data: serversProvider.serverStatus.data!.stats.topBlockedDomains, + type: 'topBlockedDomains', + ), + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 500 + ), + child: TopItems( + label: AppLocalizations.of(context)!.topBlockedDomains, + data: serversProvider.serverStatus.data!.stats.topBlockedDomains, + type: 'topBlockedDomains', + ), + ), + ), + ], + ), + ], + ) ], ); diff --git a/lib/screens/home/management_modal.dart b/lib/screens/home/management_modal.dart index e36fbc7..15eac57 100644 --- a/lib/screens/home/management_modal.dart +++ b/lib/screens/home/management_modal.dart @@ -15,7 +15,12 @@ import 'package:adguard_home_manager/services/http_requests.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart'; class ManagementModal extends StatefulWidget { - const ManagementModal({Key? key}) : super(key: key); + final bool dialog; + + const ManagementModal({ + Key? key, + required this.dialog + }) : super(key: key); @override State createState() => _ManagementModalState(); @@ -364,8 +369,112 @@ class _ManagementModalState extends State with SingleTickerProv ); } - return SafeArea( - child: Container( + Widget header() { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top: 24), + child: Icon( + Icons.shield_rounded, + size: 24, + color: Theme.of(context).listTileTheme.iconColor + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + AppLocalizations.of(context)!.manageServer, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + ), + ], + ), + ], + ); + } + + List toggles() { + return [ + mainSwitch(), + Container(height: 10), + smallSwitch( + AppLocalizations.of(context)!.ruleFiltering, + Icons.filter_list_rounded, + serversProvider.serverStatus.data!.filteringEnabled, + (value) => updateBlocking(value: value, filter: 'filtering'), + serversProvider.protectionsManagementProcess.contains('filtering') + ), + smallSwitch( + AppLocalizations.of(context)!.safeBrowsing, + Icons.vpn_lock_rounded, + serversProvider.serverStatus.data!.safeBrowsingEnabled, + (value) => updateBlocking(value: value, filter: 'safeBrowsing'), + serversProvider.protectionsManagementProcess.contains('safeBrowsing') + ), + smallSwitch( + AppLocalizations.of(context)!.parentalFiltering, + Icons.block, + serversProvider.serverStatus.data!.parentalControlEnabled, + (value) => updateBlocking(value: value, filter: 'parentalControl'), + serversProvider.protectionsManagementProcess.contains('parentalControl') + ), + smallSwitch( + AppLocalizations.of(context)!.safeSearch, + Icons.search_rounded, + serversProvider.serverStatus.data!.safeSearchEnabled, + (value) => updateBlocking(value: value, filter: 'safeSearch'), + serversProvider.protectionsManagementProcess.contains('safeSearch') + ), + ]; + } + + if (widget.dialog == true) { + return Dialog( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 400 + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: SingleChildScrollView( + child: Wrap( + children: [ + header(), + ...toggles() + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.all(24), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(AppLocalizations.of(context)!.close), + ), + ], + ), + ), + if (Platform.isIOS) const SizedBox(height: 16) + ], + ), + ), + ); + } + else { + return Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.only( @@ -373,66 +482,18 @@ class _ManagementModalState extends State with SingleTickerProv topRight: Radius.circular(28) ) ), - child: Wrap( + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.center, + Flexible( + child: SingleChildScrollView( + child: Wrap( children: [ - Padding( - padding: const EdgeInsets.only(top: 24), - child: Icon( - Icons.shield_rounded, - size: 24, - color: Theme.of(context).listTileTheme.iconColor - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Text( - AppLocalizations.of(context)!.manageServer, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - ), + header(), + ...toggles() ], ), - ], - ), - mainSwitch(), - const SizedBox(height: 10), - smallSwitch( - AppLocalizations.of(context)!.ruleFiltering, - Icons.filter_list_rounded, - serversProvider.serverStatus.data!.filteringEnabled, - (value) => updateBlocking(value: value, filter: 'filtering'), - serversProvider.protectionsManagementProcess.contains('filtering') - ), - smallSwitch( - AppLocalizations.of(context)!.safeBrowsing, - Icons.vpn_lock_rounded, - serversProvider.serverStatus.data!.safeBrowsingEnabled, - (value) => updateBlocking(value: value, filter: 'safeBrowsing'), - serversProvider.protectionsManagementProcess.contains('safeBrowsing') - ), - smallSwitch( - AppLocalizations.of(context)!.parentalFiltering, - Icons.block, - serversProvider.serverStatus.data!.parentalControlEnabled, - (value) => updateBlocking(value: value, filter: 'parentalControl'), - serversProvider.protectionsManagementProcess.contains('parentalControl') - ), - smallSwitch( - AppLocalizations.of(context)!.safeSearch, - Icons.search_rounded, - serversProvider.serverStatus.data!.safeSearchEnabled, - (value) => updateBlocking(value: value, filter: 'safeSearch'), - serversProvider.protectionsManagementProcess.contains('safeSearch') + ), ), Padding( padding: const EdgeInsets.all(24), @@ -449,7 +510,7 @@ class _ManagementModalState extends State with SingleTickerProv if (Platform.isIOS) const SizedBox(height: 16) ], ), - ), - ); + ); + } } } \ No newline at end of file diff --git a/lib/screens/home/server_status.dart b/lib/screens/home/server_status.dart index c4d8d97..1df9767 100644 --- a/lib/screens/home/server_status.dart +++ b/lib/screens/home/server_status.dart @@ -15,8 +15,12 @@ class ServerStatus extends StatelessWidget { @override Widget build(BuildContext context) { + final width = MediaQuery.of(context).size.width; + return Container( - padding: const EdgeInsets.all(20), + padding: width > 700 + ? const EdgeInsets.only(left: 20, right: 20, bottom: 20) + : const EdgeInsets.all(20), child: Column( children: [ Text( @@ -29,11 +33,11 @@ class ServerStatus extends StatelessWidget { ), const SizedBox(height: 20), SizedBox( - height: 140, + height: width > 700 ? 70 : 140, child: GridView( physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: width > 700 ? 4 : 2, crossAxisSpacing: 10, mainAxisSpacing: 10, mainAxisExtent: 65 diff --git a/lib/screens/home/status_box.dart b/lib/screens/home/status_box.dart index 62d4279..2f57106 100644 --- a/lib/screens/home/status_box.dart +++ b/lib/screens/home/status_box.dart @@ -44,13 +44,15 @@ class StatusBox extends StatelessWidget { : Colors.grey.computeLuminance() > 0.5 ? Colors.black : Colors.white, ), const SizedBox(width: 12), - Text( - label, - style: TextStyle( - color: appConfigProvider.useThemeColorForStatus == true - ? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white - : Colors.grey.computeLuminance() > 0.5 ? Colors.black : Colors.white, - fontWeight: FontWeight.w500 + Flexible( + child: Text( + label, + style: TextStyle( + color: appConfigProvider.useThemeColorForStatus == true + ? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white + : Colors.grey.computeLuminance() > 0.5 ? Colors.black : Colors.white, + fontWeight: FontWeight.w500 + ), ), ) ], diff --git a/lib/screens/home/top_items.dart b/lib/screens/home/top_items.dart index 5883523..0bf85d2 100644 --- a/lib/screens/home/top_items.dart +++ b/lib/screens/home/top_items.dart @@ -1,5 +1,9 @@ // ignore_for_file: use_build_context_synchronously +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'; @@ -35,6 +39,8 @@ class TopItems extends StatelessWidget { final appConfigProvider = Provider.of(context); final logsProvider = Provider.of(context); + final width = MediaQuery.of(context).size.width; + bool? getIsBlocked() { if (type == 'topBlockedDomains') { return true; @@ -266,16 +272,32 @@ class TopItems extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - onPressed: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => TopItemsScreen( - type: type, - title: label, - isClient: clients, - data: generateData(), + onPressed: () => { + if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => TopItemsModal( + type: type, + title: label, + isClient: clients, + data: generateData(), + ) ) - ) - ), + } + else { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => TopItemsScreen( + type: type, + title: label, + isClient: clients, + data: generateData(), + ) + ) + ) + } + }, child: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/screens/top_items/top_items_modal.dart b/lib/screens/top_items/top_items_modal.dart new file mode 100644 index 0000000..55aae09 --- /dev/null +++ b/lib/screens/top_items/top_items_modal.dart @@ -0,0 +1,276 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:io'; + +import 'package:flutter/material.dart'; +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/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'; +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/servers_provider.dart'; + +class TopItemsModal extends StatefulWidget { + final String type; + final String title; + final bool? isClient; + final List> data; + + const TopItemsModal({ + Key? key, + required this.type, + required this.title, + this.isClient, + required this.data, + }) : super(key: key); + + @override + State createState() => _TopItemsModalState(); +} + +class _TopItemsModalState extends State { + bool searchActive = false; + final TextEditingController searchController = TextEditingController(); + + List> data = []; + List> screenData = []; + + void search(String value) { + List> newValues = widget.data.where((item) => item.keys.toList()[0].contains(value)).toList(); + setState(() => screenData = newValues); + } + + @override + void initState() { + data = widget.data; + screenData = widget.data; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final serversProvider = Provider.of(context); + final appConfigProvider = Provider.of(context); + final logsProvider = Provider.of(context); + + int total = 0; + for (var element in data) { + 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( + maxWidth: 500 + ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 1, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + IconButton( + onPressed: () => Navigator.pop(context), + icon: const Icon(Icons.clear_rounded), + tooltip: AppLocalizations.of(context)!.close, + ), + ], + ), + ), + Expanded( + flex: 1, + child: TextField( + controller: searchController, + onChanged: search, + decoration: InputDecoration( + filled: true, + fillColor: Theme.of(context).colorScheme.primary.withOpacity(0.1), + hintText: AppLocalizations.of(context)!.search, + prefixIcon: const Icon(Icons.search_rounded), + contentPadding: const EdgeInsets.only(left: 14, bottom: 9, top: 11), + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.circular(25.7), + ), + enabledBorder: UnderlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.circular(25.7), + ), + ), + ), + ) + ], + ), + ), + if (screenData.isNotEmpty) Flexible( + child: ListView.builder( + padding: const EdgeInsets.only(top: 0), + itemCount: screenData.length, + itemBuilder: (context, index) { + String? name; + if (widget.isClient != null && widget.isClient == true) { + try { + name = serversProvider.serverStatus.data!.clients.firstWhere((c) => c.ids.contains(screenData[index].keys.toList()[0])).name; + } catch (e) { + // ---- // + } + } + + return CustomListTile( + onTap: () { + if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') { + logsProvider.setSearchText(screenData[index].keys.toList()[0]); + logsProvider.setSelectedClients(null); + logsProvider.setAppliedFilters( + AppliedFiters( + selectedResultStatus: 'all', + searchText: screenData[index].keys.toList()[0], + clients: null + ) + ); + appConfigProvider.setSelectedScreen(2); + Navigator.pop(context); + } + else if (widget.type == 'topClients') { + logsProvider.setSearchText(null); + logsProvider.setSelectedClients([screenData[index].keys.toList()[0]]); + logsProvider.setAppliedFilters( + AppliedFiters( + selectedResultStatus: 'all', + searchText: null, + clients: [screenData[index].keys.toList()[0]] + ) + ); + appConfigProvider.setSelectedScreen(2); + Navigator.pop(context); + } + }, + onLongPress: () => openOptionsModal( + screenData[index].keys.toList()[0], + widget.type + ), + title: screenData[index].keys.toList()[0], + trailing: Text( + screenData[index].values.toList()[0].toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant + ), + ), + subtitleWidget: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (name != null) ...[ + Text( + name, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).colorScheme.onSurface + ), + ), + const SizedBox(height: 5), + ], + Row( + children: [ + SizedBox( + width: 50, + child: Text( + "${doubleFormat((screenData[index].values.toList()[0]/total*100), Platform.localeName)}%", + style: TextStyle( + color: Theme.of(context).listTileTheme.textColor + ), + ), + ), + const SizedBox(width: 10), + Flexible( + child: LinearPercentIndicator( + animation: true, + lineHeight: 4, + animationDuration: 500, + curve: Curves.easeOut, + percent: screenData[index].values.toList()[0]/total, + barRadius: const Radius.circular(5), + progressColor: Theme.of(context).colorScheme.primary, + backgroundColor: Theme.of(context).colorScheme.surfaceVariant, + ), + ), + const SizedBox(width: 10), + ], + ), + ], + ) + ); + } + ), + ), + if (screenData.isEmpty) Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + AppLocalizations.of(context)!.noItemsSearch, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 22, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + ) + ], + ), + ), + ); + } +} \ No newline at end of file