mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-26 04:49:31 +00:00
refactor: update cover image handling to use item IDs and simplify library item actions
This commit is contained in:
parent
d25d23a0b7
commit
405d625cdc
15 changed files with 305 additions and 362 deletions
|
|
@ -234,7 +234,7 @@ class BookSearchResultMini extends HookConsumerWidget {
|
|||
final item = ref.watch(libraryItemProvider(book.libraryItemId)).valueOrNull;
|
||||
final image = item == null
|
||||
? const AsyncValue.loading()
|
||||
: ref.watch(coverImageProvider(item));
|
||||
: ref.watch(coverImageProvider(item.id));
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 50,
|
||||
|
|
|
|||
|
|
@ -26,17 +26,19 @@ import 'package:vaani/shared/extensions/model_conversions.dart';
|
|||
import 'package:vaani/shared/utils.dart';
|
||||
|
||||
class LibraryItemActions extends HookConsumerWidget {
|
||||
LibraryItemActions({
|
||||
const LibraryItemActions({
|
||||
super.key,
|
||||
required this.item,
|
||||
}) {
|
||||
book = item.media.asBookExpanded;
|
||||
}
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final String id;
|
||||
|
||||
final shelfsdk.LibraryItemExpanded item;
|
||||
late final shelfsdk.BookExpanded book;
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final item = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||
if (item == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final downloadHistory = ref.watch(downloadHistoryProvider(group: item.id));
|
||||
final apiSettings = ref.watch(apiSettingsProvider);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk;
|
||||
import 'package:vaani/api/image_provider.dart';
|
||||
import 'package:vaani/api/library_item_provider.dart';
|
||||
import 'package:vaani/constants/hero_tag_conventions.dart';
|
||||
import 'package:vaani/features/item_viewer/view/library_item_page.dart';
|
||||
import 'package:vaani/features/player/providers/audiobook_player.dart';
|
||||
|
|
@ -14,125 +15,120 @@ import 'package:vaani/settings/app_settings_provider.dart';
|
|||
import 'package:vaani/shared/extensions/duration_format.dart';
|
||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
|
||||
import 'package:vaani/theme/theme_from_cover_provider.dart';
|
||||
|
||||
class LibraryItemHeroSection extends HookConsumerWidget {
|
||||
const LibraryItemHeroSection({
|
||||
super.key,
|
||||
required this.itemId,
|
||||
required this.extraMap,
|
||||
required this.providedCacheImage,
|
||||
required this.item,
|
||||
required this.itemBookMetadata,
|
||||
required this.bookDetailsCached,
|
||||
required this.coverColorScheme,
|
||||
});
|
||||
|
||||
final String itemId;
|
||||
final LibraryItemExtras? extraMap;
|
||||
final Image? providedCacheImage;
|
||||
final AsyncValue<shelfsdk.LibraryItemExpanded> item;
|
||||
final shelfsdk.BookMetadataExpanded? itemBookMetadata;
|
||||
final shelfsdk.BookMinified? bookDetailsCached;
|
||||
final AsyncValue<ColorScheme?> coverColorScheme;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SliverToBoxAdapter(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// book cover
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SizedBox(
|
||||
width: calculateWidth(context, constraints),
|
||||
child: Column(
|
||||
children: [
|
||||
Hero(
|
||||
tag: HeroTagPrefixes.bookCover +
|
||||
itemId +
|
||||
(extraMap?.heroTagSuffix ?? ''),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: _BookCover(
|
||||
itemId: itemId,
|
||||
extraMap: extraMap,
|
||||
providedCacheImage: providedCacheImage,
|
||||
coverColorScheme: coverColorScheme.valueOrNull,
|
||||
item: item,
|
||||
),
|
||||
),
|
||||
),
|
||||
// a progress bar if available
|
||||
item.when(
|
||||
data: (libraryItem) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
right: 8.0,
|
||||
left: 8.0,
|
||||
),
|
||||
child: _LibraryItemProgressIndicator(
|
||||
libraryItem: libraryItem,
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error: (error, stack) => const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// book cover
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SizedBox(
|
||||
width: calculateWidth(context, constraints),
|
||||
child: Column(
|
||||
children: [
|
||||
Hero(
|
||||
tag: HeroTagPrefixes.bookCover +
|
||||
itemId +
|
||||
(extraMap?.heroTagSuffix ?? ''),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: _BookCover(
|
||||
itemId: itemId,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// book details
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_BookTitle(
|
||||
extraMap: extraMap,
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: bookDetailsCached,
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// authors info if available
|
||||
_BookAuthors(
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: bookDetailsCached,
|
||||
),
|
||||
// narrators info if available
|
||||
_BookNarrators(
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: bookDetailsCached,
|
||||
),
|
||||
// series info if available
|
||||
_BookSeries(
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: bookDetailsCached,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// a progress bar
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
right: 8.0,
|
||||
left: 8.0,
|
||||
),
|
||||
child: _LibraryItemProgressIndicator(
|
||||
id: itemId,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
// book details
|
||||
_BookDetails(id: itemId, extraMap: extraMap),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _BookDetails extends HookConsumerWidget {
|
||||
const _BookDetails({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.extraMap,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final LibraryItemExtras? extraMap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final itemFromApi = ref.watch(libraryItemProvider(id));
|
||||
|
||||
final itemBookMetadata =
|
||||
itemFromApi.valueOrNull?.media.metadata.asBookMetadataExpanded;
|
||||
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_BookTitle(
|
||||
extraMap: extraMap,
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
),
|
||||
);
|
||||
},
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// authors info if available
|
||||
_BookAuthors(
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: extraMap?.book,
|
||||
),
|
||||
// narrators info if available
|
||||
_BookNarrators(
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: extraMap?.book,
|
||||
),
|
||||
// series info if available
|
||||
_BookSeries(
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: extraMap?.book,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -141,14 +137,19 @@ class LibraryItemHeroSection extends HookConsumerWidget {
|
|||
class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
||||
const _LibraryItemProgressIndicator({
|
||||
super.key,
|
||||
required this.libraryItem,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final shelfsdk.LibraryItemExpanded libraryItem;
|
||||
final String id;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final player = ref.watch(audiobookPlayerProvider);
|
||||
final libraryItem = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||
if (libraryItem == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final mediaProgress = libraryItem.userMediaProgress;
|
||||
if (mediaProgress == null && player.book?.libraryItemId != libraryItem.id) {
|
||||
return const SizedBox.shrink();
|
||||
|
|
@ -188,6 +189,8 @@ class _LibraryItemProgressIndicator extends HookConsumerWidget {
|
|||
LinearProgressIndicator(
|
||||
value: progress.clamp(0.03, 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
semanticsLabel: 'Book progress',
|
||||
semanticsValue: '${progressInPercent.toStringAsFixed(2)}%',
|
||||
),
|
||||
const SizedBox.square(
|
||||
dimension: 4.0,
|
||||
|
|
@ -341,24 +344,30 @@ class _BookCover extends HookConsumerWidget {
|
|||
const _BookCover({
|
||||
super.key,
|
||||
required this.itemId,
|
||||
required this.extraMap,
|
||||
required this.providedCacheImage,
|
||||
required this.item,
|
||||
this.coverColorScheme,
|
||||
});
|
||||
|
||||
final String itemId;
|
||||
final LibraryItemExtras? extraMap;
|
||||
final Image? providedCacheImage;
|
||||
final AsyncValue<shelfsdk.LibraryItemExpanded> item;
|
||||
final ColorScheme? coverColorScheme;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final coverImage = ref.watch(coverImageProvider(itemId));
|
||||
final themeData = Theme.of(context);
|
||||
// final item = ref.watch(libraryItemProvider(itemId));
|
||||
final useMaterialThemeOnItemPage =
|
||||
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
|
||||
|
||||
ColorScheme? coverColorScheme;
|
||||
if (useMaterialThemeOnItemPage) {
|
||||
coverColorScheme = ref
|
||||
.watch(
|
||||
themeOfLibraryItemProvider(
|
||||
itemId,
|
||||
brightness: Theme.of(context).brightness,
|
||||
),
|
||||
)
|
||||
.valueOrNull;
|
||||
}
|
||||
|
||||
return ThemeSwitcher(
|
||||
builder: (context) {
|
||||
// change theme after 2 seconds
|
||||
|
|
@ -368,7 +377,7 @@ class _BookCover extends HookConsumerWidget {
|
|||
ThemeSwitcher.of(context).changeTheme(
|
||||
theme: coverColorScheme != null
|
||||
? ThemeData.from(
|
||||
colorScheme: coverColorScheme!,
|
||||
colorScheme: coverColorScheme,
|
||||
textTheme: themeData.textTheme,
|
||||
)
|
||||
: themeData,
|
||||
|
|
@ -378,42 +387,27 @@ class _BookCover extends HookConsumerWidget {
|
|||
}
|
||||
});
|
||||
}
|
||||
return providedCacheImage ??
|
||||
item.when(
|
||||
data: (libraryItem) {
|
||||
final coverImage = ref.watch(coverImageProvider(libraryItem));
|
||||
return Stack(
|
||||
children: [
|
||||
coverImage.when(
|
||||
data: (image) {
|
||||
// return const BookCoverSkeleton();
|
||||
if (image.isEmpty) {
|
||||
return const Icon(Icons.error);
|
||||
}
|
||||
// cover 80% of parent height
|
||||
return Image.memory(
|
||||
image,
|
||||
fit: BoxFit.cover,
|
||||
// cacheWidth: (height *
|
||||
// MediaQuery.of(context).devicePixelRatio)
|
||||
// .round(),
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const Center(
|
||||
child: BookCoverSkeleton(),
|
||||
);
|
||||
},
|
||||
error: (error, stack) {
|
||||
return const Icon(Icons.error);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
error: (error, stack) => const Icon(Icons.error),
|
||||
loading: () => const Center(child: BookCoverSkeleton()),
|
||||
return coverImage.when(
|
||||
data: (image) {
|
||||
// return const BookCoverSkeleton();
|
||||
if (image.isEmpty) {
|
||||
return const Icon(Icons.error);
|
||||
}
|
||||
|
||||
return Image.memory(
|
||||
image,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
},
|
||||
loading: () {
|
||||
return const Center(
|
||||
child: BookCoverSkeleton(),
|
||||
);
|
||||
},
|
||||
error: (error, stack) {
|
||||
return const Center(child: Icon(Icons.error));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -424,12 +418,10 @@ class _BookTitle extends StatelessWidget {
|
|||
super.key,
|
||||
required this.extraMap,
|
||||
required this.itemBookMetadata,
|
||||
required this.bookDetailsCached,
|
||||
});
|
||||
|
||||
final LibraryItemExtras? extraMap;
|
||||
final shelfsdk.BookMetadataExpanded? itemBookMetadata;
|
||||
final shelfsdk.BookMinified? bookDetailsCached;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -449,7 +441,7 @@ class _BookTitle extends StatelessWidget {
|
|||
// pauseBetween: 150.ms,
|
||||
// numberOfReps: 3,
|
||||
style: themeData.textTheme.headlineLarge,
|
||||
itemBookMetadata?.title ?? bookDetailsCached?.metadata.title ?? '',
|
||||
itemBookMetadata?.title ?? extraMap?.book?.metadata.title ?? '',
|
||||
),
|
||||
),
|
||||
// subtitle if available
|
||||
|
|
@ -482,7 +474,7 @@ class _BookAuthors extends StatelessWidget {
|
|||
String generateAuthorsString() {
|
||||
final authors = (itemBookMetadata)?.authors ?? [];
|
||||
if (authors.isEmpty) {
|
||||
return (bookDetailsCached?.metadata as shelfsdk.BookMetadataMinified?)
|
||||
return (bookDetailsCached?.metadata.asBookMetadataMinified)
|
||||
?.authorName ??
|
||||
'';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,65 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:vaani/api/library_item_provider.dart';
|
||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||
|
||||
class LibraryItemMetadata extends StatelessWidget {
|
||||
class LibraryItemMetadata extends HookConsumerWidget {
|
||||
const LibraryItemMetadata({
|
||||
super.key,
|
||||
required this.item,
|
||||
this.itemBookMetadata,
|
||||
this.bookDetailsCached,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final shelfsdk.LibraryItemExpanded item;
|
||||
final shelfsdk.BookMetadataExpanded? itemBookMetadata;
|
||||
final shelfsdk.BookMinified? bookDetailsCached;
|
||||
final String id;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final item = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||
|
||||
/// formats the duration of the book as `10h 30m`
|
||||
///
|
||||
/// will add up all the durations of the audio files first
|
||||
/// then convert them to the given format
|
||||
String? getDurationFormatted() {
|
||||
final book = (item?.media.asBookExpanded);
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
final duration = book.audioFiles
|
||||
.map((e) => e.duration)
|
||||
.reduce((value, element) => value + element);
|
||||
final hours = duration.inHours;
|
||||
final minutes = duration.inMinutes.remainder(60);
|
||||
return '${hours}h ${minutes}m';
|
||||
}
|
||||
|
||||
/// will return the size of the book in MB
|
||||
///
|
||||
/// will add up all the sizes of the audio files first
|
||||
/// then convert them to MB
|
||||
String? getSizeFormatted() {
|
||||
final book = (item?.media.asBookExpanded);
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
final size = book.audioFiles
|
||||
.map((e) => e.metadata.size)
|
||||
.reduce((value, element) => value + element);
|
||||
return '${size / 1024 ~/ 1024} MB';
|
||||
}
|
||||
|
||||
/// will return the codec and bitrate of the book
|
||||
String? getCodecAndBitrate() {
|
||||
final book = (item?.media.asBookExpanded);
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
final codec = book.audioFiles.first.codec.toUpperCase();
|
||||
// final bitrate = book.audioFiles.first.bitRate;
|
||||
return codec;
|
||||
}
|
||||
|
||||
final itemBookMetadata = item?.media.metadata.asBookMetadataExpanded;
|
||||
|
||||
final children = [
|
||||
// duration of the book
|
||||
_MetadataItem(
|
||||
|
|
@ -59,49 +104,6 @@ class LibraryItemMetadata extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// formats the duration of the book as `10h 30m`
|
||||
///
|
||||
/// will add up all the durations of the audio files first
|
||||
/// then convert them to the given format
|
||||
String? getDurationFormatted() {
|
||||
final book = (item.media as shelfsdk.BookExpanded?);
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
final duration = book.audioFiles
|
||||
.map((e) => e.duration)
|
||||
.reduce((value, element) => value + element);
|
||||
final hours = duration.inHours;
|
||||
final minutes = duration.inMinutes.remainder(60);
|
||||
return '${hours}h ${minutes}m';
|
||||
}
|
||||
|
||||
/// will return the size of the book in MB
|
||||
///
|
||||
/// will add up all the sizes of the audio files first
|
||||
/// then convert them to MB
|
||||
String? getSizeFormatted() {
|
||||
final book = (item.media as shelfsdk.BookExpanded?);
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
final size = book.audioFiles
|
||||
.map((e) => e.metadata.size)
|
||||
.reduce((value, element) => value + element);
|
||||
return '${size / 1024 ~/ 1024} MB';
|
||||
}
|
||||
|
||||
/// will return the codec and bitrate of the book
|
||||
String? getCodecAndBitrate() {
|
||||
final book = (item.media as shelfsdk.BookExpanded?);
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
final codec = book.audioFiles.first.codec.toUpperCase();
|
||||
// final bitrate = book.audioFiles.first.bitRate;
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
||||
/// key-value pair to display as column
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ import 'package:vaani/api/library_item_provider.dart';
|
|||
import 'package:vaani/features/item_viewer/view/library_item_sliver_app_bar.dart';
|
||||
import 'package:vaani/features/player/providers/player_form.dart';
|
||||
import 'package:vaani/router/models/library_item_extras.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||
import 'package:vaani/shared/widgets/expandable_description.dart';
|
||||
import 'package:vaani/theme/theme_from_cover_provider.dart';
|
||||
|
||||
import 'library_item_actions.dart';
|
||||
import 'library_item_hero_section.dart';
|
||||
|
|
@ -28,33 +25,9 @@ class LibraryItemPage extends HookConsumerWidget {
|
|||
final Object? extra;
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final extraMap =
|
||||
final additionalItemData =
|
||||
extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
|
||||
final bookDetailsCached = extraMap?.book;
|
||||
final providedCacheImage = extraMap?.coverImage != null
|
||||
? Image.memory(extraMap!.coverImage!)
|
||||
: null;
|
||||
|
||||
final itemFromApi = ref.watch(libraryItemProvider(itemId));
|
||||
|
||||
var itemBookMetadata =
|
||||
itemFromApi.valueOrNull?.media.metadata.asBookMetadataExpanded;
|
||||
|
||||
final useMaterialThemeOnItemPage =
|
||||
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
|
||||
AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
|
||||
if (useMaterialThemeOnItemPage) {
|
||||
coverColorScheme = ref.watch(
|
||||
themeOfLibraryItemProvider(
|
||||
itemFromApi.valueOrNull,
|
||||
brightness: Theme.of(context).brightness,
|
||||
),
|
||||
);
|
||||
debugPrint('ColorScheme: ${coverColorScheme.valueOrNull}');
|
||||
} else {
|
||||
debugPrint('useMaterialThemeOnItemPage is false');
|
||||
// AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
|
||||
}
|
||||
return ThemeProvider(
|
||||
initTheme: Theme.of(context),
|
||||
duration: 200.ms,
|
||||
|
|
@ -67,40 +40,20 @@ class LibraryItemPage extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.all(8),
|
||||
sliver: LibraryItemHeroSection(
|
||||
itemId: itemId,
|
||||
extraMap: extraMap,
|
||||
providedCacheImage: providedCacheImage,
|
||||
item: itemFromApi,
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: bookDetailsCached,
|
||||
coverColorScheme: coverColorScheme,
|
||||
extraMap: additionalItemData,
|
||||
),
|
||||
),
|
||||
// a horizontal display with dividers of metadata
|
||||
SliverToBoxAdapter(
|
||||
child: itemFromApi.valueOrNull != null
|
||||
? LibraryItemMetadata(
|
||||
item: itemFromApi.valueOrNull!,
|
||||
itemBookMetadata: itemBookMetadata,
|
||||
bookDetailsCached: bookDetailsCached,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
child: LibraryItemMetadata(id: itemId),
|
||||
),
|
||||
// a row of actions like play, download, share, etc
|
||||
SliverToBoxAdapter(
|
||||
child: itemFromApi.valueOrNull != null
|
||||
? LibraryItemActions(item: itemFromApi.valueOrNull!)
|
||||
: const SizedBox.shrink(),
|
||||
child: LibraryItemActions(id: itemId),
|
||||
),
|
||||
// a expandable section for book description
|
||||
SliverToBoxAdapter(
|
||||
child:
|
||||
itemFromApi.valueOrNull?.media.metadata.description != null
|
||||
? ExpandableDescription(
|
||||
title: 'About the Book',
|
||||
content: itemFromApi
|
||||
.valueOrNull!.media.metadata.description!,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
child: LibraryItemDescription(id: itemId),
|
||||
),
|
||||
// a padding at the bottom to make sure the last item is not hidden by mini player
|
||||
const SliverToBoxAdapter(
|
||||
|
|
@ -114,6 +67,26 @@ class LibraryItemPage extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class LibraryItemDescription extends HookConsumerWidget {
|
||||
const LibraryItemDescription({
|
||||
super.key,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final String id;
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final item = ref.watch(libraryItemProvider(id)).valueOrNull;
|
||||
if (item == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return ExpandableDescription(
|
||||
title: 'About the Book',
|
||||
content: item.media.metadata.description ?? 'Sorry, no description found',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the width of the book cover based on the screen size
|
||||
double calculateWidth(
|
||||
BuildContext context,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class AudiobookPlayer extends HookConsumerWidget {
|
|||
final player = ref.watch(audiobookPlayerProvider);
|
||||
final imageOfItemBeingPlayed = itemBeingPlayed.valueOrNull != null
|
||||
? ref.watch(
|
||||
coverImageProvider(itemBeingPlayed.valueOrNull!),
|
||||
coverImageProvider(itemBeingPlayed.valueOrNull!.id),
|
||||
)
|
||||
: null;
|
||||
final imgWidget = imageOfItemBeingPlayed?.valueOrNull != null
|
||||
|
|
@ -63,7 +63,7 @@ class AudiobookPlayer extends HookConsumerWidget {
|
|||
// theme from image
|
||||
final imageTheme = ref.watch(
|
||||
themeOfLibraryItemProvider(
|
||||
itemBeingPlayed.valueOrNull,
|
||||
itemBeingPlayed.valueOrNull?.id,
|
||||
brightness: Theme.of(context).brightness,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue