Added date filtering logs

This commit is contained in:
Juan Gilsanz Polo 2022-10-02 03:58:02 +02:00
parent 84df416011
commit f6b747f729
12 changed files with 432 additions and 72 deletions

View file

@ -6,6 +6,13 @@ String formatTimestamp(DateTime timestamp, String format) {
return f.format(timestamp);
}
String formatTimestampUTC(DateTime timestamp, String format) {
final DateFormat dateFormat = DateFormat(format);
final String utcDate = dateFormat.format(DateTime.parse(timestamp.toString()));
final String localDate = dateFormat.parse(utcDate, true).toLocal().toIso8601String();
return dateFormat.format(DateTime.parse(localDate));
}
String formatTimeOfDay(TimeOfDay timestamp, String format) {
DateFormat f = DateFormat(format);
return f.format(DateTime(0, 0, 0, timestamp.hour, timestamp.minute));

View file

@ -134,5 +134,13 @@
"unblockDomain": "Unblock domain",
"userFilteringRulesNotUpdated": "User filtering rules could not be updated",
"userFilteringRulesUpdated": "User filtering rules updated successfully",
"savingUserFilters": "Saving user filters..."
"savingUserFilters": "Saving user filters...",
"filters": "Filters",
"logsOlderThan": "Logs older than",
"responseStatus": "Response status",
"selectTime": "Select time",
"notSelected": "Not selected",
"resetFilters": "Reset filters",
"noLogsDisplay": "No logs to display",
"noLogsThatOld": "It's possible that there are no logs saved for that selected time. Try selecting a more recent time."
}

View file

@ -134,5 +134,13 @@
"unblockDomain": "Desbloquear dominio",
"userFilteringRulesNotUpdated": "No se pudieron actualizar las reglas de filtrado del usuario",
"userFilteringRulesUpdated": "Reglas de filtrado del usuario actualizadas correctamente",
"savingUserFilters": "Guardando filtros de usuario..."
"savingUserFilters": "Guardando filtros de usuario...",
"filters": "Filtros",
"logsOlderThan": "Logs anteriores a",
"responseStatus": "Estado de la respuesta",
"selectTime": "Seleccionar hora",
"notSelected": "No seleccionado",
"resetFilters": "Resetear filtros",
"noLogsDisplay": "No hay registros para mostrar",
"noLogsThatOld": "Es posible que no haya registros guardados para ese tiempo seleccionado. Prueba a seleccionar un tiempo más reciente."
}

View file

@ -14,6 +14,7 @@ import 'package:adguard_home_manager/base.dart';
import 'package:adguard_home_manager/classes/http_override.dart';
import 'package:adguard_home_manager/services/database.dart';
import 'package:adguard_home_manager/providers/logs_provider.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/config/theme.dart';
@ -27,6 +28,7 @@ void main() async {
AppConfigProvider appConfigProvider = AppConfigProvider();
ServersProvider serversProvider = ServersProvider();
LogsProvider logsProvider = LogsProvider();
final dbData = await loadDb();
@ -60,6 +62,9 @@ void main() async {
ChangeNotifierProvider(
create: ((context) => appConfigProvider)
),
ChangeNotifierProvider(
create: ((context) => logsProvider)
),
],
child: const Main(),
)

View file

@ -16,21 +16,21 @@ String logsToJson(LogsData data) => json.encode(data.toJson());
class LogsData {
List<Log> data;
final DateTime oldest;
final DateTime? oldest;
LogsData({
required this.data,
required this.oldest,
this.oldest,
});
factory LogsData.fromJson(Map<String, dynamic> json) => LogsData(
data: List<Log>.from(json["data"].map((x) => Log.fromJson(x))),
oldest: DateTime.parse(json["oldest"]),
oldest: json["oldest"] != '' ? DateTime.parse(json["oldest"]) : null,
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
"oldest": oldest.toIso8601String(),
"oldest": oldest != null ? oldest!.toIso8601String() : null,
};
}

View file

@ -0,0 +1,62 @@
import 'package:adguard_home_manager/models/logs.dart';
import 'package:flutter/material.dart';
class LogsProvider with ChangeNotifier {
int _loadStatus = 0;
LogsData? _logsData;
DateTime? _logsOlderThan;
int _logsQuantity = 100;
int _offset = 0;
int get loadStatus {
return _loadStatus;
}
LogsData? get logsData {
return _logsData;
}
DateTime? get logsOlderThan {
return _logsOlderThan;
}
int get logsQuantity {
return _logsQuantity;
}
int get offset {
return _offset;
}
void setLoadStatus(int value) {
_loadStatus = value;
notifyListeners();
}
void setLogsData(LogsData data) {
_logsData = data;
notifyListeners();
}
void setLogsOlderThan(DateTime? value) {
_logsOlderThan = value;
notifyListeners();
}
void resetFilters() {
_logsOlderThan = null;
_offset = 0;
notifyListeners();
}
void setLogsQuantity(int value) {
_logsQuantity = value;
notifyListeners();
}
void setOffset(int value) {
_offset = value;
}
}

