Added realtime logs

This commit is contained in:
Juan Gilsanz Polo 2024-09-08 18:17:05 +02:00
parent be3e76eafc
commit 7991f29707
6 changed files with 190 additions and 8 deletions

View file

@ -795,5 +795,7 @@
"clientIpCopied": "Client IP copied to the clipboard",
"clientNameCopied": "Client name copied to the clipboard",
"dnsServerAddressCopied": "DNS server address copied to the clipboard",
"select": "Select"
"select": "Select",
"liveLogs": "Live logs",
"hereWillAppearRealtimeLogs": "Here there will appear the logs on realtime."
}

View file

@ -795,5 +795,7 @@
"clientIpCopied": "Dirección IP del cliente copiada al portapapeles",
"clientNameCopied": "Nombre del cliente copiado al portapapeles",
"dnsServerAddressCopied": "Dirección del servidor DNS copiada al portapapeles",
"select": "Seleccionar"
"select": "Seleccionar",
"liveLogs": "Registros en directo",
"hereWillAppearRealtimeLogs": "Aquí aparecerán los registros en tiempo real."
}

View file

@ -0,0 +1,51 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:adguard_home_manager/models/logs.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class LiveLogsProvider with ChangeNotifier {
ServersProvider? _serversProvider;
update(ServersProvider? provider) {
_serversProvider = provider;
}
bool _isDisposed = false;
@override
void dispose() {
_isDisposed = true;
super.dispose();
}
List<Log> _logs = [];
List<Log> get logs {
return _logs;
}
DateTime? _lastTime;
void startFetchLogs() {
_lastTime = DateTime.now();
_fetchLogs();
}
void _fetchLogs() async {
if (_lastTime == null) return;
final result = await _serversProvider!.apiClient2!.getLogs(
count: 100
);
if (result.successful == false || result.content == null) return;
final valid = (result.content as LogsData).data.where((e) => e.time.isAfter(_lastTime!));
_logs = [...valid, ..._logs];
_lastTime = DateTime.now();
notifyListeners();
await Future.delayed(const Duration(seconds: 2));
if (_isDisposed == true) return;
_fetchLogs();
}
}

View file

@ -0,0 +1,89 @@
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/details/log_details_screen.dart';
import 'package:adguard_home_manager/screens/logs/log_tile.dart';
import 'package:adguard_home_manager/providers/live_logs_provider.dart';
class LiveLogsScreen extends StatefulWidget {
const LiveLogsScreen({super.key});
@override
State<LiveLogsScreen> createState() => _LiveLogsScreenState();
}
class _LiveLogsScreenState extends State<LiveLogsScreen> {
@override
void initState() {
Provider.of<LiveLogsProvider>(context, listen: false).startFetchLogs();
super.initState();
}
@override
Widget build(BuildContext context) {
final liveLogsProvider = Provider.of<LiveLogsProvider>(context);
print(liveLogsProvider.logs.length);
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar.large(
title: Text(AppLocalizations.of(context)!.liveLogs),
)
)
],
body: SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (context) => CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
if (liveLogsProvider.logs.isEmpty) SliverFillRemaining(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
AppLocalizations.of(context)!.hereWillAppearRealtimeLogs,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 24
),
),
),
),
),
if (liveLogsProvider.logs.isNotEmpty) SliverList.builder(
itemCount: liveLogsProvider.logs.length,
itemBuilder: (context, index) => LogTile(
log: liveLogsProvider.logs[index],
length: liveLogsProvider.logs.length,
index: index,
onLogTap: (log) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LogDetailsScreen(
log: log,
dialog: false,
)
)
);
},
twoColumns: false
),
)
],
),
)
)
),
);
}
}

View file

@ -6,11 +6,14 @@ 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/live/live_logs_screen.dart';
import 'package:adguard_home_manager/screens/logs/filters/logs_filters_modal.dart';
import 'package:adguard_home_manager/config/globals.dart';
import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/functions/desktop_mode.dart';
import 'package:adguard_home_manager/providers/live_logs_provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart';
@ -74,6 +77,22 @@ class LogsListAppBar extends StatelessWidget {
);
}
void openLiveLogsScreen() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MultiProvider(
providers: [
ChangeNotifierProxyProvider<ServersProvider, LiveLogsProvider>(
create: (context) => LiveLogsProvider(),
update: (context, servers, logs) => logs!..update(servers),
),
],
child: const LiveLogsScreen()
)
)
);
}
final Map<String, String> translatedString = {
"all": AppLocalizations.of(context)!.all,
"filtered": AppLocalizations.of(context)!.filtered,
@ -104,10 +123,29 @@ class LogsListAppBar extends StatelessWidget {
icon: const Icon(Icons.search_rounded),
tooltip: AppLocalizations.of(context)!.search,
),
if (logsProvider.loadStatus == LoadStatus.loaded) IconButton(
onPressed: openFilersModal,
icon: const Icon(Icons.filter_list_rounded),
tooltip: AppLocalizations.of(context)!.filters,
if (logsProvider.loadStatus == LoadStatus.loaded) PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
onTap: openFilersModal,
child: Row(
children: [
const Icon(Icons.filter_list_rounded),
const SizedBox(width: 10),
Text(AppLocalizations.of(context)!.filters)
],
)
),
PopupMenuItem(
onTap: openLiveLogsScreen,
child: Row(
children: [
const Icon(Icons.stream_rounded),
const SizedBox(width: 10),
Text(AppLocalizations.of(context)!.liveLogs)
],
)
),
],
),
const SizedBox(width: 8),
],

View file

@ -206,14 +206,14 @@ class ApiClientV2 {
}
Future<ApiResponse> getLogs({
required int count,
int? count,
int? offset,
DateTime? olderThan,
String? responseStatus,
String? search
}) async {
final result = await HttpRequestClient.get(
urlPath: '/querylog?limit=$count${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}${responseStatus != null ? '&response_status=$responseStatus' : ''}${search != null ? '&search=$search' : ''}',
urlPath: '/querylog?${count != null ? 'limit=$count' : ''}${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}${responseStatus != null ? '&response_status=$responseStatus' : ''}${search != null ? '&search=$search' : ''}',
server: server
);
if (result.successful == true) {