From 107152298e9aadfa00a47a36d1fe1fc00467c100 Mon Sep 17 00:00:00 2001 From: HueByte Date: Thu, 19 Feb 2026 22:09:54 +0100 Subject: [PATCH] feat: add force disconnect handling and improve user ban notifications --- src/EchoHub.Client/AppOrchestrator.cs | 22 +++++++++++----- .../Services/EchoHubConnection.cs | 6 +++++ src/EchoHub.Client/UI/MainWindow.cs | 26 +++++++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/EchoHub.Client/AppOrchestrator.cs b/src/EchoHub.Client/AppOrchestrator.cs index 4c90e97..64b226f 100644 --- a/src/EchoHub.Client/AppOrchestrator.cs +++ b/src/EchoHub.Client/AppOrchestrator.cs @@ -830,25 +830,33 @@ public sealed class AppOrchestrator : IDisposable InvokeUI(() => { _mainWindow.AddSystemMessage(channelName, $"{username} was kicked{reasonText}"); - if (username.Equals(_currentUsername, StringComparison.OrdinalIgnoreCase)) - { - _mainWindow.AddSystemMessage(channelName, "You were kicked from this channel."); - } }); }; connection.OnUserBanned += (username, reason) => { + var reasonText = reason is not null ? $" ({reason})" : ""; InvokeUI(() => { - if (username.Equals(_currentUsername, StringComparison.OrdinalIgnoreCase)) + // Show ban notification for other users in the channel + if (!username.Equals(_currentUsername, StringComparison.OrdinalIgnoreCase)) { - _mainWindow.ShowError("You have been banned from this server."); - HandleDisconnect(); + var channel = _mainWindow.CurrentChannel; + if (!string.IsNullOrEmpty(channel)) + _mainWindow.AddSystemMessage(channel, $"{username} was banned{reasonText}"); } }); }; + connection.OnForceDisconnect += reason => + { + InvokeUI(() => + { + _mainWindow.ShowError(reason); + HandleDisconnect(); + }); + }; + connection.OnMessageDeleted += (channelName, messageId) => { InvokeUI(() => diff --git a/src/EchoHub.Client/Services/EchoHubConnection.cs b/src/EchoHub.Client/Services/EchoHubConnection.cs index 5d15d20..3ee18fd 100644 --- a/src/EchoHub.Client/Services/EchoHubConnection.cs +++ b/src/EchoHub.Client/Services/EchoHubConnection.cs @@ -18,6 +18,7 @@ public sealed class EchoHubConnection : IAsyncDisposable public event Action? OnUserBanned; public event Action? OnMessageDeleted; public event Action? OnChannelNuked; + public event Action? OnForceDisconnect; public event Action? OnError; public event Action? OnConnectionStateChanged; public event Action? OnReconnected; @@ -105,6 +106,11 @@ public sealed class EchoHubConnection : IAsyncDisposable OnChannelNuked?.Invoke(channelName); }); + _connection.On(nameof(Core.Contracts.IEchoHubClient.ForceDisconnect), reason => + { + OnForceDisconnect?.Invoke(reason); + }); + _connection.On(nameof(Core.Contracts.IEchoHubClient.Error), message => { OnError?.Invoke(message); diff --git a/src/EchoHub.Client/UI/MainWindow.cs b/src/EchoHub.Client/UI/MainWindow.cs index 1032d5c..bf5f22b 100644 --- a/src/EchoHub.Client/UI/MainWindow.cs +++ b/src/EchoHub.Client/UI/MainWindow.cs @@ -197,6 +197,7 @@ public sealed class MainWindow : Runnable WordWrap = true }; _inputField.KeyDown += OnInputKeyDown; + _inputField.ContentsChanged += OnInputContentsChanged; _inputFrame.Add(_inputField); Add(_inputFrame); @@ -388,6 +389,31 @@ public sealed class MainWindow : Runnable } } + private bool _suppressEmojiReplace; + + private void OnInputContentsChanged(object? sender, ContentsChangedEventArgs e) + { + if (_suppressEmojiReplace) + return; + + var text = _inputField.Text; + if (string.IsNullOrEmpty(text)) + return; + + var replaced = EmojiHelper.ReplaceEmoji(text); + if (replaced == text) + return; + + // Calculate where cursor should land after replacement + var lengthDelta = replaced.Length - text.Length; + var newCol = Math.Max(0, _inputField.CurrentColumn + lengthDelta); + + _suppressEmojiReplace = true; + _inputField.Text = replaced; + _inputField.InsertionPoint = new System.Drawing.Point(newCol, _inputField.CurrentRow); + _suppressEmojiReplace = false; + } + /// /// Tab-complete slash commands in the input field. ///