Add option to remove server and disable input TextBox on channels of disconnected servers

This commit is contained in:
Stone_Red
2026-07-15 23:50:02 +02:00
parent d343eb4043
commit 31a2d874ef
8 changed files with 137 additions and 38 deletions
+30 -1
View File
@@ -137,16 +137,45 @@ public sealed class ConnectionService : IDisposable
entry.Server.IsConnected = false;
entry.Server.IsConnecting = false;
ServerStateChanged?.Invoke(entry.Server);
await entry.Manager.CleanupAsync();
entry.ApiClient.Dispose();
await entry.Manager.DisposeAsync();
_ = _connections.Remove(serverUrl);
ServerStateChanged?.Invoke(entry.Server);
}
public async Task RemoveServerAsync(string serverUrl)
{
if (_connections.TryGetValue(serverUrl, out ServerConnection? entry))
{
entry.Server.IsConnected = false;
entry.Server.IsConnecting = false;
await entry.Manager.CleanupAsync();
entry.ApiClient.Dispose();
await entry.Manager.DisposeAsync();
_ = _connections.Remove(serverUrl);
}
RemoveServerFromConfig(serverUrl);
ServerRemoved?.Invoke(serverUrl);
}
private static void RemoveServerFromConfig(string serverUrl)
{
ClientConfig config = ConfigManager.Load();
SavedServer? saved = config.SavedServers.FirstOrDefault(s =>
string.Equals(s.Url, serverUrl, StringComparison.OrdinalIgnoreCase));
if (saved is not null)
{
_ = config.SavedServers.Remove(saved);
ConfigManager.Save(config);
}
}
public async Task SendMessageAsync(string serverUrl, string channelName, string content)
{
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
+3 -10
View File
@@ -1,5 +1,3 @@
using EchoHub.Client.Commands;
using System.Collections.ObjectModel;
namespace Decho.ViewModels;
@@ -45,7 +43,7 @@ public sealed class ChatViewModel : ViewModelBase
Composer = new MessageComposerViewModel();
}
public void SetChannel(ChannelViewModel? channel, string serverUrl = "")
public void SetChannel(ChannelViewModel? channel, string serverUrl = "", bool isServerConnected = true)
{
if (channel is null)
{
@@ -55,7 +53,7 @@ public sealed class ChatViewModel : ViewModelBase
HasTopic = false;
CurrentChannelName = string.Empty;
CurrentServerUrl = string.Empty;
Composer.SetServer(string.Empty);
Composer.SetServer(string.Empty, isServerConnected);
return;
}
@@ -65,12 +63,7 @@ public sealed class ChatViewModel : ViewModelBase
HasTopic = channel.HasTopic;
CurrentChannelName = channel.Name;
CurrentServerUrl = serverUrl;
Composer.SetServer(serverUrl);
}
public void SetComposerCommandHandler(CommandHandler handler)
{
Composer.SetCommandHandler(handler);
Composer.SetServer(serverUrl, isServerConnected);
}
public void ClearMessages()
+28 -2
View File
@@ -79,6 +79,7 @@ public sealed class MainWindowViewModel : ViewModelBase
ServerViewModel serverVm = new ServerViewModel(placeholderServer);
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm);
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm);
serverVm.RemoveRequested += async () => await HandleServerRemoveRequested(serverVm);
Sidebar.Servers.Add(serverVm);
StatusText = "Click Connect on the server to get started";
@@ -352,6 +353,7 @@ public sealed class MainWindowViewModel : ViewModelBase
ServerViewModel serverVm = new ServerViewModel(server);
serverVm.ConnectRequested += () => HandleServerConnectRequested(serverVm);
serverVm.DisconnectRequested += () => HandleServerDisconnectRequested(serverVm);
serverVm.RemoveRequested += () => HandleServerRemoveRequested(serverVm);
_ = serverVm.WhenAnyValue(s => s.SelectedChannel)
.Where(channel => channel is not null)
.Subscribe(channel => Sidebar.SelectedChannel = channel!);
@@ -365,6 +367,11 @@ public sealed class MainWindowViewModel : ViewModelBase
}
Sidebar.Servers.Add(serverVm);
if (serverVm.Channels.Count > 0)
{
serverVm.SelectedChannel = serverVm.Channels[0];
}
StatusText = $"Connected to {server.Name}";
});
};
@@ -390,6 +397,11 @@ public sealed class MainWindowViewModel : ViewModelBase
if (serverVm is not null)
{
serverVm.SyncFromModel();
if (string.Equals(server.ServerUrl, Chat.CurrentServerUrl, StringComparison.OrdinalIgnoreCase))
{
Chat.Composer.IsConnected = server.IsConnected;
}
if (!server.IsConnected)
{
StatusText = $"Disconnected from {server.Name}";
@@ -493,11 +505,12 @@ public sealed class MainWindowViewModel : ViewModelBase
}
string serverUrl = FindServerUrlForChannel(channel);
Chat.SetChannel(channel, serverUrl);
bool isServerConnected = Sidebar.GetServer(serverUrl)?.IsConnected ?? false;
Chat.SetChannel(channel, serverUrl, isServerConnected);
if (!string.IsNullOrEmpty(serverUrl))
{
Chat.SetComposerCommandHandler(new CommandHandler());
Chat.Composer.SetCommandHandler(new CommandHandler());
_ = Task.Run(async () =>
{
@@ -538,6 +551,7 @@ public sealed class MainWindowViewModel : ViewModelBase
ServerViewModel serverVm = new ServerViewModel(placeholderServer);
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm);
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm);
serverVm.RemoveRequested += async () => await HandleServerRemoveRequested(serverVm);
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
@@ -642,6 +656,18 @@ public sealed class MainWindowViewModel : ViewModelBase
}
}
private async Task HandleServerRemoveRequested(ServerViewModel serverVm)
{
try
{
await ConnectionService.RemoveServerAsync(serverVm.ServerUrl);
}
catch (Exception ex)
{
StatusText = $"Remove error: {ex.Message}";
}
}
private void EnsureChannelInList(string serverUrl, string channelName)
{
ServerViewModel? serverVm = Sidebar.GetServer(serverUrl);
@@ -22,6 +22,12 @@ public sealed class MessageComposerViewModel : ViewModelBase
public string ServerUrl { get; private set; } = string.Empty;
public bool IsConnected
{
get;
set => this.RaiseAndSetIfChanged(ref field, value);
} = true;
public ReactiveCommand<Unit, Unit> SendCommand { get; }
public bool HasCommandHandler => _commandHandler is not null;
@@ -40,9 +46,10 @@ public sealed class MessageComposerViewModel : ViewModelBase
}
}
public void SetServer(string serverUrl)
public void SetServer(string serverUrl, bool isConnected = true)
{
ServerUrl = serverUrl;
IsConnected = isConnected;
}
public void SetCommandHandler(CommandHandler handler)
+19
View File
@@ -19,6 +19,12 @@ public sealed class ServerViewModel : ViewModelBase
remove => _disconnectRequested = (Func<Task>?)Delegate.Remove(_disconnectRequested, value);
}
public event Func<Task>? RemoveRequested
{
add => _removeRequested = (Func<Task>?)Delegate.Combine(_removeRequested, value);
remove => _removeRequested = (Func<Task>?)Delegate.Remove(_removeRequested, value);
}
private bool _isConnected;
private bool _isConnecting;
private string? _connectedUser;
@@ -27,6 +33,8 @@ public sealed class ServerViewModel : ViewModelBase
private Func<Task>? _disconnectRequested;
private Func<Task>? _removeRequested;
public ServerModel Model { get; }
public string Name => Model.Name;
@@ -39,6 +47,8 @@ public sealed class ServerViewModel : ViewModelBase
public ReactiveCommand<Unit, Unit> DisconnectCommand { get; }
public ReactiveCommand<Unit, Unit> RemoveCommand { get; }
public ChannelViewModel? SelectedChannel
{
get;
@@ -124,6 +134,7 @@ public sealed class ServerViewModel : ViewModelBase
ConnectCommand = ReactiveCommand.CreateFromTask(ConnectAsync);
DisconnectCommand = ReactiveCommand.CreateFromTask(DisconnectAsync);
RemoveCommand = ReactiveCommand.CreateFromTask(RemoveAsync);
}
public void SyncFromModel()
@@ -149,4 +160,12 @@ public sealed class ServerViewModel : ViewModelBase
await _disconnectRequested();
}
}
private async Task RemoveAsync()
{
if (_removeRequested is not null)
{
await _removeRequested();
}
}
}
+6 -3
View File
@@ -8,7 +8,8 @@
<TextBox Grid.Column="0"
HorizontalAlignment="Stretch"
Margin="0 0 5 0"
Text="{Binding Draft, Mode=TwoWay}">
Text="{Binding Draft, Mode=TwoWay}"
IsEnabled="{Binding IsConnected}">
<TextBox.KeyBindings>
<KeyBinding Command="{Binding SendCommand}" Gesture="Enter" />
</TextBox.KeyBindings>
@@ -19,11 +20,13 @@
Margin="0 0 5 0"
Content="Send"
i:Attached.Icon="fa-paper-plane"
Command="{Binding SendCommand}" />
Command="{Binding SendCommand}"
IsEnabled="{Binding IsConnected}" />
<Button Grid.Column="2"
Height="33"
Width="33"
Click="OnFileUploadClicked"
i:Attached.Icon="fa-paperclip" />
i:Attached.Icon="fa-paperclip"
IsEnabled="{Binding IsConnected}" />
</Grid>
</UserControl>
+34 -21
View File
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Decho.ViewModels"
xmlns:views="using:Decho.Views"
xmlns:i="https://github.com/projektanker/icons.avalonia"
x:Class="Decho.Views.SidebarView"
x:DataType="vm:SidebarViewModel">
<Border Background="{DynamicResource UiTheme00}" Width="250" CornerRadius="0 6 0 0">
@@ -14,22 +15,40 @@
HorizontalAlignment="Stretch"
IsExpanded="{Binding IsExpanded}">
<Expander.Header>
<StackPanel Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<Ellipse Width="8" Height="8"
Fill="{Binding ConnectionStatusColor}"
VerticalAlignment="Center" />
<TextBlock FontWeight="Bold"
TextTrimming="CharacterEllipsis"
MaxWidth="160"
Padding="0"
Text="{Binding Name}" />
<Grid ColumnDefinitions="*, Auto">
<StackPanel Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<Ellipse Width="8" Height="8"
Fill="{Binding ConnectionStatusColor}"
VerticalAlignment="Center" />
<TextBlock FontWeight="Bold"
TextTrimming="CharacterEllipsis"
MaxWidth="160"
Padding="0"
Text="{Binding Name}" />
</StackPanel>
<TextBlock FontSize="10"
Text="{Binding ConnectionStatusText}"
Opacity="0.6"
VerticalAlignment="Center" />
</StackPanel>
<TextBlock FontSize="10"
Text="{Binding ConnectionStatusText}"
Opacity="0.6"
VerticalAlignment="Center" />
</StackPanel>
<Button Grid.Column="1"
x:Name="SettingsButton"
Background="Transparent"
Foreground="{DynamicResource UiTheme08}"
VerticalAlignment="Center"
i:Attached.Icon="fa-cog"
Click="OnSettingsClicked">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Disconnect"
Command="{Binding DisconnectCommand}" />
<MenuItem Header="Remove Server"
Command="{Binding RemoveCommand}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
</Expander.Header>
<StackPanel>
<Button Content="Connect"
@@ -38,12 +57,6 @@
Margin="4"
Padding="5 2"
HorizontalAlignment="Stretch" />
<Button Content="Disconnect"
Command="{Binding DisconnectCommand}"
IsVisible="{Binding IsConnected}"
Margin="4"
Padding="5 2"
HorizontalAlignment="Stretch" />
<views:ChannelListView />
</StackPanel>
</Expander>
+9
View File
@@ -1,5 +1,6 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Decho.ViewModels;
@@ -22,4 +23,12 @@ public partial class SidebarView : UserControl
{
InitializeComponent();
}
private void OnSettingsClicked(object? sender, RoutedEventArgs e)
{
if (sender is Button button)
{
button.ContextMenu?.Open(button);
}
}
}