diff --git a/src/PostIt/PostIt/Models/ChatMessage.cs b/src/PostIt/PostIt/Models/ChatMessage.cs
new file mode 100644
index 000000000..52733311a
--- /dev/null
+++ b/src/PostIt/PostIt/Models/ChatMessage.cs
@@ -0,0 +1,15 @@
+namespace PostIt.ViewModels.Chat
+{
+ ///
+ /// DTO de message de salon, calqué sur la charge envoyée par
+ /// ChatHub.Send côté serveur ({ Name, Room, Message }).
+ /// Le protocole JSON de SignalR sérialise en camelCase ; la
+ /// désérialisation est insensible à la casse par défaut.
+ ///
+ public class ChatMessage
+ {
+ public string? Name { get; set; }
+ public string? Room { get; set; }
+ public string? Message { get; set; }
+ }
+}
diff --git a/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs b/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs
index 54872991e..7cbdfe62f 100644
--- a/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs
@@ -45,8 +45,17 @@ namespace PostIt.ViewModels.Chat
public partial StatusNotice Status { get; set; }
[ObservableProperty]
+ [NotifyCanExecuteChangedFor(nameof(SendMessageCommand))]
public partial bool IsConnected { get; set; } = false;
+ [ObservableProperty]
+ [NotifyCanExecuteChangedFor(nameof(SendMessageCommand))]
+ public partial bool HasJoinedRoom { get; set; } = false;
+
+ [ObservableProperty]
+ [NotifyCanExecuteChangedFor(nameof(SendMessageCommand))]
+ public partial string MessageToSend { get; set; } = "";
+
// Listes dynamiques pour l'UI
public ObservableCollection Messages { get; } = new();
public ObservableCollection ConnectedUsers { get; } = new();
@@ -58,6 +67,28 @@ namespace PostIt.ViewModels.Chat
[RelayCommand]
public async Task JoinRoomCommand() => await JoinRoomAsync();
+ // Le serveur (ChatHub.Send) refuse tout message d'un utilisateur
+ // qui n'a pas joint le salon ("NoJoinNoSend") : on garde la
+ // commande inactive tant que le Join n'a pas abouti.
+ private bool CanSendMessage() =>
+ IsConnected && HasJoinedRoom && !string.IsNullOrWhiteSpace(MessageToSend);
+
+ [RelayCommand(CanExecute = nameof(CanSendMessage))]
+ private async Task SendMessageAsync()
+ {
+ if (_connection == null) return;
+ var text = MessageToSend.Trim();
+ try
+ {
+ await _connection.InvokeAsync("Send", CurrentRoom, text);
+ MessageToSend = string.Empty;
+ }
+ catch (Exception ex)
+ {
+ Messages.Add($"Erreur d'envoi : {ex.Message}");
+ }
+ }
+
public override bool CanNavigateNext { get; protected set; } = false;
public override bool CanNavigatePrevious { get; protected set; } = true;
@@ -70,6 +101,7 @@ namespace PostIt.ViewModels.Chat
_connection = null;
}
IsConnected = false;
+ HasJoinedRoom = false;
Status = new StatusNotice("Déconnecté", StatusSeverity.Info,
new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
}
@@ -140,11 +172,19 @@ namespace PostIt.ViewModels.Chat
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
Messages.Add($"Salon rejoint avec succès : {roomInfo.Name}");
+ HasJoinedRoom = true;
ConnectedUsers.Clear();
foreach (var u in roomInfo.Users) ConnectedUsers.Add(u);
});
});
+ // 4. Réception des messages du salon (diffusés par ChatHub.Send)
+ _connection.On("ReceiveMessage", (msg) =>
+ {
+ Avalonia.Threading.Dispatcher.UIThread.Post(() =>
+ Messages.Add($"[{msg.Room}] {msg.Name} : {msg.Message}"));
+ });
+
await _connection.StartAsync();
IsConnected = true;
Status = new StatusNotice("Connecté au Hub SignalR", StatusSeverity.Info, new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
diff --git a/src/PostIt/PostIt/Views/Chat/ChatPage.axaml b/src/PostIt/PostIt/Views/Chat/ChatPage.axaml
index 55251f544..415b989b1 100644
--- a/src/PostIt/PostIt/Views/Chat/ChatPage.axaml
+++ b/src/PostIt/PostIt/Views/Chat/ChatPage.axaml
@@ -16,7 +16,7 @@
-
+
+
+
+
+
+
+
+
+
+
+