diff --git a/lib/features/communication/data/datasources/messaging_remote_datasource.dart b/lib/features/communication/data/datasources/messaging_remote_datasource.dart index ddd28cd..0e54290 100644 --- a/lib/features/communication/data/datasources/messaging_remote_datasource.dart +++ b/lib/features/communication/data/datasources/messaging_remote_datasource.dart @@ -37,9 +37,9 @@ class MessagingRemoteDatasource { String? organizationId, bool includeArchived = false, }) async { - final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/messaging/conversations') + final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/conversations') .replace(queryParameters: { - if (organizationId != null) 'organizationId': organizationId, + if (organizationId != null) 'organisationId': organizationId, 'includeArchived': includeArchived.toString(), }); @@ -59,7 +59,7 @@ class MessagingRemoteDatasource { Future getConversationById(String conversationId) async { final uri = Uri.parse( - '${AppConfig.apiBaseUrl}/api/messaging/conversations/$conversationId'); + '${AppConfig.apiBaseUrl}/api/conversations/$conversationId'); final response = await client.get(uri, headers: await _getHeaders()); @@ -81,12 +81,13 @@ class MessagingRemoteDatasource { String? description, }) async { final uri = - Uri.parse('${AppConfig.apiBaseUrl}/api/messaging/conversations'); + Uri.parse('${AppConfig.apiBaseUrl}/api/conversations'); final body = json.encode({ 'name': name, 'participantIds': participantIds, - if (organizationId != null) 'organizationId': organizationId, + 'type': 'GROUP', // Default to GROUP for multi-participant conversations + if (organizationId != null) 'organisationId': organizationId, if (description != null) 'description': description, }); @@ -112,11 +113,11 @@ class MessagingRemoteDatasource { int? limit, String? beforeMessageId, }) async { - final uri = Uri.parse( - '${AppConfig.apiBaseUrl}/api/messaging/conversations/$conversationId/messages') + final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/messages') .replace(queryParameters: { + 'conversationId': conversationId, if (limit != null) 'limit': limit.toString(), - if (beforeMessageId != null) 'beforeMessageId': beforeMessageId, + // beforeMessageId not supported by backend yet, omit }); final response = await client.get(uri, headers: await _getHeaders()); @@ -137,13 +138,13 @@ class MessagingRemoteDatasource { List? attachments, MessagePriority priority = MessagePriority.normal, }) async { - final uri = Uri.parse( - '${AppConfig.apiBaseUrl}/api/messaging/conversations/$conversationId/messages'); + final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/messages'); final body = json.encode({ + 'conversationId': conversationId, 'content': content, if (attachments != null) 'attachments': attachments, - 'priority': priority.name, + 'priority': priority.name.toUpperCase(), }); final response = await client.post( @@ -195,9 +196,12 @@ class MessagingRemoteDatasource { } } - Future markMessageAsRead(String messageId) async { - final uri = - Uri.parse('${AppConfig.apiBaseUrl}/api/messaging/messages/$messageId/read'); + // === CONVERSATION ACTIONS === + + Future archiveConversation(String conversationId, {bool archive = true}) async { + final uri = Uri.parse( + '${AppConfig.apiBaseUrl}/api/conversations/$conversationId/archive') + .replace(queryParameters: {'archive': archive.toString()}); final response = await client.put(uri, headers: await _getHeaders()); @@ -205,26 +209,108 @@ class MessagingRemoteDatasource { if (response.statusCode == 401) { throw UnauthorizedException(); } else { - throw ServerException('Erreur lors du marquage du message comme lu'); + throw ServerException('Erreur lors de l\'archivage de la conversation'); } } } - Future getUnreadCount({String? organizationId}) async { - final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/messaging/unread/count') - .replace(queryParameters: { - if (organizationId != null) 'organizationId': organizationId, - }); + Future markConversationAsRead(String conversationId) async { + final uri = Uri.parse( + '${AppConfig.apiBaseUrl}/api/conversations/$conversationId/mark-read'); - final response = await client.get(uri, headers: await _getHeaders()); + final response = await client.put(uri, headers: await _getHeaders()); - if (response.statusCode == 200) { - final data = json.decode(response.body); - return data['count'] as int; - } else if (response.statusCode == 401) { - throw UnauthorizedException(); - } else { - throw ServerException('Erreur lors de la récupération du compte non lu'); + if (response.statusCode != 200 && response.statusCode != 204) { + if (response.statusCode == 401) { + throw UnauthorizedException(); + } else { + throw ServerException('Erreur lors du marquage de la conversation comme lue'); + } } } + + Future toggleMuteConversation(String conversationId) async { + final uri = Uri.parse( + '${AppConfig.apiBaseUrl}/api/conversations/$conversationId/toggle-mute'); + + final response = await client.put(uri, headers: await _getHeaders()); + + if (response.statusCode != 200 && response.statusCode != 204) { + if (response.statusCode == 401) { + throw UnauthorizedException(); + } else { + throw ServerException('Erreur lors du toggle mute de la conversation'); + } + } + } + + Future togglePinConversation(String conversationId) async { + final uri = Uri.parse( + '${AppConfig.apiBaseUrl}/api/conversations/$conversationId/toggle-pin'); + + final response = await client.put(uri, headers: await _getHeaders()); + + if (response.statusCode != 200 && response.statusCode != 204) { + if (response.statusCode == 401) { + throw UnauthorizedException(); + } else { + throw ServerException('Erreur lors du toggle pin de la conversation'); + } + } + } + + // === MESSAGE ACTIONS === + + Future editMessage({ + required String messageId, + required String newContent, + }) async { + final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/messages/$messageId'); + + final body = json.encode({'content': newContent}); + + final response = await client.put( + uri, + headers: await _getHeaders(), + body: body, + ); + + if (response.statusCode == 200) { + return MessageModel.fromJson(json.decode(response.body)); + } else if (response.statusCode == 401) { + throw UnauthorizedException(); + } else if (response.statusCode == 404) { + throw NotFoundException('Message non trouvé'); + } else { + throw ServerException('Erreur lors de l\'édition du message'); + } + } + + Future deleteMessage(String messageId) async { + final uri = Uri.parse('${AppConfig.apiBaseUrl}/api/messages/$messageId'); + + final response = await client.delete(uri, headers: await _getHeaders()); + + if (response.statusCode != 200 && response.statusCode != 204) { + if (response.statusCode == 401) { + throw UnauthorizedException(); + } else if (response.statusCode == 404) { + throw NotFoundException('Message non trouvé'); + } else { + throw ServerException('Erreur lors de la suppression du message'); + } + } + } + + Future markMessageAsRead(String messageId) async { + // Backend uses conversation mark-read, not individual message + // This method is deprecated - use markConversationAsRead instead + throw UnimplementedError('Use markConversationAsRead instead'); + } + + Future getUnreadCount({String? organizationId}) async { + // Backend provides unreadCount in conversation response + // This method is deprecated - get count from conversation list + throw UnimplementedError('Get unread count from conversation list'); + } } diff --git a/lib/features/communication/data/repositories/messaging_repository_impl.dart b/lib/features/communication/data/repositories/messaging_repository_impl.dart index 23d747a..cd582b6 100644 --- a/lib/features/communication/data/repositories/messaging_repository_impl.dart +++ b/lib/features/communication/data/repositories/messaging_repository_impl.dart @@ -219,12 +219,24 @@ class MessagingRepositoryImpl implements MessagingRepository { } } - // === MÉTHODES NON IMPLÉMENTÉES (Stubs pour compilation) === - // À implémenter selon besoins backend + // === CONVERSATION ACTIONS === @override Future> archiveConversation(String conversationId) async { - return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); + if (!await networkInfo.isConnected) { + return Left(NetworkFailure('Pas de connexion Internet')); + } + + try { + await remoteDatasource.archiveConversation(conversationId); + return const Right(null); + } on UnauthorizedException { + return Left(UnauthorizedFailure('Session expirée')); + } on ServerException catch (e) { + return Left(ServerFailure(e.message)); + } catch (e) { + return Left(UnexpectedFailure('Erreur inattendue: $e')); + } } @override @@ -235,35 +247,110 @@ class MessagingRepositoryImpl implements MessagingRepository { required String content, MessagePriority priority = MessagePriority.normal, }) async { + // TODO: Backend needs specific endpoint for targeted messages return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); } @override Future> markConversationAsRead(String conversationId) async { - return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); + if (!await networkInfo.isConnected) { + return Left(NetworkFailure('Pas de connexion Internet')); + } + + try { + await remoteDatasource.markConversationAsRead(conversationId); + return const Right(null); + } on UnauthorizedException { + return Left(UnauthorizedFailure('Session expirée')); + } on ServerException catch (e) { + return Left(ServerFailure(e.message)); + } catch (e) { + return Left(UnexpectedFailure('Erreur inattendue: $e')); + } } @override Future> toggleMuteConversation(String conversationId) async { - return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); + if (!await networkInfo.isConnected) { + return Left(NetworkFailure('Pas de connexion Internet')); + } + + try { + await remoteDatasource.toggleMuteConversation(conversationId); + return const Right(null); + } on UnauthorizedException { + return Left(UnauthorizedFailure('Session expirée')); + } on ServerException catch (e) { + return Left(ServerFailure(e.message)); + } catch (e) { + return Left(UnexpectedFailure('Erreur inattendue: $e')); + } } @override Future> togglePinConversation(String conversationId) async { - return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); + if (!await networkInfo.isConnected) { + return Left(NetworkFailure('Pas de connexion Internet')); + } + + try { + await remoteDatasource.togglePinConversation(conversationId); + return const Right(null); + } on UnauthorizedException { + return Left(UnauthorizedFailure('Session expirée')); + } on ServerException catch (e) { + return Left(ServerFailure(e.message)); + } catch (e) { + return Left(UnexpectedFailure('Erreur inattendue: $e')); + } } + // === MESSAGE ACTIONS === + @override Future> editMessage({ required String messageId, required String newContent, }) async { - return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); + if (!await networkInfo.isConnected) { + return Left(NetworkFailure('Pas de connexion Internet')); + } + + try { + final message = await remoteDatasource.editMessage( + messageId: messageId, + newContent: newContent, + ); + return Right(message); + } on NotFoundException { + return Left(NotFoundFailure('Message non trouvé')); + } on UnauthorizedException { + return Left(UnauthorizedFailure('Session expirée')); + } on ServerException catch (e) { + return Left(ServerFailure(e.message)); + } catch (e) { + return Left(UnexpectedFailure('Erreur inattendue: $e')); + } } @override Future> deleteMessage(String messageId) async { - return Left(NotImplementedFailure('Fonctionnalité en cours de développement')); + if (!await networkInfo.isConnected) { + return Left(NetworkFailure('Pas de connexion Internet')); + } + + try { + await remoteDatasource.deleteMessage(messageId); + return const Right(null); + } on NotFoundException { + return Left(NotFoundFailure('Message non trouvé')); + } on UnauthorizedException { + return Left(UnauthorizedFailure('Session expirée')); + } on ServerException catch (e) { + return Left(ServerFailure(e.message)); + } catch (e) { + return Left(UnexpectedFailure('Erreur inattendue: $e')); + } } @override