import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:worldhopper/l10n/app_localizations.dart'; import 'package:worldhopper/config/constants.dart'; import 'package:worldhopper/models/downloaded_chapter.dart'; import 'package:worldhopper/models/downloaded_series.dart'; import 'package:worldhopper/providers/download_provider.dart'; import 'package:worldhopper/widgets/downloaded_chapter_card.dart'; import 'package:worldhopper/widgets/worldhopper_app_bar.dart'; /// Shows all chapters in a downloaded series class DownloadedSeriesDetailScreen extends ConsumerWidget { final String seriesId; const DownloadedSeriesDetailScreen({ super.key, required this.seriesId, }); @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final seriesAsync = ref.watch(downloadedSeriesListProvider); final chaptersAsync = ref.watch(downloadedChaptersProvider(seriesId)); // Find the series from the list final series = seriesAsync.whenOrNull( data: (list) { for (final s in list) { if (s.id == seriesId) return s; } return null; }, ); final hasFailedChapters = chaptersAsync.whenOrNull( data: (chapters) => chapters.any((c) => c.status == DownloadStatus.failed), ) ?? false; return Scaffold( appBar: WorldhopperAppBar( title: Text(series?.title ?? l10n.downloadedSeriesDetailTitle), actions: [ if (hasFailedChapters && series != null) IconButton( icon: const Icon(Icons.refresh), tooltip: l10n.downloadRetryAll, onPressed: () => _retryAllFailed(context, ref, series), ), IconButton( icon: const Icon(Icons.delete_outline), onPressed: () => _confirmDeleteSeries(context, ref, series), ), ], ), body: chaptersAsync.when( data: (chapters) => _buildChaptersList(context, ref, chapters, series), loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => Center( child: Text(l10n.libraryErrorLoading), ), ), ); } Widget _buildChaptersList( BuildContext context, WidgetRef ref, List chapters, DownloadedSeries? series, ) { if (chapters.isEmpty) { return const Center(child: Text('No chapters')); } return ListView.builder( padding: const EdgeInsets.all(16), itemCount: chapters.length, itemBuilder: (context, index) { final chapter = chapters[index]; return Padding( padding: const EdgeInsets.only(bottom: 8), child: DownloadedChapterCard( chapter: chapter, onTap: () => _openChapter(context, ref, chapter), onRetry: chapter.status == DownloadStatus.failed && series != null ? () => _retryChapter(context, ref, series, chapter) : null, ), ); }, ); } void _openChapter( BuildContext context, WidgetRef ref, DownloadedChapter chapter, ) { final entry = chapter.toEntry(); if (chapter.contentType == ChapterContentType.epub) { context.push(AppConstants.routeEpubReader, extra: { 'entry': entry, 'serverId': chapter.serverId, 'localFilePath': chapter.filePath, }); } else { context.push(AppConstants.routeReader, extra: { 'entry': entry, 'serverId': chapter.serverId, 'initialPage': 0, 'localContentPath': chapter.filePath, }); } } Future _retryChapter( BuildContext context, WidgetRef ref, DownloadedSeries series, DownloadedChapter chapter, ) async { final server = await ref.read(serverByIdProvider(series.serverId).future); if (server == null) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Server not found')), ); } return; } await ref .read(downloadNotifierProvider.notifier) .retryChapter(server: server, chapter: chapter); } Future _retryAllFailed( BuildContext context, WidgetRef ref, DownloadedSeries series, ) async { final server = await ref.read(serverByIdProvider(series.serverId).future); if (server == null) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Server not found')), ); } return; } await ref .read(downloadNotifierProvider.notifier) .retryFailedChapters(server: server, seriesId: series.id); } Future _confirmDeleteSeries( BuildContext context, WidgetRef ref, DownloadedSeries? series, ) async { if (series == null) return; final l10n = AppLocalizations.of(context); final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(l10n.downloadDeleteSeries), content: Text(l10n.downloadDeleteSeriesConfirmation(series.title)), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text(l10n.commonCancel), ), FilledButton( onPressed: () => Navigator.of(context).pop(true), style: FilledButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.error, ), child: Text(l10n.commonDelete), ), ], ), ); if (confirmed == true && context.mounted) { await ref.read(downloadNotifierProvider.notifier).deleteSeries(series.id); if (context.mounted) { context.pop(); } } } }