mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: logs page in settings
This commit is contained in:
parent
4a75f3dbd1
commit
b78e7f57a0
@ -8,6 +8,7 @@ import 'package:spotube/pages/lyrics/mini_lyrics.dart';
|
|||||||
import 'package:spotube/pages/search/search.dart';
|
import 'package:spotube/pages/search/search.dart';
|
||||||
import 'package:spotube/pages/settings/blacklist.dart';
|
import 'package:spotube/pages/settings/blacklist.dart';
|
||||||
import 'package:spotube/pages/settings/about.dart';
|
import 'package:spotube/pages/settings/about.dart';
|
||||||
|
import 'package:spotube/pages/settings/logs.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/components/shared/spotube_page_route.dart';
|
import 'package:spotube/components/shared/spotube_page_route.dart';
|
||||||
import 'package:spotube/pages/album/album.dart';
|
import 'package:spotube/pages/album/album.dart';
|
||||||
@ -83,10 +84,16 @@ final router = GoRouter(
|
|||||||
child: const BlackListPage(),
|
child: const BlackListPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: "logs",
|
||||||
|
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||||
|
child: const LogsPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "about",
|
path: "about",
|
||||||
pageBuilder: (context, state) => const SpotubePage(
|
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||||
child: AboutSpotube(),
|
child: const AboutSpotube(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -87,4 +87,6 @@ abstract class SpotubeIcons {
|
|||||||
static const volumeLow = FeatherIcons.volume;
|
static const volumeLow = FeatherIcons.volume;
|
||||||
static const volumeMute = FeatherIcons.volumeX;
|
static const volumeMute = FeatherIcons.volumeX;
|
||||||
static const timer = FeatherIcons.clock;
|
static const timer = FeatherIcons.clock;
|
||||||
|
static const logs = FeatherIcons.fileText;
|
||||||
|
static const clipboard = FeatherIcons.clipboard;
|
||||||
}
|
}
|
||||||
|
@ -244,5 +244,7 @@
|
|||||||
"mins": "{minutes} Minutes",
|
"mins": "{minutes} Minutes",
|
||||||
"hours": "{hours} Hours",
|
"hours": "{hours} Hours",
|
||||||
"hour": "{hours} Hour",
|
"hour": "{hours} Hour",
|
||||||
"custom_hours": "Custom Hours"
|
"custom_hours": "Custom Hours",
|
||||||
|
"logs": "Logs",
|
||||||
|
"developers": "Developers"
|
||||||
}
|
}
|
@ -26,7 +26,6 @@ import 'package:spotube/provider/palette_provider.dart';
|
|||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/themes/theme.dart';
|
import 'package:spotube/themes/theme.dart';
|
||||||
import 'package:spotube/utils/custom_toast_handler.dart';
|
|
||||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
import 'package:system_theme/system_theme.dart';
|
import 'package:system_theme/system_theme.dart';
|
||||||
@ -122,16 +121,11 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
releaseConfig: CatcherOptions(
|
releaseConfig: CatcherOptions(
|
||||||
SilentReportMode(),
|
SilentReportMode(),
|
||||||
[
|
[
|
||||||
if (arguments["verbose"] ?? false)
|
if (arguments["verbose"] ?? false) ConsoleHandler(),
|
||||||
ConsoleHandler(
|
|
||||||
enableDeviceParameters: false,
|
|
||||||
enableApplicationParameters: false,
|
|
||||||
),
|
|
||||||
FileHandler(
|
FileHandler(
|
||||||
await getLogsPath(),
|
await getLogsPath(),
|
||||||
printLogs: false,
|
printLogs: false,
|
||||||
),
|
),
|
||||||
CustomToastHandler(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
runAppFunction: () {
|
runAppFunction: () {
|
||||||
|
139
lib/pages/settings/logs.dart
Normal file
139
lib/pages/settings/logs.dart
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
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/components/settings/section_card_with_heading.dart';
|
||||||
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
|
import 'package:spotube/extensions/context.dart';
|
||||||
|
import 'package:spotube/models/logger.dart';
|
||||||
|
|
||||||
|
class LogsPage extends HookWidget {
|
||||||
|
const LogsPage({Key? key}) : super(key: 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 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 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: ListView.builder(
|
||||||
|
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(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -406,6 +406,19 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SectionCardWithHeading(
|
||||||
|
heading: context.l10n.developers,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(SpotubeIcons.logs),
|
||||||
|
title: Text(context.l10n.logs),
|
||||||
|
trailing: const Icon(SpotubeIcons.angleRight),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).push("/settings/logs");
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
SectionCardWithHeading(
|
SectionCardWithHeading(
|
||||||
heading: context.l10n.about,
|
heading: context.l10n.about,
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import 'package:catcher/model/platform_type.dart';
|
|
||||||
import 'package:catcher/model/report.dart';
|
|
||||||
import 'package:catcher/model/report_handler.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:motion_toast/motion_toast.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
|
||||||
import 'package:spotube/extensions/context.dart';
|
|
||||||
|
|
||||||
class CustomToastHandler extends ReportHandler {
|
|
||||||
CustomToastHandler();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> handle(Report error, BuildContext? context) async {
|
|
||||||
final theme = Theme.of(context!);
|
|
||||||
|
|
||||||
MotionToast(
|
|
||||||
primaryColor: theme.colorScheme.errorContainer,
|
|
||||||
icon: SpotubeIcons.error,
|
|
||||||
title: Text(
|
|
||||||
context.l10n.something_went_wrong,
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: theme.colorScheme.onError,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
description: Text(
|
|
||||||
error.error.toString(),
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: theme.colorScheme.onError,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
dismissable: true,
|
|
||||||
toastDuration: const Duration(seconds: 5),
|
|
||||||
borderRadius: 10,
|
|
||||||
).show(context);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<PlatformType> getSupportedPlatforms() => [
|
|
||||||
PlatformType.android,
|
|
||||||
PlatformType.iOS,
|
|
||||||
PlatformType.web,
|
|
||||||
PlatformType.linux,
|
|
||||||
PlatformType.macOS,
|
|
||||||
PlatformType.windows,
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool isContextRequired() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldHandleWhenRejected() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1134,14 +1134,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
motion_toast:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: motion_toast
|
|
||||||
sha256: f33fad8264d6d5359e41f2027d2d833614401c3983102e8f0aa13ccbbdcdeecd
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.6.8"
|
|
||||||
mutex:
|
mutex:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -102,7 +102,6 @@ dependencies:
|
|||||||
device_preview: ^1.1.0
|
device_preview: ^1.1.0
|
||||||
media_kit_native_event_loop: ^1.0.4
|
media_kit_native_event_loop: ^1.0.4
|
||||||
dbus: ^0.7.8
|
dbus: ^0.7.8
|
||||||
motion_toast: ^2.6.8
|
|
||||||
background_downloader: ^7.4.0
|
background_downloader: ^7.4.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user