From 068924a0f2c5e79dab19106f8b9c67c85c2a1d5c Mon Sep 17 00:00:00 2001 From: Dayron Date: Mon, 20 Oct 2025 15:34:06 +0200 Subject: [PATCH] feat: Refactor group deletion logic to use tripId and add reset trips functionality --- lib/blocs/group/group_bloc.dart | 2 +- lib/blocs/group/group_event.dart | 6 +- lib/blocs/trip/trip_bloc.dart | 36 ++++-------- lib/blocs/trip/trip_event.dart | 4 ++ lib/components/home/home_content.dart | 32 ++++++----- .../home/show_trip_details_content.dart | 57 ++++++++++++++++++- lib/pages/home.dart | 6 +- lib/repositories/group_repository.dart | 32 +++++++---- 8 files changed, 117 insertions(+), 58 deletions(-) diff --git a/lib/blocs/group/group_bloc.dart b/lib/blocs/group/group_bloc.dart index a1fafcf..be1778c 100644 --- a/lib/blocs/group/group_bloc.dart +++ b/lib/blocs/group/group_bloc.dart @@ -148,7 +148,7 @@ class GroupBloc extends Bloc { Emitter emit, ) async { try { - await _repository.deleteGroup(event.groupId); + await _repository.deleteGroup(event.tripId); emit(const GroupOperationSuccess('Groupe supprimé')); } catch (e) { emit(GroupError('Erreur lors de la suppression: $e')); diff --git a/lib/blocs/group/group_event.dart b/lib/blocs/group/group_event.dart index cf6d594..237893a 100644 --- a/lib/blocs/group/group_event.dart +++ b/lib/blocs/group/group_event.dart @@ -88,10 +88,10 @@ class UpdateGroup extends GroupEvent { // Supprimer un groupe class DeleteGroup extends GroupEvent { - final String groupId; + final String tripId; - const DeleteGroup(this.groupId); + const DeleteGroup(this.tripId); @override - List get props => [groupId]; + List get props => [tripId]; } \ No newline at end of file diff --git a/lib/blocs/trip/trip_bloc.dart b/lib/blocs/trip/trip_bloc.dart index a081d4c..f214190 100644 --- a/lib/blocs/trip/trip_bloc.dart +++ b/lib/blocs/trip/trip_bloc.dart @@ -17,15 +17,13 @@ class TripBloc extends Bloc { on(_onTripUpdateRequested); on(_onTripDeleteRequested); on<_TripsUpdated>(_onTripsUpdated); + on(_onResetTrips); } Future _onLoadTripsByUserId( LoadTripsByUserId event, Emitter emit, ) async { - print('🔍 Chargement des trips pour userId: ${event.userId}'); - - // MODIFIÉ : Toujours émettre Loading pour forcer le rechargement emit(TripLoading()); _currentUserId = event.userId; @@ -33,11 +31,9 @@ class TripBloc extends Bloc { _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())); }, ); @@ -47,7 +43,6 @@ class TripBloc extends Bloc { _TripsUpdated event, Emitter emit, ) { - print('✅ Émission de TripLoaded avec ${event.trips.length} trips'); emit(TripLoaded(event.trips)); } @@ -56,24 +51,18 @@ class TripBloc extends Bloc { Emitter emit, ) async { try { - 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) { - print('❌ Erreur création: $e'); emit(TripError('Erreur lors de la création: $e')); } } @@ -83,22 +72,14 @@ class TripBloc extends Bloc { Emitter emit, ) async { try { - 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) { - print('❌ Erreur mise à jour: $e'); emit(TripError('Erreur lors de la mise à jour: $e')); } } @@ -108,26 +89,29 @@ class TripBloc extends Bloc { Emitter emit, ) async { try { - 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) { - print('❌ Erreur suppression: $e'); emit(TripError('Erreur lors de la suppression: $e')); } } + Future _onResetTrips( + ResetTrips event, + Emitter emit, + ) async { + await _tripsSubscription?.cancel(); + _currentUserId = null; + emit(TripInitial()); + } + @override Future close() { _tripsSubscription?.cancel(); diff --git a/lib/blocs/trip/trip_event.dart b/lib/blocs/trip/trip_event.dart index 0378e72..19b2eff 100644 --- a/lib/blocs/trip/trip_event.dart +++ b/lib/blocs/trip/trip_event.dart @@ -35,6 +35,10 @@ class TripUpdateRequested extends TripEvent { List get props => [trip]; } +class ResetTrips extends TripEvent { + const ResetTrips(); +} + class TripDeleteRequested extends TripEvent { final String tripId; diff --git a/lib/components/home/home_content.dart b/lib/components/home/home_content.dart index 7e279ab..c5594b9 100644 --- a/lib/components/home/home_content.dart +++ b/lib/components/home/home_content.dart @@ -19,21 +19,25 @@ class HomeContent extends StatefulWidget { class _HomeContentState extends State with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; + + bool _hasLoadedTrips = false; @override void initState() { super.initState(); - // MODIFIÉ : Attendre un frame avant de charger + // MODIFIÉ : Utiliser addPostFrameCallback pour attendre que le widget tree soit prêt WidgetsBinding.instance.addPostFrameCallback((_) { _loadTripsIfUserLoaded(); }); } void _loadTripsIfUserLoaded() { - final userState = context.read().state; - if (userState is UserLoaded) { - print('🚀 Chargement initial des trips pour ${userState.user.id}'); - context.read().add(LoadTripsByUserId(userId: userState.user.id)); + if (!_hasLoadedTrips && mounted) { + final userState = context.read().state; + if (userState is UserLoaded) { + _hasLoadedTrips = true; + context.read().add(LoadTripsByUserId(userId: userState.user.id)); + } } } @@ -93,7 +97,6 @@ class _HomeContentState extends State with AutomaticKeepAliveClient ), ); } else if (tripState is TripCreated) { - // Afficher un message de succès temporaire ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Voyage en cours de création...'), @@ -104,12 +107,17 @@ class _HomeContentState extends State with AutomaticKeepAliveClient } }, builder: (context, tripState) { + // AJOUTÉ : Si l'état est initial et qu'on n'a pas encore chargé, charger maintenant + if (tripState is TripInitial && !_hasLoadedTrips) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadTripsIfUserLoaded(); + }); + } + return Scaffold( body: RefreshIndicator( onRefresh: () async { - print('🔄 Pull to refresh'); context.read().add(LoadTripsByUserId(userId: user.id)); - // Attendre que le chargement soit terminé await Future.delayed(Duration(milliseconds: 500)); }, child: SingleChildScrollView( @@ -137,6 +145,8 @@ class _HomeContentState extends State with AutomaticKeepAliveClient tripState.trips.isEmpty ? _buildEmptyState() : _buildTripsList(tripState.trips) + else if (tripState is TripInitial) + _buildLoadingState() // Afficher le loader pendant le premier chargement else _buildEmptyState(), @@ -152,9 +162,7 @@ class _HomeContentState extends State with AutomaticKeepAliveClient MaterialPageRoute(builder: (context) => const CreateTripContent()), ); - // AJOUTÉ : Recharger manuellement après retour if (result == true && mounted) { - print('🔄 Retour de création, rechargement...'); context.read().add(LoadTripsByUserId(userId: user.id)); } }, @@ -251,7 +259,6 @@ class _HomeContentState extends State with AutomaticKeepAliveClient ), child: InkWell( onTap: () async { - // AJOUTÉ : Recharger après retour des détails final result = await Navigator.push( context, MaterialPageRoute( @@ -262,7 +269,6 @@ class _HomeContentState extends State with AutomaticKeepAliveClient if (result == true && mounted) { final userState = context.read().state; if (userState is UserLoaded) { - print('🔄 Retour des détails, rechargement...'); context.read().add(LoadTripsByUserId(userId: userState.user.id)); } } @@ -304,7 +310,7 @@ class _HomeContentState extends State with AutomaticKeepAliveClient Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( - color: _getStatusColor(trip).withOpacity(0.2), + color: _getStatusColor(trip).withValues(alpha: 0.2), borderRadius: BorderRadius.circular(12), ), child: Text( diff --git a/lib/components/home/show_trip_details_content.dart b/lib/components/home/show_trip_details_content.dart index 02368cc..af50da9 100644 --- a/lib/components/home/show_trip_details_content.dart +++ b/lib/components/home/show_trip_details_content.dart @@ -1,4 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:travel_mate/blocs/group/group_bloc.dart'; +import 'package:travel_mate/blocs/group/group_event.dart'; +import 'package:travel_mate/blocs/trip/trip_bloc.dart'; +import 'package:travel_mate/blocs/trip/trip_event.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; import 'package:travel_mate/data/models/trip.dart'; @@ -92,8 +97,8 @@ class _ShowTripDetailsContentState extends State { MaterialPageRoute( builder: (context) => CreateTripContent( tripToEdit: widget.trip, -), -), + ), + ), ); @@ -117,6 +122,54 @@ class _ShowTripDetailsContentState extends State { ), ), ), + SizedBox(height: 16), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Confirmer la suppression'), + content: Text( + 'Êtes-vous sûr de vouloir supprimer ce voyage ? Cette action est irréversible.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text('Annuler'), + ), + TextButton( + onPressed: () { + context.read().add(TripDeleteRequested(tripId: widget.trip.id!)); + context.read().add(DeleteGroup(widget.trip.id!)); + Navigator.pop(context); // Fermer le dialogue + }, + child: Text( + 'Supprimer', + style: TextStyle(color: Colors.red), + ), + ), + ], + ), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'Supprimer le voyage', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ) ], ), ), diff --git a/lib/pages/home.dart b/lib/pages/home.dart index eddb84c..ea995a0 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:travel_mate/blocs/trip/trip_bloc.dart'; +import 'package:travel_mate/blocs/trip/trip_event.dart'; import '../components/home/home_content.dart'; import '../components/settings/settings_content.dart'; import '../components/map/map_content.dart'; @@ -9,8 +11,6 @@ import '../blocs/user/user_bloc.dart'; import '../blocs/user/user_event.dart'; import '../blocs/auth/auth_bloc.dart'; import '../blocs/auth/auth_event.dart'; -import '../blocs/trip/trip_bloc.dart'; -import '../blocs/trip/trip_event.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -106,8 +106,8 @@ class _HomePageState extends State { ); if (shouldLogout != true || !mounted) return; - try { + context.read().add(ResetTrips()); context.read().add(UserLoggedOut()); _pageCache.clear(); context.read().add(AuthSignOutRequested()); diff --git a/lib/repositories/group_repository.dart b/lib/repositories/group_repository.dart index 713aeb1..3698218 100644 --- a/lib/repositories/group_repository.dart +++ b/lib/repositories/group_repository.dart @@ -173,18 +173,30 @@ class GroupRepository { } } - Future deleteGroup(String groupId) async { - try { - final membersSnapshot = await _membersCollection(groupId).get(); - for (var doc in membersSnapshot.docs) { - await doc.reference.delete(); - } - - await _groupsCollection.doc(groupId).delete(); - } catch (e) { - throw Exception('Erreur lors de la suppression du groupe: $e'); + Future deleteGroup(String tripId) async { + try { + final querySnapshot = await _groupsCollection + .where('tripId', isEqualTo: tripId) + .limit(1) + .get(); + + if (querySnapshot.docs.isEmpty) { + throw Exception('Aucun groupe trouvé pour ce voyage'); } + + final groupDoc = querySnapshot.docs.first; + final groupId = groupDoc.id; + + final membersSnapshot = await _membersCollection(groupId).get(); + for (var doc in membersSnapshot.docs) { + await doc.reference.delete(); + } + + await _groupsCollection.doc(groupId).delete(); + } catch (e) { + throw Exception('Erreur lors de la suppression du groupe: $e'); } +} Stream> watchGroupMembers(String groupId) { return _membersCollection(groupId).snapshots().map(