import 'package:flutter/material.dart'; import '../models/video_item.dart'; import '../services/library_service.dart'; import '../widgets/video_result_card.dart'; import 'channels_page.dart'; import 'video_player_page.dart'; class LibraryHubPage extends StatelessWidget { const LibraryHubPage({super.key}); @override Widget build(BuildContext context) { return SafeArea( child: ValueListenableBuilder>( valueListenable: LibraryService.instance.history, builder: (context, history, _) { return ValueListenableBuilder>( valueListenable: LibraryService.instance.favorites, builder: (context, favorites, __) { final recentHistory = history.take(3).toList(growable: false); final recentFavorites = favorites.take(3).toList(growable: false); return ListView( padding: EdgeInsets.fromLTRB(18, 20, 18, 28), children: [ Text( 'Bibliothèque', style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.w800, ), ), SizedBox(height: 6), Text( 'Retrouve rapidement ton historique, tes favoris et tes chaînes.', style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.78), fontSize: 13, ), ), SizedBox(height: 18), LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth; final crossAxisCount = width >= 1100 ? 3 : width >= 720 ? 2 : 1; final mainAxisExtent = width >= 1100 ? 220.0 : width >= 720 ? 210.0 : 196.0; return GridView.count( crossAxisCount: crossAxisCount, shrinkWrap: true, crossAxisSpacing: 12, mainAxisSpacing: 12, mainAxisExtent: mainAxisExtent, physics: NeverScrollableScrollPhysics(), children: [ _LibraryHubCard( icon: Icons.history, title: 'Historique', value: '${history.length}', subtitle: history.isEmpty ? 'Aucune vidéo lue pour le moment.' : 'Tes vidéos récemment ouvertes.', onTap: () => _openPage( context, HistoryPage(), ), ), _LibraryHubCard( icon: Icons.favorite, title: 'Favoris', value: '${favorites.length}', subtitle: favorites.isEmpty ? 'Aucune vidéo en favori.' : 'Tes vidéos enregistrées pour plus tard.', onTap: () => _openPage( context, FavoritesPage(), ), ), _LibraryHubCard( icon: Icons.video_library, title: 'Chaînes', value: 'Explorer', subtitle: 'Accède à tes chaînes personnelles et au catalogue.', onTap: () => _openPage( context, ChannelsPage(), ), ), ], ); }, ), SizedBox(height: 24), if (recentHistory.isNotEmpty) ...[ _SectionHeader( title: 'Reprendre vite', actionLabel: 'Voir tout', onTap: () => _openPage(context, HistoryPage()), ), SizedBox(height: 8), ...recentHistory.map( (video) => Padding( padding: EdgeInsets.only(bottom: 6), child: VideoResultCard( video: video, onTap: () => _openVideo(context, video), ), ), ), SizedBox(height: 16), ], if (recentFavorites.isNotEmpty) ...[ _SectionHeader( title: 'Favoris récents', actionLabel: 'Voir tout', onTap: () => _openPage(context, FavoritesPage()), ), SizedBox(height: 8), ...recentFavorites.map( (video) => Padding( padding: EdgeInsets.only(bottom: 6), child: VideoResultCard( video: video, onTap: () => _openVideo(context, video), ), ), ), ], ], ); }, ); }, ), ); } void _openPage(BuildContext context, Widget page) { Navigator.of(context).push( MaterialPageRoute(builder: (_) => page), ); } void _openVideo(BuildContext context, VideoItem video) { Navigator.of(context).push( MaterialPageRoute( builder: (_) => VideoPlayerPage(video: video), ), ); } } class HistoryPage extends StatelessWidget { const HistoryPage({super.key}); @override Widget build(BuildContext context) { return _LibraryPage( title: 'Historique', emptyIcon: Icons.history, emptyTitle: 'Aucun historique', emptySubtitle: 'Les vidéos que tu regardes apparaîtront ici automatiquement.', listenable: LibraryService.instance.history, onClear: LibraryService.instance.clearHistory, ); } } class FavoritesPage extends StatelessWidget { const FavoritesPage({super.key}); @override Widget build(BuildContext context) { return _LibraryPage( title: 'Favoris', emptyIcon: Icons.favorite_border, emptyTitle: 'Aucun favori', emptySubtitle: 'Ajoute des vidéos aux favoris depuis le lecteur.', listenable: LibraryService.instance.favorites, onClear: LibraryService.instance.clearFavorites, ); } } class _LibraryHubCard extends StatelessWidget { const _LibraryHubCard({ required this.icon, required this.title, required this.value, required this.subtitle, required this.onTap, }); final IconData icon; final String title; final String value; final String subtitle; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( color: Theme.of(context).colorScheme.surfaceContainerLow, borderRadius: BorderRadius.circular(22), child: InkWell( borderRadius: BorderRadius.circular(22), onTap: onTap, child: Ink( padding: EdgeInsets.all(18), decoration: BoxDecoration( borderRadius: BorderRadius.circular(22), border: Border.all(color: Theme.of(context).colorScheme.outlineVariant), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 42, height: 42, decoration: BoxDecoration( color: Color(0x22FF6B63), borderRadius: BorderRadius.circular(14), ), child: Icon(icon, color: Color(0xFFFF7A73)), ), SizedBox(height: 16), Text( title, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, ), ), SizedBox(height: 4), Text( value, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 24, fontWeight: FontWeight.w800, ), ), SizedBox(height: 10), Expanded( child: Align( alignment: Alignment.topLeft, child: Text( subtitle, maxLines: 3, overflow: TextOverflow.ellipsis, style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.78), fontSize: 12.5, height: 1.35, ), ), ), ), ], ), ), ), ); } } class _SectionHeader extends StatelessWidget { const _SectionHeader({ required this.title, required this.actionLabel, required this.onTap, }); final String title; final String actionLabel; final VoidCallback onTap; @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: Text( title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, ), ), ), TextButton(onPressed: onTap, child: Text(actionLabel)), ], ); } } class _LibraryPage extends StatelessWidget { const _LibraryPage({ required this.title, required this.emptyIcon, required this.emptyTitle, required this.emptySubtitle, required this.listenable, required this.onClear, }); final String title; final IconData emptyIcon; final String emptyTitle; final String emptySubtitle; final ValueNotifier> listenable; final Future Function() onClear; @override Widget build(BuildContext context) { return SafeArea( child: ValueListenableBuilder>( valueListenable: listenable, builder: (context, videos, child) { final canGoBack = Navigator.of(context).canPop(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.fromLTRB(20, 18, 14, 10), child: Row( children: [ if (canGoBack) ...[ IconButton( tooltip: 'Retour', onPressed: () => Navigator.of(context).pop(), icon: Icon(Icons.arrow_back), ), SizedBox(width: 4), ], Expanded( child: Text( title, style: Theme.of(context).textTheme.headlineMedium, ), ), if (videos.isNotEmpty) IconButton( tooltip: 'Tout effacer', onPressed: () => _confirmClear(context), icon: Icon(Icons.delete_outline), ), ], ), ), Expanded( child: videos.isEmpty ? _EmptyLibrary( icon: emptyIcon, title: emptyTitle, subtitle: emptySubtitle, ) : ListView.separated( padding: EdgeInsets.fromLTRB(12, 4, 12, 24), itemCount: videos.length, separatorBuilder: (context, index) => SizedBox(height: 4), itemBuilder: (context, index) { final video = videos[index]; return VideoResultCard( video: video, onTap: () => _openVideo(context, video), trailing: listenable == LibraryService.instance.history ? IconButton( tooltip: "Supprimer de l'historique", icon: Icon(Icons.remove_circle_outline), onPressed: () => LibraryService.instance.removeFromHistory(video.id), ) : null, ); }, ), ), ], ); }, ), ); } void _openVideo( BuildContext context, VideoItem video, ) { Navigator.of(context).push( MaterialPageRoute( builder: (context) => VideoPlayerPage(video: video), ), ); } Future _confirmClear(BuildContext context) async { final confirmed = await showDialog( context: context, builder: (dialogContext) { return AlertDialog( title: Text('Effacer $title ?'), content: Text( 'Cette action supprimera cette liste uniquement sur cet appareil.', ), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(false), child: Text('Annuler'), ), FilledButton( onPressed: () => Navigator.of(dialogContext).pop(true), child: Text('Effacer'), ), ], ); }, ); if (confirmed == true) { await onClear(); } } } class _EmptyLibrary extends StatelessWidget { const _EmptyLibrary({ required this.icon, required this.title, required this.subtitle, }); final IconData icon; final String title; final String subtitle; @override Widget build(BuildContext context) { return Center( child: Padding( padding: EdgeInsets.all(28), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 64, color: Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.44), ), SizedBox(height: 16), Text( title, style: TextStyle( fontSize: 21, fontWeight: FontWeight.w600, ), ), SizedBox(height: 8), Text( subtitle, textAlign: TextAlign.center, style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.86), fontSize: 15, ), ), ], ), ), ); } }