mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 00:26:07 +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 string? _currentStatusMessage;
|
||||
private string _currentUsername = string.Empty;
|
||||
private readonly HashSet<string> _joinedChannels = [];
|
||||
|
||||
private bool IsConnected => _connection is not null && _connection.IsConnected;
|
||||
private bool IsAuthenticated => _apiClient is not null;
|
||||
@@ -161,6 +162,7 @@ public sealed class AppOrchestrator : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
_joinedChannels.Add(channelName);
|
||||
var history = await _connection!.JoinChannelAsync(channelName);
|
||||
InvokeUI(() =>
|
||||
{
|
||||
@@ -185,6 +187,7 @@ public sealed class AppOrchestrator : IDisposable
|
||||
try
|
||||
{
|
||||
await _connection!.LeaveChannelAsync(channel);
|
||||
_joinedChannels.Remove(channel);
|
||||
InvokeUI(() => _mainWindow.AddSystemMessage(channel, $"You left #{channel}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -294,6 +297,8 @@ public sealed class AppOrchestrator : IDisposable
|
||||
_mainWindow.UpdateStatusBar("Connected");
|
||||
});
|
||||
|
||||
_joinedChannels.Clear();
|
||||
_joinedChannels.Add(HubConstants.DefaultChannel);
|
||||
await _connection.JoinChannelAsync(HubConstants.DefaultChannel);
|
||||
InvokeUI(() => _mainWindow.SwitchToChannel(HubConstants.DefaultChannel));
|
||||
|
||||
@@ -330,6 +335,7 @@ public sealed class AppOrchestrator : IDisposable
|
||||
|
||||
_apiClient?.Dispose();
|
||||
_apiClient = null;
|
||||
_joinedChannels.Clear();
|
||||
|
||||
InvokeUI(() =>
|
||||
{
|
||||
@@ -377,11 +383,12 @@ public sealed class AppOrchestrator : IDisposable
|
||||
|
||||
RunAsync(async () =>
|
||||
{
|
||||
if (_joinedChannels.Add(channelName))
|
||||
await _connection!.JoinChannelAsync(channelName);
|
||||
|
||||
try
|
||||
{
|
||||
var history = await _connection.GetHistoryAsync(channelName);
|
||||
var history = await _connection!.GetHistoryAsync(channelName);
|
||||
InvokeUI(() => _mainWindow.LoadHistory(channelName, history));
|
||||
}
|
||||
catch
|
||||
@@ -530,6 +537,7 @@ public sealed class AppOrchestrator : IDisposable
|
||||
var channel = await _apiClient!.CreateChannelAsync(result.Name, result.Topic);
|
||||
if (channel is null) return;
|
||||
|
||||
_joinedChannels.Add(channel.Name);
|
||||
var history = await _connection!.JoinChannelAsync(channel.Name);
|
||||
|
||||
// Refresh the channel list
|
||||
@@ -579,13 +587,19 @@ public sealed class AppOrchestrator : IDisposable
|
||||
|
||||
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;
|
||||
|
||||
_joinedChannels.Clear();
|
||||
|
||||
RunAsync(async () =>
|
||||
{
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
_joinedChannels.Add(channel);
|
||||
await _connection!.JoinChannelAsync(channel);
|
||||
}
|
||||
|
||||
Log.Information("Rejoined {Count} channel(s) after reconnect", channels.Count);
|
||||
}, "Failed to rejoin channels after reconnect");
|
||||
|
||||
@@ -544,18 +544,12 @@ public sealed class MainWindow : Runnable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load historical messages into a channel (prepend).
|
||||
/// Load historical messages into a channel, replacing any existing messages.
|
||||
/// </summary>
|
||||
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();
|
||||
existing.InsertRange(0, formatted);
|
||||
_channelMessages[channelName] = formatted;
|
||||
|
||||
if (channelName == _currentChannel)
|
||||
{
|
||||
|
||||
@@ -112,12 +112,14 @@ public class ChatHub(EchoHubDbContext db, ILogger<ChatHub> logger, PresenceTrack
|
||||
return [];
|
||||
}
|
||||
|
||||
presenceTracker.JoinChannel(CurrentUsername, channelName);
|
||||
var isNewJoin = presenceTracker.JoinChannel(CurrentUsername, channelName);
|
||||
|
||||
if (isNewJoin)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, channelName);
|
||||
await Clients.OthersInGroup(channelName).UserJoined(channelName, CurrentUsername);
|
||||
|
||||
logger.LogInformation("{User} joined channel '{Channel}'", CurrentUsername, channelName);
|
||||
}
|
||||
|
||||
var history = await GetChannelHistory(channelName, HubConstants.DefaultHistoryCount);
|
||||
return history;
|
||||
@@ -293,11 +295,10 @@ public class ChatHub(EchoHubDbContext db, ILogger<ChatHub> logger, PresenceTrack
|
||||
statusMessage);
|
||||
|
||||
var channels = presenceTracker.GetChannelsForUser(CurrentUsername);
|
||||
var connections = presenceTracker.GetConnectionsInChannels(channels);
|
||||
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
await Clients.Group(channel).UserStatusChanged(presence);
|
||||
}
|
||||
if (connections.Count > 0)
|
||||
await Clients.Clients(connections).UserStatusChanged(presence);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -50,7 +50,10 @@ public class PresenceTracker
|
||||
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)
|
||||
{
|
||||
@@ -60,7 +63,7 @@ public class PresenceTracker
|
||||
_userChannels[username] = channels;
|
||||
}
|
||||
|
||||
channels.Add(channelName);
|
||||
return channels.Add(channelName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +109,34 @@ public class PresenceTracker
|
||||
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)
|
||||
{
|
||||
return _userConnections.TryGetValue(username, out var connections) && connections.Count > 0;
|
||||
|
||||
Reference in New Issue
Block a user