SignalR chat connection OK

This commit is contained in:
Paul Schneider 2026-10-03 22:47:47 +01:00
commit b22a26f61e
Signed by: notazof
GPG key ID: AD4B9B545FBD7D12
43 changed files with 430 additions and 252 deletions

View file

@ -4,18 +4,37 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using PostIt.Models;
using PostIt.Services;
using Yavsc;
namespace PostIt.ViewModels.Chat
{
/// <summary>
/// ViewModel pour la gestion du chat,
/// incluant la connexion au Hub SignalR,
/// la gestion des salons et des utilisateurs connectés.
/// </summary>
public partial class ChatViewModel : ViewModelBase
{
private readonly Settings _settings;
private readonly YavscApiClient? _api;
// Le paramètre optionnel permet l'instanciation design-time
// (Design.DataContext dans ChatPage.axaml) sans conteneur DI.
public ChatViewModel(YavscApiClient? api = null)
{
var app = App.Current as App;
_settings = app!.ServiceProvider!.GetRequiredService<Settings>(); // Assurez-vous que _settings est initialisé avant utilisation.
_api = api;
Status = new StatusNotice("Déconnecté", StatusSeverity.Info,
new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
}
private HubConnection? _connection;
[ObservableProperty]
public partial string ServerUrl { get; protected set; } = "https://yavsc.pschneider.fr/chathub";
[ObservableProperty]
public partial string CurrentRoom { get; set; } = "General";
@ -24,7 +43,6 @@ namespace PostIt.ViewModels.Chat
[ObservableProperty]
public partial StatusNotice Status { get; set; }
= new StatusNotice("Déconnecté", StatusSeverity.Info);
[ObservableProperty]
public partial bool IsConnected { get; set; } = false;
@ -40,20 +58,61 @@ namespace PostIt.ViewModels.Chat
[RelayCommand]
public async Task JoinRoomCommand() => await JoinRoomAsync();
[RelayCommand]
public async Task ChangeNickCommand() => await ChangeNickAsync();
public override bool CanNavigateNext { get; protected set; } = false;
public override bool CanNavigatePrevious { get; protected set; } = true;
private async Task DisconnectAsync()
{
if (_connection != null)
{
await _connection.StopAsync();
await _connection.DisposeAsync();
_connection = null;
}
IsConnected = false;
Status = new StatusNotice("Déconnecté", StatusSeverity.Info,
new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
}
private async Task ConnectAsync()
{
try
{
Status = new StatusNotice("Connexion en cours...", StatusSeverity.Info);
Status = new StatusNotice("Connexion en cours...", StatusSeverity.Info,
new RelayCheckCommand("Vérifier la connexion", async () => await CheckConnectionAsync()));
if (_api == null || !_api.HasValidSession)
{
Status = new StatusNotice("Session expirée : reconnectez-vous avant d'ouvrir le chat.", StatusSeverity.Error,
new RelayCheckCommand("Reconnecter", async () => await ConnectAsync()));
return;
}
// Le hub est mappé à la racine du service ("/chathub"),
// pas sous le préfixe versionné "/api/v1" : on le retire.
var hubBase = _settings.ApiUrl.TrimEnd('/');
if (hubBase.EndsWith("/api/v1", StringComparison.OrdinalIgnoreCase))
hubBase = hubBase[..^"/api/v1".Length];
var hubUrl = hubBase + "/" + Constants.ChatHubPath;
_connection = new HubConnectionBuilder()
.WithUrl(ServerUrl)
.WithUrl(hubUrl,
options =>
{
// Le hub est [Authorize] : on attache le Bearer OIDC.
// AccessTokenProvider est rappelé à chaque reconnexion,
// donc on tente un refresh silencieux à chaque appel.
// Un refresh mort (invalid_grant) ne doit pas faire
// échouer StartAsync avec une exception interne : on
// renvoie le token courant (éventuellement expiré) et
// le serveur répondra un 401 propre, surfacé à l'UI.
options.AccessTokenProvider = async () =>
{
await _api.TrySilentLoginAsync();
return _api.CurrentAccessToken;
};
})
.WithAutomaticReconnect()
.Build();
@ -88,15 +147,20 @@ namespace PostIt.ViewModels.Chat
await _connection.StartAsync();
IsConnected = true;
Status = new StatusNotice("Connecté au Hub SignalR", StatusSeverity.Info);
Status = new StatusNotice("Connecté au Hub SignalR", StatusSeverity.Info, new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
}
catch (Exception ex)
{
Status = new StatusNotice($"Erreur : {ex.Message}", StatusSeverity.Error);
Status = new StatusNotice($"Erreur : {ex.Message}", StatusSeverity.Error, new RelayCheckCommand("Reconnecter", async () => await ConnectAsync()));
IsConnected = false;
}
}
private async Task CheckConnectionAsync()
{
throw new NotImplementedException();
}
private async Task JoinRoomAsync()
{
if (_connection == null || string.IsNullOrWhiteSpace(CurrentRoom)) return;
@ -115,21 +179,5 @@ namespace PostIt.ViewModels.Chat
Messages.Add($"Erreur Join: {ex.Message}");
}
}
private async Task ChangeNickAsync()
{
if (_connection == null || string.IsNullOrWhiteSpace(NickName)) return;
try
{
// Appel de la méthode publique 'Nick' du Hub (void)
await _connection.InvokeAsync("Nick", NickName);
Messages.Add($"Demande de changement de pseudo vers '{NickName}' envoyée.");
}
catch (Exception ex)
{
Messages.Add($"Erreur Nick: {ex.Message}");
}
}
}
}