diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9a0f1814..ae7abb01 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -414,5 +414,6 @@ "no_tracks": "Looks like there are no tracks here", "no_tracks_listened_yet": "Looks like you haven't listened to anything yet", "not_following_artists": "You're not following any artists", - "no_favorite_albums_yet": "Looks like you haven't added any albums to your favorites yet" + "no_favorite_albums_yet": "Looks like you haven't added any albums to your favorites yet", + "no_logs_found": "No logs found" } \ No newline at end of file diff --git a/lib/pages/settings/logs.dart b/lib/pages/settings/logs.dart index 0f1260aa..f9439317 100644 --- a/lib/pages/settings/logs.dart +++ b/lib/pages/settings/logs.dart @@ -1,8 +1,11 @@ -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_undraw/flutter_undraw.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/button/back_button.dart'; import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/extensions/context.dart'; @@ -21,54 +24,73 @@ class LogsPage extends HookConsumerWidget { final logsQuery = ref.watch(logsProvider); return Scaffold( - appBar: TitleBar( - title: Text(context.l10n.logs), - leading: const [BackButton()], - trailing: [ - IconButton( - icon: const Icon(SpotubeIcons.clipboard), - iconSize: 16, - onPressed: () async { - final logsSnapshot = await ref.read(logsProvider.future); + headers: [ + TitleBar( + title: Text(context.l10n.logs), + leading: const [BackButton()], + trailing: [ + IconButton.ghost( + icon: const Icon(SpotubeIcons.clipboard, size: 16), + onPressed: () async { + final logsSnapshot = await ref.read(logsProvider.future); - await Clipboard.setData(ClipboardData(text: logsSnapshot)); - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.copied_to_clipboard("")), - ), - ); - } - }, - ), - IconButton( - icon: const Icon(SpotubeIcons.trash), - iconSize: 16, - onPressed: () async { - ref.invalidate(logsProvider); + await Clipboard.setData(ClipboardData(text: logsSnapshot)); + if (context.mounted) { + showToast( + context: context, + location: ToastLocation.topRight, + builder: (context, overlay) { + return SurfaceCard( + child: Basic( + title: Text(context.l10n.copied_to_clipboard("")), + ), + ); + }, + ); + } + }, + ), + IconButton.ghost( + icon: const Icon( + SpotubeIcons.trash, + size: 16, + ), + onPressed: () async { + ref.invalidate(logsProvider); - final logsFile = await AppLogger.getLogsPath(); + final logsFile = await AppLogger.getLogsPath(); - await logsFile.writeAsString(""); - }, - ) - ], - ), - body: SafeArea( + await logsFile.writeAsString(""); + }, + ) + ], + ) + ], + child: SafeArea( child: switch (logsQuery) { - AsyncData(:final value) => Card( - child: InterScrollbar( + AsyncData(:final value) => InterScrollbar( + controller: controller, + child: SingleChildScrollView( + padding: const EdgeInsets.all(8.0), controller: controller, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SingleChildScrollView( - controller: controller, - child: Text(value), - ), - ), + child: Card(child: SelectableText(value)), ), ), - AsyncError(:final error) => Center(child: Text(error.toString())), + AsyncError(:final error) => switch (error) { + StateError() => Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Undraw( + illustration: UndrawIllustration.noData, + height: 200 * context.theme.scaling, + width: 200 * context.theme.scaling, + color: context.theme.colorScheme.primary, + ), + Text(context.l10n.no_logs_found).muted().small(), + ], + ), + _ => Center(child: Text(error.toString())), + }, _ => const Center(child: CircularProgressIndicator()), }, ), diff --git a/lib/pages/settings/sections/about.dart b/lib/pages/settings/sections/about.dart index 5910fc1b..5ed26ee8 100644 --- a/lib/pages/settings/sections/about.dart +++ b/lib/pages/settings/sections/about.dart @@ -1,9 +1,8 @@ import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart' show FilledButton, ButtonStyle, ListTile; +import 'package:flutter/material.dart' show ListTile; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart' hide ButtonStyle; -import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/env.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/modules/settings/section_card_with_heading.dart'; @@ -44,16 +43,25 @@ class SettingsAboutSection extends HookConsumerWidget { ), ), ), - trailing: (context, update) => FilledButton( - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll(Colors.red[100]), - foregroundColor: const WidgetStatePropertyAll(Colors.pink), - padding: const WidgetStatePropertyAll(EdgeInsets.all(15)), - shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: context.theme.borderRadiusLg, - ), - ), + trailing: (context, update) => Button( + style: ButtonVariance.primary.copyWith( + decoration: (context, states, value) { + final decoration = ButtonVariance.primary + .decoration(context, states) as BoxDecoration; + + if (states.contains(WidgetState.hovered)) { + return decoration.copyWith(color: Colors.pink[400]); + } else if (states.contains(WidgetState.focused)) { + return decoration.copyWith(color: Colors.pink[300]); + } else if (states.isNotEmpty) { + return decoration; + } + + return decoration.copyWith(color: Colors.pink); + }, + textStyle: (context, states, value) => ButtonVariance.primary + .textStyle(context, states) + .copyWith(color: Colors.white), ), onPressed: () { launchUrlString( diff --git a/lib/provider/logs/logs_provider.dart b/lib/provider/logs/logs_provider.dart index b0e95cae..571d96cc 100644 --- a/lib/provider/logs/logs_provider.dart +++ b/lib/provider/logs/logs_provider.dart @@ -6,6 +6,11 @@ import 'package:spotube/services/logger/logger.dart'; final logsProvider = StreamProvider.autoDispose((ref) async* { final file = await AppLogger.getLogsPath(); final stream = file.openRead().transform(utf8.decoder); + + if (await stream.isEmpty) { + throw StateError('No logs found'); + } + await for (final line in stream) { yield line; } diff --git a/pubspec.yaml b/pubspec.yaml index 1594abb2..3c20981b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -188,6 +188,7 @@ flutter: - packages/flutter_undraw/assets/undraw/follow_me_drone.svg - packages/flutter_undraw/assets/undraw/taken.svg - packages/flutter_undraw/assets/undraw/empty.svg + - packages/flutter_undraw/assets/undraw/no_data.svg fonts: - family: GeistSans fonts: diff --git a/untranslated_messages.json b/untranslated_messages.json index b00b1fd4..810c3125 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -14,7 +14,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "bn": [ @@ -32,7 +33,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "ca": [ @@ -50,7 +52,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "cs": [ @@ -68,7 +71,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "de": [ @@ -86,7 +90,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "es": [ @@ -104,7 +109,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "eu": [ @@ -122,7 +128,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "fa": [ @@ -140,7 +147,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "fi": [ @@ -158,7 +166,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "fr": [ @@ -176,7 +185,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "hi": [ @@ -194,7 +204,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "id": [ @@ -212,7 +223,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "it": [ @@ -230,7 +242,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "ja": [ @@ -248,7 +261,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "ka": [ @@ -266,7 +280,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "ko": [ @@ -284,7 +299,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "ne": [ @@ -302,7 +318,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "nl": [ @@ -320,7 +337,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "pl": [ @@ -338,7 +356,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "pt": [ @@ -356,7 +375,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "ru": [ @@ -374,7 +394,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "th": [ @@ -392,7 +413,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "tr": [ @@ -410,7 +432,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "uk": [ @@ -428,7 +451,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "vi": [ @@ -446,7 +470,8 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ], "zh": [ @@ -464,6 +489,7 @@ "no_tracks", "no_tracks_listened_yet", "not_following_artists", - "no_favorite_albums_yet" + "no_favorite_albums_yet", + "no_logs_found" ] }