mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 00:46:11 +02:00
Replace ConnectDialog with ConnectDialogWindow and move UI to XAML
This commit is contained in:
@@ -634,9 +634,8 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
ClientConfig config = ConfigManager.Load();
|
ClientConfig config = ConfigManager.Load();
|
||||||
SavedServer? prefill = config.SavedServers.FirstOrDefault(s =>
|
SavedServer? prefill = config.SavedServers.FirstOrDefault(s =>
|
||||||
string.Equals(s.Url, serverVm.ServerUrl, StringComparison.OrdinalIgnoreCase));
|
string.Equals(s.Url, serverVm.ServerUrl, StringComparison.OrdinalIgnoreCase));
|
||||||
ConnectDialog dialog = new ConnectDialog(config.SavedServers, prefill);
|
ConnectDialogWindow dialog = new ConnectDialogWindow(config.SavedServers, prefill);
|
||||||
|
ConnectDialogResult? result = await dialog.ShowDialog<ConnectDialogResult?>(_mainWindow);
|
||||||
ConnectDialogResult? result = await dialog.ShowAsync(_mainWindow);
|
|
||||||
if (result is null)
|
if (result is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -754,228 +753,3 @@ public sealed class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ConnectDialogResult
|
|
||||||
{
|
|
||||||
public string ServerUrl { get; set; } = string.Empty;
|
|
||||||
public string Username { get; set; } = string.Empty;
|
|
||||||
public string Password { get; set; } = string.Empty;
|
|
||||||
public bool IsRegister { get; set; }
|
|
||||||
public bool RememberMe { get; set; }
|
|
||||||
public bool IsSavedSession { get; set; }
|
|
||||||
public string? SavedRefreshToken { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class ConnectDialog(List<SavedServer> savedServers, SavedServer? prefill = null)
|
|
||||||
{
|
|
||||||
private readonly List<SavedServer> _savedServers = savedServers;
|
|
||||||
private readonly SavedServer? _prefill = prefill;
|
|
||||||
|
|
||||||
public async Task<ConnectDialogResult?> ShowAsync(Window owner)
|
|
||||||
{
|
|
||||||
Window dialog = new Window
|
|
||||||
{
|
|
||||||
Title = "Connect to Server",
|
|
||||||
Width = 450,
|
|
||||||
Height = 380,
|
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
|
||||||
CanResize = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
StackPanel stack = new StackPanel { Spacing = 8, Margin = new Avalonia.Thickness(15) };
|
|
||||||
|
|
||||||
TextBlock urlLabel = new TextBlock { Text = "Server URL:" };
|
|
||||||
TextBox urlBox = new TextBox { Watermark = "http://localhost:5000", Text = "http://localhost:5000" };
|
|
||||||
stack.Children.Add(urlLabel);
|
|
||||||
stack.Children.Add(urlBox);
|
|
||||||
|
|
||||||
TextBlock userLabel = new TextBlock { Text = "Username:" };
|
|
||||||
TextBox userBox = new TextBox { Watermark = "username" };
|
|
||||||
stack.Children.Add(userLabel);
|
|
||||||
stack.Children.Add(userBox);
|
|
||||||
|
|
||||||
TextBlock passLabel = new TextBlock { Text = "Password:" };
|
|
||||||
TextBox passBox = new TextBox { Watermark = "password", PasswordChar = '*' };
|
|
||||||
stack.Children.Add(passLabel);
|
|
||||||
stack.Children.Add(passBox);
|
|
||||||
|
|
||||||
CheckBox rememberMe = new CheckBox { Content = "Remember me", IsChecked = true };
|
|
||||||
stack.Children.Add(rememberMe);
|
|
||||||
|
|
||||||
StackPanel buttonPanel = new StackPanel { Orientation = Avalonia.Layout.Orientation.Horizontal, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Spacing = 10 };
|
|
||||||
|
|
||||||
Button loginBtn = new Button { Content = "Login", Width = 100 };
|
|
||||||
Button registerBtn = new Button { Content = "Register", Width = 100 };
|
|
||||||
Button cancelBtn = new Button { Content = "Cancel", Width = 100 };
|
|
||||||
buttonPanel.Children.Add(loginBtn);
|
|
||||||
buttonPanel.Children.Add(registerBtn);
|
|
||||||
buttonPanel.Children.Add(cancelBtn);
|
|
||||||
stack.Children.Add(buttonPanel);
|
|
||||||
|
|
||||||
ListBox? serversList = null;
|
|
||||||
if (_savedServers.Count > 0)
|
|
||||||
{
|
|
||||||
TextBlock sep = new TextBlock { Text = "--- Saved Servers ---", HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center };
|
|
||||||
stack.Children.Add(sep);
|
|
||||||
|
|
||||||
serversList = new ListBox
|
|
||||||
{
|
|
||||||
Height = 100,
|
|
||||||
ItemsSource = _savedServers.Select(s => $"{s.Name} ({s.Url}) - {s.Username ?? "?"}").ToList()
|
|
||||||
};
|
|
||||||
stack.Children.Add(serversList);
|
|
||||||
|
|
||||||
serversList.SelectionChanged += (_, _) =>
|
|
||||||
{
|
|
||||||
int idx = serversList.SelectedIndex;
|
|
||||||
if (idx >= 0 && idx < _savedServers.Count)
|
|
||||||
{
|
|
||||||
SavedServer s = _savedServers[idx];
|
|
||||||
urlBox.Text = s.Url;
|
|
||||||
userBox.Text = s.Username ?? "";
|
|
||||||
rememberMe.IsChecked = s.RememberMe;
|
|
||||||
if (!string.IsNullOrEmpty(s.RefreshToken))
|
|
||||||
{
|
|
||||||
passBox.Text = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_prefill is not null)
|
|
||||||
{
|
|
||||||
urlBox.Text = _prefill.Url;
|
|
||||||
userBox.Text = _prefill.Username ?? "";
|
|
||||||
int idx = _savedServers.FindIndex(s =>
|
|
||||||
string.Equals(s.Url, _prefill.Url, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (idx >= 0 && idx < _savedServers.Count && serversList is not null)
|
|
||||||
{
|
|
||||||
serversList.SelectedIndex = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.Content = new ScrollViewer { Content = stack };
|
|
||||||
|
|
||||||
TaskCompletionSource<ConnectDialogResult?> tcs = new TaskCompletionSource<ConnectDialogResult?>();
|
|
||||||
ConnectDialogResult? result = null;
|
|
||||||
|
|
||||||
loginBtn.Click += async (_, _) =>
|
|
||||||
{
|
|
||||||
string url = urlBox.Text?.Trim() ?? "";
|
|
||||||
string user = userBox.Text?.Trim() ?? "";
|
|
||||||
string pass = passBox.Text ?? "";
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user))
|
|
||||||
{
|
|
||||||
await ShowMessageBox(owner, "Validation", "Server URL and username are required.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for saved session
|
|
||||||
SavedServer? saved = _savedServers.FirstOrDefault(s =>
|
|
||||||
string.Equals(s.Url, url, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
string.Equals(s.Username, user, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
!string.IsNullOrEmpty(s.RefreshToken));
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(pass) && saved is not null)
|
|
||||||
{
|
|
||||||
result = new ConnectDialogResult
|
|
||||||
{
|
|
||||||
ServerUrl = url,
|
|
||||||
Username = user,
|
|
||||||
IsSavedSession = true,
|
|
||||||
SavedRefreshToken = saved.RefreshToken,
|
|
||||||
RememberMe = saved.RememberMe,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(pass))
|
|
||||||
{
|
|
||||||
await ShowMessageBox(owner, "Validation", "Password is required.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new ConnectDialogResult
|
|
||||||
{
|
|
||||||
ServerUrl = url,
|
|
||||||
Username = user,
|
|
||||||
Password = pass,
|
|
||||||
IsRegister = false,
|
|
||||||
RememberMe = rememberMe.IsChecked ?? false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.Close();
|
|
||||||
_ = tcs.TrySetResult(result);
|
|
||||||
};
|
|
||||||
|
|
||||||
registerBtn.Click += (_, _) =>
|
|
||||||
{
|
|
||||||
string url = urlBox.Text?.Trim() ?? "";
|
|
||||||
string user = userBox.Text?.Trim() ?? "";
|
|
||||||
string pass = passBox.Text ?? "";
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass))
|
|
||||||
{
|
|
||||||
_ = ShowMessageBox(owner, "Validation", "Server URL, username, and password are required.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new ConnectDialogResult
|
|
||||||
{
|
|
||||||
ServerUrl = url,
|
|
||||||
Username = user,
|
|
||||||
Password = pass,
|
|
||||||
IsRegister = true,
|
|
||||||
RememberMe = rememberMe.IsChecked ?? false,
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Close();
|
|
||||||
_ = tcs.TrySetResult(result);
|
|
||||||
};
|
|
||||||
|
|
||||||
cancelBtn.Click += (_, _) =>
|
|
||||||
{
|
|
||||||
dialog.Close();
|
|
||||||
_ = tcs.TrySetResult(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Closed += (_, _) => tcs.TrySetResult(result);
|
|
||||||
|
|
||||||
if (owner is not null)
|
|
||||||
{
|
|
||||||
await dialog.ShowDialog(owner);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dialog.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
return await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ShowMessageBox(Window owner, 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(owner);
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
x:Class="Decho.Views.ConnectDialogWindow"
|
||||||
|
Title="Connect to Server"
|
||||||
|
Width="400"
|
||||||
|
MinWidth="400"
|
||||||
|
MinHeight="300"
|
||||||
|
SizeToContent="Height"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
CanResize="False"
|
||||||
|
Icon="/Assets/avalonia-logo.ico">
|
||||||
|
<Border Padding="15">
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock Text="Server URL:" FontWeight="SemiBold" />
|
||||||
|
<TextBox x:Name="UrlBox" Watermark="http://localhost:5000" />
|
||||||
|
|
||||||
|
<TextBlock Text="Username:" FontWeight="SemiBold" />
|
||||||
|
<TextBox x:Name="UserBox" Watermark="username" />
|
||||||
|
|
||||||
|
<TextBlock Text="Password:" FontWeight="SemiBold" />
|
||||||
|
<TextBox x:Name="PassBox" Watermark="password" PasswordChar="*" />
|
||||||
|
|
||||||
|
<CheckBox x:Name="RememberCheck" Content="Remember me" IsChecked="True" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Spacing="10"
|
||||||
|
Margin="0 4 0 0">
|
||||||
|
<Button Content="Login" Width="90" Click="OnLoginClick" />
|
||||||
|
<Button Content="Register" Width="90" Click="OnRegisterClick" />
|
||||||
|
<Button Content="Cancel" Width="90" Click="OnCancelClick" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock x:Name="SavedHeader"
|
||||||
|
Text="--- Saved Servers ---"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
IsVisible="False" />
|
||||||
|
<ListBox x:Name="ServersList"
|
||||||
|
Height="120"
|
||||||
|
IsVisible="False" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
using EchoHub.Client.Config;
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Decho.Views;
|
||||||
|
|
||||||
|
public sealed partial class ConnectDialogWindow : Window
|
||||||
|
{
|
||||||
|
private readonly List<SavedServer> _savedServers;
|
||||||
|
|
||||||
|
public ConnectDialogWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_savedServers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectDialogWindow(List<SavedServer> savedServers, SavedServer? prefill)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_savedServers = savedServers;
|
||||||
|
|
||||||
|
if (savedServers.Count > 0)
|
||||||
|
{
|
||||||
|
SavedHeader.IsVisible = true;
|
||||||
|
ServersList.IsVisible = true;
|
||||||
|
ServersList.ItemsSource = savedServers.Select(s =>
|
||||||
|
$"{s.Name} ({s.Url}) - {s.Username ?? "?"}").ToList();
|
||||||
|
|
||||||
|
ServersList.SelectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
int idx = ServersList.SelectedIndex;
|
||||||
|
if (idx >= 0 && idx < savedServers.Count)
|
||||||
|
{
|
||||||
|
SavedServer s = savedServers[idx];
|
||||||
|
UrlBox.Text = s.Url;
|
||||||
|
UserBox.Text = s.Username ?? "";
|
||||||
|
RememberCheck.IsChecked = s.RememberMe;
|
||||||
|
if (!string.IsNullOrEmpty(s.RefreshToken))
|
||||||
|
{
|
||||||
|
PassBox.Text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefill is not null)
|
||||||
|
{
|
||||||
|
UrlBox.Text = prefill.Url;
|
||||||
|
UserBox.Text = prefill.Username ?? "";
|
||||||
|
int idx = savedServers.FindIndex(s =>
|
||||||
|
string.Equals(s.Url, prefill.Url, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
ServersList.SelectedIndex = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnLoginClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string url = UrlBox.Text?.Trim() ?? "";
|
||||||
|
string user = UserBox.Text?.Trim() ?? "";
|
||||||
|
string pass = PassBox.Text ?? "";
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user))
|
||||||
|
{
|
||||||
|
await ShowMessageBox("Validation", "Server URL and username are required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SavedServer? saved = _savedServers.FirstOrDefault(s =>
|
||||||
|
string.Equals(s.Url, url, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
string.Equals(s.Username, user, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!string.IsNullOrEmpty(s.RefreshToken));
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(pass) && saved is not null)
|
||||||
|
{
|
||||||
|
Close(new ConnectDialogResult
|
||||||
|
{
|
||||||
|
ServerUrl = url,
|
||||||
|
Username = user,
|
||||||
|
IsSavedSession = true,
|
||||||
|
SavedRefreshToken = saved.RefreshToken,
|
||||||
|
RememberMe = saved.RememberMe,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(pass))
|
||||||
|
{
|
||||||
|
await ShowMessageBox("Validation", "Password is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Close(new ConnectDialogResult
|
||||||
|
{
|
||||||
|
ServerUrl = url,
|
||||||
|
Username = user,
|
||||||
|
Password = pass,
|
||||||
|
IsRegister = false,
|
||||||
|
RememberMe = RememberCheck.IsChecked ?? false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRegisterClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string url = UrlBox.Text?.Trim() ?? "";
|
||||||
|
string user = UserBox.Text?.Trim() ?? "";
|
||||||
|
string pass = PassBox.Text ?? "";
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(user) || string.IsNullOrEmpty(pass))
|
||||||
|
{
|
||||||
|
_ = ShowMessageBox("Validation", "Server URL, username, and password are required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Close(new ConnectDialogResult
|
||||||
|
{
|
||||||
|
ServerUrl = url,
|
||||||
|
Username = user,
|
||||||
|
Password = pass,
|
||||||
|
IsRegister = true,
|
||||||
|
RememberMe = RememberCheck.IsChecked ?? false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCancelClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
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 string ServerUrl { get; set; } = string.Empty;
|
||||||
|
public string Username { get; set; } = string.Empty;
|
||||||
|
public string Password { get; set; } = string.Empty;
|
||||||
|
public bool IsRegister { get; set; }
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
public bool IsSavedSession { get; set; }
|
||||||
|
public string? SavedRefreshToken { get; set; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user