Files
TravelMate/lib/blocs/activity/approved_activities/approved_activity_bloc.dart
Dayron f6c8432335 Refactor ActivityCard UI and improve voting functionality
- Updated ActivityCard layout for better visual consistency and responsiveness.
- Simplified the category badge and adjusted styles for better readability.
- Enhanced the voting section with a progress bar and improved button designs.
- Added a new method in Activity model to check if all trip participants approved an activity.
- Improved error handling and validation in ActivityRepository for voting and fetching activities.
- Implemented pagination in ActivityPlacesService for activity searches.
- Removed outdated scripts for cleaning up duplicate images.
2025-11-04 20:21:54 +01:00

158 lines
5.7 KiB
Dart

import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../models/activity.dart';
import '../../../repositories/activity_repository.dart';
import '../../../services/error_service.dart';
import 'approved_activity_event.dart';
import 'approved_activity_state.dart';
/// BLoC pour gérer les activités approuvées par tous les participants
class ApprovedActivityBloc extends Bloc<ApprovedActivityEvent, ApprovedActivityState> {
final ActivityRepository _repository;
final ErrorService _errorService;
ApprovedActivityBloc({
required ActivityRepository repository,
required ErrorService errorService,
}) : _repository = repository,
_errorService = errorService,
super(const ApprovedActivityInitial()) {
on<LoadApprovedActivities>(_onLoadApprovedActivities);
on<SearchApprovedActivities>(_onSearchApprovedActivities);
on<FilterApprovedActivities>(_onFilterApprovedActivities);
on<RefreshApprovedActivities>(_onRefreshApprovedActivities);
on<ClearApprovedSearchResults>(_onClearApprovedSearchResults);
}
/// Charger les activités approuvées par tous les participants
Future<void> _onLoadApprovedActivities(
LoadApprovedActivities event,
Emitter<ApprovedActivityState> emit,
) async {
try {
emit(const ApprovedActivityLoading());
final allActivities = await _repository.getActivitiesByTrip(event.tripId);
// Filtrer les activités qui ont reçu des votes positifs de TOUS les participants
final approvedActivities = allActivities.where((activity) {
// Une activité est approuvée si tous les participants ont voté positivement
final positiveVoters = activity.votes.entries
.where((entry) => entry.value > 0)
.map((entry) => entry.key)
.toSet();
// Vérifier que tous les participants ont voté positivement
return event.tripParticipants.every((participant) =>
positiveVoters.contains(participant));
}).toList();
// Trier par nombre total de votes puis par rating
approvedActivities.sort((a, b) {
final aVotes = a.totalVotes;
final bVotes = b.totalVotes;
if (aVotes != bVotes) {
return bVotes.compareTo(aVotes);
}
return (b.rating ?? 0).compareTo(a.rating ?? 0);
});
emit(ApprovedActivityLoaded(
approvedActivities: approvedActivities,
tripParticipants: event.tripParticipants,
));
} catch (e) {
_errorService.logError('approved_activity_bloc', 'Erreur chargement activités approuvées: $e');
emit(const ApprovedActivityError('Impossible de charger les activités approuvées'));
}
}
/// Rechercher dans les activités approuvées
Future<void> _onSearchApprovedActivities(
SearchApprovedActivities event,
Emitter<ApprovedActivityState> emit,
) async {
try {
emit(const ApprovedActivitySearching());
// Charger d'abord toutes les activités approuvées
final allActivities = await _repository.getActivitiesByTrip(event.tripId);
// Filtrer les approuvées puis rechercher
final approvedActivities = allActivities.where((activity) {
final positiveVoters = activity.votes.entries
.where((entry) => entry.value > 0)
.map((entry) => entry.key)
.toSet();
return event.tripParticipants.every((participant) =>
positiveVoters.contains(participant));
}).toList();
// Rechercher dans les activités approuvées
final results = approvedActivities
.where((activity) =>
activity.name.toLowerCase().contains(event.query.toLowerCase()) ||
activity.description.toLowerCase().contains(event.query.toLowerCase()) ||
activity.category.toLowerCase().contains(event.query.toLowerCase()))
.toList();
emit(ApprovedActivitySearchResults(
results: results,
query: event.query,
tripParticipants: event.tripParticipants,
));
} catch (e) {
_errorService.logError('approved_activity_bloc', 'Erreur recherche approuvées: $e');
emit(const ApprovedActivityError('Erreur lors de la recherche'));
}
}
/// Filtrer les activités approuvées
Future<void> _onFilterApprovedActivities(
FilterApprovedActivities event,
Emitter<ApprovedActivityState> emit,
) async {
if (state is! ApprovedActivityLoaded) return;
final currentState = state as ApprovedActivityLoaded;
List<Activity> filteredActivities = List.from(currentState.approvedActivities);
// Filtrer par catégorie
if (event.category != null && event.category!.isNotEmpty) {
filteredActivities = filteredActivities
.where((activity) => activity.category == event.category)
.toList();
}
// Filtrer par rating minimum
if (event.minRating != null) {
filteredActivities = filteredActivities
.where((activity) => (activity.rating ?? 0) >= event.minRating!)
.toList();
}
emit(currentState.copyWith(approvedActivities: filteredActivities));
}
/// Rafraîchir les activités approuvées
Future<void> _onRefreshApprovedActivities(
RefreshApprovedActivities event,
Emitter<ApprovedActivityState> emit,
) async {
add(LoadApprovedActivities(
tripId: event.tripId,
tripParticipants: event.tripParticipants,
));
}
/// Effacer les résultats de recherche
Future<void> _onClearApprovedSearchResults(
ClearApprovedSearchResults event,
Emitter<ApprovedActivityState> emit,
) async {
if (state is ApprovedActivitySearchResults) {
emit(const ApprovedActivityInitial());
}
}
}