fix(chat): Correction race condition + Implémentation TODOs
## Corrections Critiques ### Race Condition - Statuts de Messages - Fix : Les icônes de statut (✓, ✓✓, ✓✓ bleu) ne s'affichaient pas - Cause : WebSocket delivery confirmations arrivaient avant messages locaux - Solution : Pattern Optimistic UI dans chat_bloc.dart - Création message temporaire immédiate - Ajout à la liste AVANT requête HTTP - Remplacement par message serveur à la réponse - Fichier : lib/presentation/state_management/chat_bloc.dart ## Implémentation TODOs (13/21) ### Social (social_header_widget.dart) - ✅ Copier lien du post dans presse-papiers - ✅ Partage natif via Share.share() - ✅ Dialogue de signalement avec 5 raisons ### Partage (share_post_dialog.dart) - ✅ Interface sélection d'amis avec checkboxes - ✅ Partage externe via Share API ### Média (media_upload_service.dart) - ✅ Parsing JSON réponse backend - ✅ Méthode deleteMedia() pour suppression - ✅ Génération miniature vidéo ### Posts (create_post_dialog.dart, edit_post_dialog.dart) - ✅ Extraction URL depuis uploads - ✅ Documentation chargement médias ### Chat (conversations_screen.dart) - ✅ Navigation vers notifications - ✅ ConversationSearchDelegate pour recherche ## Nouveaux Fichiers ### Configuration - build-prod.ps1 : Script build production avec dart-define - lib/core/constants/env_config.dart : Gestion environnements ### Documentation - TODOS_IMPLEMENTED.md : Documentation complète TODOs ## Améliorations ### Architecture - Refactoring injection de dépendances - Amélioration routing et navigation - Optimisation providers (UserProvider, FriendsProvider) ### UI/UX - Amélioration thème et couleurs - Optimisation animations - Meilleure gestion erreurs ### Services - Configuration API avec env_config - Amélioration datasources (events, users) - Optimisation modèles de données
This commit is contained in:
405
lib/data/repositories/chat_repository_impl.dart
Normal file
405
lib/data/repositories/chat_repository_impl.dart
Normal file
@@ -0,0 +1,405 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../core/errors/exceptions.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../core/utils/app_logger.dart';
|
||||
import '../../domain/entities/chat_message.dart';
|
||||
import '../../domain/entities/conversation.dart';
|
||||
import '../../domain/repositories/chat_repository.dart';
|
||||
import '../datasources/chat_remote_data_source.dart';
|
||||
|
||||
/// Implémentation du repository de chat.
|
||||
///
|
||||
/// Cette classe fait le lien entre la couche domaine et la couche données,
|
||||
/// en convertissant les exceptions en failures et en gérant la transformation
|
||||
/// entre les modèles de données et les entités de domaine.
|
||||
///
|
||||
/// **Usage:**
|
||||
/// ```dart
|
||||
/// final repository = ChatRepositoryImpl(
|
||||
/// remoteDataSource: chatRemoteDataSource,
|
||||
/// );
|
||||
/// final result = await repository.getConversations('userId');
|
||||
/// result.fold(
|
||||
/// (failure) => print('Erreur: $failure'),
|
||||
/// (conversations) => print('${conversations.length} conversations'),
|
||||
/// );
|
||||
/// ```
|
||||
class ChatRepositoryImpl implements ChatRepository {
|
||||
/// Crée une nouvelle instance de [ChatRepositoryImpl].
|
||||
///
|
||||
/// [remoteDataSource] La source de données distante pour le chat
|
||||
ChatRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
/// Source de données distante pour les opérations de chat
|
||||
final ChatRemoteDataSource remoteDataSource;
|
||||
|
||||
/// Log un message si le mode debug est activé.
|
||||
void _log(String message) {
|
||||
AppLogger.d(message, tag: 'ChatRepositoryImpl');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Conversation>>> getConversations(
|
||||
String userId,
|
||||
) async {
|
||||
_log('Récupération des conversations pour $userId');
|
||||
|
||||
try {
|
||||
if (userId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID utilisateur ne peut pas être vide',
|
||||
field: 'userId',
|
||||
));
|
||||
}
|
||||
|
||||
final conversationModels = await remoteDataSource.getConversations(userId);
|
||||
final conversations = conversationModels.map((model) => model.toEntity()).toList();
|
||||
_log('${conversations.length} conversations récupérées');
|
||||
return Right(conversations);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la récupération des conversations: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Conversation>> getOrCreateConversation(
|
||||
String userId,
|
||||
String participantId,
|
||||
) async {
|
||||
_log('Récupération/création conversation entre $userId et $participantId');
|
||||
|
||||
try {
|
||||
if (userId.isEmpty || participantId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'Les IDs utilisateur et participant sont requis',
|
||||
));
|
||||
}
|
||||
|
||||
if (userId == participantId) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'Impossible de créer une conversation avec soi-même',
|
||||
));
|
||||
}
|
||||
|
||||
final conversationModel = await remoteDataSource.getOrCreateConversation(
|
||||
userId,
|
||||
participantId,
|
||||
);
|
||||
_log('Conversation récupérée/créée avec succès');
|
||||
return Right(conversationModel.toEntity());
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la récupération/création de la conversation: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<ChatMessage>>> getMessages(
|
||||
String conversationId, {
|
||||
int page = 0,
|
||||
int size = 50,
|
||||
}) async {
|
||||
_log('Récupération des messages de $conversationId (page: $page)');
|
||||
|
||||
try {
|
||||
if (conversationId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID de conversation ne peut pas être vide',
|
||||
field: 'conversationId',
|
||||
));
|
||||
}
|
||||
|
||||
final messageModels = await remoteDataSource.getMessages(
|
||||
conversationId,
|
||||
page: page,
|
||||
size: size,
|
||||
);
|
||||
final messages = messageModels.map((model) => model.toEntity()).toList();
|
||||
_log('${messages.length} messages récupérés');
|
||||
return Right(messages);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la récupération des messages: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, ChatMessage>> sendMessage({
|
||||
required String senderId,
|
||||
required String recipientId,
|
||||
required String content,
|
||||
String? messageType,
|
||||
String? mediaUrl,
|
||||
}) async {
|
||||
_log('Envoi d\'un message de $senderId à $recipientId');
|
||||
|
||||
try {
|
||||
if (senderId.isEmpty || recipientId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'Les IDs expéditeur et destinataire sont requis',
|
||||
));
|
||||
}
|
||||
|
||||
if (content.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'Le contenu du message ne peut pas être vide',
|
||||
field: 'content',
|
||||
));
|
||||
}
|
||||
|
||||
if (senderId == recipientId) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'Impossible d\'envoyer un message à soi-même',
|
||||
));
|
||||
}
|
||||
|
||||
final messageModel = await remoteDataSource.sendMessage(
|
||||
senderId: senderId,
|
||||
recipientId: recipientId,
|
||||
content: content,
|
||||
messageType: messageType,
|
||||
mediaUrl: mediaUrl,
|
||||
);
|
||||
_log('Message envoyé avec succès');
|
||||
return Right(messageModel.toEntity());
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de l\'envoi du message: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> markMessageAsRead(String messageId) async {
|
||||
_log('Marquage du message $messageId comme lu');
|
||||
|
||||
try {
|
||||
if (messageId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID du message ne peut pas être vide',
|
||||
field: 'messageId',
|
||||
));
|
||||
}
|
||||
|
||||
await remoteDataSource.markMessageAsRead(messageId);
|
||||
_log('Message marqué comme lu');
|
||||
return const Right(null);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors du marquage du message: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> markConversationAsRead(
|
||||
String conversationId,
|
||||
String userId,
|
||||
) async {
|
||||
_log('Marquage de tous les messages de $conversationId comme lus');
|
||||
|
||||
try {
|
||||
if (conversationId.isEmpty || userId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'Les IDs conversation et utilisateur sont requis',
|
||||
));
|
||||
}
|
||||
|
||||
await remoteDataSource.markConversationAsRead(conversationId, userId);
|
||||
_log('Conversation marquée comme lue');
|
||||
return const Right(null);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors du marquage de la conversation: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteMessage(String messageId) async {
|
||||
_log('Suppression du message $messageId');
|
||||
|
||||
try {
|
||||
if (messageId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID du message ne peut pas être vide',
|
||||
field: 'messageId',
|
||||
));
|
||||
}
|
||||
|
||||
await remoteDataSource.deleteMessage(messageId);
|
||||
_log('Message supprimé');
|
||||
return const Right(null);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la suppression du message: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteConversation(String conversationId) async {
|
||||
_log('Suppression de la conversation $conversationId');
|
||||
|
||||
try {
|
||||
if (conversationId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID de la conversation ne peut pas être vide',
|
||||
field: 'conversationId',
|
||||
));
|
||||
}
|
||||
|
||||
await remoteDataSource.deleteConversation(conversationId);
|
||||
_log('Conversation supprimée');
|
||||
return const Right(null);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la suppression de la conversation: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, int>> getUnreadMessagesCount(String userId) async {
|
||||
_log('Récupération du nombre de messages non lus pour $userId');
|
||||
|
||||
try {
|
||||
if (userId.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID utilisateur ne peut pas être vide',
|
||||
field: 'userId',
|
||||
));
|
||||
}
|
||||
|
||||
final count = await remoteDataSource.getUnreadMessagesCount(userId);
|
||||
_log('$count messages non lus');
|
||||
return Right(count);
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Non autorisé: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la récupération du nombre de messages non lus: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,4 +95,13 @@ abstract class FriendsRepository {
|
||||
///
|
||||
/// Retourne un `Future<void>`. En cas d'erreur, l'implémentation peut lancer une exception.
|
||||
Future<void> cancelFriendRequest(String friendshipId);
|
||||
|
||||
/// Récupère les suggestions d'amis pour un utilisateur.
|
||||
///
|
||||
/// [userId] : Identifiant unique de l'utilisateur.
|
||||
/// [limit] : Nombre maximum de suggestions à retourner (par défaut 10).
|
||||
///
|
||||
/// Retourne une liste de suggestions d'amis basées sur les amis en commun
|
||||
/// et d'autres critères de pertinence.
|
||||
Future<List<dynamic>> getFriendSuggestions(String userId, {int limit = 10});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:http/http.dart' as http;
|
||||
import '../../core/constants/env_config.dart';
|
||||
import '../../core/constants/urls.dart';
|
||||
import '../../core/errors/exceptions.dart';
|
||||
import '../../core/utils/app_logger.dart';
|
||||
import '../../domain/entities/friend.dart';
|
||||
import '../../domain/entities/friend_request.dart';
|
||||
import 'friends_repository.dart';
|
||||
@@ -221,9 +222,7 @@ class FriendsRepositoryImpl implements FriendsRepository {
|
||||
|
||||
/// Log un message si le mode debug est activé.
|
||||
void _log(String message) {
|
||||
if (EnvConfig.enableDetailedLogs) {
|
||||
print('[FriendsRepositoryImpl] $message');
|
||||
}
|
||||
AppLogger.d(message, tag: 'FriendsRepositoryImpl');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -683,4 +682,56 @@ class FriendsRepositoryImpl implements FriendsRepository {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les suggestions d'amis pour un utilisateur.
|
||||
///
|
||||
/// [userId] L'identifiant unique de l'utilisateur
|
||||
/// [limit] Nombre maximum de suggestions (par défaut 10)
|
||||
///
|
||||
/// Returns une liste d'objets [FriendSuggestion]
|
||||
///
|
||||
/// Throws [ServerException] en cas d'erreur
|
||||
///
|
||||
/// **Exemple:**
|
||||
/// ```dart
|
||||
/// final suggestions = await repository.getFriendSuggestions('user123', limit: 5);
|
||||
/// ```
|
||||
@override
|
||||
Future<List<dynamic>> getFriendSuggestions(String userId, {int limit = 10}) async {
|
||||
_log('Récupération des suggestions d\'amis pour l\'utilisateur $userId (limit: $limit)');
|
||||
|
||||
if (userId.isEmpty) {
|
||||
throw ValidationException('L\'ID utilisateur ne peut pas être vide');
|
||||
}
|
||||
|
||||
if (limit <= 0) {
|
||||
throw ValidationException('La limite doit être > 0');
|
||||
}
|
||||
|
||||
try {
|
||||
final uri = Uri.parse(Urls.getFriendSuggestionsWithUserId(userId, limit: limit));
|
||||
final response = await _performRequest('GET', uri);
|
||||
|
||||
if (response.statusCode == 404) {
|
||||
_log('Aucune suggestion trouvée (404) - retour d\'une liste vide');
|
||||
return [];
|
||||
}
|
||||
|
||||
final jsonResponse = _parseJsonResponse(response, [200]) as List<dynamic>?;
|
||||
|
||||
if (jsonResponse == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final suggestions = jsonResponse
|
||||
.map((json) => json as Map<String, dynamic>)
|
||||
.toList();
|
||||
|
||||
_log('${suggestions.length} suggestions d\'amis récupérées avec succès');
|
||||
return suggestions;
|
||||
} catch (e) {
|
||||
_log('Erreur lors de la récupération des suggestions d\'amis: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,308 @@
|
||||
import 'dart:convert';
|
||||
import 'package:afterwork/data/datasources/user_remote_data_source.dart';
|
||||
import 'package:afterwork/data/models/user_model.dart';
|
||||
import 'package:afterwork/domain/entities/user.dart';
|
||||
import 'package:afterwork/domain/repositories/user_repository.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../core/constants/urls.dart';
|
||||
import '../../core/constants/env_config.dart';
|
||||
import '../../core/errors/exceptions.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../core/utils/app_logger.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../../domain/repositories/user_repository.dart';
|
||||
import '../datasources/user_remote_data_source.dart';
|
||||
import '../models/user_model.dart';
|
||||
|
||||
/// Implémentation du repository des utilisateurs.
|
||||
/// Cette classe fait le lien entre les appels de l'application et les services distants pour les opérations sur les utilisateurs.
|
||||
///
|
||||
/// Cette classe fait le lien entre la couche domaine et la couche données,
|
||||
/// en convertissant les exceptions en failures et en gérant la transformation
|
||||
/// entre les modèles de données et les entités de domaine.
|
||||
///
|
||||
/// **Usage:**
|
||||
/// ```dart
|
||||
/// final repository = UserRepositoryImpl(
|
||||
/// remoteDataSource: userRemoteDataSource,
|
||||
/// );
|
||||
/// final result = await repository.getUser('user123');
|
||||
/// result.fold(
|
||||
/// (failure) => print('Erreur: $failure'),
|
||||
/// (user) => print('Utilisateur: $user'),
|
||||
/// );
|
||||
/// ```
|
||||
class UserRepositoryImpl implements UserRepository {
|
||||
final UserRemoteDataSource remoteDataSource;
|
||||
|
||||
/// Constructeur avec injection de la source de données distante.
|
||||
/// Crée une nouvelle instance de [UserRepositoryImpl].
|
||||
///
|
||||
/// [remoteDataSource] La source de données distante pour les utilisateurs
|
||||
UserRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
/// Récupère un utilisateur par son ID depuis la source de données distante.
|
||||
@override
|
||||
Future<User> getUser(String id) async {
|
||||
UserModel userModel = await remoteDataSource.getUser(id);
|
||||
return userModel; // Retourne un UserModel qui est un sous-type de User.
|
||||
/// Source de données distante pour les opérations sur les utilisateurs
|
||||
final UserRemoteDataSource remoteDataSource;
|
||||
|
||||
/// Log un message si le mode debug est activé.
|
||||
void _log(String message) {
|
||||
AppLogger.d(message, tag: 'UserRepositoryImpl');
|
||||
}
|
||||
|
||||
/// Authentifie un utilisateur par email et mot de passe (en clair, temporairement).
|
||||
Future<UserModel> authenticateUser(String email, String password) async {
|
||||
print("Tentative d'authentification pour l'email : $email");
|
||||
/// Récupère un utilisateur par son ID depuis la source de données distante.
|
||||
///
|
||||
/// [id] L'identifiant de l'utilisateur
|
||||
///
|
||||
/// Returns [Right] avec l'utilisateur si succès, [Left] avec une [Failure] si erreur
|
||||
///
|
||||
/// **Exemple:**
|
||||
/// ```dart
|
||||
/// final result = await repository.getUser('user123');
|
||||
/// result.fold(
|
||||
/// (failure) => handleError(failure),
|
||||
/// (user) => displayUser(user),
|
||||
/// );
|
||||
/// ```
|
||||
@override
|
||||
Future<Either<Failure, User>> getUser(String id) async {
|
||||
_log('Récupération de l\'utilisateur $id');
|
||||
|
||||
try {
|
||||
// Requête POST avec les identifiants utilisateur pour l'authentification
|
||||
final response = await http.post(
|
||||
Uri.parse('${Urls.baseUrl}/users/authenticate'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({'email': email, 'motDePasse': password}),
|
||||
if (id.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID utilisateur ne peut pas être vide',
|
||||
field: 'id',
|
||||
));
|
||||
}
|
||||
|
||||
final userModel = await remoteDataSource.getUser(id);
|
||||
_log('Utilisateur $id récupéré avec succès');
|
||||
return Right(userModel.toEntity());
|
||||
} on UserNotFoundException catch (e) {
|
||||
_log('Utilisateur $id non trouvé: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
code: 'USER_NOT_FOUND',
|
||||
statusCode: 404,
|
||||
));
|
||||
} on ValidationException catch (e) {
|
||||
_log('Erreur de validation: ${e.message}');
|
||||
return Left(ValidationFailure(message:
|
||||
e.message,
|
||||
field: e.field,
|
||||
));
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la récupération de l\'utilisateur: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Authentifie un utilisateur avec son email et mot de passe.
|
||||
///
|
||||
/// [email] L'adresse email de l'utilisateur
|
||||
/// [password] Le mot de passe de l'utilisateur
|
||||
///
|
||||
/// Returns [Right] avec l'utilisateur authentifié si succès,
|
||||
/// [Left] avec une [Failure] si erreur
|
||||
///
|
||||
/// **Exemple:**
|
||||
/// ```dart
|
||||
/// final result = await repository.authenticateUser(
|
||||
/// 'user@example.com',
|
||||
/// 'password123',
|
||||
/// );
|
||||
/// ```
|
||||
Future<Either<Failure, User>> authenticateUser(
|
||||
String email,
|
||||
String password,
|
||||
) async {
|
||||
_log('Authentification pour: $email');
|
||||
|
||||
try {
|
||||
if (email.isEmpty || password.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'email et le mot de passe sont requis',
|
||||
));
|
||||
}
|
||||
|
||||
final userModel = await remoteDataSource.authenticateUser(email, password);
|
||||
_log('Authentification réussie pour: ${userModel.email}');
|
||||
return Right(userModel.toEntity());
|
||||
} on UnauthorizedException catch (e) {
|
||||
_log('Authentification échouée: ${e.message}');
|
||||
return Left(AuthenticationFailure(
|
||||
message: e.message,
|
||||
code: 'UNAUTHORIZED',
|
||||
));
|
||||
} on ValidationException catch (e) {
|
||||
_log('Erreur de validation: ${e.message}');
|
||||
return Left(ValidationFailure(message:
|
||||
e.message,
|
||||
field: e.field,
|
||||
));
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de l\'authentification: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée un nouvel utilisateur.
|
||||
///
|
||||
/// [user] L'entité utilisateur à créer
|
||||
///
|
||||
/// Returns [Right] avec l'utilisateur créé si succès,
|
||||
/// [Left] avec une [Failure] si erreur
|
||||
Future<Either<Failure, User>> createUser(User user) async {
|
||||
_log('Création d\'un nouvel utilisateur: ${user.email}');
|
||||
|
||||
try {
|
||||
final userModel = UserModel(
|
||||
userId: user.userId,
|
||||
userLastName: user.userLastName,
|
||||
userFirstName: user.userFirstName,
|
||||
email: user.email,
|
||||
motDePasse: user.motDePasse,
|
||||
profileImageUrl: user.profileImageUrl,
|
||||
eventsCount: user.eventsCount,
|
||||
friendsCount: user.friendsCount,
|
||||
postsCount: user.postsCount,
|
||||
visitedPlacesCount: user.visitedPlacesCount,
|
||||
);
|
||||
|
||||
print("Réponse du serveur pour l'authentification : ${response.statusCode} - ${response.body}");
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return UserModel.fromJson(jsonDecode(response.body));
|
||||
} else {
|
||||
throw Exception("Erreur lors de l'authentification : ${response.statusCode}");
|
||||
}
|
||||
final createdUser = await remoteDataSource.createUser(userModel);
|
||||
_log('Utilisateur créé avec succès: ${createdUser.userId}');
|
||||
return Right(createdUser.toEntity());
|
||||
} on ConflictException catch (e) {
|
||||
_log('Conflit lors de la création: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
code: 'CONFLICT',
|
||||
statusCode: 409,
|
||||
));
|
||||
} on ValidationException catch (e) {
|
||||
_log('Erreur de validation: ${e.message}');
|
||||
return Left(ValidationFailure(message:
|
||||
e.message,
|
||||
field: e.field,
|
||||
));
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} catch (e) {
|
||||
print("Erreur d'authentification : $e");
|
||||
throw Exception("Erreur lors de l'authentification : $e");
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la création de l\'utilisateur: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour un utilisateur existant.
|
||||
///
|
||||
/// [user] L'entité utilisateur avec les nouvelles données
|
||||
///
|
||||
/// Returns [Right] avec l'utilisateur mis à jour si succès,
|
||||
/// [Left] avec une [Failure] si erreur
|
||||
Future<Either<Failure, User>> updateUser(User user) async {
|
||||
_log('Mise à jour de l\'utilisateur ${user.userId}');
|
||||
|
||||
try {
|
||||
final userModel = UserModel(
|
||||
userId: user.userId,
|
||||
userLastName: user.userLastName,
|
||||
userFirstName: user.userFirstName,
|
||||
email: user.email,
|
||||
motDePasse: user.motDePasse,
|
||||
profileImageUrl: user.profileImageUrl,
|
||||
eventsCount: user.eventsCount,
|
||||
friendsCount: user.friendsCount,
|
||||
postsCount: user.postsCount,
|
||||
visitedPlacesCount: user.visitedPlacesCount,
|
||||
);
|
||||
|
||||
final updatedUser = await remoteDataSource.updateUser(userModel);
|
||||
_log('Utilisateur ${user.userId} mis à jour avec succès');
|
||||
return Right(updatedUser.toEntity());
|
||||
} on UserNotFoundException catch (e) {
|
||||
_log('Utilisateur non trouvé: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
code: 'USER_NOT_FOUND',
|
||||
statusCode: 404,
|
||||
));
|
||||
} on ValidationException catch (e) {
|
||||
_log('Erreur de validation: ${e.message}');
|
||||
return Left(ValidationFailure(message:
|
||||
e.message,
|
||||
field: e.field,
|
||||
));
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la mise à jour de l\'utilisateur: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime un utilisateur.
|
||||
///
|
||||
/// [id] L'identifiant de l'utilisateur à supprimer
|
||||
///
|
||||
/// Returns [Right] avec `null` si succès, [Left] avec une [Failure] si erreur
|
||||
Future<Either<Failure, void>> deleteUser(String id) async {
|
||||
_log('Suppression de l\'utilisateur $id');
|
||||
|
||||
try {
|
||||
if (id.isEmpty) {
|
||||
return const Left(ValidationFailure(message:
|
||||
'L\'ID utilisateur ne peut pas être vide',
|
||||
field: 'id',
|
||||
));
|
||||
}
|
||||
|
||||
await remoteDataSource.deleteUser(id);
|
||||
_log('Utilisateur $id supprimé avec succès');
|
||||
return const Right(null);
|
||||
} on UserNotFoundException catch (e) {
|
||||
_log('Utilisateur non trouvé: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
code: 'USER_NOT_FOUND',
|
||||
statusCode: 404,
|
||||
));
|
||||
} on ValidationException catch (e) {
|
||||
_log('Erreur de validation: ${e.message}');
|
||||
return Left(ValidationFailure(message:
|
||||
e.message,
|
||||
field: e.field,
|
||||
));
|
||||
} on ServerException catch (e) {
|
||||
_log('Erreur serveur: ${e.message}');
|
||||
return Left(ServerFailure(
|
||||
message: e.message,
|
||||
statusCode: e.statusCode,
|
||||
));
|
||||
} catch (e) {
|
||||
_log('Erreur inattendue: $e');
|
||||
return Left(ServerFailure(
|
||||
message: 'Erreur lors de la suppression de l\'utilisateur: $e',
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user