import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/network/api_client.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/utils/time_utils.dart'; import '../../../core/widgets/glass_container.dart'; import '../../../core/widgets/status_badge.dart'; import '../../news/bloc/news_bloc.dart'; import '../../news/bloc/news_event.dart'; import '../../news/bloc/news_state.dart'; import '../../news/models/news_article_model.dart'; import '../../news/repositories/news_repository.dart'; import '../../news/widgets/article_sentiment_dialog.dart'; class DailyNewsSnapshot extends StatelessWidget { final ApiClient apiClient; final String? backendUrl; const DailyNewsSnapshot({ super.key, required this.apiClient, this.backendUrl, }); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => NewsBloc( repository: NewsRepository(apiClient: apiClient, backendUrl: backendUrl ?? ApiClient.baseUrl), )..add(FetchNews(date: DateTime.now().toIso8601String().substring(0, 10))), child: const _DailyNewsSnapshotContent(), ); } } class _DailyNewsSnapshotContent extends StatefulWidget { const _DailyNewsSnapshotContent(); @override State<_DailyNewsSnapshotContent> createState() => _DailyNewsSnapshotContentState(); } class _DailyNewsSnapshotContentState extends State<_DailyNewsSnapshotContent> { final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); _scrollController.addListener(() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 80) { context.read().add(LoadMoreNews()); } }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return GlassContainer( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ const Text('Tagesnachrichten', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), const SizedBox(width: 8), BlocBuilder( builder: (context, state) { if (state is NewsLoaded && state.articles.isNotEmpty) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: AppTheme.accentCyan.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: Text( '${state.articles.length} Artikel', style: TextStyle(color: AppTheme.accentCyan, fontSize: 11, fontWeight: FontWeight.bold), ), ); } return const SizedBox.shrink(); }, ), ], ), IconButton( icon: Icon(Icons.refresh, color: AppTheme.accentCyan, size: 18), padding: EdgeInsets.zero, constraints: const BoxConstraints(), onPressed: () { context.read().add(FetchNews(isRefresh: true, date: DateTime.now().toIso8601String().substring(0, 10))); }, ), ], ), const SizedBox(height: 12), BlocBuilder( builder: (context, state) { if (state is NewsInitial || (state is NewsLoading && context.read().state is! NewsLoaded)) { return Padding( padding: const EdgeInsets.symmetric(vertical: 32), child: Center( child: CircularProgressIndicator(color: AppTheme.primaryEmerald, strokeWidth: 2), ), ); } if (state is NewsError) { return Padding( padding: const EdgeInsets.symmetric(vertical: 24), child: Center( child: Text( state.message, style: TextStyle(color: AppTheme.textMuted, fontSize: 12), ), ), ); } if (state is NewsLoaded) { final articles = state.articles; if (articles.isEmpty) { return Padding( padding: const EdgeInsets.symmetric(vertical: 24), child: Center( child: Text( 'Keine aktuellen Nachrichten verfügbar', style: TextStyle(color: AppTheme.textMuted, fontSize: 12), ), ), ); } return SizedBox( height: 380, child: ListView.builder( controller: _scrollController, physics: const AlwaysScrollableScrollPhysics(), itemCount: articles.length + (state.hasReachedMax ? 0 : 1), itemBuilder: (context, index) { if (index == articles.length) { return Padding( padding: const EdgeInsets.all(12), child: Center( child: CircularProgressIndicator(color: AppTheme.primaryEmerald, strokeWidth: 2), ), ); } final NewsArticleModel article = articles[index]; final timeStr = TimeUtils.formatRelativeTime(article.publishedAt.toIso8601String()); final Widget? badgeWidget = article.sentiment.trim().isNotEmpty ? StatusBadge.sentiment(article.sentiment, score: article.sentimentScore) : null; return ListTile( contentPadding: EdgeInsets.zero, title: Text( article.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13), maxLines: 2, overflow: TextOverflow.ellipsis, ), subtitle: Text( '${article.author}${timeStr.isNotEmpty ? ' • $timeStr' : ''}', style: TextStyle(color: AppTheme.textMuted, fontSize: 11), ), trailing: badgeWidget, onTap: () { showDialog( context: context, builder: (_) => ArticleSentimentDialog( articleData: article, ), ); }, ); }, ), ); } return const SizedBox.shrink(); }, ), ], ), ); } }