feat: Implement trip invitation functionality and notification handling

- Added TripInvitationRepository for managing trip invitations.
- Created TripInvitation model with serialization methods.
- Implemented notification payload parser for handling FCM notifications.
- Enhanced NotificationService to manage trip invitations and related actions.
- Updated UserRepository to include user search functionality.
- Modified AuthRepository to store multiple FCM tokens.
- Added tests for trip invitation logic and notification payload parsing.
- Updated pubspec.yaml and pubspec.lock for dependency management.
This commit is contained in:
Van Leemput Dayron
2026-03-13 13:54:47 +01:00
parent e665dea82a
commit 3215a990d1
27 changed files with 1961 additions and 321 deletions

View File

@@ -0,0 +1,146 @@
import 'package:cloud_firestore/cloud_firestore.dart';
/// Représente une invitation d'un utilisateur à rejoindre un voyage.
///
/// Une invitation passe par les statuts `pending`, `accepted` ou `rejected`.
/// Elle contient le contexte minimum nécessaire pour envoyer les notifications
/// et appliquer la réponse (trip, expéditeur, destinataire).
class TripInvitation {
/// Identifiant Firestore de l'invitation.
final String id;
/// Identifiant du voyage concerné.
final String tripId;
/// Titre du voyage au moment de l'invitation.
final String tripTitle;
/// Identifiant de l'utilisateur qui invite.
final String inviterId;
/// Nom affiché de l'utilisateur qui invite.
final String inviterName;
/// Identifiant de l'utilisateur invité.
final String inviteeId;
/// Email de l'utilisateur invité (utile pour affichage et debug).
final String inviteeEmail;
/// Statut courant de l'invitation: `pending`, `accepted`, `rejected`.
final String status;
/// Date de création de l'invitation.
final DateTime createdAt;
/// Date de réponse (acceptation/refus), null si encore en attente.
final DateTime? respondedAt;
/// Crée une instance de [TripInvitation].
///
/// [status] vaut `pending` par défaut pour une nouvelle invitation.
TripInvitation({
required this.id,
required this.tripId,
required this.tripTitle,
required this.inviterId,
required this.inviterName,
required this.inviteeId,
required this.inviteeEmail,
this.status = 'pending',
required this.createdAt,
this.respondedAt,
});
/// Crée une invitation à partir d'un document Firestore.
///
/// Gère les formats `Timestamp`, `int` et `DateTime` pour les dates.
factory TripInvitation.fromFirestore(
DocumentSnapshot<Map<String, dynamic>> doc,
) {
final data = doc.data() ?? <String, dynamic>{};
return TripInvitation(
id: doc.id,
tripId: data['tripId'] as String? ?? '',
tripTitle: data['tripTitle'] as String? ?? '',
inviterId: data['inviterId'] as String? ?? '',
inviterName: data['inviterName'] as String? ?? 'Quelqu\'un',
inviteeId: data['inviteeId'] as String? ?? '',
inviteeEmail: data['inviteeEmail'] as String? ?? '',
status: data['status'] as String? ?? 'pending',
createdAt: _parseDate(data['createdAt']) ?? DateTime.now(),
respondedAt: _parseDate(data['respondedAt']),
);
}
/// Convertit l'invitation en map Firestore.
///
/// [respondedAt] est omis si null pour éviter d'écraser inutilement la donnée.
Map<String, dynamic> toMap() {
final map = <String, dynamic>{
'tripId': tripId,
'tripTitle': tripTitle,
'inviterId': inviterId,
'inviterName': inviterName,
'inviteeId': inviteeId,
'inviteeEmail': inviteeEmail,
'status': status,
'createdAt': Timestamp.fromDate(createdAt),
};
if (respondedAt != null) {
map['respondedAt'] = Timestamp.fromDate(respondedAt!);
}
return map;
}
/// Retourne une copie avec les champs fournis.
///
/// Utile pour mettre à jour un statut localement sans muter l'instance initiale.
TripInvitation copyWith({
String? id,
String? tripId,
String? tripTitle,
String? inviterId,
String? inviterName,
String? inviteeId,
String? inviteeEmail,
String? status,
DateTime? createdAt,
DateTime? respondedAt,
}) {
return TripInvitation(
id: id ?? this.id,
tripId: tripId ?? this.tripId,
tripTitle: tripTitle ?? this.tripTitle,
inviterId: inviterId ?? this.inviterId,
inviterName: inviterName ?? this.inviterName,
inviteeId: inviteeId ?? this.inviteeId,
inviteeEmail: inviteeEmail ?? this.inviteeEmail,
status: status ?? this.status,
createdAt: createdAt ?? this.createdAt,
respondedAt: respondedAt ?? this.respondedAt,
);
}
/// Convertit une valeur de date Firestore vers [DateTime].
///
/// Retourne `null` si la valeur est absente ou non reconnue.
static DateTime? _parseDate(dynamic value) {
if (value == null) {
return null;
}
if (value is Timestamp) {
return value.toDate();
}
if (value is int) {
return DateTime.fromMillisecondsSinceEpoch(value);
}
if (value is DateTime) {
return value;
}
return null;
}
}