2024-05-09 23:23:50 -04:00
|
|
|
import 'dart:math';
|
|
|
|
|
|
2024-05-11 05:13:56 -04:00
|
|
|
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
|
2024-05-09 00:41:19 -04:00
|
|
|
import 'package:flutter/material.dart';
|
2024-05-11 05:13:56 -04:00
|
|
|
import 'package:flutter_animate/flutter_animate.dart';
|
2024-05-09 00:41:19 -04:00
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
2024-08-23 04:21:46 -04:00
|
|
|
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';
|
2024-05-09 00:41:19 -04:00
|
|
|
|
2024-06-16 22:24:32 -04:00
|
|
|
import 'library_item_actions.dart';
|
|
|
|
|
import 'library_item_hero_section.dart';
|
|
|
|
|
import 'library_item_metadata.dart';
|
2024-05-11 04:06:25 -04:00
|
|
|
|
2024-05-09 00:41:19 -04:00
|
|
|
class LibraryItemPage extends HookConsumerWidget {
|
|
|
|
|
const LibraryItemPage({
|
|
|
|
|
super.key,
|
|
|
|
|
required this.itemId,
|
|
|
|
|
this.extra,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final String itemId;
|
|
|
|
|
final Object? extra;
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
|
|
|
final extraMap =
|
|
|
|
|
extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
|
2024-05-11 04:06:25 -04:00
|
|
|
final bookDetailsCached = extraMap?.book;
|
2024-05-09 23:23:50 -04:00
|
|
|
final providedCacheImage = extraMap?.coverImage != null
|
|
|
|
|
? Image.memory(extraMap!.coverImage!)
|
|
|
|
|
: null;
|
2024-05-09 00:41:19 -04:00
|
|
|
|
2024-05-12 05:38:30 -04:00
|
|
|
final itemFromApi = ref.watch(libraryItemProvider(itemId));
|
2024-05-14 06:13:16 -04:00
|
|
|
|
2024-06-16 22:24:32 -04:00
|
|
|
var itemBookMetadata =
|
|
|
|
|
itemFromApi.valueOrNull?.media.metadata.asBookMetadataExpanded;
|
2024-05-11 04:06:25 -04:00
|
|
|
|
|
|
|
|
final useMaterialThemeOnItemPage =
|
2024-08-20 11:39:26 -04:00
|
|
|
ref.watch(appSettingsProvider).themeSettings.useMaterialThemeOnItemPage;
|
2024-05-11 04:06:25 -04:00
|
|
|
AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
|
|
|
|
|
if (useMaterialThemeOnItemPage) {
|
|
|
|
|
coverColorScheme = ref.watch(
|
|
|
|
|
themeOfLibraryItemProvider(
|
2024-05-12 05:38:30 -04:00
|
|
|
itemFromApi.valueOrNull,
|
2024-05-11 04:06:25 -04:00
|
|
|
brightness: Theme.of(context).brightness,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
debugPrint('ColorScheme: ${coverColorScheme.valueOrNull}');
|
|
|
|
|
} else {
|
|
|
|
|
debugPrint('useMaterialThemeOnItemPage is false');
|
|
|
|
|
// AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
|
|
|
|
|
}
|
2024-05-11 05:13:56 -04:00
|
|
|
return ThemeProvider(
|
|
|
|
|
initTheme: Theme.of(context),
|
|
|
|
|
duration: 200.ms,
|
|
|
|
|
child: ThemeSwitchingArea(
|
2024-06-16 22:24:32 -04:00
|
|
|
child: Scaffold(
|
|
|
|
|
body: CustomScrollView(
|
|
|
|
|
slivers: [
|
|
|
|
|
const LibraryItemSliverAppBar(),
|
|
|
|
|
SliverPadding(
|
|
|
|
|
padding: const EdgeInsets.all(8),
|
|
|
|
|
sliver: LibraryItemHeroSection(
|
|
|
|
|
itemId: itemId,
|
|
|
|
|
extraMap: extraMap,
|
|
|
|
|
providedCacheImage: providedCacheImage,
|
|
|
|
|
item: itemFromApi,
|
|
|
|
|
itemBookMetadata: itemBookMetadata,
|
|
|
|
|
bookDetailsCached: bookDetailsCached,
|
|
|
|
|
coverColorScheme: coverColorScheme,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
// a horizontal display with dividers of metadata
|
|
|
|
|
SliverToBoxAdapter(
|
|
|
|
|
child: itemFromApi.valueOrNull != null
|
|
|
|
|
? LibraryItemMetadata(
|
|
|
|
|
item: itemFromApi.valueOrNull!,
|
|
|
|
|
itemBookMetadata: itemBookMetadata,
|
|
|
|
|
bookDetailsCached: bookDetailsCached,
|
|
|
|
|
)
|
|
|
|
|
: const SizedBox.shrink(),
|
|
|
|
|
),
|
|
|
|
|
// a row of actions like play, download, share, etc
|
|
|
|
|
SliverToBoxAdapter(
|
|
|
|
|
child: itemFromApi.valueOrNull != null
|
|
|
|
|
? LibraryItemActions(item: itemFromApi.valueOrNull!)
|
|
|
|
|
: const SizedBox.shrink(),
|
|
|
|
|
),
|
|
|
|
|
// 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!,
|
2024-05-12 05:38:30 -04:00
|
|
|
)
|
|
|
|
|
: const SizedBox.shrink(),
|
2024-05-09 23:23:50 -04:00
|
|
|
),
|
2024-08-20 08:36:39 -04:00
|
|
|
// a padding at the bottom to make sure the last item is not hidden by mini player
|
|
|
|
|
const SliverToBoxAdapter(
|
|
|
|
|
child: SizedBox(height: playerMinHeight),
|
|
|
|
|
),
|
2024-06-16 22:24:32 -04:00
|
|
|
],
|
2024-05-12 05:38:30 -04:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-11 04:06:25 -04:00
|
|
|
/// Calculate the width of the book cover based on the screen size
|
2024-05-09 23:23:50 -04:00
|
|
|
double calculateWidth(
|
|
|
|
|
BuildContext context,
|
|
|
|
|
BoxConstraints constraints, {
|
2024-05-11 04:06:25 -04:00
|
|
|
/// width ratio of the cover image to the available width
|
|
|
|
|
double widthRatio = 0.4,
|
|
|
|
|
|
|
|
|
|
/// height ratio of the cover image to the available height
|
2024-05-12 05:38:30 -04:00
|
|
|
double maxHeightToUse = 0.25,
|
2024-05-09 23:23:50 -04:00
|
|
|
}) {
|
|
|
|
|
final availHeight =
|
|
|
|
|
min(constraints.maxHeight, MediaQuery.of(context).size.height);
|
|
|
|
|
final availWidth =
|
|
|
|
|
min(constraints.maxWidth, MediaQuery.of(context).size.width);
|
|
|
|
|
|
2024-05-11 04:06:25 -04:00
|
|
|
// make the width widthRatio of the available width
|
2024-05-09 23:23:50 -04:00
|
|
|
var width = availWidth * widthRatio;
|
2024-05-11 04:06:25 -04:00
|
|
|
// but never exceed more than heightRatio of height
|
|
|
|
|
if (width > availHeight * maxHeightToUse) {
|
|
|
|
|
width = availHeight * maxHeightToUse;
|
2024-05-09 23:23:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return width;
|
|
|
|
|
}
|