View file

@ -1,14 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/logs/logs_filters_modal.dart';
class LogsAppBar extends StatelessWidget with PreferredSizeWidget {
const LogsAppBar({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
void openFilersModal() {
showModalBottomSheet(
context: context,
builder: (context) => const LogsFiltersModal(),
backgroundColor: Colors.transparent,
isScrollControlled: true
);
}
return AppBar(
title: Text(AppLocalizations.of(context)!.logs),
centerTitle: true,
actions: [
IconButton(
onPressed: openFilersModal,
icon: const Icon(Icons.filter_list_rounded)
),
const SizedBox(width: 5),
],
);
}

View file

@ -143,7 +143,7 @@ class LogDetailsModal extends StatelessWidget {
LogListTile(
icon: Icons.schedule,
title: AppLocalizations.of(context)!.time,
subtitle: formatTimestamp(log.time, 'HH:mm:ss')
subtitle: formatTimestampUTC(log.time, 'HH:mm:ss')
),
Padding(
padding: const EdgeInsets.all(20),

View file

@ -200,7 +200,7 @@ class LogTile extends StatelessWidget {
],
),
Text(
formatTimestamp(log.time, 'HH:mm:ss')
formatTimestampUTC(log.time, 'HH:mm:ss')
),
],
),

View file

@ -6,6 +6,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/logs/log_tile.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart';
import 'package:adguard_home_manager/models/filtering_status.dart';
import 'package:adguard_home_manager/models/app_log.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
@ -21,25 +22,26 @@ class Logs extends StatelessWidget {
Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
final logsProvider = Provider.of<LogsProvider>(context);
return LogsWidget(
server: serversProvider.selectedServer!,
createLog: appConfigProvider.addLog,
setFilteringStatus: serversProvider.setFilteringStatus,
serversProvider: serversProvider,
appConfigProvider: appConfigProvider,
logsProvider: logsProvider,
);
}
}
class LogsWidget extends StatefulWidget {
final Server server;
final void Function(AppLog) createLog;
final void Function(FilteringStatus) setFilteringStatus;
final ServersProvider serversProvider;
final AppConfigProvider appConfigProvider;
final LogsProvider logsProvider;
const LogsWidget({
Key? key,
required this.server,
required this.createLog,
required this.setFilteringStatus,
required this.serversProvider,
required this.appConfigProvider,
required this.logsProvider,
}) : super(key: key);
@override
@ -47,14 +49,6 @@ class LogsWidget extends StatefulWidget {
}
class _LogsWidgetState extends State<LogsWidget> {
LogsList logsList = LogsList(
loadStatus: 0,
logsData: null
);
int itemsPerLoad = 100;
int offset = 0;
late ScrollController scrollController;
bool isLoadingMore = false;
@ -63,42 +57,44 @@ class _LogsWidgetState extends State<LogsWidget> {
int? inOffset,
bool? loadingMore
}) async {
int offst = inOffset ?? offset;
int offst = inOffset ?? widget.logsProvider.offset;
if (loadingMore != null && loadingMore == true) {
setState(() => isLoadingMore = true);
}
final result = await getLogs(server: widget.server, count: itemsPerLoad, offset: offst);
final result = await getLogs(
server: widget.serversProvider.selectedServer!,
count: widget.logsProvider.logsQuantity,
offset: offst
);
if (loadingMore != null && loadingMore == true) {
setState(() => isLoadingMore = false);
}
if (result['result'] == 'success') {
setState(() {
offset = inOffset != null ? inOffset+itemsPerLoad : offset+itemsPerLoad;
if (loadingMore != null && loadingMore == true) {
logsList.logsData!.data = [...logsList.logsData!.data, ...result['data'].data];
widget.logsProvider.setOffset(inOffset != null ? inOffset+widget.logsProvider.logsQuantity : widget.logsProvider.offset+widget.logsProvider.logsQuantity);
if (loadingMore != null && loadingMore == true && widget.logsProvider.logsData != null) {
LogsData newLogsData = result['data'];
newLogsData.data = [...widget.logsProvider.logsData!.data, ...result['data'].data];
widget.logsProvider.setLogsData(newLogsData);
}
else {
logsList.logsData = result['data'];
widget.logsProvider.setLogsData(result['data']);
}
logsList.loadStatus = 1;
});
widget.logsProvider.setLoadStatus(1);
}
else {
setState(() {
logsList.loadStatus = 2;
});
widget.createLog(result['log']);
widget.logsProvider.setLoadStatus(2);
widget.appConfigProvider.addLog(result['log']);
}
}
void fetchFilteringRules() async {
final result = await getFilteringRules(server: widget.server);
final result = await getFilteringRules(server: widget.serversProvider.selectedServer!);
if (result['result'] == 'success') {
widget.setFilteringStatus(result['data']);
widget.serversProvider.setFilteringStatus(result['data']);
}
else {
ScaffoldMessenger.of(context).showSnackBar(
@ -126,7 +122,9 @@ class _LogsWidgetState extends State<LogsWidget> {
@override
Widget build(BuildContext context) {
switch (logsList.loadStatus) {
final logsProvider = Provider.of<LogsProvider>(context);
switch (logsProvider.loadStatus) {
case 0:
return SizedBox(
width: double.maxFinite,
@ -153,14 +151,15 @@ class _LogsWidgetState extends State<LogsWidget> {
onRefresh: () async {
await fetchLogs(inOffset: 0);
},
child: ListView.builder(
child: logsProvider.logsData!.data.isNotEmpty
? ListView.builder(
controller: scrollController,
padding: const EdgeInsets.only(top: 0),
itemCount: isLoadingMore == true
? logsList.logsData!.data.length+1
: logsList.logsData!.data.length,
? logsProvider.logsData!.data.length+1
: logsProvider.logsData!.data.length,
itemBuilder: (context, index) {
if (isLoadingMore == true && index == logsList.logsData!.data.length) {
if (isLoadingMore == true && index == logsProvider.logsData!.data.length) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Center(
@ -170,13 +169,42 @@ class _LogsWidgetState extends State<LogsWidget> {
}
else {
return LogTile(
log: logsList.logsData!.data[index],
log: logsProvider.logsData!.data[index],
index: index,
length: logsList.logsData!.data.length,
length: logsProvider.logsData!.data.length,
);
}
}
)
: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
AppLocalizations.of(context)!.noLogsDisplay,
style: const TextStyle(
fontSize: 24,
color: Colors.grey
),
),
if (logsProvider.logsOlderThan != null) Padding(
padding: const EdgeInsets.only(
top: 30,
left: 20,
right: 20
),
child: Text(
AppLocalizations.of(context)!.noLogsThatOld,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
color: Colors.grey
),
),
),
]
),
)
);
case 2:

View file

@ -0,0 +1,224 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.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/functions/format_time.dart';
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<LogsProvider>(context);
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
void selectTime() async {
DateTime now = DateTime.now();
DateTime? dateValue = await showDatePicker(
context: context,
initialDate: now,
firstDate: DateTime(now.year, now.month-1, now.day),
lastDate: now
);
if (dateValue != null) {
TimeOfDay? timeValue = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
helpText: AppLocalizations.of(context)!.selectTime,
);
if (timeValue != null) {
DateTime value = DateTime(
dateValue.year,
dateValue.month,
dateValue.day,
timeValue.hour,
timeValue.minute,
dateValue.second
).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);
}
}
}
}
void resetFilters() async {
logsProvider.setLoadStatus(0);
logsProvider.resetFilters();
final result = await getLogs(
server: serversProvider.selectedServer!,
count: logsProvider.logsQuantity
);
if (result['result'] == 'success') {
logsProvider.setLogsData(result['data']);
logsProvider.setLoadStatus(1);
}
else {
appConfigProvider.addLog(result['log']);
logsProvider.setLoadStatus(2);
}
}
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: () => {},
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),
const Text(
"12/12/2000",
style: 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)
),
],
),
)
],
),
);
}
}

View file

@ -363,11 +363,11 @@ Future requestAllowedBlockedClientsHosts(Server server, Map<String, List<String>
Future getLogs({
required Server server,
required int count,
int? offset
int? offset,
DateTime? olderThan,
}) async {
try {
final result = await getRequest(
urlPath: '/querylog?limit=$count${offset != null ? '&offset=$offset' : ''}',
urlPath: '/querylog?limit=$count${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}',
server: server
);
@ -389,6 +389,7 @@ Future getLogs({
)
};
}
try {
} on SocketException {
return {
'result': 'no_connection',