mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 08:56:14 +02:00
Add option to remove server and disable input TextBox on channels of disconnected servers
This commit is contained in:
@@ -137,16 +137,45 @@ public sealed class ConnectionService : IDisposable
|
|||||||
|
|
||||||
entry.Server.IsConnected = false;
|
entry.Server.IsConnected = false;
|
||||||
entry.Server.IsConnecting = false;
|
entry.Server.IsConnecting = false;
|
||||||
ServerStateChanged?.Invoke(entry.Server);
|
|
||||||
|
|
||||||
await entry.Manager.CleanupAsync();
|
await entry.Manager.CleanupAsync();
|
||||||
entry.ApiClient.Dispose();
|
entry.ApiClient.Dispose();
|
||||||
await entry.Manager.DisposeAsync();
|
await entry.Manager.DisposeAsync();
|
||||||
|
|
||||||
_ = _connections.Remove(serverUrl);
|
_ = _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);
|
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)
|
public async Task SendMessageAsync(string serverUrl, string channelName, string content)
|
||||||
{
|
{
|
||||||
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
if (!_connections.TryGetValue(serverUrl, out ServerConnection? entry))
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using EchoHub.Client.Commands;
|
|
||||||
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Decho.ViewModels;
|
namespace Decho.ViewModels;
|
||||||
@@ -45,7 +43,7 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
Composer = new MessageComposerViewModel();
|
Composer = new MessageComposerViewModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetChannel(ChannelViewModel? channel, string serverUrl = "")
|
public void SetChannel(ChannelViewModel? channel, string serverUrl = "", bool isServerConnected = true)
|
||||||
{
|
{
|
||||||
if (channel is null)
|
if (channel is null)
|
||||||
{
|
{
|
||||||
@@ -55,7 +53,7 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
HasTopic = false;
|
HasTopic = false;
|
||||||
CurrentChannelName = string.Empty;
|
CurrentChannelName = string.Empty;
|
||||||
CurrentServerUrl = string.Empty;
|
CurrentServerUrl = string.Empty;
|
||||||
Composer.SetServer(string.Empty);
|
Composer.SetServer(string.Empty, isServerConnected);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,12 +63,7 @@ public sealed class ChatViewModel : ViewModelBase
|
|||||||
HasTopic = channel.HasTopic;
|
HasTopic = channel.HasTopic;
|
||||||
CurrentChannelName = channel.Name;
|
CurrentChannelName = channel.Name;
|
||||||
CurrentServerUrl = serverUrl;
|
CurrentServerUrl = serverUrl;
|
||||||
Composer.SetServer(serverUrl);
|
Composer.SetServer(serverUrl, isServerConnected);
|
||||||
}
|
|
||||||
|
|
||||||
public void SetComposerCommandHandler(CommandHandler handler)
|
|
||||||
{
|
|
||||||
Composer.SetCommandHandler(handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearMessages()
|
public void ClearMessages()
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
ServerViewModel serverVm = new ServerViewModel(placeholderServer);
|
ServerViewModel serverVm = new ServerViewModel(placeholderServer);
|
||||||
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm);
|
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm);
|
||||||
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm);
|
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm);
|
||||||
|
serverVm.RemoveRequested += async () => await HandleServerRemoveRequested(serverVm);
|
||||||
|
|
||||||
Sidebar.Servers.Add(serverVm);
|
Sidebar.Servers.Add(serverVm);
|
||||||
StatusText = "Click Connect on the server to get started";
|
StatusText = "Click Connect on the server to get started";
|
||||||
@@ -352,6 +353,7 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
ServerViewModel serverVm = new ServerViewModel(server);
|
ServerViewModel serverVm = new ServerViewModel(server);
|
||||||
serverVm.ConnectRequested += () => HandleServerConnectRequested(serverVm);
|
serverVm.ConnectRequested += () => HandleServerConnectRequested(serverVm);
|
||||||
serverVm.DisconnectRequested += () => HandleServerDisconnectRequested(serverVm);
|
serverVm.DisconnectRequested += () => HandleServerDisconnectRequested(serverVm);
|
||||||
|
serverVm.RemoveRequested += () => HandleServerRemoveRequested(serverVm);
|
||||||
_ = serverVm.WhenAnyValue(s => s.SelectedChannel)
|
_ = serverVm.WhenAnyValue(s => s.SelectedChannel)
|
||||||
.Where(channel => channel is not null)
|
.Where(channel => channel is not null)
|
||||||
.Subscribe(channel => Sidebar.SelectedChannel = channel!);
|
.Subscribe(channel => Sidebar.SelectedChannel = channel!);
|
||||||
@@ -365,6 +367,11 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
Sidebar.Servers.Add(serverVm);
|
Sidebar.Servers.Add(serverVm);
|
||||||
|
if (serverVm.Channels.Count > 0)
|
||||||
|
{
|
||||||
|
serverVm.SelectedChannel = serverVm.Channels[0];
|
||||||
|
}
|
||||||
|
|
||||||
StatusText = $"Connected to {server.Name}";
|
StatusText = $"Connected to {server.Name}";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -390,6 +397,11 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
if (serverVm is not null)
|
if (serverVm is not null)
|
||||||
{
|
{
|
||||||
serverVm.SyncFromModel();
|
serverVm.SyncFromModel();
|
||||||
|
if (string.Equals(server.ServerUrl, Chat.CurrentServerUrl, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Chat.Composer.IsConnected = server.IsConnected;
|
||||||
|
}
|
||||||
|
|
||||||
if (!server.IsConnected)
|
if (!server.IsConnected)
|
||||||
{
|
{
|
||||||
StatusText = $"Disconnected from {server.Name}";
|
StatusText = $"Disconnected from {server.Name}";
|
||||||
@@ -493,11 +505,12 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
string serverUrl = FindServerUrlForChannel(channel);
|
string serverUrl = FindServerUrlForChannel(channel);
|
||||||
Chat.SetChannel(channel, serverUrl);
|
bool isServerConnected = Sidebar.GetServer(serverUrl)?.IsConnected ?? false;
|
||||||
|
Chat.SetChannel(channel, serverUrl, isServerConnected);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(serverUrl))
|
if (!string.IsNullOrEmpty(serverUrl))
|
||||||
{
|
{
|
||||||
Chat.SetComposerCommandHandler(new CommandHandler());
|
Chat.Composer.SetCommandHandler(new CommandHandler());
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -538,6 +551,7 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
ServerViewModel serverVm = new ServerViewModel(placeholderServer);
|
ServerViewModel serverVm = new ServerViewModel(placeholderServer);
|
||||||
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm);
|
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm);
|
||||||
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm);
|
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm);
|
||||||
|
serverVm.RemoveRequested += async () => await HandleServerRemoveRequested(serverVm);
|
||||||
|
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
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)
|
private void EnsureChannelInList(string serverUrl, string channelName)
|
||||||
{
|
{
|
||||||
ServerViewModel? serverVm = Sidebar.GetServer(serverUrl);
|
ServerViewModel? serverVm = Sidebar.GetServer(serverUrl);
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ public sealed class MessageComposerViewModel : ViewModelBase
|
|||||||
|
|
||||||
public string ServerUrl { get; private set; } = string.Empty;
|
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 ReactiveCommand<Unit, Unit> SendCommand { get; }
|
||||||
|
|
||||||
public bool HasCommandHandler => _commandHandler is not null;
|
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;
|
ServerUrl = serverUrl;
|
||||||
|
IsConnected = isConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCommandHandler(CommandHandler handler)
|
public void SetCommandHandler(CommandHandler handler)
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ public sealed class ServerViewModel : ViewModelBase
|
|||||||
remove => _disconnectRequested = (Func<Task>?)Delegate.Remove(_disconnectRequested, value);
|
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 _isConnected;
|
||||||
private bool _isConnecting;
|
private bool _isConnecting;
|
||||||
private string? _connectedUser;
|
private string? _connectedUser;
|
||||||
@@ -27,6 +33,8 @@ public sealed class ServerViewModel : ViewModelBase
|
|||||||
|
|
||||||
private Func<Task>? _disconnectRequested;
|
private Func<Task>? _disconnectRequested;
|
||||||
|
|
||||||
|
private Func<Task>? _removeRequested;
|
||||||
|
|
||||||
public ServerModel Model { get; }
|
public ServerModel Model { get; }
|
||||||
|
|
||||||
public string Name => Model.Name;
|
public string Name => Model.Name;
|
||||||
@@ -39,6 +47,8 @@ public sealed class ServerViewModel : ViewModelBase
|
|||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> DisconnectCommand { get; }
|
public ReactiveCommand<Unit, Unit> DisconnectCommand { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> RemoveCommand { get; }
|
||||||
|
|
||||||
public ChannelViewModel? SelectedChannel
|
public ChannelViewModel? SelectedChannel
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -124,6 +134,7 @@ public sealed class ServerViewModel : ViewModelBase
|
|||||||
|
|
||||||
ConnectCommand = ReactiveCommand.CreateFromTask(ConnectAsync);
|
ConnectCommand = ReactiveCommand.CreateFromTask(ConnectAsync);
|
||||||
DisconnectCommand = ReactiveCommand.CreateFromTask(DisconnectAsync);
|
DisconnectCommand = ReactiveCommand.CreateFromTask(DisconnectAsync);
|
||||||
|
RemoveCommand = ReactiveCommand.CreateFromTask(RemoveAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SyncFromModel()
|
public void SyncFromModel()
|
||||||
@@ -149,4 +160,12 @@ public sealed class ServerViewModel : ViewModelBase
|
|||||||
await _disconnectRequested();
|
await _disconnectRequested();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RemoveAsync()
|
||||||
|
{
|
||||||
|
if (_removeRequested is not null)
|
||||||
|
{
|
||||||
|
await _removeRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
<TextBox Grid.Column="0"
|
<TextBox Grid.Column="0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Margin="0 0 5 0"
|
Margin="0 0 5 0"
|
||||||
Text="{Binding Draft, Mode=TwoWay}">
|
Text="{Binding Draft, Mode=TwoWay}"
|
||||||
|
IsEnabled="{Binding IsConnected}">
|
||||||
<TextBox.KeyBindings>
|
<TextBox.KeyBindings>
|
||||||
<KeyBinding Command="{Binding SendCommand}" Gesture="Enter" />
|
<KeyBinding Command="{Binding SendCommand}" Gesture="Enter" />
|
||||||
</TextBox.KeyBindings>
|
</TextBox.KeyBindings>
|
||||||
@@ -19,11 +20,13 @@
|
|||||||
Margin="0 0 5 0"
|
Margin="0 0 5 0"
|
||||||
Content="Send"
|
Content="Send"
|
||||||
i:Attached.Icon="fa-paper-plane"
|
i:Attached.Icon="fa-paper-plane"
|
||||||
Command="{Binding SendCommand}" />
|
Command="{Binding SendCommand}"
|
||||||
|
IsEnabled="{Binding IsConnected}" />
|
||||||
<Button Grid.Column="2"
|
<Button Grid.Column="2"
|
||||||
Height="33"
|
Height="33"
|
||||||
Width="33"
|
Width="33"
|
||||||
Click="OnFileUploadClicked"
|
Click="OnFileUploadClicked"
|
||||||
i:Attached.Icon="fa-paperclip" />
|
i:Attached.Icon="fa-paperclip"
|
||||||
|
IsEnabled="{Binding IsConnected}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:Decho.ViewModels"
|
xmlns:vm="using:Decho.ViewModels"
|
||||||
xmlns:views="using:Decho.Views"
|
xmlns:views="using:Decho.Views"
|
||||||
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
x:Class="Decho.Views.SidebarView"
|
x:Class="Decho.Views.SidebarView"
|
||||||
x:DataType="vm:SidebarViewModel">
|
x:DataType="vm:SidebarViewModel">
|
||||||
<Border Background="{DynamicResource UiTheme00}" Width="250" CornerRadius="0 6 0 0">
|
<Border Background="{DynamicResource UiTheme00}" Width="250" CornerRadius="0 6 0 0">
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
IsExpanded="{Binding IsExpanded}">
|
IsExpanded="{Binding IsExpanded}">
|
||||||
<Expander.Header>
|
<Expander.Header>
|
||||||
|
<Grid ColumnDefinitions="*, Auto">
|
||||||
<StackPanel Spacing="5">
|
<StackPanel Spacing="5">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
<Ellipse Width="8" Height="8"
|
<Ellipse Width="8" Height="8"
|
||||||
@@ -30,6 +32,23 @@
|
|||||||
Opacity="0.6"
|
Opacity="0.6"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
</StackPanel>
|
</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>
|
</Expander.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<Button Content="Connect"
|
<Button Content="Connect"
|
||||||
@@ -38,12 +57,6 @@
|
|||||||
Margin="4"
|
Margin="4"
|
||||||
Padding="5 2"
|
Padding="5 2"
|
||||||
HorizontalAlignment="Stretch" />
|
HorizontalAlignment="Stretch" />
|
||||||
<Button Content="Disconnect"
|
|
||||||
Command="{Binding DisconnectCommand}"
|
|
||||||
IsVisible="{Binding IsConnected}"
|
|
||||||
Margin="4"
|
|
||||||
Padding="5 2"
|
|
||||||
HorizontalAlignment="Stretch" />
|
|
||||||
<views:ChannelListView />
|
<views:ChannelListView />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
using Decho.ViewModels;
|
using Decho.ViewModels;
|
||||||
|
|
||||||
@@ -22,4 +23,12 @@ public partial class SidebarView : UserControl
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSettingsClicked(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Button button)
|
||||||
|
{
|
||||||
|
button.ContextMenu?.Open(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user