mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-07 07:36:01 +02:00
feat: Manage joined channels in AppOrchestrator and update channel history handling
This commit is contained in:
@@ -28,6 +28,7 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
private UserStatus _currentStatus = UserStatus.Online;
|
private UserStatus _currentStatus = UserStatus.Online;
|
||||||
private string? _currentStatusMessage;
|
private string? _currentStatusMessage;
|
||||||
private string _currentUsername = string.Empty;
|
private string _currentUsername = string.Empty;
|
||||||
|
private readonly HashSet<string> _joinedChannels = [];
|
||||||
|
|
||||||
private bool IsConnected => _connection is not null && _connection.IsConnected;
|
private bool IsConnected => _connection is not null && _connection.IsConnected;
|
||||||
private bool IsAuthenticated => _apiClient is not null;
|
private bool IsAuthenticated => _apiClient is not null;
|
||||||
@@ -161,6 +162,7 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_joinedChannels.Add(channelName);
|
||||||
var history = await _connection!.JoinChannelAsync(channelName);
|
var history = await _connection!.JoinChannelAsync(channelName);
|
||||||
InvokeUI(() =>
|
InvokeUI(() =>
|
||||||
{
|
{
|
||||||
@@ -185,6 +187,7 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _connection!.LeaveChannelAsync(channel);
|
await _connection!.LeaveChannelAsync(channel);
|
||||||
|
_joinedChannels.Remove(channel);
|
||||||
InvokeUI(() => _mainWindow.AddSystemMessage(channel, $"You left #{channel}"));
|
InvokeUI(() => _mainWindow.AddSystemMessage(channel, $"You left #{channel}"));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -294,6 +297,8 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
_mainWindow.UpdateStatusBar("Connected");
|
_mainWindow.UpdateStatusBar("Connected");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_joinedChannels.Clear();
|
||||||
|
_joinedChannels.Add(HubConstants.DefaultChannel);
|
||||||
await _connection.JoinChannelAsync(HubConstants.DefaultChannel);
|
await _connection.JoinChannelAsync(HubConstants.DefaultChannel);
|
||||||
InvokeUI(() => _mainWindow.SwitchToChannel(HubConstants.DefaultChannel));
|
InvokeUI(() => _mainWindow.SwitchToChannel(HubConstants.DefaultChannel));
|
||||||
|
|
||||||
@@ -330,6 +335,7 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
|
|
||||||
_apiClient?.Dispose();
|
_apiClient?.Dispose();
|
||||||
_apiClient = null;
|
_apiClient = null;
|
||||||
|
_joinedChannels.Clear();
|
||||||
|
|
||||||
InvokeUI(() =>
|
InvokeUI(() =>
|
||||||
{
|
{
|
||||||
@@ -377,11 +383,12 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
|
|
||||||
RunAsync(async () =>
|
RunAsync(async () =>
|
||||||
{
|
{
|
||||||
await _connection!.JoinChannelAsync(channelName);
|
if (_joinedChannels.Add(channelName))
|
||||||
|
await _connection!.JoinChannelAsync(channelName);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var history = await _connection.GetHistoryAsync(channelName);
|
var history = await _connection!.GetHistoryAsync(channelName);
|
||||||
InvokeUI(() => _mainWindow.LoadHistory(channelName, history));
|
InvokeUI(() => _mainWindow.LoadHistory(channelName, history));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -530,6 +537,7 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
var channel = await _apiClient!.CreateChannelAsync(result.Name, result.Topic);
|
var channel = await _apiClient!.CreateChannelAsync(result.Name, result.Topic);
|
||||||
if (channel is null) return;
|
if (channel is null) return;
|
||||||
|
|
||||||
|
_joinedChannels.Add(channel.Name);
|
||||||
var history = await _connection!.JoinChannelAsync(channel.Name);
|
var history = await _connection!.JoinChannelAsync(channel.Name);
|
||||||
|
|
||||||
// Refresh the channel list
|
// Refresh the channel list
|
||||||
@@ -579,13 +587,19 @@ public sealed class AppOrchestrator : IDisposable
|
|||||||
|
|
||||||
connection.OnReconnected += () =>
|
connection.OnReconnected += () =>
|
||||||
{
|
{
|
||||||
var channels = _mainWindow.GetChannelNames();
|
// Server-side state is lost on reconnect — rejoin all channels
|
||||||
|
var channels = _joinedChannels.ToList();
|
||||||
if (channels.Count == 0) return;
|
if (channels.Count == 0) return;
|
||||||
|
|
||||||
|
_joinedChannels.Clear();
|
||||||
|
|
||||||
RunAsync(async () =>
|
RunAsync(async () =>
|
||||||
{
|
{
|
||||||
foreach (var channel in channels)
|
foreach (var channel in channels)
|
||||||
|
{
|
||||||
|
_joinedChannels.Add(channel);
|
||||||
await _connection!.JoinChannelAsync(channel);
|
await _connection!.JoinChannelAsync(channel);
|
||||||
|
}
|
||||||
|
|
||||||
Log.Information("Rejoined {Count} channel(s) after reconnect", channels.Count);
|
Log.Information("Rejoined {Count} channel(s) after reconnect", channels.Count);
|
||||||
}, "Failed to rejoin channels after reconnect");
|
}, "Failed to rejoin channels after reconnect");
|
||||||
|
|||||||
@@ -544,18 +544,12 @@ public sealed class MainWindow : Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load historical messages into a channel (prepend).
|
/// Load historical messages into a channel, replacing any existing messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadHistory(string channelName, List<MessageDto> messages)
|
public void LoadHistory(string channelName, List<MessageDto> messages)
|
||||||
{
|
{
|
||||||
if (!_channelMessages.TryGetValue(channelName, out var existing))
|
|
||||||
{
|
|
||||||
existing = [];
|
|
||||||
_channelMessages[channelName] = existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatted = messages.SelectMany(FormatMessage).ToList();
|
var formatted = messages.SelectMany(FormatMessage).ToList();
|
||||||
existing.InsertRange(0, formatted);
|
_channelMessages[channelName] = formatted;
|
||||||
|
|
||||||
if (channelName == _currentChannel)
|
if (channelName == _currentChannel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,12 +112,14 @@ public class ChatHub(EchoHubDbContext db, ILogger<ChatHub> logger, PresenceTrack
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
presenceTracker.JoinChannel(CurrentUsername, channelName);
|
var isNewJoin = presenceTracker.JoinChannel(CurrentUsername, channelName);
|
||||||
|
|
||||||
await Groups.AddToGroupAsync(Context.ConnectionId, channelName);
|
if (isNewJoin)
|
||||||
await Clients.OthersInGroup(channelName).UserJoined(channelName, CurrentUsername);
|
{
|
||||||
|
await Groups.AddToGroupAsync(Context.ConnectionId, channelName);
|
||||||
logger.LogInformation("{User} joined channel '{Channel}'", CurrentUsername, channelName);
|
await Clients.OthersInGroup(channelName).UserJoined(channelName, CurrentUsername);
|
||||||
|
logger.LogInformation("{User} joined channel '{Channel}'", CurrentUsername, channelName);
|
||||||
|
}
|
||||||
|
|
||||||
var history = await GetChannelHistory(channelName, HubConstants.DefaultHistoryCount);
|
var history = await GetChannelHistory(channelName, HubConstants.DefaultHistoryCount);
|
||||||
return history;
|
return history;
|
||||||
@@ -293,11 +295,10 @@ public class ChatHub(EchoHubDbContext db, ILogger<ChatHub> logger, PresenceTrack
|
|||||||
statusMessage);
|
statusMessage);
|
||||||
|
|
||||||
var channels = presenceTracker.GetChannelsForUser(CurrentUsername);
|
var channels = presenceTracker.GetChannelsForUser(CurrentUsername);
|
||||||
|
var connections = presenceTracker.GetConnectionsInChannels(channels);
|
||||||
|
|
||||||
foreach (var channel in channels)
|
if (connections.Count > 0)
|
||||||
{
|
await Clients.Clients(connections).UserStatusChanged(presence);
|
||||||
await Clients.Group(channel).UserStatusChanged(presence);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ public class PresenceTracker
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void JoinChannel(string username, string channelName)
|
/// <summary>
|
||||||
|
/// Returns true if this is a new join, false if the user was already in the channel.
|
||||||
|
/// </summary>
|
||||||
|
public bool JoinChannel(string username, string channelName)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -60,7 +63,7 @@ public class PresenceTracker
|
|||||||
_userChannels[username] = channels;
|
_userChannels[username] = channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
channels.Add(channelName);
|
return channels.Add(channelName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +109,34 @@ public class PresenceTracker
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all unique connection IDs for users who share any of the given channels.
|
||||||
|
/// </summary>
|
||||||
|
public List<string> GetConnectionsInChannels(List<string> channels)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
var usernames = new HashSet<string>();
|
||||||
|
foreach (var channel in channels)
|
||||||
|
{
|
||||||
|
foreach (var (username, userChannels) in _userChannels)
|
||||||
|
{
|
||||||
|
if (userChannels.Contains(channel))
|
||||||
|
usernames.Add(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var connections = new List<string>();
|
||||||
|
foreach (var username in usernames)
|
||||||
|
{
|
||||||
|
if (_userConnections.TryGetValue(username, out var conns))
|
||||||
|
connections.AddRange(conns);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsOnline(string username)
|
public bool IsOnline(string username)
|
||||||
{
|
{
|
||||||
return _userConnections.TryGetValue(username, out var connections) && connections.Count > 0;
|
return _userConnections.TryGetValue(username, out var connections) && connections.Count > 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user