mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
148 lines
4.7 KiB
Dart
148 lines
4.7 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:spotube/collections/spotube_icons.dart';
|
|
import 'package:spotube/modules/settings/section_card_with_heading.dart';
|
|
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
|
|
import 'package:spotube/components/titlebar/titlebar.dart';
|
|
import 'package:spotube/extensions/context.dart';
|
|
import 'package:spotube/services/logger/logger.dart';
|
|
|
|
class LogsPage extends HookWidget {
|
|
static const name = "logs";
|
|
|
|
const LogsPage({super.key});
|
|
|
|
List<({DateTime? date, String body})> parseLogs(String raw) {
|
|
return raw
|
|
.split(
|
|
"======================================================================",
|
|
)
|
|
.map(
|
|
(line) {
|
|
DateTime? date;
|
|
line = line
|
|
.replaceAll(
|
|
"============================== CATCHER LOG ==============================",
|
|
"",
|
|
)
|
|
.split("\n")
|
|
.map((l) {
|
|
if (l.startsWith("Crash occurred on")) {
|
|
date = DateTime.parse(
|
|
l.split("Crash occurred on")[1].trim(),
|
|
);
|
|
return "";
|
|
}
|
|
return l;
|
|
})
|
|
.where((l) => l.replaceAll("\n", "").trim().isNotEmpty)
|
|
.join("\n");
|
|
|
|
return (
|
|
date: date,
|
|
body: line,
|
|
);
|
|
},
|
|
)
|
|
.where((e) => e.date != null && e.body.isNotEmpty)
|
|
.toList()
|
|
..sort((a, b) => b.date!.compareTo(a.date!));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final controller = useScrollController();
|
|
final logs = useState<List<({DateTime? date, String body})>>([]);
|
|
final rawLogs = useRef<String>("");
|
|
final path = useRef<File?>(null);
|
|
|
|
useEffect(() {
|
|
final timer = Timer.periodic(const Duration(seconds: 5), (t) async {
|
|
path.value ??= await AppLogger.getLogsPath();
|
|
final raw = await path.value!.readAsString();
|
|
final hasChanged = rawLogs.value != raw;
|
|
rawLogs.value = raw;
|
|
if (hasChanged) logs.value = parseLogs(rawLogs.value);
|
|
});
|
|
|
|
return () {
|
|
timer.cancel();
|
|
};
|
|
}, []);
|
|
|
|
return Scaffold(
|
|
appBar: PageWindowTitleBar(
|
|
title: Text(context.l10n.logs),
|
|
leading: const BackButton(),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(SpotubeIcons.clipboard),
|
|
iconSize: 16,
|
|
onPressed: () async {
|
|
await Clipboard.setData(ClipboardData(text: rawLogs.value));
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(context.l10n.copied_to_clipboard("")),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
body: SafeArea(
|
|
child: InterScrollbar(
|
|
controller: controller,
|
|
child: ListView.builder(
|
|
controller: controller,
|
|
itemCount: logs.value.length,
|
|
itemBuilder: (context, index) {
|
|
final log = logs.value[index];
|
|
return Stack(
|
|
children: [
|
|
SectionCardWithHeading(
|
|
heading: log.date.toString(),
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: SelectableText(log.body),
|
|
),
|
|
],
|
|
),
|
|
Positioned(
|
|
right: 10,
|
|
top: 0,
|
|
child: IconButton(
|
|
icon: const Icon(SpotubeIcons.clipboard),
|
|
onPressed: () async {
|
|
await Clipboard.setData(
|
|
ClipboardData(text: log.body),
|
|
);
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
context.l10n.copied_to_clipboard(
|
|
log.date.toString(),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|