Vaani/lib/features/library_browser/view/library_browser_page.dart
Claude 53027bf74c
feat: implement library view with Authors, Genres, and Series browsing
This commit implements a comprehensive library browsing feature:

- Add LibraryBrowserProvider with providers for authors, genres, and series data
- Create LibraryAuthorsPage with grid view of authors including images and book counts
- Create LibraryGenresPage with list view of all genres
- Create LibrarySeriesPage with list view of series and book counts
- Update LibraryBrowserPage navigation to route to the new views
- Add routes for /browser/authors, /browser/genres, and /browser/series
- Replace "Not Implemented" toasts with functional navigation

The implementation uses the Audiobookshelf API via shelfsdk to fetch:
- Authors list with metadata (getAuthors)
- Genres from library filter data (getFilterData)
- Series with pagination support (getSeries)

All views follow Material Design 3 patterns and include proper loading/error states.
2025-11-20 10:52:18 +00:00

84 lines
3.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/library_provider.dart' show currentLibraryProvider;
import 'package:vaani/features/you/view/widgets/library_switch_chip.dart'
show showLibrarySwitcher;
import 'package:vaani/router/router.dart' show Routes;
import 'package:vaani/shared/icons/abs_icons.dart' show AbsIcons;
class LibraryBrowserPage extends HookConsumerWidget {
const LibraryBrowserPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
// Determine the icon to use, with a fallback
final IconData libraryIconData =
AbsIcons.getIconByName(currentLibrary?.icon) ?? Icons.library_books;
// Determine the title text
final String appBarTitle = '${currentLibrary?.name ?? 'Your'} Library';
return Scaffold(
// Use CustomScrollView to enable slivers
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
// floating: true, // Optional: uncomment if you want floating behavior
// snap:
// true, // Optional: uncomment if you want snapping behavior (usually with floating: true)
leading: IconButton(
icon: Icon(libraryIconData),
tooltip: 'Switch Library', // Helpful tooltip for users
onPressed: () {
showLibrarySwitcher(context, ref);
},
),
title: Text(appBarTitle),
),
SliverList(
delegate: SliverChildListDelegate(
[
ListTile(
title: const Text('Authors'),
leading: const Icon(Icons.person),
trailing: const Icon(Icons.chevron_right),
onTap: () {
GoRouter.of(context).pushNamed(Routes.libraryAuthors.name);
},
),
ListTile(
title: const Text('Genres'),
leading: const Icon(Icons.category),
trailing: const Icon(Icons.chevron_right),
onTap: () {
GoRouter.of(context).pushNamed(Routes.libraryGenres.name);
},
),
ListTile(
title: const Text('Series'),
leading: const Icon(Icons.list),
trailing: const Icon(Icons.chevron_right),
onTap: () {
GoRouter.of(context).pushNamed(Routes.librarySeries.name);
},
),
// Downloads
ListTile(
title: const Text('Downloads'),
leading: const Icon(Icons.download),
trailing: const Icon(Icons.chevron_right),
onTap: () {
GoRouter.of(context).pushNamed(Routes.downloads.name);
},
),
],
),
),
],
),
);
}
}