diff --git a/lib/providers/logs_provider.dart b/lib/providers/logs_provider.dart index 0b32a2a..4123e9b 100644 --- a/lib/providers/logs_provider.dart +++ b/lib/providers/logs_provider.dart @@ -7,6 +7,7 @@ class LogsProvider with ChangeNotifier { DateTime? _logsOlderThan; String _selectedResultStatus = 'all'; + String? _searchIpDomain; int _logsQuantity = 100; int _offset = 0; @@ -26,6 +27,10 @@ class LogsProvider with ChangeNotifier { String get selectedResultStatus { return _selectedResultStatus; } + + String? get searchIpDomain { + return _searchIpDomain; + } int get logsQuantity { return _logsQuantity; @@ -55,6 +60,7 @@ class LogsProvider with ChangeNotifier { _logsOlderThan = null; _offset = 0; _selectedResultStatus = 'all'; + _searchIpDomain = null; notifyListeners(); } @@ -71,4 +77,8 @@ class LogsProvider with ChangeNotifier { _selectedResultStatus = value; notifyListeners(); } + void setSearchIpDomain(String value) { + _searchIpDomain = value; + notifyListeners(); + } } \ No newline at end of file diff --git a/lib/screens/logs/filter_status_modal.dart b/lib/screens/logs/filter_status_modal.dart index 6873fee..5452f67 100644 --- a/lib/screens/logs/filter_status_modal.dart +++ b/lib/screens/logs/filter_status_modal.dart @@ -2,9 +2,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:adguard_home_manager/services/http_requests.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/logs_provider.dart'; class FilterStatusModal extends StatefulWidget { @@ -31,30 +28,11 @@ class _FilterStatusModalState extends State { @override Widget build(BuildContext context) { final logsProvider = Provider.of(context); - final serversProvider = Provider.of(context); - final appConfigProvider = Provider.of(context); void apply() async { - logsProvider.setLoadStatus(0); - logsProvider.setSelectedResultStatus(selectedResultStatus); Navigator.pop(context); - - final result = await getLogs( - server: serversProvider.selectedServer!, - count: logsProvider.logsQuantity, - responseStatus: selectedResultStatus - ); - - if (result['result'] == 'success') { - logsProvider.setLogsData(result['data']); - logsProvider.setLoadStatus(1); - } - else { - appConfigProvider.addLog(result['log']); - logsProvider.setLoadStatus(2); - } } Widget filterStatusListItem({ diff --git a/lib/screens/logs/logs_filters_modal.dart b/lib/screens/logs/logs_filters_modal.dart index 340d76d..61458f0 100644 --- a/lib/screens/logs/logs_filters_modal.dart +++ b/lib/screens/logs/logs_filters_modal.dart @@ -14,6 +14,38 @@ import 'package:adguard_home_manager/providers/logs_provider.dart'; class LogsFiltersModal extends StatelessWidget { const LogsFiltersModal({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + final logsProvider = Provider.of(context); + + return LogsFiltersModalWidget( + logsProvider: logsProvider + ); + } +} + +class LogsFiltersModalWidget extends StatefulWidget { + final LogsProvider logsProvider; + + const LogsFiltersModalWidget({ + Key? key, + required this.logsProvider + }) : super(key: key); + + @override + State createState() => _LogsFiltersModalWidgetState(); +} + +class _LogsFiltersModalWidgetState extends State { + TextEditingController addressController = TextEditingController(); + String? addressFieldError; + + @override + void initState() { + addressController.text = widget.logsProvider.searchIpDomain ?? ''; + super.initState(); + } + @override Widget build(BuildContext context) { final logsProvider = Provider.of(context); @@ -56,24 +88,6 @@ class LogsFiltersModal extends StatelessWidget { ).toUtc(); logsProvider.setLogsOlderThan(value); - - logsProvider.setLoadStatus(0); - - logsProvider.setOffset(0); - - final result = await getLogs( - server: serversProvider.selectedServer!, - count: logsProvider.logsQuantity, - olderThan: logsProvider.logsOlderThan - ); - if (result['result'] == 'success') { - logsProvider.setLogsData(result['data']); - logsProvider.setLoadStatus(1); - } - else { - appConfigProvider.addLog(result['log']); - logsProvider.setLoadStatus(2); - } } } } @@ -109,138 +123,220 @@ class LogsFiltersModal extends StatelessWidget { ); } - return Container( - height: 350, - decoration: BoxDecoration( - color: Theme.of(context).dialogBackgroundColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(28), - topRight: Radius.circular(28) - ) - ), - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only( - top: 24, - bottom: 20, - ), - child: Icon( - Icons.filter_list_rounded, - size: 26, - ), - ), - Text( - AppLocalizations.of(context)!.filters, - style: const TextStyle( - fontSize: 24 - ), - ), - const SizedBox(height: 20), - Expanded( - child: ListView( - physics: const NeverScrollableScrollPhysics(), - children: [ - Material( - color: Colors.transparent, - child: InkWell( - onTap: selectTime, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Row( - children: [ - const Icon( - Icons.schedule, - size: 24, - color: Colors.grey, - ), - const SizedBox(width: 20), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context)!.logsOlderThan, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500 - ), - ), - const SizedBox(height: 5), - Text( - logsProvider.logsOlderThan != null - ? formatTimestampUTC(logsProvider.logsOlderThan!, 'HH:mm - dd/MM/yyyy') - : AppLocalizations.of(context)!.notSelected, - style: const TextStyle( - fontSize: 14, - color: Colors.grey - ), - ) - ], - ) - ], - ), - ), - ), - ), - Material( - color: Colors.transparent, - child: InkWell( - onTap: openSelectFilterStatus, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Row( - children: [ - const Icon( - Icons.shield_rounded, - size: 24, - color: Colors.grey, - ), - const SizedBox(width: 20), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context)!.responseStatus, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500 - ), - ), - const SizedBox(height: 5), - Text( - "${translatedString[logsProvider.selectedResultStatus]}", - style: const TextStyle( - fontSize: 14, - color: Colors.grey - ), - ) - ], - ) - ], - ), - ), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TextButton( - onPressed: resetFilters, - child: Text(AppLocalizations.of(context)!.resetFilters) - ), - TextButton( - onPressed: () => Navigator.pop(context), - child: Text(AppLocalizations.of(context)!.close) - ), - ], - ), + void validateAddress(String? value) { + if (value != null && value != '') { + RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$'); + RegExp domain = RegExp(r'^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$'); + if (ipAddress.hasMatch(value) == true || domain.hasMatch(value) == true) { + setState(() { + addressFieldError = null; + }); + logsProvider.setSearchIpDomain(addressController.text); + } + else { + setState(() { + addressFieldError = AppLocalizations.of(context)!.invalidIpDomain; + }); + } + } + else { + setState(() { + addressFieldError = AppLocalizations.of(context)!.ipDomainNotEmpty; + }); + } + } + + void filterLogs() async { + Navigator.pop(context); + + logsProvider.setLoadStatus(0); + + logsProvider.setOffset(0); + + final result = await getLogs( + server: serversProvider.selectedServer!, + count: logsProvider.logsQuantity, + olderThan: logsProvider.logsOlderThan, + responseStatus: logsProvider.selectedResultStatus, + search: logsProvider.searchIpDomain, + ); + if (result['result'] == 'success') { + logsProvider.setLogsData(result['data']); + logsProvider.setLoadStatus(1); + } + else { + appConfigProvider.addLog(result['log']); + logsProvider.setLoadStatus(2); + } + } + + return Padding( + padding: MediaQuery.of(context).viewInsets, + child: Container( + height: 470, + decoration: BoxDecoration( + color: Theme.of(context).dialogBackgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28) ) - ], + ), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only( + top: 24, + bottom: 20, + ), + child: Icon( + Icons.filter_list_rounded, + size: 26, + ), + ), + Text( + AppLocalizations.of(context)!.filters, + style: const TextStyle( + fontSize: 24 + ), + ), + const SizedBox(height: 20), + Expanded( + child: ListView( + physics: const NeverScrollableScrollPhysics(), + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + child: Row( + children: [ + SizedBox( + width: MediaQuery.of(context).size.width - 108, + child: TextFormField( + controller: addressController, + onChanged: validateAddress, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.link_rounded), + errorText: addressFieldError, + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + labelText: AppLocalizations.of(context)!.ipDomain, + ), + ), + ), + const SizedBox(width: 20), + IconButton( + onPressed: () => setState(() { + addressController.text = ''; + addressFieldError = null; + }), + icon: const Icon(Icons.clear) + ) + ], + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onTap: selectTime, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Row( + children: [ + const Icon( + Icons.schedule, + size: 24, + color: Colors.grey, + ), + const SizedBox(width: 20), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.logsOlderThan, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500 + ), + ), + const SizedBox(height: 5), + Text( + logsProvider.logsOlderThan != null + ? formatTimestampUTC(logsProvider.logsOlderThan!, 'HH:mm - dd/MM/yyyy') + : AppLocalizations.of(context)!.notSelected, + style: const TextStyle( + fontSize: 14, + color: Colors.grey + ), + ) + ], + ) + ], + ), + ), + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onTap: openSelectFilterStatus, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Row( + children: [ + const Icon( + Icons.shield_rounded, + size: 24, + color: Colors.grey, + ), + const SizedBox(width: 20), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.responseStatus, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500 + ), + ), + const SizedBox(height: 5), + Text( + "${translatedString[logsProvider.selectedResultStatus]}", + style: const TextStyle( + fontSize: 14, + color: Colors.grey + ), + ) + ], + ) + ], + ), + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: resetFilters, + child: Text(AppLocalizations.of(context)!.resetFilters) + ), + TextButton( + onPressed: filterLogs, + child: Text(AppLocalizations.of(context)!.apply) + ), + ], + ), + ) + ], + ), ), ); } diff --git a/lib/services/http_requests.dart b/lib/services/http_requests.dart index fb49b0f..1290380 100644 --- a/lib/services/http_requests.dart +++ b/lib/services/http_requests.dart @@ -366,9 +366,10 @@ Future getLogs({ int? offset, DateTime? olderThan, String? responseStatus, + String? search }) async { final result = await getRequest( - urlPath: '/querylog?limit=$count${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}${responseStatus != null ? '&response_status=$responseStatus' : ''}', + urlPath: '/querylog?limit=$count${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}${responseStatus != null ? '&response_status=$responseStatus' : ''}${search != null ? '&search=$search' : ''}', server: server );