feat(communication): module messagerie unifié + contact policies + blocages
Aligné avec le backend MessagingResource : - Nouveau module communication (conversations, messages, participants) - Respect des ContactPolicy (qui peut parler à qui par rôle) - Gestion MemberBlock (blocages individuels) - UI : conversations list, conversation detail, broadcast, tiles - BLoC : MessagingBloc avec events (envoyer, démarrer conversation rôle, etc.)
This commit is contained in:
@@ -1,416 +1,252 @@
|
||||
/// Implémentation du repository de messagerie
|
||||
/// Implémentation du repository de messagerie v4
|
||||
library messaging_repository_impl;
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../../core/error/exceptions.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/network/network_info.dart';
|
||||
import '../../domain/entities/conversation.dart';
|
||||
import '../../domain/entities/message.dart';
|
||||
import '../../domain/entities/message_template.dart';
|
||||
import '../../domain/entities/contact_policy.dart';
|
||||
import '../../domain/repositories/messaging_repository.dart';
|
||||
import '../datasources/messaging_remote_datasource.dart';
|
||||
|
||||
@LazySingleton(as: MessagingRepository)
|
||||
class MessagingRepositoryImpl implements MessagingRepository {
|
||||
final MessagingRemoteDatasource remoteDatasource;
|
||||
final NetworkInfo networkInfo;
|
||||
|
||||
MessagingRepositoryImpl({
|
||||
required this.remoteDatasource,
|
||||
required this.networkInfo,
|
||||
});
|
||||
MessagingRepositoryImpl({required this.remoteDatasource});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Conversation>>> getConversations({
|
||||
String? organizationId,
|
||||
bool includeArchived = false,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<List<ConversationSummary>> getMesConversations() async {
|
||||
try {
|
||||
final conversations = await remoteDatasource.getConversations(
|
||||
organizationId: organizationId,
|
||||
includeArchived: includeArchived,
|
||||
);
|
||||
return Right(conversations);
|
||||
return await remoteDatasource.getMesConversations();
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Conversation>> getConversationById(
|
||||
String conversationId) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<Conversation> getConversation(String conversationId) async {
|
||||
try {
|
||||
final conversation =
|
||||
await remoteDatasource.getConversationById(conversationId);
|
||||
return Right(conversation);
|
||||
return await remoteDatasource.getConversation(conversationId);
|
||||
} on NotFoundException {
|
||||
return Left(NotFoundFailure('Conversation non trouvée'));
|
||||
throw Exception('Conversation non trouvée');
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Conversation>> createConversation({
|
||||
required String name,
|
||||
required List<String> participantIds,
|
||||
String? organizationId,
|
||||
String? description,
|
||||
Future<Conversation> demarrerConversationDirecte({
|
||||
required String destinataireId,
|
||||
required String organisationId,
|
||||
String? premierMessage,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final conversation = await remoteDatasource.createConversation(
|
||||
name: name,
|
||||
participantIds: participantIds,
|
||||
organizationId: organizationId,
|
||||
description: description,
|
||||
return await remoteDatasource.demarrerConversationDirecte(
|
||||
destinataireId: destinataireId,
|
||||
organisationId: organisationId,
|
||||
premierMessage: premierMessage,
|
||||
);
|
||||
return Right(conversation);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Message>>> getMessages({
|
||||
required String conversationId,
|
||||
int? limit,
|
||||
String? beforeMessageId,
|
||||
Future<Conversation> demarrerConversationRole({
|
||||
required String roleCible,
|
||||
required String organisationId,
|
||||
String? premierMessage,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final messages = await remoteDatasource.getMessages(
|
||||
conversationId: conversationId,
|
||||
limit: limit,
|
||||
beforeMessageId: beforeMessageId,
|
||||
return await remoteDatasource.demarrerConversationRole(
|
||||
roleCible: roleCible,
|
||||
organisationId: organisationId,
|
||||
premierMessage: premierMessage,
|
||||
);
|
||||
return Right(messages);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Message>> sendMessage({
|
||||
required String conversationId,
|
||||
required String content,
|
||||
List<String>? attachments,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<void> archiverConversation(String conversationId) async {
|
||||
try {
|
||||
final message = await remoteDatasource.sendMessage(
|
||||
conversationId: conversationId,
|
||||
content: content,
|
||||
attachments: attachments,
|
||||
priority: priority,
|
||||
);
|
||||
return Right(message);
|
||||
await remoteDatasource.archiverConversation(conversationId);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Message>> sendBroadcast({
|
||||
required String organizationId,
|
||||
required String subject,
|
||||
required String content,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
List<String>? attachments,
|
||||
Future<Message> envoyerMessage(
|
||||
String conversationId, {
|
||||
required String typeMessage,
|
||||
String? contenu,
|
||||
String? urlFichier,
|
||||
int? dureeAudio,
|
||||
String? messageParentId,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final message = await remoteDatasource.sendBroadcast(
|
||||
organizationId: organizationId,
|
||||
subject: subject,
|
||||
content: content,
|
||||
priority: priority,
|
||||
attachments: attachments,
|
||||
return await remoteDatasource.envoyerMessage(
|
||||
conversationId,
|
||||
typeMessage: typeMessage,
|
||||
contenu: contenu,
|
||||
urlFichier: urlFichier,
|
||||
dureeAudio: dureeAudio,
|
||||
messageParentId: messageParentId,
|
||||
);
|
||||
return Right(message);
|
||||
} on UnauthorizedException {
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ForbiddenException catch (e) {
|
||||
return Left(ForbiddenFailure(e.message));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception(e.message);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> markMessageAsRead(String messageId) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<List<Message>> getMessages(String conversationId, {int page = 0}) async {
|
||||
try {
|
||||
await remoteDatasource.markMessageAsRead(messageId);
|
||||
return const Right(null);
|
||||
return await remoteDatasource.getMessages(conversationId, page: page);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, int>> getUnreadCount({String? organizationId}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<void> marquerLu(String conversationId) async {
|
||||
try {
|
||||
final count =
|
||||
await remoteDatasource.getUnreadCount(organizationId: organizationId);
|
||||
return Right(count);
|
||||
} 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'));
|
||||
await remoteDatasource.marquerLu(conversationId);
|
||||
} catch (_) {
|
||||
// Non-bloquant — ignorer les erreurs de marquage
|
||||
}
|
||||
}
|
||||
|
||||
// === CONVERSATION ACTIONS ===
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> archiveConversation(String conversationId) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<void> supprimerMessage(String conversationId, String messageId) async {
|
||||
try {
|
||||
await remoteDatasource.archiveConversation(conversationId);
|
||||
return const Right(null);
|
||||
await remoteDatasource.supprimerMessage(conversationId, messageId);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Message>> sendTargetedMessage({
|
||||
required String organizationId,
|
||||
required List<String> targetRoles,
|
||||
required String subject,
|
||||
required String content,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
Future<void> bloquerMembre({
|
||||
required String membreABloquerId,
|
||||
String? organisationId,
|
||||
String? raison,
|
||||
}) async {
|
||||
// TODO: Backend needs specific endpoint for targeted messages
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> markConversationAsRead(String conversationId) async {
|
||||
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<Either<Failure, void>> toggleMuteConversation(String conversationId) async {
|
||||
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<Either<Failure, void>> togglePinConversation(String conversationId) async {
|
||||
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<Either<Failure, Message>> editMessage({
|
||||
required String messageId,
|
||||
required String newContent,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final message = await remoteDatasource.editMessage(
|
||||
messageId: messageId,
|
||||
newContent: newContent,
|
||||
await remoteDatasource.bloquerMembre(
|
||||
membreABloquerId: membreABloquerId,
|
||||
organisationId: organisationId,
|
||||
raison: raison,
|
||||
);
|
||||
return Right(message);
|
||||
} on NotFoundException {
|
||||
return Left(NotFoundFailure('Message non trouvé'));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteMessage(String messageId) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
Future<void> debloquerMembre(String membreId, {String? organisationId}) async {
|
||||
try {
|
||||
await remoteDatasource.deleteMessage(messageId);
|
||||
return const Right(null);
|
||||
} on NotFoundException {
|
||||
return Left(NotFoundFailure('Message non trouvé'));
|
||||
await remoteDatasource.debloquerMembre(membreId, organisationId: organisationId);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<MessageTemplate>>> getTemplates({
|
||||
String? organizationId,
|
||||
TemplateCategory? category,
|
||||
Future<List<Map<String, dynamic>>> getMesBlocages() async {
|
||||
try {
|
||||
return await remoteDatasource.getMesBlocages();
|
||||
} on UnauthorizedException {
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ContactPolicy> getPolitique(String organisationId) async {
|
||||
try {
|
||||
return await remoteDatasource.getPolitique(organisationId);
|
||||
} on UnauthorizedException {
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ServerException catch (e) {
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ContactPolicy> mettreAJourPolitique(
|
||||
String organisationId, {
|
||||
required String typePolitique,
|
||||
required bool autoriserMembreVersMembre,
|
||||
required bool autoriserMembreVersRole,
|
||||
required bool autoriserNotesVocales,
|
||||
}) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, MessageTemplate>> getTemplateById(String templateId) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, MessageTemplate>> createTemplate({
|
||||
required String name,
|
||||
required String description,
|
||||
required TemplateCategory category,
|
||||
required String subject,
|
||||
required String body,
|
||||
List<Map<String, dynamic>>? variables,
|
||||
String? organizationId,
|
||||
}) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, MessageTemplate>> updateTemplate({
|
||||
required String templateId,
|
||||
String? name,
|
||||
String? description,
|
||||
String? subject,
|
||||
String? body,
|
||||
bool? isActive,
|
||||
}) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteTemplate(String templateId) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Message>> sendFromTemplate({
|
||||
required String templateId,
|
||||
required Map<String, String> variables,
|
||||
required List<String> recipientIds,
|
||||
}) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, dynamic>>> getMessagingStats({
|
||||
required String organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
}) async {
|
||||
return Left(NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
try {
|
||||
return await remoteDatasource.mettreAJourPolitique(
|
||||
organisationId,
|
||||
typePolitique: typePolitique,
|
||||
autoriserMembreVersMembre: autoriserMembreVersMembre,
|
||||
autoriserMembreVersRole: autoriserMembreVersRole,
|
||||
autoriserNotesVocales: autoriserNotesVocales,
|
||||
);
|
||||
} on UnauthorizedException {
|
||||
throw Exception('Session expirée — veuillez vous reconnecter');
|
||||
} on ForbiddenException catch (e) {
|
||||
throw Exception(e.message);
|
||||
} on ServerException catch (e) {
|
||||
throw Exception(e.message);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur inattendue: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user