Files
Client/lib/widget/debug/cache_view.dart
T

118 lines
3.5 KiB
Dart

import 'dart:convert';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import '../../api/cache_store.dart';
import '../../extensions/date_time.dart';
import '../../routing/app_routes.dart';
import '../app_progress_indicator.dart';
import '../confirm_dialog.dart';
import '../placeholder_view.dart';
/// Lists the [CacheStore] entries (one file per request key), newest first.
class CacheView extends StatefulWidget {
const CacheView({super.key});
@override
State<CacheView> createState() => _CacheViewState();
}
class _CacheViewState extends State<CacheView> {
late Future<List<MapEntry<String, CacheEntry>>> _entries = _read();
Future<List<MapEntry<String, CacheEntry>>> _read() async =>
(await CacheStore.instance.readAll()).entries.toList()
..sort((a, b) => b.value.lastUpdate.compareTo(a.value.lastUpdate));
void _reload() => setState(() {
_entries = _read();
});
void _confirmDelete(String key) {
ConfirmDialog(
title: 'Eintrag löschen',
content: '„$key“ wird gelöscht und bei Bedarf neu geladen.',
confirmButton: 'Löschen',
errorBuilder: (e) => '$e',
onConfirmAsync: () async {
await CacheStore.instance.delete(key);
if (mounted) _reload();
},
).asDialog(context);
}
void _confirmClear() {
ConfirmDialog(
title: 'Request-Cache leeren',
content:
'Alle Einträge werden gelöscht. Der Cache baut sich bei Nutzung der '
'App automatisch wieder auf.',
confirmButton: 'Alles löschen',
errorBuilder: (e) => '$e',
onConfirmAsync: () async {
await CacheStore.instance.clear();
if (mounted) _reload();
},
).asDialog(context);
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Request-Cache'),
actions: [
IconButton(
tooltip: 'Neu laden',
icon: const Icon(Icons.refresh),
onPressed: _reload,
),
IconButton(
tooltip: 'Alles löschen',
icon: const Icon(Icons.delete_sweep_outlined),
onPressed: _confirmClear,
),
],
),
body: FutureBuilder(
future: _entries,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: AppProgressIndicator.large());
}
final entries = snapshot.data ?? const [];
if (entries.isEmpty) {
return const PlaceholderView(
icon: Icons.hourglass_empty,
text: 'Keine Daten',
);
}
return ListView.builder(
itemCount: entries.length,
itemBuilder: (context, index) {
final MapEntry(:key, :value) = entries[index];
return ListTile(
leading: const Icon(Icons.text_snippet_outlined),
title: Text(key),
subtitle: Text(
'${filesize(utf8.encode(value.json).length)} · '
'${DateTime.fromMillisecondsSinceEpoch(value.lastUpdate).formatRelative()}',
),
onTap: () => AppRoutes.openJsonViewer(
context,
jsonDecode(value.json),
title: key,
),
trailing: IconButton(
tooltip: 'Löschen',
icon: const Icon(Icons.delete_outline),
onPressed: () => _confirmDelete(key),
),
);
},
);
},
),
);
}