diff --git a/src/Decho/Services/ConnectionService.cs b/src/Decho/Services/ConnectionService.cs index 9a5a107..89ac898 100644 --- a/src/Decho/Services/ConnectionService.cs +++ b/src/Decho/Services/ConnectionService.cs @@ -49,6 +49,8 @@ public sealed class ConnectionService : IConnectionService public event Action? ChannelDeleted; + public event Action? Reconnected; + private readonly Dictionary _connections = new(StringComparer.OrdinalIgnoreCase); private readonly IConfigPersistenceService _config; private readonly IAttachmentService _attachment; @@ -395,5 +397,18 @@ public sealed class ConnectionService : IConnectionService entry.Server.IsConnecting = status is "Connecting..." or "Authenticating..." or "Reconnecting..."; ServerStateChanged?.Invoke(entry.Server); }; + + conn.Reconnected += async () => + { + try + { + await entry.Manager.RejoinChannelsAsync(); + Reconnected?.Invoke(entry.Server.ServerUrl); + } + catch (Exception ex) + { + Debug.WriteLine($"Rejoin failed: {ex.Message}"); + } + }; } } \ No newline at end of file diff --git a/src/Decho/Services/IConnectionService.cs b/src/Decho/Services/IConnectionService.cs index b1d1f52..e4bd2aa 100644 --- a/src/Decho/Services/IConnectionService.cs +++ b/src/Decho/Services/IConnectionService.cs @@ -17,6 +17,7 @@ public interface IConnectionService : IDisposable event Action? UserLeft; event Action? ErrorOccurred; event Action? ChannelDeleted; + event Action? Reconnected; Task ConnectAsync(string serverUrl, string username, string password, bool isRegister, bool rememberMe); Task ConnectWithSavedTokenAsync(string serverUrl, string username, string refreshToken, bool rememberMe); diff --git a/src/Decho/ViewModels/MainWindowViewModel.cs b/src/Decho/ViewModels/MainWindowViewModel.cs index 771cf40..73aae9b 100644 --- a/src/Decho/ViewModels/MainWindowViewModel.cs +++ b/src/Decho/ViewModels/MainWindowViewModel.cs @@ -20,6 +20,7 @@ using MsBox.Avalonia.Enums; using System.Diagnostics; using System.Reactive; using System.Reactive.Linq; +using System.Linq; namespace Decho.ViewModels; @@ -787,6 +788,11 @@ public sealed class MainWindowViewModel : ViewModelBase } }); }; + + ConnectionService.Reconnected += serverUrl => + { + _ = RefreshCurrentChannelAsync(serverUrl); + }; } private async Task HandleSendTextAsync(string text) @@ -824,6 +830,40 @@ public sealed class MainWindowViewModel : ViewModelBase } } + private async Task RefreshCurrentChannelAsync(string serverUrl) + { + if (!string.Equals(serverUrl, Chat.CurrentServerUrl, StringComparison.OrdinalIgnoreCase)) + return; + + string channelName = Chat.CurrentChannelName; + if (string.IsNullOrEmpty(channelName)) + return; + + ChannelViewModel? channel = FindChannelViewModel(serverUrl, channelName); + if (channel is null) + return; + + try + { + List latest = await ChannelService.GetHistoryAsync(serverUrl, channelName, HubConstants.DefaultHistoryCount, 0); + HashSet existingIds = channel.Messages.Select(m => m.Model.Id).ToHashSet(); + Avalonia.Threading.Dispatcher.UIThread.Post(() => + { + foreach (MessageModel msg in latest) + { + if (!existingIds.Contains(msg.Id)) + { + channel.AddMessage(msg); + } + } + }); + } + catch (Exception ex) + { + Debug.WriteLine($"RefreshCurrentChannel failed: {ex.Message}"); + } + } + private async Task HandleCommandAsync(string commandText) { CommandResult result = await _commandHandler.HandleAsync(commandText); @@ -1104,12 +1144,10 @@ public sealed class MainWindowViewModel : ViewModelBase Chat.Composer.IsReadOnly = false; } - if (!channel.Messages.Any(m => m.AuthorName != "System")) + HashSet existingIds = channel.Messages.Select(m => m.Model.Id).ToHashSet(); + foreach (var msg in joinResult.History.Where(msg => !existingIds.Contains(msg.Id))) { - foreach (MessageModel msg in joinResult.History) - { - channel.AddMessage(msg); - } + channel.AddMessage(msg); } });