import 'package:dio/dio.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:injectable/injectable.dart'; import '../../../../core/utils/logger.dart'; import 'unified_feed_event.dart'; import 'unified_feed_state.dart'; import '../../domain/entities/feed_item.dart'; import '../../data/repositories/feed_repository.dart'; /// BLoC Centralisé pour le mur d'actualité (DRY). /// Aucune logique graphique, juste la gestion d'états. @injectable class UnifiedFeedBloc extends Bloc { final FeedRepository _repository; UnifiedFeedBloc(this._repository) : super(UnifiedFeedInitial()) { on(_onLoadFeedRequested); on(_onLoadMoreRequested); on(_onClearLoadMoreError); on(_onFeedItemLiked); } void _onClearLoadMoreError(ClearLoadMoreError event, Emitter emit) { if (state is UnifiedFeedLoaded) { emit((state as UnifiedFeedLoaded).copyWith(loadMoreErrorMessage: null)); } } Future _onLoadFeedRequested(LoadFeedRequested event, Emitter emit) async { if (!event.isRefresh) { emit(UnifiedFeedLoading()); } try { final items = await _repository.getFeed(page: 0, size: 10); // On suppose qu'on n'a pas atteint la fin si on a reçu la taille max demandée (10) final hasReachedMax = items.length < 10; emit(UnifiedFeedLoaded(items: items, hasReachedMax: hasReachedMax)); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(UnifiedFeedError('Erreur de chargement du flux: $e')); } } Future _onLoadMoreRequested(FeedLoadMoreRequested event, Emitter emit) async { if (state is UnifiedFeedLoaded) { final currentState = state as UnifiedFeedLoaded; if (currentState.hasReachedMax || currentState.isFetchingMore) return; emit(currentState.copyWith(isFetchingMore: true)); try { final nextPage = (currentState.items.length / 10).floor(); final moreItems = await _repository.getFeed(page: nextPage, size: 10); emit(currentState.copyWith( items: List.of(currentState.items)..addAll(moreItems), hasReachedMax: moreItems.isEmpty, isFetchingMore: false, )); } catch (e, st) { if (e is DioException && e.type == DioExceptionType.cancel) return; AppLogger.error('UnifiedFeedBloc: chargement supplémentaire échoué', error: e, stackTrace: st); emit(currentState.copyWith( isFetchingMore: false, loadMoreErrorMessage: 'Impossible de charger plus', )); } } } void _onFeedItemLiked(FeedItemLiked event, Emitter emit) { if (state is UnifiedFeedLoaded) { final currentState = state as UnifiedFeedLoaded; final updatedItems = currentState.items.map((item) { if (item.id == event.itemId) { return FeedItem( id: item.id, type: item.type, authorName: item.authorName, authorAvatarUrl: item.authorAvatarUrl, createdAt: item.createdAt, content: item.content, likesCount: item.isLikedByMe ? item.likesCount - 1 : item.likesCount + 1, commentsCount: item.commentsCount, isLikedByMe: !item.isLikedByMe, customActionLabel: item.customActionLabel, actionUrlTarget: item.actionUrlTarget, ); } return item; }).toList(); emit(currentState.copyWith(items: updatedItems)); } } }