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:
@@ -0,0 +1,35 @@
|
||||
/// Entité métier ContactPolicy v4
|
||||
///
|
||||
/// Correspond au DTO backend : ContactPolicyResponse
|
||||
library contact_policy;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Politique de communication d'une organisation
|
||||
class ContactPolicy extends Equatable {
|
||||
final String? id;
|
||||
final String? organisationId;
|
||||
final String typePolitique; // OUVERT | BUREAU_SEULEMENT | GROUPES_INTERNES
|
||||
final bool autoriserMembreVersMembre;
|
||||
final bool autoriserMembreVersRole;
|
||||
final bool autoriserNotesVocales;
|
||||
|
||||
const ContactPolicy({
|
||||
this.id,
|
||||
this.organisationId,
|
||||
required this.typePolitique,
|
||||
this.autoriserMembreVersMembre = true,
|
||||
this.autoriserMembreVersRole = true,
|
||||
this.autoriserNotesVocales = true,
|
||||
});
|
||||
|
||||
bool get isOuvert => typePolitique == 'OUVERT';
|
||||
bool get isBureauSeulement => typePolitique == 'BUREAU_SEULEMENT';
|
||||
bool get isGroupesInternes => typePolitique == 'GROUPES_INTERNES';
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id, organisationId, typePolitique,
|
||||
autoriserMembreVersMembre, autoriserMembreVersRole, autoriserNotesVocales,
|
||||
];
|
||||
}
|
||||
@@ -1,127 +1,120 @@
|
||||
/// Entité métier Conversation
|
||||
/// Entités métier Conversation v4
|
||||
///
|
||||
/// Représente une conversation (fil de messages) dans UnionFlow
|
||||
/// Correspond aux DTOs backend : ConversationSummaryResponse et ConversationResponse
|
||||
library conversation;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'message.dart';
|
||||
|
||||
/// Type de conversation
|
||||
enum ConversationType {
|
||||
/// Conversation individuelle (1-1)
|
||||
individual,
|
||||
// ── Résumé de conversation (liste) ───────────────────────────────────────────
|
||||
|
||||
/// Conversation de groupe
|
||||
group,
|
||||
|
||||
/// Canal broadcast (lecture seule pour la plupart)
|
||||
broadcast,
|
||||
|
||||
/// Canal d'annonces organisation
|
||||
announcement,
|
||||
}
|
||||
|
||||
/// Entité Conversation
|
||||
class Conversation extends Equatable {
|
||||
/// Résumé d'une conversation pour l'affichage en liste
|
||||
class ConversationSummary extends Equatable {
|
||||
final String id;
|
||||
final String name;
|
||||
final String? description;
|
||||
final ConversationType type;
|
||||
final List<String> participantIds;
|
||||
final String? organizationId;
|
||||
final Message? lastMessage;
|
||||
final int unreadCount;
|
||||
final bool isMuted;
|
||||
final bool isPinned;
|
||||
final bool isArchived;
|
||||
final DateTime createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final String? avatarUrl;
|
||||
final Map<String, dynamic>? metadata;
|
||||
final String typeConversation; // DIRECTE | ROLE_CANAL | GROUPE
|
||||
final String titre;
|
||||
final String statut; // ACTIVE | ARCHIVEE
|
||||
final String? dernierMessageApercu;
|
||||
final String? dernierMessageType;
|
||||
final DateTime? dernierMessageAt;
|
||||
final int nonLus;
|
||||
final String? organisationId;
|
||||
|
||||
const Conversation({
|
||||
const ConversationSummary({
|
||||
required this.id,
|
||||
required this.name,
|
||||
this.description,
|
||||
required this.type,
|
||||
required this.participantIds,
|
||||
this.organizationId,
|
||||
this.lastMessage,
|
||||
this.unreadCount = 0,
|
||||
this.isMuted = false,
|
||||
this.isPinned = false,
|
||||
this.isArchived = false,
|
||||
required this.createdAt,
|
||||
this.updatedAt,
|
||||
this.avatarUrl,
|
||||
this.metadata,
|
||||
required this.typeConversation,
|
||||
required this.titre,
|
||||
required this.statut,
|
||||
this.dernierMessageApercu,
|
||||
this.dernierMessageType,
|
||||
this.dernierMessageAt,
|
||||
this.nonLus = 0,
|
||||
this.organisationId,
|
||||
});
|
||||
|
||||
/// Vérifie si la conversation a des messages non lus
|
||||
bool get hasUnread => unreadCount > 0;
|
||||
|
||||
/// Vérifie si c'est une conversation individuelle
|
||||
bool get isIndividual => type == ConversationType.individual;
|
||||
|
||||
/// Vérifie si c'est un broadcast
|
||||
bool get isBroadcast => type == ConversationType.broadcast;
|
||||
|
||||
/// Nombre de participants
|
||||
int get participantCount => participantIds.length;
|
||||
|
||||
/// Copie avec modifications
|
||||
Conversation copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
String? description,
|
||||
ConversationType? type,
|
||||
List<String>? participantIds,
|
||||
String? organizationId,
|
||||
Message? lastMessage,
|
||||
int? unreadCount,
|
||||
bool? isMuted,
|
||||
bool? isPinned,
|
||||
bool? isArchived,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
String? avatarUrl,
|
||||
Map<String, dynamic>? metadata,
|
||||
}) {
|
||||
return Conversation(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
type: type ?? this.type,
|
||||
participantIds: participantIds ?? this.participantIds,
|
||||
organizationId: organizationId ?? this.organizationId,
|
||||
lastMessage: lastMessage ?? this.lastMessage,
|
||||
unreadCount: unreadCount ?? this.unreadCount,
|
||||
isMuted: isMuted ?? this.isMuted,
|
||||
isPinned: isPinned ?? this.isPinned,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
avatarUrl: avatarUrl ?? this.avatarUrl,
|
||||
metadata: metadata ?? this.metadata,
|
||||
);
|
||||
}
|
||||
bool get hasUnread => nonLus > 0;
|
||||
bool get isDirecte => typeConversation == 'DIRECTE';
|
||||
bool get isRoleCanal => typeConversation == 'ROLE_CANAL';
|
||||
bool get isGroupe => typeConversation == 'GROUPE';
|
||||
bool get isActive => statut == 'ACTIVE';
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
participantIds,
|
||||
organizationId,
|
||||
lastMessage,
|
||||
unreadCount,
|
||||
isMuted,
|
||||
isPinned,
|
||||
isArchived,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
avatarUrl,
|
||||
metadata,
|
||||
id, typeConversation, titre, statut,
|
||||
dernierMessageApercu, dernierMessageType, dernierMessageAt,
|
||||
nonLus, organisationId,
|
||||
];
|
||||
}
|
||||
|
||||
// ── Participant ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// Participant dans une conversation
|
||||
class ConversationParticipant extends Equatable {
|
||||
final String membreId;
|
||||
final String? prenom;
|
||||
final String? nom;
|
||||
final String? roleDansConversation;
|
||||
final DateTime? luJusqua;
|
||||
|
||||
const ConversationParticipant({
|
||||
required this.membreId,
|
||||
this.prenom,
|
||||
this.nom,
|
||||
this.roleDansConversation,
|
||||
this.luJusqua,
|
||||
});
|
||||
|
||||
String get nomComplet {
|
||||
if (prenom != null && nom != null) return '$prenom $nom';
|
||||
if (prenom != null) return prenom!;
|
||||
if (nom != null) return nom!;
|
||||
return membreId;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [membreId, prenom, nom, roleDansConversation, luJusqua];
|
||||
}
|
||||
|
||||
// ── Conversation complète (détail) ───────────────────────────────────────────
|
||||
|
||||
/// Conversation complète avec participants et messages
|
||||
class Conversation extends Equatable {
|
||||
final String id;
|
||||
final String typeConversation;
|
||||
final String titre;
|
||||
final String statut;
|
||||
final String? organisationId;
|
||||
final String? organisationNom;
|
||||
final DateTime? dateCreation;
|
||||
final int nombreMessages;
|
||||
final List<ConversationParticipant> participants;
|
||||
final List<Message> messages;
|
||||
final int nonLus;
|
||||
final String? roleCible;
|
||||
|
||||
const Conversation({
|
||||
required this.id,
|
||||
required this.typeConversation,
|
||||
required this.titre,
|
||||
required this.statut,
|
||||
this.organisationId,
|
||||
this.organisationNom,
|
||||
this.dateCreation,
|
||||
this.nombreMessages = 0,
|
||||
this.participants = const [],
|
||||
this.messages = const [],
|
||||
this.nonLus = 0,
|
||||
this.roleCible,
|
||||
});
|
||||
|
||||
bool get isDirecte => typeConversation == 'DIRECTE';
|
||||
bool get isRoleCanal => typeConversation == 'ROLE_CANAL';
|
||||
bool get isGroupe => typeConversation == 'GROUPE';
|
||||
bool get isActive => statut == 'ACTIVE';
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id, typeConversation, titre, statut, organisationId, organisationNom,
|
||||
dateCreation, nombreMessages, participants, messages, nonLus, roleCible,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,173 +1,68 @@
|
||||
/// Entité métier Message
|
||||
/// Entité métier Message v4
|
||||
///
|
||||
/// Représente un message dans le système de communication UnionFlow
|
||||
/// Correspond au DTO backend : MessageResponse
|
||||
library message;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// Type de message
|
||||
enum MessageType {
|
||||
/// Message individuel (membre à membre)
|
||||
individual,
|
||||
|
||||
/// Broadcast organisation (OrgAdmin → tous)
|
||||
broadcast,
|
||||
|
||||
/// Message ciblé par rôle (Moderator → groupe)
|
||||
targeted,
|
||||
|
||||
/// Notification système
|
||||
system,
|
||||
}
|
||||
|
||||
/// Statut de lecture du message
|
||||
enum MessageStatus {
|
||||
/// Envoyé mais non lu
|
||||
sent,
|
||||
|
||||
/// Livré (reçu par le serveur)
|
||||
delivered,
|
||||
|
||||
/// Lu par le destinataire
|
||||
read,
|
||||
|
||||
/// Échec d'envoi
|
||||
failed,
|
||||
}
|
||||
|
||||
/// Priorité du message
|
||||
enum MessagePriority {
|
||||
/// Priorité normale
|
||||
normal,
|
||||
|
||||
/// Priorité élevée (important)
|
||||
high,
|
||||
|
||||
/// Priorité urgente (critique)
|
||||
urgent,
|
||||
}
|
||||
|
||||
/// Entité Message
|
||||
/// Message dans une conversation
|
||||
class Message extends Equatable {
|
||||
final String id;
|
||||
final String conversationId;
|
||||
final String senderId;
|
||||
final String senderName;
|
||||
final String? senderAvatar;
|
||||
final String content;
|
||||
final MessageType type;
|
||||
final MessageStatus status;
|
||||
final MessagePriority priority;
|
||||
final List<String> recipientIds;
|
||||
final List<String>? recipientRoles;
|
||||
final String? organizationId;
|
||||
final DateTime createdAt;
|
||||
final DateTime? readAt;
|
||||
final Map<String, dynamic>? metadata;
|
||||
final List<String>? attachments;
|
||||
final bool isEdited;
|
||||
final DateTime? editedAt;
|
||||
final bool isDeleted;
|
||||
final String typeMessage; // TEXTE | VOCAL | IMAGE | SYSTEME
|
||||
final String? contenu;
|
||||
final String? urlFichier;
|
||||
final int? dureeAudio;
|
||||
final bool supprime;
|
||||
final String? expediteurId;
|
||||
final String? expediteurNom;
|
||||
final String? expediteurPrenom;
|
||||
final String? messageParentId;
|
||||
final String? messageParentApercu;
|
||||
final DateTime? dateEnvoi;
|
||||
|
||||
const Message({
|
||||
required this.id,
|
||||
required this.conversationId,
|
||||
required this.senderId,
|
||||
required this.senderName,
|
||||
this.senderAvatar,
|
||||
required this.content,
|
||||
required this.type,
|
||||
required this.status,
|
||||
this.priority = MessagePriority.normal,
|
||||
required this.recipientIds,
|
||||
this.recipientRoles,
|
||||
this.organizationId,
|
||||
required this.createdAt,
|
||||
this.readAt,
|
||||
this.metadata,
|
||||
this.attachments,
|
||||
this.isEdited = false,
|
||||
this.editedAt,
|
||||
this.isDeleted = false,
|
||||
required this.typeMessage,
|
||||
this.contenu,
|
||||
this.urlFichier,
|
||||
this.dureeAudio,
|
||||
this.supprime = false,
|
||||
this.expediteurId,
|
||||
this.expediteurNom,
|
||||
this.expediteurPrenom,
|
||||
this.messageParentId,
|
||||
this.messageParentApercu,
|
||||
this.dateEnvoi,
|
||||
});
|
||||
|
||||
/// Vérifie si le message a été lu
|
||||
bool get isRead => status == MessageStatus.read;
|
||||
String get expediteurNomComplet {
|
||||
if (expediteurPrenom != null && expediteurNom != null) {
|
||||
return '$expediteurPrenom $expediteurNom';
|
||||
}
|
||||
if (expediteurPrenom != null) return expediteurPrenom!;
|
||||
if (expediteurNom != null) return expediteurNom!;
|
||||
return '';
|
||||
}
|
||||
|
||||
/// Vérifie si le message est urgent
|
||||
bool get isUrgent => priority == MessagePriority.urgent;
|
||||
bool get isTexte => typeMessage == 'TEXTE';
|
||||
bool get isVocal => typeMessage == 'VOCAL';
|
||||
bool get isImage => typeMessage == 'IMAGE';
|
||||
bool get isSysteme => typeMessage == 'SYSTEME';
|
||||
bool get hasParent => messageParentId != null;
|
||||
|
||||
/// Vérifie si le message est un broadcast
|
||||
bool get isBroadcast => type == MessageType.broadcast;
|
||||
|
||||
/// Vérifie si le message a des pièces jointes
|
||||
bool get hasAttachments => attachments != null && attachments!.isNotEmpty;
|
||||
|
||||
/// Copie avec modifications
|
||||
Message copyWith({
|
||||
String? id,
|
||||
String? conversationId,
|
||||
String? senderId,
|
||||
String? senderName,
|
||||
String? senderAvatar,
|
||||
String? content,
|
||||
MessageType? type,
|
||||
MessageStatus? status,
|
||||
MessagePriority? priority,
|
||||
List<String>? recipientIds,
|
||||
List<String>? recipientRoles,
|
||||
String? organizationId,
|
||||
DateTime? createdAt,
|
||||
DateTime? readAt,
|
||||
Map<String, dynamic>? metadata,
|
||||
List<String>? attachments,
|
||||
bool? isEdited,
|
||||
DateTime? editedAt,
|
||||
bool? isDeleted,
|
||||
}) {
|
||||
return Message(
|
||||
id: id ?? this.id,
|
||||
conversationId: conversationId ?? this.conversationId,
|
||||
senderId: senderId ?? this.senderId,
|
||||
senderName: senderName ?? this.senderName,
|
||||
senderAvatar: senderAvatar ?? this.senderAvatar,
|
||||
content: content ?? this.content,
|
||||
type: type ?? this.type,
|
||||
status: status ?? this.status,
|
||||
priority: priority ?? this.priority,
|
||||
recipientIds: recipientIds ?? this.recipientIds,
|
||||
recipientRoles: recipientRoles ?? this.recipientRoles,
|
||||
organizationId: organizationId ?? this.organizationId,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
readAt: readAt ?? this.readAt,
|
||||
metadata: metadata ?? this.metadata,
|
||||
attachments: attachments ?? this.attachments,
|
||||
isEdited: isEdited ?? this.isEdited,
|
||||
editedAt: editedAt ?? this.editedAt,
|
||||
isDeleted: isDeleted ?? this.isDeleted,
|
||||
);
|
||||
/// Texte à afficher dans la liste (aperçu)
|
||||
String get apercu {
|
||||
if (supprime) return '🚫 Message supprimé';
|
||||
if (isVocal) return '🎙️ Note vocale${dureeAudio != null ? ' (${dureeAudio}s)' : ''}';
|
||||
if (isImage) return '📷 Image';
|
||||
if (isSysteme) return contenu ?? '🔔 Notification système';
|
||||
return contenu ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
conversationId,
|
||||
senderId,
|
||||
senderName,
|
||||
senderAvatar,
|
||||
content,
|
||||
type,
|
||||
status,
|
||||
priority,
|
||||
recipientIds,
|
||||
recipientRoles,
|
||||
organizationId,
|
||||
createdAt,
|
||||
readAt,
|
||||
metadata,
|
||||
attachments,
|
||||
isEdited,
|
||||
editedAt,
|
||||
isDeleted,
|
||||
id, typeMessage, contenu, urlFichier, dureeAudio, supprime,
|
||||
expediteurId, expediteurNom, expediteurPrenom,
|
||||
messageParentId, messageParentApercu, dateEnvoi,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,145 +1,87 @@
|
||||
/// Repository interface pour la communication
|
||||
/// Repository interface pour la messagerie v4
|
||||
///
|
||||
/// Contrat de données pour les messages, conversations et templates
|
||||
/// Contrat de données aligné sur l'API backend v4 (/api/messagerie/*)
|
||||
library messaging_repository;
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../entities/message.dart';
|
||||
import '../entities/conversation.dart';
|
||||
import '../entities/message_template.dart';
|
||||
import '../entities/message.dart';
|
||||
import '../entities/contact_policy.dart';
|
||||
|
||||
/// Interface du repository de messagerie
|
||||
abstract class MessagingRepository {
|
||||
// === CONVERSATIONS ===
|
||||
|
||||
/// Récupère toutes les conversations de l'utilisateur
|
||||
Future<Either<Failure, List<Conversation>>> getConversations({
|
||||
String? organizationId,
|
||||
bool includeArchived = false,
|
||||
// ── Conversations ─────────────────────────────────────────────────────────
|
||||
|
||||
/// Récupère les conversations résumées de l'utilisateur connecté
|
||||
Future<List<ConversationSummary>> getMesConversations();
|
||||
|
||||
/// Récupère la conversation complète (avec participants et messages)
|
||||
Future<Conversation> getConversation(String conversationId);
|
||||
|
||||
/// Démarre une conversation directe avec un membre
|
||||
Future<Conversation> demarrerConversationDirecte({
|
||||
required String destinataireId,
|
||||
required String organisationId,
|
||||
String? premierMessage,
|
||||
});
|
||||
|
||||
/// Récupère une conversation par son ID
|
||||
Future<Either<Failure, Conversation>> getConversationById(String conversationId);
|
||||
|
||||
/// Crée une nouvelle conversation
|
||||
Future<Either<Failure, Conversation>> createConversation({
|
||||
required String name,
|
||||
required List<String> participantIds,
|
||||
String? organizationId,
|
||||
String? description,
|
||||
/// Démarre un canal de communication avec un rôle officiel
|
||||
Future<Conversation> demarrerConversationRole({
|
||||
required String roleCible,
|
||||
required String organisationId,
|
||||
String? premierMessage,
|
||||
});
|
||||
|
||||
/// Archive une conversation
|
||||
Future<Either<Failure, void>> archiveConversation(String conversationId);
|
||||
Future<void> archiverConversation(String conversationId);
|
||||
|
||||
/// Marque une conversation comme lue
|
||||
Future<Either<Failure, void>> markConversationAsRead(String conversationId);
|
||||
// ── Messages ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// Mute/démute une conversation
|
||||
Future<Either<Failure, void>> toggleMuteConversation(String conversationId);
|
||||
|
||||
/// Pin/unpin une conversation
|
||||
Future<Either<Failure, void>> togglePinConversation(String conversationId);
|
||||
|
||||
// === MESSAGES ===
|
||||
|
||||
/// Récupère les messages d'une conversation
|
||||
Future<Either<Failure, List<Message>>> getMessages({
|
||||
required String conversationId,
|
||||
int? limit,
|
||||
String? beforeMessageId,
|
||||
/// Envoie un message dans une conversation
|
||||
Future<Message> envoyerMessage(
|
||||
String conversationId, {
|
||||
required String typeMessage,
|
||||
String? contenu,
|
||||
String? urlFichier,
|
||||
int? dureeAudio,
|
||||
String? messageParentId,
|
||||
});
|
||||
|
||||
/// Envoie un message individuel
|
||||
Future<Either<Failure, Message>> sendMessage({
|
||||
required String conversationId,
|
||||
required String content,
|
||||
List<String>? attachments,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
/// Récupère l'historique des messages (paginé)
|
||||
Future<List<Message>> getMessages(String conversationId, {int page = 0});
|
||||
|
||||
/// Marque tous les messages d'une conversation comme lus
|
||||
Future<void> marquerLu(String conversationId);
|
||||
|
||||
/// Supprime un message (soft-delete)
|
||||
Future<void> supprimerMessage(String conversationId, String messageId);
|
||||
|
||||
// ── Blocages ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// Bloque un membre
|
||||
Future<void> bloquerMembre({
|
||||
required String membreABloquerId,
|
||||
String? organisationId,
|
||||
String? raison,
|
||||
});
|
||||
|
||||
/// Envoie un broadcast à toute l'organisation
|
||||
Future<Either<Failure, Message>> sendBroadcast({
|
||||
required String organizationId,
|
||||
required String subject,
|
||||
required String content,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
List<String>? attachments,
|
||||
});
|
||||
/// Débloque un membre
|
||||
Future<void> debloquerMembre(String membreId, {String? organisationId});
|
||||
|
||||
/// Envoie un message ciblé par rôles
|
||||
Future<Either<Failure, Message>> sendTargetedMessage({
|
||||
required String organizationId,
|
||||
required List<String> targetRoles,
|
||||
required String subject,
|
||||
required String content,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
});
|
||||
/// Récupère la liste des membres bloqués
|
||||
Future<List<Map<String, dynamic>>> getMesBlocages();
|
||||
|
||||
/// Marque un message comme lu
|
||||
Future<Either<Failure, void>> markMessageAsRead(String messageId);
|
||||
// ── Politique de communication ────────────────────────────────────────────
|
||||
|
||||
/// Édite un message
|
||||
Future<Either<Failure, Message>> editMessage({
|
||||
required String messageId,
|
||||
required String newContent,
|
||||
});
|
||||
/// Récupère la politique de communication d'une organisation
|
||||
Future<ContactPolicy> getPolitique(String organisationId);
|
||||
|
||||
/// Supprime un message
|
||||
Future<Either<Failure, void>> deleteMessage(String messageId);
|
||||
|
||||
// === TEMPLATES ===
|
||||
|
||||
/// Récupère tous les templates disponibles
|
||||
Future<Either<Failure, List<MessageTemplate>>> getTemplates({
|
||||
String? organizationId,
|
||||
TemplateCategory? category,
|
||||
});
|
||||
|
||||
/// Récupère un template par son ID
|
||||
Future<Either<Failure, MessageTemplate>> getTemplateById(String templateId);
|
||||
|
||||
/// Crée un nouveau template
|
||||
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,
|
||||
});
|
||||
|
||||
/// Met à jour un template
|
||||
Future<Either<Failure, MessageTemplate>> updateTemplate({
|
||||
required String templateId,
|
||||
String? name,
|
||||
String? description,
|
||||
String? subject,
|
||||
String? body,
|
||||
bool? isActive,
|
||||
});
|
||||
|
||||
/// Supprime un template
|
||||
Future<Either<Failure, void>> deleteTemplate(String templateId);
|
||||
|
||||
/// Envoie un message à partir d'un template
|
||||
Future<Either<Failure, Message>> sendFromTemplate({
|
||||
required String templateId,
|
||||
required Map<String, String> variables,
|
||||
required List<String> recipientIds,
|
||||
});
|
||||
|
||||
// === STATISTIQUES ===
|
||||
|
||||
/// Récupère le nombre de messages non lus
|
||||
Future<Either<Failure, int>> getUnreadCount({String? organizationId});
|
||||
|
||||
/// Récupère les statistiques de communication
|
||||
Future<Either<Failure, Map<String, dynamic>>> getMessagingStats({
|
||||
required String organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
/// Met à jour la politique de communication (ADMIN seulement)
|
||||
Future<ContactPolicy> mettreAJourPolitique(
|
||||
String organisationId, {
|
||||
required String typePolitique,
|
||||
required bool autoriserMembreVersMembre,
|
||||
required bool autoriserMembreVersRole,
|
||||
required bool autoriserNotesVocales,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/// Use case: Récupérer les conversations
|
||||
/// Use case: Récupérer les conversations v4
|
||||
library get_conversations;
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
|
||||
import '../entities/conversation.dart';
|
||||
import '../repositories/messaging_repository.dart';
|
||||
|
||||
@@ -13,13 +12,7 @@ class GetConversations {
|
||||
|
||||
GetConversations(this.repository);
|
||||
|
||||
Future<Either<Failure, List<Conversation>>> call({
|
||||
String? organizationId,
|
||||
bool includeArchived = false,
|
||||
}) async {
|
||||
return await repository.getConversations(
|
||||
organizationId: organizationId,
|
||||
includeArchived: includeArchived,
|
||||
);
|
||||
Future<List<ConversationSummary>> call() async {
|
||||
return await repository.getMesConversations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/// Use case: Récupérer les messages d'une conversation
|
||||
/// Use case: Récupérer les messages d'une conversation v4
|
||||
library get_messages;
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
|
||||
import '../entities/message.dart';
|
||||
import '../repositories/messaging_repository.dart';
|
||||
|
||||
@@ -13,19 +12,10 @@ class GetMessages {
|
||||
|
||||
GetMessages(this.repository);
|
||||
|
||||
Future<Either<Failure, List<Message>>> call({
|
||||
Future<List<Message>> call({
|
||||
required String conversationId,
|
||||
int? limit,
|
||||
String? beforeMessageId,
|
||||
int page = 0,
|
||||
}) async {
|
||||
if (conversationId.isEmpty) {
|
||||
return Left(ValidationFailure('ID conversation requis'));
|
||||
}
|
||||
|
||||
return await repository.getMessages(
|
||||
conversationId: conversationId,
|
||||
limit: limit,
|
||||
beforeMessageId: beforeMessageId,
|
||||
);
|
||||
return await repository.getMessages(conversationId, page: page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/// Use case: Envoyer un broadcast organisation
|
||||
/// Use case: Broadcast — non utilisé en v4 (remplacé par canal rôle)
|
||||
///
|
||||
/// Conservé pour la compatibilité du graphe de dépendances.
|
||||
library send_broadcast;
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../entities/message.dart';
|
||||
|
||||
import '../repositories/messaging_repository.dart';
|
||||
|
||||
@lazySingleton
|
||||
@@ -13,32 +13,13 @@ class SendBroadcast {
|
||||
|
||||
SendBroadcast(this.repository);
|
||||
|
||||
Future<Either<Failure, Message>> call({
|
||||
required String organizationId,
|
||||
required String subject,
|
||||
required String content,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
List<String>? attachments,
|
||||
/// Démarre un canal de communication avec le rôle BUREAU
|
||||
Future<void> call({
|
||||
required String organisationId,
|
||||
}) async {
|
||||
// Validation
|
||||
if (subject.trim().isEmpty) {
|
||||
return Left(ValidationFailure('Le sujet ne peut pas être vide'));
|
||||
}
|
||||
|
||||
if (content.trim().isEmpty) {
|
||||
return Left(ValidationFailure('Le message ne peut pas être vide'));
|
||||
}
|
||||
|
||||
if (organizationId.isEmpty) {
|
||||
return Left(ValidationFailure('ID organisation requis'));
|
||||
}
|
||||
|
||||
return await repository.sendBroadcast(
|
||||
organizationId: organizationId,
|
||||
subject: subject,
|
||||
content: content,
|
||||
priority: priority,
|
||||
attachments: attachments,
|
||||
await repository.demarrerConversationRole(
|
||||
roleCible: 'PRESIDENT',
|
||||
organisationId: organisationId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/// Use case: Envoyer un message
|
||||
/// Use case: Envoyer un message v4
|
||||
library send_message;
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
|
||||
import '../entities/message.dart';
|
||||
import '../repositories/messaging_repository.dart';
|
||||
|
||||
@@ -13,22 +12,15 @@ class SendMessage {
|
||||
|
||||
SendMessage(this.repository);
|
||||
|
||||
Future<Either<Failure, Message>> call({
|
||||
Future<Message> call({
|
||||
required String conversationId,
|
||||
required String content,
|
||||
List<String>? attachments,
|
||||
MessagePriority priority = MessagePriority.normal,
|
||||
required String contenu,
|
||||
String typeMessage = 'TEXTE',
|
||||
}) async {
|
||||
// Validation
|
||||
if (content.trim().isEmpty) {
|
||||
return Left(ValidationFailure('Le message ne peut pas être vide'));
|
||||
}
|
||||
|
||||
return await repository.sendMessage(
|
||||
conversationId: conversationId,
|
||||
content: content,
|
||||
attachments: attachments,
|
||||
priority: priority,
|
||||
return await repository.envoyerMessage(
|
||||
conversationId,
|
||||
typeMessage: typeMessage,
|
||||
contenu: contenu,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user