chore: deprecate standalone SecureStoreManager utility
Move SecureStoreManager project and tests to Deprecated folder and remove from solution. SecureStore functionality is now integrated into ConfigManager.
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:JdeScoping.SecureStoreManager.ViewModels"
|
||||
x:Class="JdeScoping.SecureStoreManager.Views.MainWindow"
|
||||
Title="{Binding WindowTitle}"
|
||||
Height="500" Width="800"
|
||||
MinHeight="400" MinWidth="600"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<!-- DataContext is set via DI in App.axaml.cs -->
|
||||
|
||||
<Window.KeyBindings>
|
||||
<KeyBinding Gesture="Ctrl+N" Command="{Binding NewStoreCommand}" />
|
||||
<KeyBinding Gesture="Ctrl+O" Command="{Binding OpenStoreCommand}" />
|
||||
<KeyBinding Gesture="Ctrl+S" Command="{Binding SaveCommand}" />
|
||||
<KeyBinding Gesture="Ctrl+W" Command="{Binding CloseStoreCommand}" />
|
||||
<KeyBinding Gesture="Delete" Command="{Binding DeleteSecretCommand}" />
|
||||
</Window.KeyBindings>
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
||||
<!-- Menu Bar -->
|
||||
<Menu Grid.Row="0">
|
||||
<MenuItem Header="_File">
|
||||
<MenuItem Header="_New Store..." Command="{Binding NewStoreCommand}" InputGesture="Ctrl+N" />
|
||||
<MenuItem Header="_Open Store..." Command="{Binding OpenStoreCommand}" InputGesture="Ctrl+O" />
|
||||
<Separator />
|
||||
<MenuItem Header="_Save" Command="{Binding SaveCommand}" InputGesture="Ctrl+S" />
|
||||
<Separator />
|
||||
<MenuItem Header="_Close Store" Command="{Binding CloseStoreCommand}" InputGesture="Ctrl+W" />
|
||||
<Separator />
|
||||
<MenuItem Header="E_xit" Command="{Binding ExitCommand}" InputGesture="Alt+F4" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Secrets">
|
||||
<MenuItem Header="_Add Secret..." Command="{Binding AddSecretCommand}" />
|
||||
<MenuItem Header="_Edit Secret..." Command="{Binding EditSecretCommand}" />
|
||||
<Separator />
|
||||
<MenuItem Header="_Delete Secret" Command="{Binding DeleteSecretCommand}" InputGesture="Delete" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Tools">
|
||||
<MenuItem Header="_Generate Key File..." Command="{Binding GenerateKeyFileCommand}" />
|
||||
<MenuItem Header="_Export Current Key..." Command="{Binding ExportKeyCommand}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Border Grid.Row="1" BorderBrush="LightGray" BorderThickness="0,0,0,1" Padding="5">
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<Button Content="New" Command="{Binding NewStoreCommand}" ToolTip.Tip="Create new store (Ctrl+N)" Padding="8,4" />
|
||||
<Button Content="Open" Command="{Binding OpenStoreCommand}" ToolTip.Tip="Open existing store (Ctrl+O)" Padding="8,4" />
|
||||
<Button Content="Save" Command="{Binding SaveCommand}" ToolTip.Tip="Save changes (Ctrl+S)" Padding="8,4" />
|
||||
<Rectangle Width="1" Fill="LightGray" Margin="5,0" />
|
||||
<Button Content="Add" Command="{Binding AddSecretCommand}" ToolTip.Tip="Add new secret" Padding="8,4" />
|
||||
<Button Content="Edit" Command="{Binding EditSecretCommand}" ToolTip.Tip="Edit selected secret" Padding="8,4" />
|
||||
<Button Content="Delete" Command="{Binding DeleteSecretCommand}" ToolTip.Tip="Delete selected secret (Delete)" Padding="8,4" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Main Content -->
|
||||
<Grid Grid.Row="2" Margin="10">
|
||||
<!-- Empty State -->
|
||||
<TextBlock Text="No store open. Use File > New Store or File > Open Store to begin."
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
FontSize="14" Foreground="Gray"
|
||||
IsVisible="{Binding !IsStoreOpen}" />
|
||||
|
||||
<!-- Secrets Grid -->
|
||||
<DataGrid ItemsSource="{Binding Secrets}"
|
||||
SelectedItem="{Binding SelectedSecret}"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
SelectionMode="Single"
|
||||
CanUserReorderColumns="True"
|
||||
CanUserResizeColumns="True"
|
||||
CanUserSortColumns="True"
|
||||
GridLinesVisibility="Horizontal"
|
||||
IsVisible="{Binding IsStoreOpen}"
|
||||
x:Name="SecretsDataGrid"
|
||||
DoubleTapped="DataGrid_DoubleTapped">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Key" Binding="{Binding Key}" Width="200" />
|
||||
<DataGridTextColumn Header="Value" Binding="{Binding DisplayValue}" Width="*" />
|
||||
<DataGridTemplateColumn Header="Actions" Width="150">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="5">
|
||||
<Button Content="{Binding IsValueVisible, Converter={StaticResource BoolToVisibilityIcon}}"
|
||||
Command="{Binding ToggleVisibilityCommand}"
|
||||
ToolTip.Tip="Show/Hide value"
|
||||
Width="50" Height="24"
|
||||
FontSize="11" />
|
||||
<Button Content="Copy"
|
||||
Command="{Binding CopyToClipboardCommand}"
|
||||
ToolTip.Tip="Copy value to clipboard"
|
||||
Width="50" Height="24"
|
||||
FontSize="11" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
|
||||
<!-- Status Bar -->
|
||||
<Border Grid.Row="3" Background="#F0F0F0" BorderBrush="LightGray" BorderThickness="0,1,0,0" Padding="10,5">
|
||||
<DockPanel>
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Right">
|
||||
<TextBlock Text="Secrets: " />
|
||||
<TextBlock Text="{Binding Secrets.Count}" />
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding StatusMessage}" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,106 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using JdeScoping.SecureStoreManager.ViewModels;
|
||||
|
||||
namespace JdeScoping.SecureStoreManager.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private MainWindowViewModel? ViewModel => DataContext as MainWindowViewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainWindow"/> class.
|
||||
/// </summary>
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += MainWindow_Loaded;
|
||||
Closing += MainWindow_Closing;
|
||||
}
|
||||
|
||||
private void MainWindow_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel == null)
|
||||
return;
|
||||
|
||||
// Subscribe to dialog request events (these open dialogs with their own DataContext)
|
||||
ViewModel.OnRequestNewStoreDialog += ShowNewStoreDialog;
|
||||
ViewModel.OnRequestOpenStoreDialog += ShowOpenStoreDialog;
|
||||
ViewModel.OnRequestAddSecretDialog += ShowAddSecretDialog;
|
||||
ViewModel.OnRequestEditSecretDialog += ShowEditSecretDialog;
|
||||
ViewModel.OnRequestClose += () => Close();
|
||||
}
|
||||
|
||||
private async void MainWindow_Closing(object? sender, WindowClosingEventArgs e)
|
||||
{
|
||||
if (ViewModel == null)
|
||||
return;
|
||||
|
||||
e.Cancel = true;
|
||||
if (await ViewModel.PromptForUnsavedChangesAsync())
|
||||
{
|
||||
e.Cancel = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGrid_DoubleTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
if (ViewModel?.SelectedSecret != null)
|
||||
{
|
||||
ViewModel.EditSecretCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ShowNewStoreDialog()
|
||||
{
|
||||
if (ViewModel == null) return;
|
||||
|
||||
var dialog = new NewStoreDialog();
|
||||
var result = await dialog.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var vm = dialog.ViewModel;
|
||||
await ViewModel.CreateNewStoreAsync(vm.StorePath, vm.KeyFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ShowOpenStoreDialog()
|
||||
{
|
||||
if (ViewModel == null) return;
|
||||
|
||||
var dialog = new OpenStoreDialog();
|
||||
var result = await dialog.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var vm = dialog.ViewModel;
|
||||
await ViewModel.OpenExistingStoreAsync(vm.StorePath, vm.KeyFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ShowAddSecretDialog()
|
||||
{
|
||||
if (ViewModel == null) return;
|
||||
|
||||
var dialog = new SecretEditDialog();
|
||||
var result = await dialog.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var vm = dialog.ViewModel;
|
||||
await ViewModel.SaveSecretAsync(vm.Key, vm.Value, isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ShowEditSecretDialog(string key, string value)
|
||||
{
|
||||
if (ViewModel == null) return;
|
||||
|
||||
var dialog = new SecretEditDialog(key, value);
|
||||
var result = await dialog.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var vm = dialog.ViewModel;
|
||||
await ViewModel.SaveSecretAsync(vm.Key, vm.Value, isNew: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:JdeScoping.SecureStoreManager.ViewModels"
|
||||
x:Class="JdeScoping.SecureStoreManager.Views.NewStoreDialog"
|
||||
Title="Create New Store"
|
||||
Height="280" Width="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
ShowInTaskbar="False">
|
||||
<!-- DataContext is set in code-behind -->
|
||||
|
||||
<Grid Margin="15" RowDefinitions="Auto,Auto,*,Auto">
|
||||
<!-- Store Path -->
|
||||
<Border Grid.Row="0" BorderBrush="Gray" BorderThickness="1" CornerRadius="3" Padding="10" Margin="0,0,0,10">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Store Location" FontWeight="SemiBold" Margin="0,0,0,10" />
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding StorePath, Mode=TwoWay}"
|
||||
Margin="0,0,5,0" />
|
||||
<Button Grid.Column="1"
|
||||
Content="Browse..."
|
||||
Command="{Binding BrowseStorePathCommand}"
|
||||
Padding="10,5" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Key File Settings -->
|
||||
<Border Grid.Row="1"
|
||||
BorderBrush="Gray" BorderThickness="1" CornerRadius="3" Padding="10" Margin="0,0,0,10">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Key File" FontWeight="SemiBold" Margin="0,0,0,10" />
|
||||
<TextBlock Text="The key file is required to encrypt and decrypt the store."
|
||||
FontSize="11" Foreground="Gray" Margin="0,0,0,10" TextWrapping="Wrap" />
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding KeyFilePath, Mode=TwoWay}"
|
||||
Margin="0,0,5,0" />
|
||||
<Button Grid.Column="1"
|
||||
Content="Browse..."
|
||||
Command="{Binding BrowseKeyFilePathCommand}"
|
||||
Padding="10,5" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Validation Error -->
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding ValidationError}"
|
||||
Foreground="Red"
|
||||
FontSize="11"
|
||||
Margin="0,5,0,0"
|
||||
IsVisible="{Binding ValidationError, Converter={StaticResource StringToBool}}"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
|
||||
<Button Content="Create" Click="CreateButton_Click" IsEnabled="{Binding IsValid}" MinWidth="80" Padding="10,5" />
|
||||
<Button Content="Cancel" Click="CancelButton_Click" MinWidth="80" Padding="10,5" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,70 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using JdeScoping.SecureStoreManager.Constants;
|
||||
using JdeScoping.SecureStoreManager.ViewModels;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
namespace JdeScoping.SecureStoreManager.Views;
|
||||
|
||||
public partial class NewStoreDialog : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the view model for this dialog.
|
||||
/// </summary>
|
||||
public NewStoreDialogViewModel ViewModel => (NewStoreDialogViewModel)DataContext!;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the NewStoreDialog.
|
||||
/// </summary>
|
||||
public NewStoreDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new NewStoreDialogViewModel();
|
||||
Loaded += NewStoreDialog_Loaded;
|
||||
}
|
||||
|
||||
private void NewStoreDialog_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.OnShowSaveFileDialog += ShowSaveFileDialogAsync;
|
||||
}
|
||||
|
||||
private async Task<string?> ShowSaveFileDialogAsync(string title, string fileTypeName, string pattern, string defaultExtension)
|
||||
{
|
||||
var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
|
||||
{
|
||||
Title = title,
|
||||
DefaultExtension = defaultExtension,
|
||||
FileTypeChoices = new[]
|
||||
{
|
||||
new FilePickerFileType(fileTypeName) { Patterns = new[] { pattern } },
|
||||
new FilePickerFileType(FileExtensions.AllFilesTypeName) { Patterns = new[] { FileExtensions.AllFilesPattern } }
|
||||
}
|
||||
});
|
||||
|
||||
return file?.Path.LocalPath;
|
||||
}
|
||||
|
||||
private async void CreateButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsValid)
|
||||
{
|
||||
var box = MessageBoxManager
|
||||
.GetMessageBoxStandard(
|
||||
DialogStrings.ValidationErrorTitle,
|
||||
ViewModel.ValidationError ?? DialogStrings.DefaultValidationError,
|
||||
ButtonEnum.Ok,
|
||||
MsBox.Avalonia.Enums.Icon.Warning);
|
||||
await box.ShowWindowDialogAsync(this);
|
||||
return;
|
||||
}
|
||||
|
||||
Close(true);
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Close(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:JdeScoping.SecureStoreManager.ViewModels"
|
||||
x:Class="JdeScoping.SecureStoreManager.Views.OpenStoreDialog"
|
||||
Title="Open Store"
|
||||
Height="280" Width="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
ShowInTaskbar="False">
|
||||
<!-- DataContext is set in code-behind -->
|
||||
|
||||
<Grid Margin="15" RowDefinitions="Auto,Auto,*,Auto">
|
||||
<!-- Store Path -->
|
||||
<Border Grid.Row="0" BorderBrush="Gray" BorderThickness="1" CornerRadius="3" Padding="10" Margin="0,0,0,10">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Store File" FontWeight="SemiBold" Margin="0,0,0,10" />
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding StorePath, Mode=TwoWay}"
|
||||
Margin="0,0,5,0" />
|
||||
<Button Grid.Column="1"
|
||||
Content="Browse..."
|
||||
Command="{Binding BrowseStorePathCommand}"
|
||||
Padding="10,5" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Key File Settings -->
|
||||
<Border Grid.Row="1"
|
||||
BorderBrush="Gray" BorderThickness="1" CornerRadius="3" Padding="10" Margin="0,0,0,10">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Key File" FontWeight="SemiBold" Margin="0,0,0,10" />
|
||||
<TextBlock Text="Select the key file used to encrypt this store."
|
||||
FontSize="11" Foreground="Gray" Margin="0,0,0,10" TextWrapping="Wrap" />
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding KeyFilePath, Mode=TwoWay}"
|
||||
Margin="0,0,5,0" />
|
||||
<Button Grid.Column="1"
|
||||
Content="Browse..."
|
||||
Command="{Binding BrowseKeyFilePathCommand}"
|
||||
Padding="10,5" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Validation Error -->
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding ValidationError}"
|
||||
Foreground="Red"
|
||||
FontSize="11"
|
||||
Margin="0,5,0,0"
|
||||
IsVisible="{Binding ValidationError, Converter={StaticResource StringToBool}}"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
|
||||
<Button Content="Open" Click="OpenButton_Click" IsEnabled="{Binding IsValid}" MinWidth="80" Padding="10,5" />
|
||||
<Button Content="Cancel" Click="CancelButton_Click" MinWidth="80" Padding="10,5" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,70 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using JdeScoping.SecureStoreManager.Constants;
|
||||
using JdeScoping.SecureStoreManager.ViewModels;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
namespace JdeScoping.SecureStoreManager.Views;
|
||||
|
||||
public partial class OpenStoreDialog : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the view model for this dialog.
|
||||
/// </summary>
|
||||
public OpenStoreDialogViewModel ViewModel => (OpenStoreDialogViewModel)DataContext!;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenStoreDialog"/> class.
|
||||
/// </summary>
|
||||
public OpenStoreDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new OpenStoreDialogViewModel();
|
||||
Loaded += OpenStoreDialog_Loaded;
|
||||
}
|
||||
|
||||
private void OpenStoreDialog_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.OnShowOpenFileDialog += ShowOpenFileDialogAsync;
|
||||
}
|
||||
|
||||
private async Task<string?> ShowOpenFileDialogAsync(string title, string fileTypeName, string pattern)
|
||||
{
|
||||
var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
Title = title,
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new[]
|
||||
{
|
||||
new FilePickerFileType(fileTypeName) { Patterns = new[] { pattern } },
|
||||
new FilePickerFileType(FileExtensions.AllFilesTypeName) { Patterns = new[] { FileExtensions.AllFilesPattern } }
|
||||
}
|
||||
});
|
||||
|
||||
return files.Count > 0 ? files[0].Path.LocalPath : null;
|
||||
}
|
||||
|
||||
private async void OpenButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsValid)
|
||||
{
|
||||
var box = MessageBoxManager
|
||||
.GetMessageBoxStandard(
|
||||
DialogStrings.ValidationErrorTitle,
|
||||
ViewModel.ValidationError ?? DialogStrings.DefaultValidationError,
|
||||
ButtonEnum.Ok,
|
||||
MsBox.Avalonia.Enums.Icon.Warning);
|
||||
await box.ShowWindowDialogAsync(this);
|
||||
return;
|
||||
}
|
||||
|
||||
Close(true);
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Close(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:JdeScoping.SecureStoreManager.ViewModels"
|
||||
x:Class="JdeScoping.SecureStoreManager.Views.SecretEditDialog"
|
||||
Title="{Binding DialogTitle}"
|
||||
Height="280" Width="450"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
ShowInTaskbar="False">
|
||||
<!-- DataContext is set in code-behind -->
|
||||
|
||||
<Grid Margin="15" RowDefinitions="Auto,Auto,*,Auto">
|
||||
<!-- Key -->
|
||||
<Grid Grid.Row="0" ColumnDefinitions="80,*" Margin="0,0,0,10">
|
||||
<TextBlock Grid.Column="0" Text="Key:" VerticalAlignment="Center" Margin="0,5,10,5" />
|
||||
<TextBox Grid.Column="1"
|
||||
Text="{Binding Key, Mode=TwoWay}"
|
||||
IsEnabled="{Binding IsKeyEditable}"
|
||||
Margin="0,5" />
|
||||
</Grid>
|
||||
|
||||
<!-- Value -->
|
||||
<Grid Grid.Row="1" ColumnDefinitions="80,*" Margin="0,0,0,10">
|
||||
<TextBlock Grid.Column="0" Text="Value:" VerticalAlignment="Top" Margin="0,8,10,5" />
|
||||
<TextBox Grid.Column="1"
|
||||
Text="{Binding Value, Mode=TwoWay}"
|
||||
TextWrapping="Wrap"
|
||||
AcceptsReturn="True"
|
||||
Height="80"
|
||||
Margin="0,5" />
|
||||
</Grid>
|
||||
|
||||
<!-- Validation Error -->
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding ValidationError}"
|
||||
Foreground="Red"
|
||||
FontSize="11"
|
||||
Margin="0,5,0,0"
|
||||
IsVisible="{Binding ValidationError, Converter={StaticResource StringToBool}}"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
|
||||
<Button Content="Save" Click="SaveButton_Click" IsEnabled="{Binding IsValid}" MinWidth="80" Padding="10,5" />
|
||||
<Button Content="Cancel" Click="CancelButton_Click" MinWidth="80" Padding="10,5" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,57 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using JdeScoping.SecureStoreManager.ViewModels;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
namespace JdeScoping.SecureStoreManager.Views;
|
||||
|
||||
public partial class SecretEditDialog : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the view model for this dialog.
|
||||
/// </summary>
|
||||
public SecretEditDialogViewModel ViewModel => (SecretEditDialogViewModel)DataContext!;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SecretEditDialog"/> class for creating a new secret.
|
||||
/// </summary>
|
||||
public SecretEditDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new SecretEditDialogViewModel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SecretEditDialog"/> class for editing an existing secret.
|
||||
/// </summary>
|
||||
/// <param name="key">The secret key.</param>
|
||||
/// <param name="value">The secret value.</param>
|
||||
public SecretEditDialog(string key, string value)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new SecretEditDialogViewModel(key, value);
|
||||
}
|
||||
|
||||
private async void SaveButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsValid)
|
||||
{
|
||||
var box = MessageBoxManager
|
||||
.GetMessageBoxStandard(
|
||||
"Validation Error",
|
||||
ViewModel.ValidationError ?? "Please fill in all required fields.",
|
||||
ButtonEnum.Ok,
|
||||
MsBox.Avalonia.Enums.Icon.Warning);
|
||||
await box.ShowWindowDialogAsync(this);
|
||||
return;
|
||||
}
|
||||
|
||||
Close(true);
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Close(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user