diff --git a/lib/features/downloads/core/download_manager.dart b/lib/features/downloads/core/download_manager.dart index 19738bf..1e09268 100644 --- a/lib/features/downloads/core/download_manager.dart +++ b/lib/features/downloads/core/download_manager.dart @@ -12,7 +12,7 @@ import 'package:vaani/shared/extensions/obfuscation.dart'; final _logger = Logger('AudiobookDownloadManager'); final tq = MemoryTaskQueue(); -const downloadDirectory = BaseDirectory.applicationSupport; +// const downloadDirectory = BaseDirectory.applicationSupport; class AudiobookDownloadManager { // takes in the baseUrl and the token @@ -72,16 +72,12 @@ class AudiobookDownloadManager { late StreamSubscription _updatesSubscription; Future queueAudioBookDownload( - LibraryItemExpanded item, { - String prePath = '', - }) async { + LibraryItemExpanded item, + ) async { _logger.info('queuing download for item: ${item.id}'); // create a download task for each file in the item - for (final file in item.libraryFiles) { - // 仅下载音频和视频 - if (![FileType.audio, FileType.video].contains(file.fileType)) { - continue; - } + // for (final file in item.libraryFiles) { + for (final file in item.media.asBookExpanded.audioFiles) { // check if the file is already downloaded if (isFileDownloaded( constructFilePath(item, file), @@ -93,13 +89,13 @@ class AudiobookDownloadManager { final task = DownloadTask( taskId: file.ino, url: file.url(baseUrl, item.id, token).toString(), - directory: prePath + item.relPath, + directory: getPath(item.relPath), filename: file.metadata.filename, requiresWiFi: requiresWiFi, retries: retries, allowPause: allowPause, group: item.id, - baseDirectory: downloadDirectory, + baseDirectory: baseDirectory, updates: Updates.statusAndProgress, // metaData: token ); @@ -111,9 +107,9 @@ class AudiobookDownloadManager { String constructFilePath( LibraryItemExpanded item, - LibraryFile file, + AudioFile file, ) => - '${appSupportDir.path}/${item.relPath}/${file.metadata.filename}'; + '$basePath/${item.relPath}/${file.metadata.filename}'; void dispose() { _updatesSubscription.cancel(); @@ -129,11 +125,12 @@ class AudiobookDownloadManager { return File(filePath).existsSync(); } - Future> getDownloadedFilesMetadata( + Future> getDownloadedFilesMetadata( LibraryItemExpanded item, ) async { - final downloadedFiles = []; - for (final file in item.libraryFiles) { + final downloadedFiles = []; + // for (final file in item.libraryFiles) { + for (final file in item.media.asBookExpanded.audioFiles) { final filePath = constructFilePath(item, file); if (isFileDownloaded(filePath)) { downloadedFiles.add(file); @@ -152,7 +149,8 @@ class AudiobookDownloadManager { } Future isItemDownloaded(LibraryItemExpanded item) async { - for (final file in item.libraryFiles) { + // for (final file in item.libraryFiles) { + for (final file in item.media.asBookExpanded.audioFiles) { if (!isFileDownloaded(constructFilePath(item, file))) { _logger.info('file not downloaded: ${file.metadata.filename}'); return false; @@ -164,7 +162,8 @@ class AudiobookDownloadManager { Future deleteDownloadedItem(LibraryItemExpanded item) async { _logger.info('deleting downloaded item with id: ${item.id}'); - for (final file in item.libraryFiles) { + // for (final file in item.libraryFiles) { + for (final file in item.media.asBookExpanded.audioFiles) { final filePath = constructFilePath(item, file); if (isFileDownloaded(filePath)) { File(filePath).deleteSync(); @@ -175,7 +174,8 @@ class AudiobookDownloadManager { Future> getDownloadedFilesUri(LibraryItemExpanded item) async { final files = []; - for (final file in item.libraryFiles) { + // for (final file in item.libraryFiles) { + for (final file in item.media.asBookExpanded.audioFiles) { final filePath = constructFilePath(item, file); if (isFileDownloaded(filePath)) { files.add(Uri.file(filePath)); @@ -208,4 +208,29 @@ class AudiobookDownloadManager { _logger.warning('Error when listening to download manager updates: $e'); } } + + String getPath(String relPath) { + if (path.isNotEmpty) { + return '$path/$relPath'; + } + return relPath; + } + + String get basePath => switch (baseDirectory) { + BaseDirectory.applicationSupport => appSupportDir.path, + BaseDirectory.applicationDocuments => appDocumentsDir.path, + _ => path, + }; + + BaseDirectory get baseDirectory { + if (path.isNotEmpty) { + return BaseDirectory.root; + } else if (Platform.isIOS || Platform.isMacOS) { + return BaseDirectory.applicationDocuments; + } + // else if (Platform.isAndroid) { + // return BaseDirectory.temporary; + // } + return BaseDirectory.applicationSupport; + } } diff --git a/lib/features/downloads/providers/download_manager.dart b/lib/features/downloads/providers/download_manager.dart index bdbe15e..9bb1d18 100644 --- a/lib/features/downloads/providers/download_manager.dart +++ b/lib/features/downloads/providers/download_manager.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:background_downloader/background_downloader.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; @@ -71,24 +69,6 @@ class DownloadManager extends _$DownloadManager { await state.deleteDownloadedItem(item); ref.notifyListeners(); } - - String _getDirectory(String path) { - if (Platform.isWindows) { - return path; - } - return path; - } - - BaseDirectory _getBaseDirectory() { - if (Platform.isIOS) { - return BaseDirectory.applicationDocuments; - } else if (Platform.isAndroid) { - return BaseDirectory.temporary; - } else if (Platform.isWindows) { - return BaseDirectory.root; - } - return BaseDirectory.applicationSupport; - } } @riverpod diff --git a/lib/features/downloads/providers/download_manager.g.dart b/lib/features/downloads/providers/download_manager.g.dart index 0df3141..4c1751c 100644 --- a/lib/features/downloads/providers/download_manager.g.dart +++ b/lib/features/downloads/providers/download_manager.g.dart @@ -176,7 +176,7 @@ final simpleDownloadManagerProvider = NotifierProvider; -String _$downloadManagerHash() => r'92afe484d6735d5de53473011ea9ecbad107fc1c'; +String _$downloadManagerHash() => r'852012e32e613f86445afc7f7e4e85bec808e982'; /// See also [DownloadManager]. @ProviderFor(DownloadManager) diff --git a/lib/features/item_viewer/view/library_item_actions.dart b/lib/features/item_viewer/view/library_item_actions.dart index 230eb13..991a65e 100644 --- a/lib/features/item_viewer/view/library_item_actions.dart +++ b/lib/features/item_viewer/view/library_item_actions.dart @@ -22,6 +22,7 @@ import 'package:vaani/globals.dart'; import 'package:vaani/router/router.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; import 'package:vaani/shared/utils.dart'; +import 'package:vaani/shared/utils/custom_dialog.dart'; class LibraryItemActions extends HookConsumerWidget { const LibraryItemActions({ @@ -236,6 +237,8 @@ class LibItemDownSheet extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { // final downloadHistory = // ref.watch(downloadHistoryProvider(group: libraryItemId)); + // final downloadManager = ref.watch(downloadManagerProvider); + // downloadManager. final libraryItem = ref.watch(libraryItemProvider(libraryItemId)); return libraryItem.when( data: (item) { @@ -247,19 +250,24 @@ class LibItemDownSheet extends HookConsumerWidget { children: [ Row( children: [ - Text('下载管理'), + Text(S.of(context).bookDownloads), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ IconButton( onPressed: () { - appLogger.fine('Pressed delete download button'); - ref - .read(downloadManagerProvider.notifier) - .deleteDownloadedItem( - item, - ); + DialogUtils.deleteDialog( + context, + name: item.media.metadata.title, + onPressed: () { + ref + .read(downloadManagerProvider.notifier) + .deleteDownloadedItem( + item, + ); + }, + ); }, icon: Icon(Icons.delete_outlined), ), @@ -307,38 +315,6 @@ class LibItemDownSheet extends HookConsumerWidget { ), ); } - - void _showDialog(context, task) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Delete'), - content: Text( - 'Are you sure you want to delete ${task.filename}?', - ), - actions: [ - TextButton( - onPressed: () { - // delete the file - FileDownloader().database.deleteRecordWithId( - task.taskId, - ); - Navigator.pop(context); - }, - child: const Text('Yes'), - ), - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('No'), - ), - ], - ); - }, - ); - } } class LibItemDownloadButton extends HookConsumerWidget { @@ -572,12 +548,12 @@ class _LibraryItemPlayButton extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final currentBook = ref.watch(currentBookProvider); final book = item.media.asBookExpanded; - final playerStateNotifier = ref.watch(playerStateProvider.notifier); + final playing = ref.watch(playerStateProvider.select((v) => v.playing)); + final playerStateNotifier = ref.read(playerStateProvider.notifier); final isLoading = playerStateNotifier.isLoading(book.libraryItemId); final isCurrentBookSetInPlayer = currentBook?.libraryItemId == book.libraryItemId; - final isPlayingThisBook = - playerStateNotifier.isPlaying() && isCurrentBookSetInPlayer; + final isPlayingThisBook = playing && isCurrentBookSetInPlayer; final userMediaProgress = item.userMediaProgress; final isBookCompleted = userMediaProgress?.isFinished ?? false; diff --git a/lib/features/player/providers/abs_provider.dart b/lib/features/player/providers/abs_provider.dart index 3a3a14e..3dccffd 100644 --- a/lib/features/player/providers/abs_provider.dart +++ b/lib/features/player/providers/abs_provider.dart @@ -88,6 +88,8 @@ class AbsPlayer extends _$AbsPlayer { final api = ref.read(authenticatedApiProvider); final downloadManager = ref.read(simpleDownloadManagerProvider); + print(downloadManager.basePath); + final libItem = await ref.read(libraryItemProvider(book.libraryItemId).future); final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem); diff --git a/lib/features/player/providers/abs_provider.g.dart b/lib/features/player/providers/abs_provider.g.dart index 3bf89d6..f32f703 100644 --- a/lib/features/player/providers/abs_provider.g.dart +++ b/lib/features/player/providers/abs_provider.g.dart @@ -243,7 +243,7 @@ final currentChaptersProvider = @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element typedef CurrentChaptersRef = AutoDisposeProviderRef>; -String _$absPlayerHash() => r'74a59dbf0f9396fef6bb60363fb186f5e4619a63'; +String _$absPlayerHash() => r'e682fea03793a0370cb143602980d5c1e37396c7'; /// 音频播放器 riverpod状态 /// diff --git a/lib/features/player/view/widgets/chapter_selection_button.dart b/lib/features/player/view/widgets/chapter_selection_button.dart index 4ff7888..6d9cf2d 100644 --- a/lib/features/player/view/widgets/chapter_selection_button.dart +++ b/lib/features/player/view/widgets/chapter_selection_button.dart @@ -90,32 +90,41 @@ class ChapterSelectionModal extends HookConsumerWidget { final chapter = book.chapters[index]; final isCurrent = currentChapter.id == chapter.id; final isPlayed = index < initialIndex; - return ListTile( - autofocus: isCurrent, - iconColor: isPlayed && !isCurrent ? theme.disabledColor : null, - title: Text( - chapter.title, - style: isPlayed && !isCurrent - ? TextStyle(color: theme.disabledColor) - : null, + return Container( + // 自定义autofocus,防止autofocus出现在其他组件底层 + decoration: isCurrent + ? BoxDecoration( + color: Theme.of(context).focusColor, // 背景色 + ) + : null, + child: ListTile( + // autofocus: isCurrent, + iconColor: + isPlayed && !isCurrent ? theme.disabledColor : null, + title: Text( + chapter.title, + style: isPlayed && !isCurrent + ? TextStyle(color: theme.disabledColor) + : null, + ), + subtitle: Text( + '(${chapter.duration.smartBinaryFormat})', + style: isPlayed && !isCurrent + ? TextStyle(color: theme.disabledColor) + : null, + ), + trailing: isCurrent + ? const PlayingIndicatorIcon() + : const Icon(Icons.play_arrow), + selected: isCurrent, + onTap: () { + if (back) { + Navigator.of(context).pop(); + } else { + ref.read(absPlayerProvider).switchChapter(chapter.id); + } + }, ), - subtitle: Text( - '(${chapter.duration.smartBinaryFormat})', - style: isPlayed && !isCurrent - ? TextStyle(color: theme.disabledColor) - : null, - ), - trailing: isCurrent - ? const PlayingIndicatorIcon() - : const Icon(Icons.play_arrow), - selected: isCurrent, - onTap: () { - if (back) { - Navigator.of(context).pop(); - } else { - ref.read(absPlayerProvider).switchChapter(chapter.id); - } - }, ); }, ), diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index fd01b59..e457dc8 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -163,6 +163,17 @@ class MessageLookup extends MessageLookupByLibrary { "delete": MessageLookupByLibrary.simpleMessage("Delete"), "deleteDialog": m2, "deleted": m3, + "erArmedText": MessageLookupByLibrary.simpleMessage("Release ready"), + "erDragText": MessageLookupByLibrary.simpleMessage("Pull to refresh"), + "erDragTextUp": MessageLookupByLibrary.simpleMessage("Pull to refresh"), + "erFailedText": MessageLookupByLibrary.simpleMessage("Failed"), + "erMessageText": + MessageLookupByLibrary.simpleMessage("Last updated at %T"), + "erNoMoreText": MessageLookupByLibrary.simpleMessage("No more"), + "erProcessedText": MessageLookupByLibrary.simpleMessage("Succeeded"), + "erProcessingText": + MessageLookupByLibrary.simpleMessage("Refreshing..."), + "erReadyText": MessageLookupByLibrary.simpleMessage("Refreshing..."), "explore": MessageLookupByLibrary.simpleMessage("explore"), "exploreHint": MessageLookupByLibrary.simpleMessage( "Seek and you shall discover...", diff --git a/lib/generated/intl/messages_zh.dart b/lib/generated/intl/messages_zh.dart index 480a87b..99eaa2d 100644 --- a/lib/generated/intl/messages_zh.dart +++ b/lib/generated/intl/messages_zh.dart @@ -24,7 +24,7 @@ class MessageLookup extends MessageLookupByLibrary { static String m1(user) => "用户数: ${user}"; - static String m2(item) => "确定要删除 ${item} 吗?"; + static String m2(item) => "是否要删除 ${item} ?"; static String m3(item) => "已删除 ${item}"; @@ -129,6 +129,15 @@ class MessageLookup extends MessageLookupByLibrary { "delete": MessageLookupByLibrary.simpleMessage("删除"), "deleteDialog": m2, "deleted": m3, + "erArmedText": MessageLookupByLibrary.simpleMessage("准备就绪"), + "erDragText": MessageLookupByLibrary.simpleMessage("下拉刷新"), + "erDragTextUp": MessageLookupByLibrary.simpleMessage("上拉加载"), + "erFailedText": MessageLookupByLibrary.simpleMessage("失败"), + "erMessageText": MessageLookupByLibrary.simpleMessage("最后更新于 %T"), + "erNoMoreText": MessageLookupByLibrary.simpleMessage("没有更多"), + "erProcessedText": MessageLookupByLibrary.simpleMessage("成功"), + "erProcessingText": MessageLookupByLibrary.simpleMessage("刷新..."), + "erReadyText": MessageLookupByLibrary.simpleMessage("刷新..."), "explore": MessageLookupByLibrary.simpleMessage("探索"), "exploreHint": MessageLookupByLibrary.simpleMessage("搜索与探索..."), "exploreTooltip": MessageLookupByLibrary.simpleMessage("搜索和探索"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index cbfe6ca..7dd03d3 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -1894,6 +1894,86 @@ class S { ); } + /// `Pull to refresh` + String get erDragText { + return Intl.message( + 'Pull to refresh', + name: 'erDragText', + desc: '', + args: [], + ); + } + + /// `Pull to refresh` + String get erDragTextUp { + return Intl.message( + 'Pull to refresh', + name: 'erDragTextUp', + desc: '', + args: [], + ); + } + + /// `Release ready` + String get erArmedText { + return Intl.message( + 'Release ready', + name: 'erArmedText', + desc: '', + args: [], + ); + } + + /// `Refreshing...` + String get erReadyText { + return Intl.message( + 'Refreshing...', + name: 'erReadyText', + desc: '', + args: [], + ); + } + + /// `Refreshing...` + String get erProcessingText { + return Intl.message( + 'Refreshing...', + name: 'erProcessingText', + desc: '', + args: [], + ); + } + + /// `Succeeded` + String get erProcessedText { + return Intl.message( + 'Succeeded', + name: 'erProcessedText', + desc: '', + args: [], + ); + } + + /// `No more` + String get erNoMoreText { + return Intl.message('No more', name: 'erNoMoreText', desc: '', args: []); + } + + /// `Failed` + String get erFailedText { + return Intl.message('Failed', name: 'erFailedText', desc: '', args: []); + } + + /// `Last updated at %T` + String get erMessageText { + return Intl.message( + 'Last updated at %T', + name: 'erMessageText', + desc: '', + args: [], + ); + } + /// `Logs` String get logs { return Intl.message('Logs', name: 'logs', desc: '', args: []); diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index cb6ab76..ba8f0ae 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -272,6 +272,16 @@ "resetAppSettingsDescription": "Reset the app settings to the default values", "resetAppSettingsDialog": "Are you sure you want to reset the app settings?", + "erDragText": "Pull to refresh", + "erDragTextUp": "Pull to refresh", + "erArmedText": "Release ready", + "erReadyText": "Refreshing...", + "erProcessingText": "Refreshing...", + "erProcessedText": "Succeeded", + "erNoMoreText": "No more", + "erFailedText": "Failed", + "erMessageText": "Last updated at %T", + "logs": "Logs", "notImplemented": "Not implemented" } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index d4fd895..0685fa4 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -9,7 +9,7 @@ "reset": "重置", "retry": "重试", "delete": "删除", - "deleteDialog": "确定要删除 {item} 吗?", + "deleteDialog": "是否要删除 {item} ?", "@deleteDialog": { "placeholders": { "item": { @@ -272,6 +272,15 @@ "resetAppSettingsDescription": "将应用程序设置重置为默认值", "resetAppSettingsDialog": "您确定要重置应用程序设置吗?", + "erDragText": "下拉刷新", + "erDragTextUp": "上拉加载", + "erArmedText": "准备就绪", + "erReadyText": "刷新...", + "erProcessingText": "刷新...", + "erProcessedText": "成功", + "erNoMoreText": "没有更多", + "erFailedText": "失败", + "erMessageText": "最后更新于 %T", "logs": "日志", "notImplemented": "未实现" } \ No newline at end of file diff --git a/lib/pages/library_page.dart b/lib/pages/library_page.dart index 6fc3394..b49189a 100644 --- a/lib/pages/library_page.dart +++ b/lib/pages/library_page.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; @@ -16,6 +17,7 @@ import 'package:vaani/router/router.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; import 'package:vaani/shared/extensions/style.dart'; import 'package:vaani/shared/icons/abs_icons.dart'; +import 'package:vaani/shared/utils/components.dart'; import 'package:vaani/shared/widgets/skeletons.dart'; // TODO: implement the library page @@ -70,6 +72,11 @@ class LibraryPage extends HookConsumerWidget { }, ), actions: [ + IconButton( + icon: Icon(Icons.next_plan), + tooltip: '加载下一页', // Helpful tooltip for users + onPressed: () => ref.read(libraryItemsProvider.notifier).loadMore(), + ), IconButton( icon: Icon(Icons.refresh), tooltip: '刷新', // Helpful tooltip for users @@ -85,8 +92,12 @@ class LibraryPage extends HookConsumerWidget { ], ), // drawer: const MyDrawer(), - body: RefreshIndicator( + + body: EasyRefresh( + header: Components.easyRefreshHeader(context), + footer: Components.easyRefreshFooter(context), onRefresh: () => ref.read(libraryItemsProvider.notifier).refresh(), + onLoad: () => ref.read(libraryItemsProvider.notifier).loadMore(), child: LayoutBuilder( builder: (context, constraints) { final height = getDefaultHeight(context); diff --git a/lib/shared/extensions/model_conversions.dart b/lib/shared/extensions/model_conversions.dart index 14966c2..9821a83 100644 --- a/lib/shared/extensions/model_conversions.dart +++ b/lib/shared/extensions/model_conversions.dart @@ -52,6 +52,20 @@ extension UserConversion on User { User get asUser => User.fromJson(toJson()); } +extension ContentUrlExtension on AudioFile { + Uri url(String baseUrl, String itemId, String token) { + // /api/items/{itemId}/file/{ino}?{token} + // return Uri.parse('$baseUrl/api/items/$itemId/file/$ino?token=$token'); + var baseUri = Uri.parse(baseUrl); + return Uri( + scheme: baseUri.scheme, + host: baseUri.host, + path: '/api/items/$itemId/file/$ino', + queryParameters: {'token': token}, + ); + } +} + extension ContentUrl on LibraryFile { Uri url(String baseUrl, String itemId, String token) { // /api/items/{itemId}/file/{ino}?{token} diff --git a/lib/shared/utils/components.dart b/lib/shared/utils/components.dart new file mode 100644 index 0000000..cd170a2 --- /dev/null +++ b/lib/shared/utils/components.dart @@ -0,0 +1,33 @@ +import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/widgets.dart'; +import 'package:vaani/generated/l10n.dart'; + +class Components { + Components._(); + static ClassicHeader easyRefreshHeader(BuildContext context) { + return ClassicHeader( + dragText: S.of(context).erDragText, + armedText: S.of(context).erArmedText, + readyText: S.of(context).erReadyText, + processingText: S.of(context).erProcessingText, + processedText: S.of(context).erProcessedText, + noMoreText: S.of(context).erNoMoreText, + failedText: S.of(context).erFailedText, + messageText: S.of(context).erMessageText, + ); + } + + static ClassicFooter easyRefreshFooter(BuildContext context) { + return ClassicFooter( + dragText: S.of(context).erDragTextUp, + armedText: S.of(context).erArmedText, + readyText: S.of(context).erReadyText, + processingText: S.of(context).erProcessingText, + processedText: S.of(context).erProcessedText, + noMoreText: S.of(context).erNoMoreText, + failedText: S.of(context).erFailedText, + messageText: S.of(context).erMessageText, + infiniteOffset: 0, + ); + } +} diff --git a/lib/shared/utils/custom_dialog.dart b/lib/shared/utils/custom_dialog.dart new file mode 100644 index 0000000..f0d9c0c --- /dev/null +++ b/lib/shared/utils/custom_dialog.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:vaani/generated/l10n.dart'; + +class DialogUtils { + DialogUtils._(); + + // 自定义删除 dialog + static deleteDialog( + BuildContext context, { + String? name, + required Function() onPressed, + }) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(S.of(context).delete), + content: Text(S.of(context).deleteDialog(name ?? '')), + actions: [ + TextButton( + onPressed: () { + onPressed(); + Navigator.pop(context); + }, + child: Text(S.of(context).yes), + ), + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text(S.of(context).no), + ), + ], + ); + }, + ); + } +} diff --git a/lib/shared/widgets/shelves/author_shelf.dart b/lib/shared/widgets/shelves/author_shelf.dart index aa0acce..e3998c2 100644 --- a/lib/shared/widgets/shelves/author_shelf.dart +++ b/lib/shared/widgets/shelves/author_shelf.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; +import 'package:vaani/api/image_provider.dart'; import 'package:vaani/shared/extensions/model_conversions.dart'; import 'package:vaani/shared/widgets/shelves/home_shelf.dart'; @@ -40,7 +41,7 @@ class AuthorOnShelf extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final author = item.asMinified; - // final coverImage = ref.watch(coverImageProvider(item)); + final coverImage = ref.watch(coverImageProvider(item.id)); return Container( margin: const EdgeInsets.only(right: 10, bottom: 10), @@ -53,17 +54,17 @@ class AuthorOnShelf extends HookConsumerWidget { aspectRatio: 1, child: Container( constraints: const BoxConstraints(maxWidth: 50), - // child: coverImage.when( - // data: (image) { - // return Image.memory(image, fit: BoxFit.cover); - // }, - // loading: () { - // return const Center(child: CircularProgressIndicator()); - // }, - // error: (error, stack) { - // return const Icon(Icons.error); - // }, - // ), + child: coverImage.when( + data: (image) { + return Image.memory(image, fit: BoxFit.cover); + }, + loading: () { + return const Center(child: CircularProgressIndicator()); + }, + error: (error, stack) { + return const Icon(Icons.error); + }, + ), ), ), ), diff --git a/lib/shared/widgets/shelves/book_shelf.dart b/lib/shared/widgets/shelves/book_shelf.dart index e1ff7ee..8d6a24f 100644 --- a/lib/shared/widgets/shelves/book_shelf.dart +++ b/lib/shared/widgets/shelves/book_shelf.dart @@ -213,12 +213,12 @@ class _BookOnShelfPlayButton extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final me = ref.watch(meProvider); final currentBook = ref.watch(currentBookProvider); - final playerStateNotifier = ref.watch(playerStateProvider.notifier); + final playing = ref.watch(playerStateProvider.select((v) => v.playing)); + final playerStateNotifier = ref.read(playerStateProvider.notifier); final isLoading = playerStateNotifier.isLoading(libraryItemId); final isCurrentBookSetInPlayer = currentBook?.libraryItemId == libraryItemId; - final isPlayingThisBook = - playerStateNotifier.isPlaying() && isCurrentBookSetInPlayer; + final isPlayingThisBook = playing && isCurrentBookSetInPlayer; final userProgress = me.valueOrNull?.mediaProgress ?.firstWhereOrNull((element) => element.libraryItemId == libraryItemId); diff --git a/pubspec.lock b/pubspec.lock index 501feaf..4770efd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -398,6 +398,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.1" + easy_refresh: + dependency: "direct main" + description: + name: easy_refresh + sha256: "486e30abfcaae66c0f2c2798a10de2298eb9dc5e0bb7e1dba9328308968cae0c" + url: "https://pub.dev" + source: hosted + version: "3.4.0" fake_async: dependency: transitive description: @@ -954,6 +962,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_drawing: + dependency: transitive + description: + name: path_drawing + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_parsing: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0bb2ffc..92f97c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: # flutter_platform_widgets: ^9.0.0 flutter_staggered_grid_view: ^0.7.0 super_sliver_list: ^0.4.1 + easy_refresh: ^3.4.0 duration_picker: ^1.2.0 dynamic_color: ^1.7.0 # easy_stepper: ^0.8.4