Files
TravelMate/lib/repositories/group_repository.dart

290 lines
9.1 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:travel_mate/services/error_service.dart';
import '../models/group.dart';
import '../models/group_member.dart';
class GroupRepository {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
final _errorService = ErrorService();
CollectionReference get _groupsCollection => _firestore.collection('groups');
CollectionReference _membersCollection(String groupId) {
return _groupsCollection.doc(groupId).collection('members');
}
Future<String> createGroupWithMembers({
required Group group,
required List<GroupMember> members,
}) async {
try {
return await _firestore.runTransaction<String>((transaction) async {
final groupRef = _groupsCollection.doc();
// Ajouter les IDs des membres à la liste memberIds
final memberIds = members.map((m) => m.userId).toList();
final groupData = group.copyWith(memberIds: memberIds).toMap();
transaction.set(groupRef, groupData);
for (var member in members) {
final memberRef = groupRef.collection('members').doc(member.userId);
transaction.set(memberRef, member.toMap());
}
return groupRef.id;
});
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur création groupe: $e',
stackTrace,
);
throw Exception('Impossible de créer le groupe');
}
}
Stream<List<Group>> getGroupsByUserId(String userId) {
return _groupsCollection
.where('memberIds', arrayContains: userId)
.snapshots()
.map((snapshot) {
return snapshot.docs.map((doc) {
final groupData = doc.data() as Map<String, dynamic>;
return Group.fromMap(groupData, doc.id);
}).toList();
})
.handleError((error, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur stream groups: $error',
stackTrace,
);
return <Group>[];
});
}
Future<Group?> getGroupById(String groupId) async {
try {
final doc = await _groupsCollection.doc(groupId).get();
if (!doc.exists) return null;
final group = Group.fromMap(doc.data() as Map<String, dynamic>, doc.id);
final members = await getGroupMembers(groupId);
return group.copyWith(members: members);
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur get group: $e',
stackTrace,
);
throw Exception('Impossible de récupérer le groupe');
}
}
Future<Group?> getGroupByTripId(String tripId) async {
try {
final userId = _auth.currentUser?.uid;
if (userId == null) return null;
// Tentative 1: Requête optimisée avec memberIds
var querySnapshot = await _groupsCollection
.where('tripId', isEqualTo: tripId)
.where('memberIds', arrayContains: userId)
.limit(1)
.get();
// Tentative 2: Fallback pour le créateur (si memberIds est manquant - anciennes données)
if (querySnapshot.docs.isEmpty) {
querySnapshot = await _groupsCollection
.where('tripId', isEqualTo: tripId)
.where('createdBy', isEqualTo: userId)
.limit(1)
.get();
// Si on trouve le groupe via le fallback, on lance une migration
if (querySnapshot.docs.isNotEmpty) {
_migrateGroupData(querySnapshot.docs.first.id);
}
}
if (querySnapshot.docs.isEmpty) return null;
final doc = querySnapshot.docs.first;
final group = Group.fromMap(doc.data() as Map<String, dynamic>, doc.id);
final members = await getGroupMembers(doc.id);
return group.copyWith(members: members);
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur get group by trip: $e',
stackTrace,
);
throw Exception('Impossible de récupérer le groupe du voyage');
}
}
/// Méthode utilitaire pour migrer les anciennes données
Future<void> _migrateGroupData(String groupId) async {
try {
final members = await getGroupMembers(groupId);
final memberIds = members.map((m) => m.userId).toList();
if (memberIds.isNotEmpty) {
await _groupsCollection.doc(groupId).update({'memberIds': memberIds});
_errorService.logSuccess(
'GroupRepository',
'Migration réussie pour le groupe $groupId',
);
}
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur de migration pour le groupe $groupId: $e',
stackTrace,
);
}
}
Future<List<GroupMember>> getGroupMembers(String groupId) async {
try {
final snapshot = await _membersCollection(groupId).get();
return snapshot.docs.map((doc) {
return GroupMember.fromMap(doc.data() as Map<String, dynamic>, doc.id);
}).toList();
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur get members: $e',
stackTrace,
);
throw Exception('Impossible de récupérer les membres');
}
}
Future<void> addMember(String groupId, GroupMember member) async {
try {
// 1. Récupérer le groupe pour avoir le tripId
final group = await getGroupById(groupId);
if (group == null) throw Exception('Groupe introuvable');
// 2. Ajouter le membre dans la sous-collection members du groupe
await _membersCollection(groupId).doc(member.userId).set(member.toMap());
// 3. Mettre à jour la liste memberIds du groupe
await _groupsCollection.doc(groupId).update({
'updatedAt': DateTime.now().millisecondsSinceEpoch,
'memberIds': FieldValue.arrayUnion([member.userId]),
});
// 4. Mettre à jour la liste participants du voyage
await _firestore.collection('trips').doc(group.tripId).update({
'participants': FieldValue.arrayUnion([member.userId]),
});
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur add member: $e',
stackTrace,
);
throw Exception('Impossible d\'ajouter le membre');
}
}
Future<void> removeMember(String groupId, String userId) async {
try {
// 1. Récupérer le groupe pour avoir le tripId
final group = await getGroupById(groupId);
if (group == null) throw Exception('Groupe introuvable');
// 2. Supprimer le membre de la sous-collection members du groupe
await _membersCollection(groupId).doc(userId).delete();
// 3. Mettre à jour la liste memberIds du groupe
await _groupsCollection.doc(groupId).update({
'updatedAt': DateTime.now().millisecondsSinceEpoch,
'memberIds': FieldValue.arrayRemove([userId]),
});
// 4. Mettre à jour la liste participants du voyage
await _firestore.collection('trips').doc(group.tripId).update({
'participants': FieldValue.arrayRemove([userId]),
});
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur remove member: $e',
stackTrace,
);
throw Exception('Impossible de supprimer le membre');
}
}
Future<void> updateGroup(String groupId, Group group) async {
try {
await _groupsCollection
.doc(groupId)
.update(
group.toMap()
..['updatedAt'] = DateTime.now().millisecondsSinceEpoch,
);
} catch (e, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur update group: $e',
stackTrace,
);
throw Exception('Impossible de mettre à jour le groupe');
}
}
Future<void> deleteGroup(String tripId) async {
try {
final userId = _auth.currentUser?.uid;
if (userId == null) throw Exception('Utilisateur non connecté');
final querySnapshot = await _groupsCollection
.where('tripId', isEqualTo: tripId)
.where('createdBy', isEqualTo: userId)
.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, stackTrace) {
_errorService.logError(
'GroupRepository',
'Erreur delete group: $e',
stackTrace,
);
throw Exception('Impossible de supprimer le groupe');
}
}
Stream<List<GroupMember>> watchGroupMembers(String groupId) {
return _membersCollection(groupId).snapshots().map(
(snapshot) => snapshot.docs
.map(
(doc) =>
GroupMember.fromMap(doc.data() as Map<String, dynamic>, doc.id),
)
.toList(),
);
}
}