Implemented proper viewModels

This commit is contained in:
2025-09-30 11:31:38 +02:00
parent c8c24cb7cc
commit a99b9fe0bb
7 changed files with 184 additions and 110 deletions

View File

@@ -15,7 +15,8 @@ class Program
var application = Adw.Application.New("org.typomustakes.keychain", Gio.ApplicationFlags.FlagsNone); var application = Adw.Application.New("org.typomustakes.keychain", Gio.ApplicationFlags.FlagsNone);
application.OnActivate += (sender, args) => application.OnActivate += (sender, args) =>
{ {
var window = new UI.MainWindow().Window; var passwordStoreService = provider.GetRequiredService<IPasswordStoreService>();
var window = new UI.MainWindow(passwordStoreService).Window;
window.Application = (Adw.Application)sender; window.Application = (Adw.Application)sender;
window.Show(); window.Show();
}; };

View File

@@ -1,59 +1,109 @@
using Adw; using Adw;
using Keychain.ViewModels;
namespace Keychain.UI; namespace Keychain.UI;
public class AddShortcutWindow public class AddShortcutWindow
{ {
private readonly PasswordStoreShortcutCollection shortcuts;
public Dialog Dialog { get; } public Dialog Dialog { get; }
private const string dialogId = "add_shortcut_dialog";
private Gtk.Button? closeButton; private Gtk.Button? closeButton;
private const string closeButtonId = "close_button";
private Gtk.Button? iconPickerButton; private Gtk.Button? iconPickerButton;
private const string iconPickerButtonId = "icon_picker_button";
private EntryRow? displayNameEntryRow;
private const string displayNameEntryRowId = "display_name_entry_row";
private ActionRow? folderActionRow;
private const string folderActionRowId = "folder_action_row";
private Gtk.Button? browseButton; private Gtk.Button? browseButton;
private const string browseButtonId = "folder_browse_button";
private Gtk.Button? clearSelectedFolderButton; private Gtk.Button? clearSelectedFolderButton;
private const string clearSelectedFolderButtonId = "clear_selected_folder_button";
private Gtk.Button? saveButton; private Gtk.Button? saveButton;
private const string saveButtonId = "save_button";
public AddShortcutWindow() public AddShortcutWindow(PasswordStoreShortcutCollection shortcuts)
{ {
this.shortcuts = shortcuts;
var builder = new Gtk.Builder("Keychain.UI.AddShortcutWindow.AddShortcutWindow.xml"); var builder = new Gtk.Builder("Keychain.UI.AddShortcutWindow.AddShortcutWindow.xml");
Dialog = builder.GetObject("add_shortcut_dialog") as Dialog; Dialog = builder.GetObject(dialogId) as Dialog;
if (Dialog == null) if (Dialog == null)
{ {
throw new Exception("Failed to load embedded resource AddShortcutWindow.xml"); throw new NullReferenceException("Failed to load embedded resource AddShortcutWindow.xml");
} }
closeButton = builder.GetObject("close_button") as Gtk.Button; try
{
closeButton = builder.GetObject(closeButtonId) as Gtk.Button;
if (closeButton == null) if (closeButton == null)
{ {
throw new Exception("Failed to load UI element with ID: close_button"); throw new NullReferenceException(closeButtonId);
} }
closeButton.OnClicked += Close; closeButton.OnClicked += Close;
iconPickerButton = builder.GetObject("icon_picker_button") as Gtk.Button; iconPickerButton = builder.GetObject(iconPickerButtonId) as Gtk.Button;
if (iconPickerButton == null) if (iconPickerButton == null)
{ {
throw new Exception("Failed to load UI element with ID: icon_picker_button"); throw new NullReferenceException(iconPickerButtonId);
} }
iconPickerButton.OnClicked += OpenIconPicker; iconPickerButton.OnClicked += OpenIconPicker;
clearSelectedFolderButton = builder.GetObject("clear_selected_folder_button") as Gtk.Button; displayNameEntryRow = builder.GetObject(displayNameEntryRowId) as EntryRow;
if (displayNameEntryRow == null)
{
throw new NullReferenceException(displayNameEntryRowId);
}
folderActionRow = builder.GetObject(folderActionRowId) as ActionRow;
if (folderActionRow == null)
{
throw new NullReferenceException(folderActionRowId);
}
clearSelectedFolderButton = builder.GetObject(clearSelectedFolderButtonId) as Gtk.Button;
if (clearSelectedFolderButton == null) if (clearSelectedFolderButton == null)
{ {
throw new Exception("Failed to load UI element with ID: icon_picker_button"); throw new NullReferenceException(clearSelectedFolderButtonId);
} }
clearSelectedFolderButton.OnClicked += ClearSelectedFolder; clearSelectedFolderButton.OnClicked += ClearSelectedFolder;
browseButton = builder.GetObject("folder_browse_button") as Gtk.Button; browseButton = builder.GetObject(browseButtonId) as Gtk.Button;
if (browseButton == null) if (browseButton == null)
{ {
throw new Exception("Failed to load UI element with ID: folder_browse_button"); throw new NullReferenceException(browseButtonId);
} }
browseButton.OnClicked += BrowseFolder; browseButton.OnClicked += BrowseFolder;
saveButton = builder.GetObject("save_button") as Gtk.Button; saveButton = builder.GetObject(saveButtonId) as Gtk.Button;
if (saveButton == null) if (saveButton == null)
{ {
throw new Exception("Failed to load UI element with ID: save_button"); throw new NullReferenceException(saveButtonId);
} }
saveButton.OnClicked += (sender, e) => CreateShortcut();
}
catch (NullReferenceException e)
{
throw new Exception($"Failed to load UI element: {e.Message}");
}
}
private void CreateShortcut()
{
if (displayNameEntryRow == null || browseButton == null || iconPickerButton == null)
return;
var displayName = displayNameEntryRow.GetText();
var path = ((ButtonContent)browseButton.Child).Label;
var iconName = iconPickerButton.Label;
if (string.IsNullOrWhiteSpace(displayName) || path.Equals("Browse") || string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(iconName))
return;
shortcuts.Add(path, displayName, iconName);
Dialog.Close();
} }
private void OpenIconPicker(object sender, EventArgs e) private void OpenIconPicker(object sender, EventArgs e)

View File

@@ -34,7 +34,7 @@
<child> <child>
<object class="AdwPreferencesGroup"> <object class="AdwPreferencesGroup">
<child> <child>
<object class="AdwEntryRow"> <object class="AdwEntryRow" id="display_name_entry_row">
<property name="title" translatable="yes" context="Input field placeholder" comments="Noun. Tells the user that the display name of the new password store is to be supplied here">Name</property> <property name="title" translatable="yes" context="Input field placeholder" comments="Noun. Tells the user that the display name of the new password store is to be supplied here">Name</property>
</object> </object>
</child> </child>
@@ -51,7 +51,7 @@
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow" id="folder_action_row">
<property name="title" translatable="yes" context="Label" comments="Noun. Marks a button that allows the user to pick a folder where the new store will be.">Location</property> <property name="title" translatable="yes" context="Label" comments="Noun. Marks a button that allows the user to pick a folder where the new store will be.">Location</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">

View File

@@ -1,5 +1,6 @@
using Adw; using Adw;
using Keychain.ViewModels; using Keychain.ViewModels;
using Logic;
namespace Keychain.UI; namespace Keychain.UI;
@@ -12,6 +13,8 @@ public class MainWindow
private Gtk.Stack titleStack; private Gtk.Stack titleStack;
private Gtk.SearchEntry searchEntry; private Gtk.SearchEntry searchEntry;
private readonly IPasswordStoreService passwordStoreService;
private readonly string windowId = "main_window"; private readonly string windowId = "main_window";
private readonly string shortcutsGroupId = "shortcuts_group"; private readonly string shortcutsGroupId = "shortcuts_group";
private readonly string addShortcutButtonId = "add_shortcut_button"; private readonly string addShortcutButtonId = "add_shortcut_button";
@@ -19,8 +22,9 @@ public class MainWindow
private readonly string titleStackId = "title_stack"; private readonly string titleStackId = "title_stack";
private readonly string searchEntryId = "search_entry"; private readonly string searchEntryId = "search_entry";
public MainWindow() public MainWindow(IPasswordStoreService passwordStoreService)
{ {
this.passwordStoreService = passwordStoreService;
var builder = new Gtk.Builder("Keychain.UI.MainWindow.MainWindow.xml"); var builder = new Gtk.Builder("Keychain.UI.MainWindow.MainWindow.xml");
@@ -75,37 +79,26 @@ public class MainWindow
} }
// Initialize the observable collection with property binding // Initialize the observable collection with property binding
shortcuts = new PasswordStoreShortcutCollection(shortcutsGroup); shortcuts = new PasswordStoreShortcutCollection(shortcutsGroup, passwordStoreService);
LoadDefaultShortcuts(); LoadDefaultShortcuts();
} }
private void OnAddShortcutClicked(object sender, EventArgs e) private void OnAddShortcutClicked(object sender, EventArgs e)
{ {
var dialog = new AddShortcutWindow().Dialog; var dialog = new AddShortcutWindow(shortcuts).Dialog;
dialog.Present(Window); dialog.Present(Window);
} }
private void AddShortcut(string path)
{
var newShortcut = new PasswordStoreShortcut(path: path);
shortcuts.Add(newShortcut); // This will automatically update the UI
}
private void RemoveShortcut(PasswordStoreViewModel shortcut)
{
shortcuts.Remove(shortcut); // This will automatically update the UI
}
private void LoadDefaultShortcuts() private void LoadDefaultShortcuts()
{ {
shortcuts.Add(new PasswordStoreShortcut(displayName: "Default", path: Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/.password_store")); shortcuts.Add(new PasswordStoreViewModel(displayName: "Default", path: Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/.password_store"));
} }
private void UpdateShortcutName(PasswordStoreViewModel shortcut, string newName) // private void UpdateShortcutName(PasswordStoreViewModel shortcut, string newName)
{ // {
shortcut.DisplayName = newName; // This will automatically update the UI row // shortcut.DisplayName = newName; // This will automatically update the UI row
} // }
private void SetSearchBarVisible(object sender, EventArgs e) private void SetSearchBarVisible(object sender, EventArgs e)
{ {

View File

@@ -2,17 +2,22 @@ namespace Keychain.ViewModels;
using Adw; using Adw;
using Gtk; using Gtk;
using Logic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
public class PasswordStoreShortcutCollection : ObservableCollection<PasswordStoreViewModel> public class PasswordStoreShortcutCollection : ObservableCollection<PasswordStoreViewModel>
{ {
private IPasswordStoreService _passwordStoreService;
private readonly PreferencesGroup shortcutsGroup; private readonly PreferencesGroup shortcutsGroup;
private readonly Dictionary<PasswordStoreViewModel, ActionRow> itemToRowMap = new(); private readonly Dictionary<PasswordStoreViewModel, ActionRow> itemToRowMap = new();
public PasswordStoreShortcutCollection(PreferencesGroup shortcutsGroup) public PasswordStoreShortcutCollection(PreferencesGroup shortcutsGroup, IPasswordStoreService passwordStoreService)
: base()
{ {
this.shortcutsGroup = shortcutsGroup; this.shortcutsGroup = shortcutsGroup;
_passwordStoreService = passwordStoreService;
CollectionChanged += OnCollectionChanged; CollectionChanged += OnCollectionChanged;
} }
@@ -59,7 +64,8 @@ public class PasswordStoreShortcutCollection : ObservableCollection<PasswordStor
UpdateRowFromItem(shortcut, ref row); UpdateRowFromItem(shortcut, ref row);
row.SetActivatable(true); row.SetActivatable(true);
row.OnActivated += (sender, args) => { row.OnActivated += (sender, args) =>
{
Console.WriteLine($"[DEBUG] Opening: {shortcut.Path}"); Console.WriteLine($"[DEBUG] Opening: {shortcut.Path}");
}; };
@@ -91,4 +97,11 @@ public class PasswordStoreShortcutCollection : ObservableCollection<PasswordStor
edit.IconName = "document-edit-symbolic"; edit.IconName = "document-edit-symbolic";
row.AddSuffix(edit); row.AddSuffix(edit);
} }
public void Add(string path, string? displayName = null, string? iconName = null)
{
PasswordStoreViewModel item = new PasswordStoreViewModel(path, displayName, iconName);
Add(item);
_passwordStoreService.Create(item.Model);
}
} }

View File

@@ -23,8 +23,20 @@ public class PasswordStoreViewModel : INotifyPropertyChanged
get => _model.Path; get => _model.Path;
} }
public PasswordStore Model { get => _model; }
public PasswordStoreViewModel(PasswordStore item) public PasswordStoreViewModel(PasswordStore item)
{ {
_model = item; _model = item;
} }
public PasswordStoreViewModel(string path, string? displayName = "New Shortcut", string? iconName = "text-x-generic-symbolic")
{
_model = new PasswordStore
{
DisplayName = displayName,
Path = path,
IconName = iconName
};
}
} }

View File

@@ -7,6 +7,11 @@ public class PasswordStoreService : IPasswordStoreService
{ {
private readonly IRepository repository; private readonly IRepository repository;
public PasswordStoreService(IRepository repository)
{
this.repository = repository;
}
public void Create(PasswordStore item) public void Create(PasswordStore item)
{ {
repository.Create(item); repository.Create(item);