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:
146
lib/models/trip_invitation.dart
Normal file
146
lib/models/trip_invitation.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user