diff --git a/lib/screens/logs/clients_modal.dart b/lib/screens/logs/clients_modal.dart index fcd9d74..7bb0574 100644 --- a/lib/screens/logs/clients_modal.dart +++ b/lib/screens/logs/clients_modal.dart @@ -32,156 +32,18 @@ class _ClientsModalState extends State { @override Widget build(BuildContext context) { - final logsProvider = Provider.of(context); - final clientsProvider = Provider.of(context); - final height = MediaQuery.of(context).size.height; - void apply() async { - logsProvider.setSelectedClients( - selectedClients.isNotEmpty ? selectedClients : null - ); - - Navigator.pop(context); - } - - Widget listItem({ - required String label, - required void Function() onChanged - }) { - return Material( - color: Colors.transparent, - child: InkWell( - onTap: () => onChanged(), - child: Padding( - padding: const EdgeInsets.only( - left: 24, - top: 8, - right: 12, - bottom: 8 - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( - label, - style: TextStyle( - fontSize: 16, - color: Theme.of(context).colorScheme.onSurface - ), - ), - ), - Checkbox( - value: selectedClients.contains(label), - onChanged: (_) => onChanged(), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5) - ), - ) - ], - ), - ), - ), - ); - } - - void selectAll() { - setState(() { - selectedClients = clientsProvider.clients!.autoClients.map((item) => item.ip).toList(); - }); - } - - void unselectAll() { - setState(() { - selectedClients = []; - }); - } - - Widget content() { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Column( - children: [ - Padding( - padding: const EdgeInsets.only( - top: 24, - bottom: 16, - ), - child: Icon( - Icons.smartphone_rounded, - size: 24, - color: Theme.of(context).listTileTheme.iconColor - ), - ), - Text( - AppLocalizations.of(context)!.clients, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w400, - color: Theme.of(context).colorScheme.onSurface - ), - ), - const SizedBox(height: 16), - ], - ), - Flexible( - child: ListView.builder( - itemCount: clientsProvider.clients!.autoClients.length, - itemBuilder: (context, index) => listItem( - label: clientsProvider.clients!.autoClients[index].ip, - onChanged: () { - if (selectedClients.contains(clientsProvider.clients!.autoClients[index].ip)) { - setState(() { - selectedClients = selectedClients.where( - (item) => item != clientsProvider.clients!.autoClients[index].ip - ).toList(); - }); - } - else { - setState(() { - selectedClients.add(clientsProvider.clients!.autoClients[index].ip); - }); - } - } - ) - ) - ), - Padding( - padding: const EdgeInsets.all(24), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TextButton( - onPressed: selectedClients.length == clientsProvider.clients!.autoClients.length - ? () => unselectAll() - : () => selectAll(), - child: Text( - selectedClients.length == clientsProvider.clients!.autoClients.length - ? AppLocalizations.of(context)!.unselectAll - : AppLocalizations.of(context)!.selectAll - ) - ), - TextButton( - onPressed: apply, - child: Text(AppLocalizations.of(context)!.apply) - ) - ], - ), - ), - if (Platform.isIOS) const SizedBox(height: 16) - ], - ); - } - if (widget.dialog == true) { return Dialog( child: ConstrainedBox( constraints: const BoxConstraints( maxWidth: 500 ), - child: content() + child: _ModalContent( + selectedClients: selectedClients, + onClientsSelected: (v) => setState(() => selectedClients = v), + ) ), ); } @@ -198,9 +60,176 @@ class _ClientsModalState extends State { ), color: Theme.of(context).dialogBackgroundColor ), - child: content() + child: _ModalContent( + selectedClients: selectedClients, + onClientsSelected: (v) => setState(() => selectedClients = v), + ) ), ); } } +} + +class _ModalContent extends StatelessWidget { + final List selectedClients; + final void Function(List) onClientsSelected; + + const _ModalContent({ + Key? key, + required this.selectedClients, + required this.onClientsSelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final clientsProvider = Provider.of(context); + final logsProvider = Provider.of(context); + + void apply() async { + logsProvider.setSelectedClients( + selectedClients.isNotEmpty ? selectedClients : null + ); + + Navigator.pop(context); + } + + void selectAll() { + onClientsSelected( + clientsProvider.clients!.autoClients.map((item) => item.ip).toList() + ); + } + + void unselectAll() { + onClientsSelected([]); + } + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Column( + children: [ + Padding( + padding: const EdgeInsets.only( + top: 24, + bottom: 16, + ), + child: Icon( + Icons.smartphone_rounded, + size: 24, + color: Theme.of(context).listTileTheme.iconColor + ), + ), + Text( + AppLocalizations.of(context)!.clients, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurface + ), + ), + const SizedBox(height: 16), + ], + ), + Flexible( + child: ListView.builder( + itemCount: clientsProvider.clients!.autoClients.length, + itemBuilder: (context, index) => _ListItem( + label: clientsProvider.clients!.autoClients[index].ip, + checkboxActive: selectedClients.contains(clientsProvider.clients!.autoClients[index].ip), + onChanged: (isSelected) { + if (isSelected == true) { + onClientsSelected([ + ...selectedClients, + clientsProvider.clients!.autoClients[index].ip + ]); + } + else { + onClientsSelected( + selectedClients.where( + (item) => item != clientsProvider.clients!.autoClients[index].ip + ).toList() + ); + } + } + ) + ) + ), + Padding( + padding: const EdgeInsets.all(24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: selectedClients.length == clientsProvider.clients!.autoClients.length + ? () => unselectAll() + : () => selectAll(), + child: Text( + selectedClients.length == clientsProvider.clients!.autoClients.length + ? AppLocalizations.of(context)!.unselectAll + : AppLocalizations.of(context)!.selectAll + ) + ), + TextButton( + onPressed: apply, + child: Text(AppLocalizations.of(context)!.apply) + ) + ], + ), + ), + if (Platform.isIOS) const SizedBox(height: 16) + ], + ); + } +} + +class _ListItem extends StatelessWidget { + final String label; + final bool checkboxActive; + final void Function(bool) onChanged; + + const _ListItem({ + Key? key, + required this.label, + required this.checkboxActive, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.transparent, + child: InkWell( + onTap: () => onChanged(!checkboxActive), + child: Padding( + padding: const EdgeInsets.only( + left: 24, + top: 8, + right: 12, + bottom: 8 + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + label, + style: TextStyle( + fontSize: 16, + color: Theme.of(context).colorScheme.onSurface + ), + ), + ), + Checkbox( + value: checkboxActive, + onChanged: (v) => onChanged(!checkboxActive), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5) + ), + ) + ], + ), + ), + ), + ); + } } \ No newline at end of file diff --git a/lib/screens/logs/logs.dart b/lib/screens/logs/logs.dart index b218ad7..863f93d 100644 --- a/lib/screens/logs/logs.dart +++ b/lib/screens/logs/logs.dart @@ -18,8 +18,6 @@ class _LogsState extends State { @override Widget build(BuildContext context) { - - return LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 1000) { diff --git a/lib/screens/logs/logs_filters_modal.dart b/lib/screens/logs/logs_filters_modal.dart index 8f1d7f0..e41efa5 100644 --- a/lib/screens/logs/logs_filters_modal.dart +++ b/lib/screens/logs/logs_filters_modal.dart @@ -15,7 +15,7 @@ import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/providers/clients_provider.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart'; -class LogsFiltersModal extends StatelessWidget { +class LogsFiltersModal extends StatefulWidget { final bool dialog; const LogsFiltersModal({ @@ -24,39 +24,67 @@ class LogsFiltersModal extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) { - final logsProvider = Provider.of(context); - - return LogsFiltersModalWidget( - logsProvider: logsProvider, - dialog: dialog, - ); - } + State createState() => _LogsFiltersModalState(); } -class LogsFiltersModalWidget extends StatefulWidget { - final LogsProvider logsProvider; - final bool dialog; - - const LogsFiltersModalWidget({ - Key? key, - required this.logsProvider, - required this.dialog - }) : super(key: key); - - @override - State createState() => _LogsFiltersModalWidgetState(); -} - -class _LogsFiltersModalWidgetState extends State { +class _LogsFiltersModalState extends State { TextEditingController searchController = TextEditingController(); @override void initState() { - searchController.text = widget.logsProvider.searchText ?? ''; + searchController.text = Provider.of(context, listen: false).searchText ?? ''; super.initState(); } + @override + Widget build(BuildContext context) { + if (widget.dialog == true) { + return Padding( + padding: MediaQuery.of(context).viewInsets, + child: Dialog( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 500 + ), + child: _FiltersList( + searchController: searchController, + onClearSearch: () => setState(() => searchController.text = "") + ) + ) + ), + ); + } + else { + return Padding( + padding: MediaQuery.of(context).viewInsets, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).dialogBackgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28) + ) + ), + child: _FiltersList( + searchController: searchController, + onClearSearch: () => setState(() => searchController.text = "") + ) + ), + ); + } + } +} + +class _FiltersList extends StatelessWidget { + final TextEditingController searchController; + final void Function() onClearSearch; + + const _FiltersList({ + Key? key, + required this.searchController, + required this.onClearSearch, + }) : super(key: key); + @override Widget build(BuildContext context) { final logsProvider = Provider.of(context); @@ -125,200 +153,167 @@ class _LogsFiltersModalWidgetState extends State { } } - Widget content() { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: SingleChildScrollView( - child: Wrap( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - children: [ - Padding( - padding: const EdgeInsets.only( - top: 24, - bottom: 16, - ), - child: Icon( - Icons.filter_list_rounded, - size: 24, - color: Theme.of(context).listTileTheme.iconColor - ), - ), - Text( - AppLocalizations.of(context)!.filters, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w400, - height: 1.3, - color: Theme.of(context).colorScheme.onSurface - ), - ), - const SizedBox(height: 16), - ], - ), - ], - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Row( + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: SingleChildScrollView( + child: Wrap( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( children: [ - Expanded( - child: TextFormField( - controller: searchController, - onChanged: logsProvider.setSearchText, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.search_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - labelText: AppLocalizations.of(context)!.search, - suffixIcon: IconButton( - onPressed: () { - setState(() { - searchController.text = ''; - }); - logsProvider.setSearchText(null); - }, - icon: const Icon(Icons.clear) - ), - ), + Padding( + padding: const EdgeInsets.only( + top: 24, + bottom: 16, ), - ) + child: Icon( + Icons.filter_list_rounded, + size: 24, + color: Theme.of(context).listTileTheme.iconColor + ), + ), + Text( + AppLocalizations.of(context)!.filters, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w400, + height: 1.3, + color: Theme.of(context).colorScheme.onSurface + ), + ), + const SizedBox(height: 16), ], ), - ), - Container(height: 16), - CustomListTile( - title: AppLocalizations.of(context)!.client, - subtitle: logsProvider.selectedClients != null - ? "${logsProvider.selectedClients!.length} ${AppLocalizations.of(context)!.clientsSelected}" - : AppLocalizations.of(context)!.all, - onTap: clientsProvider.loadStatus == LoadStatus.loaded - ? openSelectClients - : null, - disabled: clientsProvider.loadStatus != LoadStatus.loaded, - icon: Icons.smartphone_rounded, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), - trailing: clientsProvider.loadStatus == LoadStatus.loading - ? const SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, + ], + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + children: [ + Expanded( + child: TextFormField( + controller: searchController, + onChanged: logsProvider.setSearchText, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.search_rounded), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + labelText: AppLocalizations.of(context)!.search, + suffixIcon: IconButton( + onPressed: () { + onClearSearch(); + logsProvider.setSearchText(null); + }, + icon: const Icon(Icons.clear) + ), + ), ), ) - : clientsProvider.loadStatus == LoadStatus.error - ? const Icon( - Icons.error_rounded, - color: Colors.red, - ) - : null, - ), - SectionLabel( - label: AppLocalizations.of(context)!.quickFilters, - padding: const EdgeInsets.symmetric( - horizontal: 24, - vertical: 16 - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - FilterChip( - selected: logsProvider.selectedResultStatus == "all", - label: Text(AppLocalizations.of(context)!.all), - onSelected: (_) => logsProvider.setSelectedResultStatus("all") - ), - FilterChip( - selected: logsProvider.selectedResultStatus == "processed" || - logsProvider.selectedResultStatus == "whitelisted", - label: Text(AppLocalizations.of(context)!.allowed), - onSelected: (_) => logsProvider.setSelectedResultStatus("processed") - ), - FilterChip( - selected: logsProvider.selectedResultStatus == "blocked" || - logsProvider.selectedResultStatus == "blocked_safebrowsing" || - logsProvider.selectedResultStatus == "blocked_parental" || - logsProvider.selectedResultStatus == "safe_search", - label: Text(AppLocalizations.of(context)!.blocked), - onSelected: (_) => logsProvider.setSelectedResultStatus("blocked") - ), ], ), - const Padding(padding: EdgeInsets.all(8)), - CustomListTile( - title: AppLocalizations.of(context)!.responseStatus, - subtitle: "${translatedString[logsProvider.selectedResultStatus]}", - onTap: openSelectFilterStatus, - icon: Icons.shield_rounded, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), - ), - ], - ), - ), - ), - Padding( - padding: const EdgeInsets.all(24), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TextButton( - onPressed: () { - searchController.text = ""; - logsProvider.requestResetFilters(); - }, - child: Text(AppLocalizations.of(context)!.resetFilters) ), - TextButton( - onPressed: () { - Navigator.pop(context); - logsProvider.filterLogs(); - }, - child: Text(AppLocalizations.of(context)!.apply) + Container(height: 16), + CustomListTile( + title: AppLocalizations.of(context)!.client, + subtitle: logsProvider.selectedClients != null + ? "${logsProvider.selectedClients!.length} ${AppLocalizations.of(context)!.clientsSelected}" + : AppLocalizations.of(context)!.all, + onTap: clientsProvider.loadStatus == LoadStatus.loaded + ? openSelectClients + : null, + disabled: clientsProvider.loadStatus != LoadStatus.loaded, + icon: Icons.smartphone_rounded, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + trailing: clientsProvider.loadStatus == LoadStatus.loading + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ) + : clientsProvider.loadStatus == LoadStatus.error + ? const Icon( + Icons.error_rounded, + color: Colors.red, + ) + : null, + ), + SectionLabel( + label: AppLocalizations.of(context)!.quickFilters, + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 16 + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + FilterChip( + selected: logsProvider.selectedResultStatus == "all", + label: Text(AppLocalizations.of(context)!.all), + onSelected: (_) => logsProvider.setSelectedResultStatus("all") + ), + FilterChip( + selected: logsProvider.selectedResultStatus == "processed" || + logsProvider.selectedResultStatus == "whitelisted", + label: Text(AppLocalizations.of(context)!.allowed), + onSelected: (_) => logsProvider.setSelectedResultStatus("processed") + ), + FilterChip( + selected: logsProvider.selectedResultStatus == "blocked" || + logsProvider.selectedResultStatus == "blocked_safebrowsing" || + logsProvider.selectedResultStatus == "blocked_parental" || + logsProvider.selectedResultStatus == "safe_search", + label: Text(AppLocalizations.of(context)!.blocked), + onSelected: (_) => logsProvider.setSelectedResultStatus("blocked") + ), + ], + ), + const Padding(padding: EdgeInsets.all(8)), + CustomListTile( + title: AppLocalizations.of(context)!.responseStatus, + subtitle: "${translatedString[logsProvider.selectedResultStatus]}", + onTap: openSelectFilterStatus, + icon: Icons.shield_rounded, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), ), ], ), ), - if (Platform.isIOS) const SizedBox(height: 16) - ], - ); - } - - if (widget.dialog == true) { - return Padding( - padding: MediaQuery.of(context).viewInsets, - child: Dialog( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 500 - ), - child: content() - ) ), - ); - } - else { - return Padding( - padding: MediaQuery.of(context).viewInsets, - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).dialogBackgroundColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(28), - topRight: Radius.circular(28) - ) + Padding( + padding: const EdgeInsets.all(24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: () { + searchController.text = ""; + logsProvider.requestResetFilters(); + }, + child: Text(AppLocalizations.of(context)!.resetFilters) + ), + TextButton( + onPressed: () { + Navigator.pop(context); + logsProvider.filterLogs(); + }, + child: Text(AppLocalizations.of(context)!.apply) + ), + ], ), - child: content() ), - ); - } + if (Platform.isIOS) const SizedBox(height: 16) + ], + ); } } \ No newline at end of file diff --git a/lib/screens/logs/logs_list.dart b/lib/screens/logs/logs_list.dart index b04fe74..72978ce 100644 --- a/lib/screens/logs/logs_list.dart +++ b/lib/screens/logs/logs_list.dart @@ -1,27 +1,19 @@ // ignore_for_file: use_build_context_synchronously -import 'dart:io'; - 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/logs/log_details_screen.dart'; import 'package:adguard_home_manager/screens/logs/log_tile.dart'; -import 'package:adguard_home_manager/screens/logs/logs_config_modal.dart'; -import 'package:adguard_home_manager/screens/logs/logs_filters_modal.dart'; +import 'package:adguard_home_manager/screens/logs/logs_list_appbar.dart'; -import 'package:adguard_home_manager/classes/process_modal.dart'; import 'package:adguard_home_manager/constants/enums.dart'; -import 'package:adguard_home_manager/functions/compare_versions.dart'; -import 'package:adguard_home_manager/functions/desktop_mode.dart'; import 'package:adguard_home_manager/functions/snackbar.dart'; -import 'package:adguard_home_manager/models/applied_filters.dart'; import 'package:adguard_home_manager/models/logs.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/clients_provider.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart'; -import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/status_provider.dart'; class LogsListWidget extends StatefulWidget { @@ -99,292 +91,17 @@ class _LogsListWidgetState extends State { @override Widget build(BuildContext context) { - final serversProvider = Provider.of(context); - final statusProvider = Provider.of(context); - final appConfigProvider = Provider.of(context); final logsProvider = Provider.of(context); - - final width = MediaQuery.of(context).size.width; - - void updateConfig(Map data) async { - ProcessModal processModal = ProcessModal(context: context); - processModal.open(AppLocalizations.of(context)!.updatingSettings); - - final result = serverVersionIsAhead( - currentVersion: statusProvider.serverStatus!.serverVersion, - referenceVersion: 'v0.107.28', - referenceVersionBeta: 'v0.108.0-b.33' - ) == true - ? await serversProvider.apiClient!.updateQueryLogParameters(data: data) - : await serversProvider.apiClient!.updateQueryLogParametersLegacy(data: data); - - processModal.close(); - - if (result['result'] == 'success') { - showSnacbkar( - appConfigProvider: appConfigProvider, - label: AppLocalizations.of(context)!.logsConfigUpdated, - color: Colors.green - ); - } - else { - appConfigProvider.addLog(result['log']); - - showSnacbkar( - appConfigProvider: appConfigProvider, - label: AppLocalizations.of(context)!.logsConfigNotUpdated, - color: Colors.red - ); - } - } - - void clearQueries() async { - ProcessModal processModal = ProcessModal(context: context); - processModal.open(AppLocalizations.of(context)!.updatingSettings); - - final result = await serversProvider.apiClient!.clearLogs(); - - processModal.close(); - - if (result['result'] == 'success') { - showSnacbkar( - appConfigProvider: appConfigProvider, - label: AppLocalizations.of(context)!.logsCleared, - color: Colors.green - ); - } - else { - showSnacbkar( - appConfigProvider: appConfigProvider, - label: AppLocalizations.of(context)!.logsNotCleared, - color: Colors.red - ); - } - } - - - void openFilersModal() { - if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) { - showDialog( - context: context, - builder: (context) => const LogsFiltersModal( - dialog: true, - ), - barrierDismissible: false - ); - } - else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - builder: (context) => const LogsFiltersModal( - dialog: false, - ), - backgroundColor: Colors.transparent, - isScrollControlled: true - ); - } - } - - final Map translatedString = { - "all": AppLocalizations.of(context)!.all, - "filtered": AppLocalizations.of(context)!.filtered, - "processed": AppLocalizations.of(context)!.processedRow, - "whitelisted": AppLocalizations.of(context)!.processedWhitelistRow, - "blocked": AppLocalizations.of(context)!.blocked, - "blocked_safebrowsing": AppLocalizations.of(context)!.blockedSafeBrowsingRow, - "blocked_parental": AppLocalizations.of(context)!.blockedParentalRow, - "safe_search": AppLocalizations.of(context)!.safeSearch, - }; return Scaffold( body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) => [ SliverOverlapAbsorber( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar.large( - pinned: true, - floating: true, - centerTitle: false, - forceElevated: innerBoxIsScrolled, - surfaceTintColor: isDesktop(width) ? Colors.transparent : null, - title: Text(AppLocalizations.of(context)!.logs), - expandedHeight: logsProvider.appliedFilters.searchText != null || logsProvider.appliedFilters.selectedResultStatus != 'all' || logsProvider.appliedFilters.clients != null - ? 170 : null, - actions: [ - if (!(Platform.isAndroid || Platform.isIOS)) IconButton( - onPressed: () => logsProvider.fetchLogs(inOffset: 0), - icon: const Icon(Icons.refresh_rounded), - tooltip: AppLocalizations.of(context)!.refresh, - ), - logsProvider.loadStatus == LoadStatus.loaded - ? IconButton( - onPressed: openFilersModal, - icon: const Icon(Icons.filter_list_rounded), - tooltip: AppLocalizations.of(context)!.filters, - ) - : const SizedBox(), - if (statusProvider.serverStatus != null) IconButton( - tooltip: AppLocalizations.of(context)!.settings, - onPressed: () => { - if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) { - showDialog( - context: context, - builder: (context) => LogsConfigModal( - onConfirm: updateConfig, - onClear: clearQueries, - dialog: true, - serverVersion: statusProvider.serverStatus!.serverVersion, - ), - barrierDismissible: false - ) - } - else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - builder: (context) => LogsConfigModal( - onConfirm: updateConfig, - onClear: clearQueries, - dialog: false, - serverVersion: statusProvider.serverStatus!.serverVersion, - ), - backgroundColor: Colors.transparent, - isScrollControlled: true - ) - } - }, - icon: const Icon(Icons.settings) - ), - const SizedBox(width: 5), - ], - bottom: logsProvider.appliedFilters.searchText != null || logsProvider.appliedFilters.selectedResultStatus != 'all' || logsProvider.appliedFilters.clients != null - ? PreferredSize( - preferredSize: const Size(double.maxFinite, 70), - child: Container( - height: 50, - width: double.maxFinite, - padding: const EdgeInsets.only(bottom: 10), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: showDivider == true - ? Theme.of(context).colorScheme.onSurface.withOpacity(0.1) - : Colors.transparent, - ) - ) - ), - child: ListView( - scrollDirection: Axis.horizontal, - children: [ - if (logsProvider.appliedFilters.searchText != null) ...[ - const SizedBox(width: 15), - Chip( - avatar: const Icon( - Icons.search_rounded, - ), - label: Row( - children: [ - Text( - logsProvider.appliedFilters.searchText!, - ), - ], - ), - deleteIcon: const Icon( - Icons.clear, - size: 18, - ), - onDeleted: () { - logsProvider.setAppliedFilters( - AppliedFiters( - selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus, - searchText: null, - clients: logsProvider.appliedFilters.clients - ) - ); - logsProvider.setSearchText(null); - logsProvider.fetchLogs( - inOffset: 0, - searchText: '' - ); - }, - ), - ], - if (logsProvider.appliedFilters.selectedResultStatus != 'all') ...[ - const SizedBox(width: 15), - Chip( - avatar: const Icon( - Icons.shield_rounded, - ), - label: Row( - children: [ - Text( - translatedString[logsProvider.appliedFilters.selectedResultStatus]!, - ), - ], - ), - deleteIcon: const Icon( - Icons.clear, - size: 18, - ), - onDeleted: () { - logsProvider.setAppliedFilters( - AppliedFiters( - selectedResultStatus: 'all', - searchText: logsProvider.appliedFilters.searchText, - clients: logsProvider.appliedFilters.clients - ) - ); - logsProvider.setSelectedResultStatus('all'); - logsProvider.fetchLogs( - inOffset: 0, - responseStatus: 'all' - ); - }, - ), - ], - if (logsProvider.appliedFilters.clients != null) ...[ - const SizedBox(width: 15), - Chip( - avatar: const Icon( - Icons.smartphone_rounded, - ), - label: Row( - children: [ - Text( - logsProvider.appliedFilters.clients!.length == 1 - ? logsProvider.appliedFilters.clients![0] - : "${logsProvider.appliedFilters.clients!.length} ${AppLocalizations.of(context)!.clients}", - ), - ], - ), - deleteIcon: const Icon( - Icons.clear, - size: 18, - ), - onDeleted: () { - logsProvider.setAppliedFilters( - AppliedFiters( - selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus, - searchText: logsProvider.appliedFilters.searchText, - clients: null - ) - ); - logsProvider.setSelectedClients(null); - logsProvider.fetchLogs( - inOffset: 0, - responseStatus: logsProvider.appliedFilters.selectedResultStatus - ); - }, - ), - ], - const SizedBox(width: 15), - ], - ), - ) - ) - : null, - ), + sliver: LogsListAppBar( + innerBoxIsScrolled: innerBoxIsScrolled, + showDivider: showDivider, + ) ) ], body: Builder( diff --git a/lib/screens/logs/logs_list_appbar.dart b/lib/screens/logs/logs_list_appbar.dart new file mode 100644 index 0000000..39b4548 --- /dev/null +++ b/lib/screens/logs/logs_list_appbar.dart @@ -0,0 +1,317 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:io'; + +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/logs/logs_config_modal.dart'; +import 'package:adguard_home_manager/screens/logs/logs_filters_modal.dart'; + +import 'package:adguard_home_manager/classes/process_modal.dart'; +import 'package:adguard_home_manager/constants/enums.dart'; +import 'package:adguard_home_manager/functions/compare_versions.dart'; +import 'package:adguard_home_manager/functions/desktop_mode.dart'; +import 'package:adguard_home_manager/functions/snackbar.dart'; +import 'package:adguard_home_manager/models/applied_filters.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/providers/status_provider.dart'; +import 'package:adguard_home_manager/providers/logs_provider.dart'; + +class LogsListAppBar extends StatelessWidget { + final bool innerBoxIsScrolled; + final bool showDivider; + + const LogsListAppBar({ + Key? key, + required this.innerBoxIsScrolled, + required this.showDivider, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final logsProvider = Provider.of(context); + final statusProvider = Provider.of(context); + final serversProvider = Provider.of(context); + final appConfigProvider = Provider.of(context); + + final width = MediaQuery.of(context).size.width; + + void updateConfig(Map data) async { + ProcessModal processModal = ProcessModal(context: context); + processModal.open(AppLocalizations.of(context)!.updatingSettings); + + final result = serverVersionIsAhead( + currentVersion: statusProvider.serverStatus!.serverVersion, + referenceVersion: 'v0.107.28', + referenceVersionBeta: 'v0.108.0-b.33' + ) == true + ? await serversProvider.apiClient!.updateQueryLogParameters(data: data) + : await serversProvider.apiClient!.updateQueryLogParametersLegacy(data: data); + + processModal.close(); + + if (result['result'] == 'success') { + showSnacbkar( + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.logsConfigUpdated, + color: Colors.green + ); + } + else { + appConfigProvider.addLog(result['log']); + + showSnacbkar( + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.logsConfigNotUpdated, + color: Colors.red + ); + } + } + + void clearQueries() async { + ProcessModal processModal = ProcessModal(context: context); + processModal.open(AppLocalizations.of(context)!.updatingSettings); + + final result = await serversProvider.apiClient!.clearLogs(); + + processModal.close(); + + if (result['result'] == 'success') { + showSnacbkar( + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.logsCleared, + color: Colors.green + ); + } + else { + showSnacbkar( + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.logsNotCleared, + color: Colors.red + ); + } + } + + + void openFilersModal() { + if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) { + showDialog( + context: context, + builder: (context) => const LogsFiltersModal( + dialog: true, + ), + barrierDismissible: false + ); + } + else { + showModalBottomSheet( + context: context, + useRootNavigator: true, + builder: (context) => const LogsFiltersModal( + dialog: false, + ), + backgroundColor: Colors.transparent, + isScrollControlled: true + ); + } + } + + final Map translatedString = { + "all": AppLocalizations.of(context)!.all, + "filtered": AppLocalizations.of(context)!.filtered, + "processed": AppLocalizations.of(context)!.processedRow, + "whitelisted": AppLocalizations.of(context)!.processedWhitelistRow, + "blocked": AppLocalizations.of(context)!.blocked, + "blocked_safebrowsing": AppLocalizations.of(context)!.blockedSafeBrowsingRow, + "blocked_parental": AppLocalizations.of(context)!.blockedParentalRow, + "safe_search": AppLocalizations.of(context)!.safeSearch, + }; + + return SliverAppBar.large( + pinned: true, + floating: true, + centerTitle: false, + forceElevated: innerBoxIsScrolled, + surfaceTintColor: isDesktop(width) ? Colors.transparent : null, + title: Text(AppLocalizations.of(context)!.logs), + expandedHeight: logsProvider.appliedFilters.searchText != null || logsProvider.appliedFilters.selectedResultStatus != 'all' || logsProvider.appliedFilters.clients != null + ? 170 : null, + actions: [ + if (!(Platform.isAndroid || Platform.isIOS)) IconButton( + onPressed: () => logsProvider.fetchLogs(inOffset: 0), + icon: const Icon(Icons.refresh_rounded), + tooltip: AppLocalizations.of(context)!.refresh, + ), + logsProvider.loadStatus == LoadStatus.loaded + ? IconButton( + onPressed: openFilersModal, + icon: const Icon(Icons.filter_list_rounded), + tooltip: AppLocalizations.of(context)!.filters, + ) + : const SizedBox(), + if (statusProvider.serverStatus != null) IconButton( + tooltip: AppLocalizations.of(context)!.settings, + onPressed: () => { + if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) { + showDialog( + context: context, + builder: (context) => LogsConfigModal( + onConfirm: updateConfig, + onClear: clearQueries, + dialog: true, + serverVersion: statusProvider.serverStatus!.serverVersion, + ), + barrierDismissible: false + ) + } + else { + showModalBottomSheet( + context: context, + useRootNavigator: true, + builder: (context) => LogsConfigModal( + onConfirm: updateConfig, + onClear: clearQueries, + dialog: false, + serverVersion: statusProvider.serverStatus!.serverVersion, + ), + backgroundColor: Colors.transparent, + isScrollControlled: true + ) + } + }, + icon: const Icon(Icons.settings) + ), + const SizedBox(width: 5), + ], + bottom: logsProvider.appliedFilters.searchText != null || logsProvider.appliedFilters.selectedResultStatus != 'all' || logsProvider.appliedFilters.clients != null + ? PreferredSize( + preferredSize: const Size(double.maxFinite, 70), + child: Container( + height: 50, + width: double.maxFinite, + padding: const EdgeInsets.only(bottom: 10), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: showDivider == true + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.1) + : Colors.transparent, + ) + ) + ), + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + if (logsProvider.appliedFilters.searchText != null) ...[ + const SizedBox(width: 15), + Chip( + avatar: const Icon( + Icons.search_rounded, + ), + label: Row( + children: [ + Text( + logsProvider.appliedFilters.searchText!, + ), + ], + ), + deleteIcon: const Icon( + Icons.clear, + size: 18, + ), + onDeleted: () { + logsProvider.setAppliedFilters( + AppliedFiters( + selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus, + searchText: null, + clients: logsProvider.appliedFilters.clients + ) + ); + logsProvider.setSearchText(null); + logsProvider.fetchLogs( + inOffset: 0, + searchText: '' + ); + }, + ), + ], + if (logsProvider.appliedFilters.selectedResultStatus != 'all') ...[ + const SizedBox(width: 15), + Chip( + avatar: const Icon( + Icons.shield_rounded, + ), + label: Row( + children: [ + Text( + translatedString[logsProvider.appliedFilters.selectedResultStatus]!, + ), + ], + ), + deleteIcon: const Icon( + Icons.clear, + size: 18, + ), + onDeleted: () { + logsProvider.setAppliedFilters( + AppliedFiters( + selectedResultStatus: 'all', + searchText: logsProvider.appliedFilters.searchText, + clients: logsProvider.appliedFilters.clients + ) + ); + logsProvider.setSelectedResultStatus('all'); + logsProvider.fetchLogs( + inOffset: 0, + responseStatus: 'all' + ); + }, + ), + ], + if (logsProvider.appliedFilters.clients != null) ...[ + const SizedBox(width: 15), + Chip( + avatar: const Icon( + Icons.smartphone_rounded, + ), + label: Row( + children: [ + Text( + logsProvider.appliedFilters.clients!.length == 1 + ? logsProvider.appliedFilters.clients![0] + : "${logsProvider.appliedFilters.clients!.length} ${AppLocalizations.of(context)!.clients}", + ), + ], + ), + deleteIcon: const Icon( + Icons.clear, + size: 18, + ), + onDeleted: () { + logsProvider.setAppliedFilters( + AppliedFiters( + selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus, + searchText: logsProvider.appliedFilters.searchText, + clients: null + ) + ); + logsProvider.setSelectedClients(null); + logsProvider.fetchLogs( + inOffset: 0, + responseStatus: logsProvider.appliedFilters.selectedResultStatus + ); + }, + ), + ], + const SizedBox(width: 15), + ], + ), + ) + ) + : null, + ); + } +} \ No newline at end of file