Add MessageBox.Avalonia and show error dialog on connection failure

This commit is contained in:
Stone_Red
2026-07-16 00:55:26 +02:00
parent 24b9e8ab71
commit 39c7fe4f48
3 changed files with 71 additions and 72 deletions
+1
View File
@@ -23,6 +23,7 @@
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets> <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" /> <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" />
<PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" /> <PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageReference Include="Romzetron.Avalonia" Version="11.3.4" /> <PackageReference Include="Romzetron.Avalonia" Version="11.3.4" />
+59 -42
View File
@@ -10,6 +10,9 @@ using EchoHub.Core.Constants;
using EchoHub.Core.DTOs; using EchoHub.Core.DTOs;
using EchoHub.Core.Models; using EchoHub.Core.Models;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
@@ -69,21 +72,61 @@ public sealed class MainWindowViewModel : ViewModelBase
ConnectionService.Dispose(); ConnectionService.Dispose();
} }
private void AddServer() private async void AddServer()
{ {
ServerModel placeholderServer = new ServerModel( if (_mainWindow is null)
Guid.NewGuid().ToString("N"), {
"New Server", return;
[], }
isConnected: false);
ServerViewModel serverVm = new ServerViewModel(placeholderServer); ClientConfig config = ConfigManager.Load();
serverVm.ConnectRequested += async () => await HandleServerConnectRequested(serverVm); ConnectDialogWindow dialog = new ConnectDialogWindow(config.SavedServers);
serverVm.DisconnectRequested += async () => await HandleServerDisconnectRequested(serverVm); ConnectDialogResult? result = await dialog.ShowDialog<ConnectDialogResult?>(_mainWindow);
serverVm.RemoveRequested += async () => await HandleServerRemoveRequested(serverVm); if (result is null)
{
return;
}
Sidebar.Servers.Add(serverVm); try
StatusText = "Click Connect on the server to get started"; {
await ConnectAndSaveAsync(result);
}
catch (Exception ex)
{
var box = MessageBoxManager.GetMessageBoxStandard(
"Connection Failed",
$"Could not connect to server:\n{ex.Message}",
ButtonEnum.Ok);
await box.ShowWindowDialogAsync(_mainWindow);
}
}
private async Task ConnectAndSaveAsync(ConnectDialogResult result)
{
StatusText = "Connecting...";
if (result.IsSavedSession && result.SavedRefreshToken is not null)
{
await ConnectionService.ConnectWithSavedTokenAsync(
result.ServerUrl, result.Username, result.SavedRefreshToken, result.RememberMe);
}
else
{
_ = await ConnectionService.ConnectAsync(
result.ServerUrl, result.Username, result.Password, result.IsRegister, result.RememberMe);
}
string? refreshToken = ConnectionService.GetRefreshToken(result.ServerUrl);
SavedServer savedServer = new SavedServer
{
Name = new Uri(result.ServerUrl).Host,
Url = result.ServerUrl,
Username = result.Username,
RefreshToken = result.RememberMe ? refreshToken : null,
RememberMe = result.RememberMe,
LastConnected = DateTimeOffset.Now,
};
ConfigManager.SaveServer(savedServer);
} }
private void WireCommandHandlerEvents() private void WireCommandHandlerEvents()
@@ -397,13 +440,15 @@ public sealed class MainWindowViewModel : ViewModelBase
Avalonia.Threading.Dispatcher.UIThread.Post(() => Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{ {
int insertIndex = Sidebar.Servers.Count;
ServerViewModel? existing = Sidebar.GetServer(server.ServerUrl); ServerViewModel? existing = Sidebar.GetServer(server.ServerUrl);
if (existing is not null) if (existing is not null)
{ {
insertIndex = Sidebar.Servers.IndexOf(existing);
_ = Sidebar.Servers.Remove(existing); _ = Sidebar.Servers.Remove(existing);
} }
Sidebar.Servers.Add(serverVm); Sidebar.Servers.Insert(insertIndex, serverVm);
if (serverVm.Channels.Count > 0) if (serverVm.Channels.Count > 0)
{ {
serverVm.SelectedChannel = serverVm.Channels[0]; serverVm.SelectedChannel = serverVm.Channels[0];
@@ -625,7 +670,6 @@ public sealed class MainWindowViewModel : ViewModelBase
private async Task HandleServerConnectRequested(ServerViewModel serverVm) private async Task HandleServerConnectRequested(ServerViewModel serverVm)
{ {
// Show connect dialog
if (_mainWindow is null) if (_mainWindow is null)
{ {
return; return;
@@ -644,34 +688,7 @@ public sealed class MainWindowViewModel : ViewModelBase
try try
{ {
serverVm.IsConnecting = true; serverVm.IsConnecting = true;
StatusText = "Connecting..."; await ConnectAndSaveAsync(result);
if (result.IsSavedSession && result.SavedRefreshToken is not null)
{
await ConnectionService.ConnectWithSavedTokenAsync(
result.ServerUrl, result.Username, result.SavedRefreshToken, result.RememberMe);
}
else
{
_ = await ConnectionService.ConnectAsync(
result.ServerUrl, result.Username, result.Password, result.IsRegister, result.RememberMe);
}
// Remove the placeholder and let the real server added event handle it
Sidebar.RemoveServer(serverVm.ServerUrl);
// Save to config with refresh token
string? refreshToken = ConnectionService.GetRefreshToken(result.ServerUrl);
SavedServer savedServer = new SavedServer
{
Name = new Uri(result.ServerUrl).Host,
Url = result.ServerUrl,
Username = result.Username,
RefreshToken = result.RememberMe ? refreshToken : null,
RememberMe = result.RememberMe,
LastConnected = DateTimeOffset.Now,
};
ConfigManager.SaveServer(savedServer);
} }
catch (Exception ex) catch (Exception ex)
{ {
+11 -30
View File
@@ -2,6 +2,9 @@ using Avalonia.Controls;
using EchoHub.Client.Config; using EchoHub.Client.Config;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using System.Linq; using System.Linq;
namespace Decho.Views; namespace Decho.Views;
@@ -16,7 +19,7 @@ public sealed partial class ConnectDialogWindow : Window
_savedServers = []; _savedServers = [];
} }
public ConnectDialogWindow(List<SavedServer> savedServers, SavedServer? prefill) public ConnectDialogWindow(List<SavedServer> savedServers, SavedServer? prefill = null)
{ {
InitializeComponent(); InitializeComponent();
_savedServers = savedServers; _savedServers = savedServers;
@@ -66,7 +69,8 @@ public sealed partial class ConnectDialogWindow : Window
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user)) if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user))
{ {
await ShowMessageBox("Validation", "Server URL and username are required."); var box = MessageBoxManager.GetMessageBoxStandard("Validation", "Server URL and username are required.", ButtonEnum.Ok);
await box.ShowWindowDialogAsync(this);
return; return;
} }
@@ -90,7 +94,8 @@ public sealed partial class ConnectDialogWindow : Window
{ {
if (string.IsNullOrEmpty(pass)) if (string.IsNullOrEmpty(pass))
{ {
await ShowMessageBox("Validation", "Password is required."); var box = MessageBoxManager.GetMessageBoxStandard("Validation", "Password is required.", ButtonEnum.Ok);
await box.ShowWindowDialogAsync(this);
return; return;
} }
@@ -105,7 +110,7 @@ public sealed partial class ConnectDialogWindow : Window
} }
} }
private void OnRegisterClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e) private async void OnRegisterClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
string url = UrlBox.Text?.Trim() ?? ""; string url = UrlBox.Text?.Trim() ?? "";
string user = UserBox.Text?.Trim() ?? ""; string user = UserBox.Text?.Trim() ?? "";
@@ -113,7 +118,8 @@ public sealed partial class ConnectDialogWindow : Window
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass)) if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass))
{ {
_ = ShowMessageBox("Validation", "Server URL, username, and password are required."); var box = MessageBoxManager.GetMessageBoxStandard("Validation", "Server URL, username, and password are required.", ButtonEnum.Ok);
await box.ShowWindowDialogAsync(this);
return; return;
} }
@@ -131,31 +137,6 @@ public sealed partial class ConnectDialogWindow : Window
{ {
Close(null); Close(null);
} }
private async Task ShowMessageBox(string title, string message)
{
Window msgBox = new Window
{
Title = title,
Width = 350,
Height = 150,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
CanResize = false,
};
StackPanel stack = new StackPanel { Spacing = 10, Margin = new Avalonia.Thickness(15) };
stack.Children.Add(new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap });
Button okBtn = new Button { Content = "OK", HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center };
stack.Children.Add(okBtn);
msgBox.Content = stack;
TaskCompletionSource tcs = new TaskCompletionSource();
okBtn.Click += (_, _) => { msgBox.Close(); _ = tcs.TrySetResult(); };
msgBox.Closed += (_, _) => tcs.TrySetResult();
await msgBox.ShowDialog(this);
await tcs.Task;
}
} }
public sealed class ConnectDialogResult public sealed class ConnectDialogResult