feat: Enhance trip management features and improve UI responsiveness

- Implemented AutomaticKeepAliveClientMixin in HomeContent to maintain state during navigation.
- Modified trip loading logic to trigger after the first frame for better performance.
- Updated trip loading events to use LoadTripsByUserId for consistency.
- Added temporary success messages for trip creation and operations.
- Improved UI elements for better user experience, including updated text styles and spacing.
- Refactored trip model to support Firestore timestamps and improved error handling during parsing.
- Streamlined trip repository methods for better clarity and performance.
- Enhanced trip service methods to ensure correct mapping from Firestore documents.
- Removed unnecessary trip reset logic on logout.
This commit is contained in:
Dayron
2025-10-20 14:31:41 +02:00
parent af93ac54ff
commit d0a76b5043
12 changed files with 863 additions and 756 deletions

View File

@@ -1,124 +1,133 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../repositories/trip_repository.dart';
import 'package:travel_mate/data/models/trip.dart';
import 'trip_event.dart';
import 'trip_state.dart';
import '../../data/models/trip.dart';
import '../../repositories/trip_repository.dart';
class TripBloc extends Bloc<TripEvent, TripState> {
final TripRepository _tripRepository;
final TripRepository _repository;
StreamSubscription? _tripsSubscription;
String? _currentUserId;
TripBloc({required TripRepository tripRepository})
: _tripRepository = tripRepository,
super(TripInitial()) {
on<TripLoadRequested>(_onLoadRequested);
on<_TripUpdated>(_onTripUpdated);
on<TripCreateRequested>(_onCreateRequested);
on<TripUpdateRequested>(_onUpdateRequested);
on<TripDeleteRequested>(_onDeleteRequested);
on<TripParticipantAddRequested>(_onParticipantAddRequested);
on<TripParticipantRemoveRequested>(_onParticipantRemoveRequested);
on<ResetTrips>(_onResetTrips);
TripBloc(this._repository) : super(TripInitial()) {
on<LoadTripsByUserId>(_onLoadTripsByUserId);
on<TripCreateRequested>(_onTripCreateRequested);
on<TripUpdateRequested>(_onTripUpdateRequested);
on<TripDeleteRequested>(_onTripDeleteRequested);
on<_TripsUpdated>(_onTripsUpdated);
}
Future<void> _onLoadRequested(
TripLoadRequested event,
Future<void> _onLoadTripsByUserId(
LoadTripsByUserId event,
Emitter<TripState> emit,
) async {
emit(TripLoading());
await _tripsSubscription?.cancel();
print('🔍 Chargement des trips pour userId: ${event.userId}');
_tripsSubscription = _tripRepository.getUserTrips(event.userId).listen(
(trips) => add(_TripUpdated(trips: trips)),
onError: (error) => emit(TripError(message: error.toString())),
// MODIFIÉ : Toujours émettre Loading pour forcer le rechargement
emit(TripLoading());
_currentUserId = event.userId;
await _tripsSubscription?.cancel();
_tripsSubscription = _repository.getTripsByUserId(event.userId).listen(
(trips) {
print('📦 Stream reçu: ${trips.length} trips');
add(_TripsUpdated(trips));
},
onError: (error) {
print('❌ Erreur stream: $error');
emit(TripError(error.toString()));
},
);
}
Future<void> _onTripUpdated(
_TripUpdated event,
void _onTripsUpdated(
_TripsUpdated event,
Emitter<TripState> emit,
) async {
emit(TripLoaded(trips: event.trips));
) {
print('✅ Émission de TripLoaded avec ${event.trips.length} trips');
emit(TripLoaded(event.trips));
}
Future<void> _onCreateRequested(
Future<void> _onTripCreateRequested(
TripCreateRequested event,
Emitter<TripState> emit,
) async {
try {
await _tripRepository.createTrip(event.trip);
emit(const TripOperationSuccess(message: 'Voyage créé avec succès'));
print('📝 Création du voyage: ${event.trip.title}');
emit(TripLoading());
final tripId = await _repository.createTrip(event.trip);
print('✅ Voyage créé avec ID: $tripId');
// Émettre TripCreated pour que create_trip_content puisse créer le groupe
emit(TripCreated(tripId: tripId));
// AJOUTÉ : Attendre un peu puis recharger manuellement
await Future.delayed(const Duration(milliseconds: 800));
if (_currentUserId != null) {
print('🔄 Rechargement forcé après création');
add(LoadTripsByUserId(userId: _currentUserId!));
}
} catch (e) {
emit(TripError(message: e.toString()));
print('❌ Erreur création: $e');
emit(TripError('Erreur lors de la création: $e'));
}
}
Future<void> _onUpdateRequested(
Future<void> _onTripUpdateRequested(
TripUpdateRequested event,
Emitter<TripState> emit,
) async {
try {
await _tripRepository.updateTrip(event.trip);
emit(const TripOperationSuccess(message: 'Voyage mis à jour'));
print('📝 Mise à jour du voyage: ${event.trip.title}');
await _repository.updateTrip(event.trip.id!, event.trip);
print('✅ Voyage mis à jour');
emit(const TripOperationSuccess('Voyage mis à jour avec succès'));
// AJOUTÉ : Recharger après mise à jour
await Future.delayed(const Duration(milliseconds: 500));
if (_currentUserId != null) {
print('🔄 Rechargement forcé après mise à jour');
add(LoadTripsByUserId(userId: _currentUserId!));
}
} catch (e) {
emit(TripError(message: e.toString()));
print('❌ Erreur mise à jour: $e');
emit(TripError('Erreur lors de la mise à jour: $e'));
}
}
Future<void> _onDeleteRequested(
Future<void> _onTripDeleteRequested(
TripDeleteRequested event,
Emitter<TripState> emit,
) async {
try {
await _tripRepository.deleteTrip(event.tripId);
emit(const TripOperationSuccess(message: 'Voyage supprimé'));
print('🗑️ Suppression du voyage: ${event.tripId}');
await _repository.deleteTrip(event.tripId);
print('✅ Voyage supprimé');
emit(const TripOperationSuccess('Voyage supprimé avec succès'));
// AJOUTÉ : Recharger après suppression
await Future.delayed(const Duration(milliseconds: 500));
if (_currentUserId != null) {
print('🔄 Rechargement forcé après suppression');
add(LoadTripsByUserId(userId: _currentUserId!));
}
} catch (e) {
emit(TripError(message: e.toString()));
print('❌ Erreur suppression: $e');
emit(TripError('Erreur lors de la suppression: $e'));
}
}
Future<void> _onParticipantAddRequested(
TripParticipantAddRequested event,
Emitter<TripState> emit,
) async {
try {
await _tripRepository.addParticipant(
event.tripId,
event.participantEmail,
);
emit(const TripOperationSuccess(message: 'Participant ajouté'));
} catch (e) {
emit(TripError(message: e.toString()));
}
}
Future<void> _onParticipantRemoveRequested(
TripParticipantRemoveRequested event,
Emitter<TripState> emit,
) async {
try {
await _tripRepository.removeParticipant(
event.tripId,
event.participantEmail,
);
emit(const TripOperationSuccess(message: 'Participant retiré'));
} catch (e) {
emit(TripError(message: e.toString()));
}
}
Future<void> _onResetTrips(
ResetTrips event,
Emitter<TripState> emit,
) async {
await _tripsSubscription?.cancel();
_tripsSubscription = null;
emit(TripInitial());
}
@override
Future<void> close() {
_tripsSubscription?.cancel();
@@ -126,10 +135,10 @@ class TripBloc extends Bloc<TripEvent, TripState> {
}
}
class _TripUpdated extends TripEvent {
class _TripsUpdated extends TripEvent {
final List<Trip> trips;
const _TripUpdated({required this.trips});
const _TripsUpdated(this.trips);
@override
List<Object?> get props => [trips];