fix(chat): KeyNotFoundException dans HubConnectionManager.OnConnected
ChatUserNames n'etait jamais alimente (relique d'un SetUserName disparu),
ce qui faisait lever KeyNotFoundException a chaque connexion SignalR et
fermait immediatement la connexion cote client ("Déconnecté (The server
closed the connection...)" dans PostIt).
- IConnexionManager.OnConnected prend desormais le userName en parametre
- HubConnectionManager.OnConnected alimente ChatUserNames et ChatCxIds
- ChatHub.OnConnectedAsync passe le userName resolu depuis les claims
Inclut aussi les changements PostIt lies (bandeau de statut avec
commande d'action, handlers Reconnecting/Reconnected/Closed).
This commit is contained in:
parent
b22a26f61e
commit
75bb2600b5
6 changed files with 70 additions and 16 deletions
|
|
@ -18,6 +18,7 @@
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center"/>
|
||||||
<Button Content="{Binding CheckCommandLabel}"
|
<Button Content="{Binding CheckCommandLabel}"
|
||||||
|
Foreground="{Binding Foreground}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
IsVisible="{Binding HasCheckCommand}"
|
IsVisible="{Binding HasCheckCommand}"
|
||||||
Command="{Binding CheckCommand}"
|
Command="{Binding CheckCommand}"
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,41 @@ namespace PostIt.ViewModels.Chat
|
||||||
await _connection.StartAsync();
|
await _connection.StartAsync();
|
||||||
IsConnected = true;
|
IsConnected = true;
|
||||||
Status = new StatusNotice("Connecté au Hub SignalR", StatusSeverity.Info, new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
|
Status = new StatusNotice("Connecté au Hub SignalR", StatusSeverity.Info, new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
|
||||||
|
|
||||||
|
// WithAutomaticReconnect ne lève pas d'exception en cas de
|
||||||
|
// perte réseau : l'état passe par Reconnecting/Closed. Sans
|
||||||
|
// ces handlers, IsConnected resterait optimiste et Join()
|
||||||
|
// échouerait avec "connection is not active".
|
||||||
|
_connection.Reconnecting += error =>
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
IsConnected = false;
|
||||||
|
Status = new StatusNotice($"Connexion perdue ({error?.Message ?? "réseau"}), reconnexion...", StatusSeverity.Error,
|
||||||
|
new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
_connection.Reconnected += _ =>
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
IsConnected = true;
|
||||||
|
Status = new StatusNotice("Reconnecté au Hub SignalR", StatusSeverity.Info,
|
||||||
|
new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
_connection.Closed += error =>
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
IsConnected = false;
|
||||||
|
Status = new StatusNotice($"Déconnecté ({error?.Message ?? "fin de connexion"})", StatusSeverity.Info,
|
||||||
|
new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -156,15 +191,34 @@ namespace PostIt.ViewModels.Chat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckConnectionAsync()
|
private Task CheckConnectionAsync()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var state = _connection?.State.ToString() ?? "aucune connexion";
|
||||||
|
Messages.Add($"État de la connexion : {state}");
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task JoinRoomAsync()
|
private async Task JoinRoomAsync()
|
||||||
{
|
{
|
||||||
if (_connection == null || string.IsNullOrWhiteSpace(CurrentRoom)) return;
|
if (_connection == null || string.IsNullOrWhiteSpace(CurrentRoom)) return;
|
||||||
|
|
||||||
|
// InvokeCoreAsync exige une connexion active : avec la
|
||||||
|
// reconnexion automatique, l'état peut être Reconnecting au
|
||||||
|
// moment de l'appel. On tente une (re)connexion si nécessaire.
|
||||||
|
if (_connection.State != HubConnectionState.Connected)
|
||||||
|
{
|
||||||
|
Messages.Add($"Connexion non active ({_connection.State}), tentative de reconnexion...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _connection.StartAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Messages.Add($"Reconnexion impossible : {ex.Message}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Appel de la méthode publique 'Join' du Hub (renvoie un ChatRoomInfo)
|
// Appel de la méthode publique 'Join' du Hub (renvoie un ChatRoomInfo)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public sealed class StatusNotice
|
||||||
public string BorderBrush { get; }
|
public string BorderBrush { get; }
|
||||||
public string Foreground { get; }
|
public string Foreground { get; }
|
||||||
|
|
||||||
public bool HasCheckCommand { get; }
|
public bool HasCheckCommand { get => CheckCommand != null; }
|
||||||
|
|
||||||
public ICommand? CheckCommand { get; }
|
public ICommand? CheckCommand { get; }
|
||||||
|
|
||||||
|
|
@ -29,12 +29,9 @@ public sealed class StatusNotice
|
||||||
StatusSeverity.Warning => ("~", "#7C4A03", "#E6A700", "#eaeaea"),
|
StatusSeverity.Warning => ("~", "#7C4A03", "#E6A700", "#eaeaea"),
|
||||||
_ => ("i", "#E8F0FE", "#5B8DEF", "#1E3A8A"),
|
_ => ("i", "#E8F0FE", "#5B8DEF", "#1E3A8A"),
|
||||||
};
|
};
|
||||||
HasCheckCommand = checkCommand != null;
|
|
||||||
CheckCommand = checkCommand;
|
CheckCommand = checkCommand;
|
||||||
CheckCommandLabel = HasCheckCommand ? "Check" :
|
CheckCommandLabel = HasCheckCommand ?
|
||||||
checkCommand == null ?
|
(checkCommand is IWithLabel labeled ? labeled.Label : "Check") : string.Empty;
|
||||||
string.Empty :
|
|
||||||
checkCommand is IWithLabel labeled ? labeled.Label : string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StatusNotice Info(string message, ICommand? checkCommand = null) => new(message, StatusSeverity.Info, checkCommand);
|
public static StatusNotice Info(string message, ICommand? checkCommand = null) => new(message, StatusSeverity.Info, checkCommand);
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace Yavsc.Server.Hubs
|
||||||
{
|
{
|
||||||
await Groups.AddToGroupAsync(Context.ConnectionId, ChatHubConstants.HubGroupFollowingPrefix + uid);
|
await Groups.AddToGroupAsync(Context.ConnectionId, ChatHubConstants.HubGroupFollowingPrefix + uid);
|
||||||
}
|
}
|
||||||
_cxManager.OnConnected(Context.ConnectionId, isCop);
|
_cxManager.OnConnected(Context.ConnectionId, userName, isCop);
|
||||||
await base.OnConnectedAsync();
|
await base.OnConnectedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ namespace Yavsc.Services
|
||||||
{
|
{
|
||||||
public interface IConnexionManager {
|
public interface IConnexionManager {
|
||||||
string GetUserName (string cxId);
|
string GetUserName (string cxId);
|
||||||
void OnConnected(string cxId, bool isCop);
|
void OnConnected(string cxId, string userName, bool isCop);
|
||||||
bool IsConnected(string candidate);
|
bool IsConnected(string candidate);
|
||||||
|
|
||||||
void OnDisconnected (string cxId);
|
void OnDisconnected (string cxId);
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,15 @@ namespace Yavsc.Services
|
||||||
_localizer = stringLocFactory.Create(typeof(HubConnectionManager));
|
_localizer = stringLocFactory.Create(typeof(HubConnectionManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Username must have been set before calling this method.
|
public void OnConnected(string cxId, string userName, bool isCop)
|
||||||
public void OnConnected(string cxId, bool isCop)
|
|
||||||
{
|
{
|
||||||
var username = ChatUserNames[cxId];
|
ChatUserNames[cxId] = userName;
|
||||||
if (!IsConnected(username))
|
ChatCxIds.AddOrUpdate(userName,
|
||||||
ChatRoomPresence[username] = new List<string>();
|
_ => new List<string> { cxId },
|
||||||
_isCop[username] = isCop;
|
(_, list) => { lock (list) { if (!list.Contains(cxId)) list.Add(cxId); } return list; });
|
||||||
|
if (!IsConnected(userName))
|
||||||
|
ChatRoomPresence[userName] = new List<string>();
|
||||||
|
_isCop[userName] = isCop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsConnected(string candidate)
|
public bool IsConnected(string candidate)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue