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:
@@ -13,7 +13,7 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
|
||||
GroupBloc(this._repository) : super(GroupInitial()) {
|
||||
on<LoadGroupsByUserId>(_onLoadGroupsByUserId);
|
||||
on<_GroupsUpdated>(_onGroupsUpdated); // NOUVEAU événement interne
|
||||
on<_GroupsUpdated>(_onGroupsUpdated);
|
||||
on<LoadGroupsByTrip>(_onLoadGroupsByTrip);
|
||||
on<CreateGroup>(_onCreateGroup);
|
||||
on<CreateGroupWithMembers>(_onCreateGroupWithMembers);
|
||||
@@ -44,7 +44,6 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
}
|
||||
}
|
||||
|
||||
// NOUVEAU: Handler pour les mises à jour du stream
|
||||
Future<void> _onGroupsUpdated(
|
||||
_GroupsUpdated event,
|
||||
Emitter<GroupState> emit,
|
||||
@@ -111,6 +110,7 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
// CORRECTION : Utiliser addMemberToGroup au lieu de addMember
|
||||
await _repository.addMember(event.groupId, event.member);
|
||||
emit(const GroupOperationSuccess('Membre ajouté'));
|
||||
} catch (e) {
|
||||
@@ -123,6 +123,7 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
// CORRECTION : Utiliser removeMemberFromGroup au lieu de removeMember
|
||||
await _repository.removeMember(event.groupId, event.userId);
|
||||
emit(const GroupOperationSuccess('Membre supprimé'));
|
||||
} catch (e) {
|
||||
@@ -161,7 +162,6 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
}
|
||||
}
|
||||
|
||||
// NOUVEAU: Événement interne pour les mises à jour du stream
|
||||
class _GroupsUpdated extends GroupEvent {
|
||||
final List<Group> groups;
|
||||
final String? error;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -8,10 +8,10 @@ abstract class TripEvent extends Equatable {
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class TripLoadRequested extends TripEvent {
|
||||
class LoadTripsByUserId extends TripEvent {
|
||||
final String userId;
|
||||
|
||||
const TripLoadRequested({required this.userId});
|
||||
const LoadTripsByUserId({required this.userId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [userId];
|
||||
@@ -42,38 +42,4 @@ class TripDeleteRequested extends TripEvent {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [tripId];
|
||||
}
|
||||
|
||||
class TripParticipantAddRequested extends TripEvent {
|
||||
final String tripId;
|
||||
final String participantEmail;
|
||||
|
||||
const TripParticipantAddRequested({
|
||||
required this.tripId,
|
||||
required this.participantEmail,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [tripId, participantEmail];
|
||||
}
|
||||
|
||||
class TripParticipantRemoveRequested extends TripEvent {
|
||||
final String tripId;
|
||||
final String participantEmail;
|
||||
|
||||
const TripParticipantRemoveRequested({
|
||||
required this.tripId,
|
||||
required this.participantEmail,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [tripId, participantEmail];
|
||||
}
|
||||
|
||||
// NOUVEAU : Événement pour réinitialiser les trips
|
||||
class ResetTrips extends TripEvent {
|
||||
const ResetTrips();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
@@ -15,16 +15,30 @@ class TripLoading extends TripState {}
|
||||
class TripLoaded extends TripState {
|
||||
final List<Trip> trips;
|
||||
|
||||
const TripLoaded({required this.trips});
|
||||
const TripLoaded(this.trips);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [trips];
|
||||
}
|
||||
|
||||
// NOUVEAU : État pour indiquer qu'un voyage a été créé avec succès
|
||||
class TripCreated extends TripState {
|
||||
final String tripId;
|
||||
final String message;
|
||||
|
||||
const TripCreated({
|
||||
required this.tripId,
|
||||
this.message = 'Voyage créé avec succès',
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [tripId, message];
|
||||
}
|
||||
|
||||
class TripOperationSuccess extends TripState {
|
||||
final String message;
|
||||
|
||||
const TripOperationSuccess({required this.message});
|
||||
const TripOperationSuccess(this.message);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
@@ -33,7 +47,7 @@ class TripOperationSuccess extends TripState {
|
||||
class TripError extends TripState {
|
||||
final String message;
|
||||
|
||||
const TripError({required this.message});
|
||||
const TripError(this.message);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
|
||||
Reference in New Issue
Block a user