add "Add Group" button

This commit is contained in:
2026-05-03 20:07:21 +03:00
parent 69934338f2
commit 6ecabed730
7 changed files with 181 additions and 3 deletions

View File

@@ -2,7 +2,7 @@ using System;
namespace KeyKeeper.PasswordStore;
public static class EntryIconType
public static class BuiltinEntryIconType
{
public static readonly Guid KEY = Guid.Parse("65ab3d55-1652-4f66-aac9-c3617f14e308");
public static readonly Guid DEFAULT = KEY;

View File

@@ -323,7 +323,6 @@ public class EntryEditViewModel : ViewModelBase
}
catch (Exception)
{
// Validation should have caught this, but handle gracefully
totp = null;
}
}
@@ -332,7 +331,7 @@ public class EntryEditViewModel : ViewModelBase
id,
created,
DateTime.UtcNow,
EntryIconType.DEFAULT,
BuiltinEntryIconType.DEFAULT,
EntryName.Trim(),
new LoginField() { Type = LOGIN_FIELD_USERNAME_ID, Value = Username.Trim() },
new LoginField() { Type = LOGIN_FIELD_PASSWORD_ID, Value = Password },

View File

@@ -95,6 +95,15 @@ public class UnlockedRepositoryViewModel : ViewModelBase
}
}
public void AddGroup(PassStoreEntryGroup group)
{
if (rootDirectory == null)
return;
rootDirectory.AddEntry(group);
HasUnsavedChanges = true;
OnPropertyChanged(nameof(PasswordGroups));
}
public void DeleteEntry(Guid id)
{
currentDirectory.DeleteEntry(id);

View File

@@ -0,0 +1,60 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="KeyKeeper.Views.CreateGroupDialog"
Title="Create New Group"
Background="White"
Icon="/Assets/icon.ico"
Width="420" Height="320"
WindowStartupLocation="CenterOwner"
CanResize="False">
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="Create new group"
FontSize="20"
FontWeight="Bold"
Foreground="#2328C4"/>
<StackPanel Spacing="6">
<TextBlock Text="Group name" FontWeight="SemiBold" Foreground="Black" />
<TextBox x:Name="NameTextBox"
Watermark="Enter group name"
Padding="10,8"/>
</StackPanel>
<StackPanel Spacing="6">
<TextBlock Text="Icon" FontWeight="SemiBold" Foreground="Black" />
<ListBox x:Name="IconListBox"
Height="80"
SelectionMode="Single"
Background="#F5F5F5">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate x:CompileBindings="False">
<Svg Path="{Binding IconPath}" Width="48" Height="48" Margin="6"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<TextBlock x:Name="ErrorText"
FontSize="12"
Foreground="Red"
Text=""
IsVisible="False"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
<Button Content="Cancel"
Width="80"
Click="CancelButton_Click"/>
<Button x:Name="CreateButton"
Content="Create"
Width="80"
IsEnabled="False"
Click="CreateButton_Click"/>
</StackPanel>
</StackPanel>
</Window>

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using KeyKeeper.PasswordStore;
namespace KeyKeeper.Views;
public partial class CreateGroupDialog : Window
{
public string GroupName { get; private set; } = string.Empty;
public Guid IconType { get; private set; } = BuiltinEntryIconType.DEFAULT;
public bool Success { get; private set; }
private record IconChoice(Guid Id)
{
public string IconPath => $"avares://KeyKeeper/Assets/builtin-entry-icon-{Id}.svg";
}
public CreateGroupDialog()
{
InitializeComponent();
var icons = new List<IconChoice>
{
new(BuiltinEntryIconType.KEY),
};
IconListBox.ItemsSource = icons;
IconListBox.SelectedIndex = 0;
NameTextBox.TextChanged += (_, _) => UpdateCreateButtonState();
KeyDown += OnKeyDown;
}
private void UpdateCreateButtonState()
{
CreateButton.IsEnabled = !string.IsNullOrWhiteSpace(NameTextBox.Text);
}
private void OnKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
Close();
else if (e.Key == Key.Enter && CreateButton.IsEnabled)
Submit();
}
private void CreateButton_Click(object? sender, RoutedEventArgs e) => Submit();
private void CancelButton_Click(object? sender, RoutedEventArgs e)
{
Success = false;
Close();
}
private void Submit()
{
var name = NameTextBox.Text?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(name))
{
ErrorText.Text = "Name cannot be empty";
ErrorText.IsVisible = true;
return;
}
GroupName = name;
if (IconListBox.SelectedItem is IconChoice choice)
IconType = choice.Id;
Success = true;
Close();
}
}

View File

@@ -96,6 +96,14 @@
Height="30"
HorizontalAlignment="Left"
Margin="0,20,0,0"/>
<!-- New Group -->
<Button Content="New Group"
Classes="accentSidebarButton"
Click="AddGroupButton_Click"
Height="30"
HorizontalAlignment="Left"
Margin="0,20,0,0"/>
</StackPanel>
</Border>

View File

@@ -153,6 +153,34 @@ public partial class RepositoryWindow : Window
}
}
private async void AddGroupButton_Click(object sender, RoutedEventArgs args)
{
if (DataContext is RepositoryWindowViewModel vm_ && vm_.CurrentPage is UnlockedRepositoryViewModel vm)
{
CreateGroupDialog dialog = new();
vm_.StopLockTimer();
await dialog.ShowDialog(this);
vm_.StartLockTimer();
if (dialog.Success)
{
var group = new PassStoreEntryGroup(
Guid.NewGuid(),
DateTime.UtcNow,
DateTime.UtcNow,
dialog.IconType,
dialog.GroupName,
FileFormatConstants.GROUP_TYPE_SIMPLE
);
vm.AddGroup(group);
this.FindControlRecursive<ToastNotificationHost>("NotificationHost")?.Show("Group created");
}
}
}
private void SaveButton_Click(object sender, RoutedEventArgs args)
{
if (DataContext is RepositoryWindowViewModel vm && vm.CurrentPage is UnlockedRepositoryViewModel pageVm)