From d4d3864b81570626f84fd883e435c3c05f4cf39a Mon Sep 17 00:00:00 2001 From: Slavasil Date: Thu, 26 Mar 2026 23:29:22 +0300 Subject: [PATCH 1/8] basic recent files list, no persistence --- src/KeyKeeper/App.axaml.cs | 5 +++-- .../ViewModels/MainWindowViewModel.cs | 10 ---------- src/KeyKeeper/Views/MainWindow.axaml | 13 ++++++++----- src/KeyKeeper/Views/MainWindow.axaml.cs | 19 ++++++++++++++++++- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/KeyKeeper/App.axaml.cs b/src/KeyKeeper/App.axaml.cs index a55380a..0dba5a8 100644 --- a/src/KeyKeeper/App.axaml.cs +++ b/src/KeyKeeper/App.axaml.cs @@ -24,9 +24,10 @@ public partial class App : Application // Avoid duplicate validations from both Avalonia and the CommunityToolkit. // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins DisableAvaloniaDataAnnotationValidation(); - desktop.MainWindow = new MainWindow + var recentFilesService = new RecentFilesService(); + desktop.MainWindow = new MainWindow(recentFilesService) { - DataContext = new MainWindowViewModel(new RecentFilesService()), + DataContext = new MainWindowViewModel(recentFilesService), }; } diff --git a/src/KeyKeeper/ViewModels/MainWindowViewModel.cs b/src/KeyKeeper/ViewModels/MainWindowViewModel.cs index 7903556..f967df0 100644 --- a/src/KeyKeeper/ViewModels/MainWindowViewModel.cs +++ b/src/KeyKeeper/ViewModels/MainWindowViewModel.cs @@ -20,16 +20,6 @@ public partial class MainWindowViewModel : ViewModelBase this.recentFilesService = recentFilesService; } - public void OpenVault(string filename) - { - recentFilesService.Remember(filename); - } - - public void CreateVault(string filename) - { - recentFilesService.Remember(filename); - } - [RelayCommand] private async Task OpenSettings() { diff --git a/src/KeyKeeper/Views/MainWindow.axaml b/src/KeyKeeper/Views/MainWindow.axaml index 00b2174..c3d9dad 100644 --- a/src/KeyKeeper/Views/MainWindow.axaml +++ b/src/KeyKeeper/Views/MainWindow.axaml @@ -78,9 +78,14 @@ ItemsSource="{Binding RecentFiles}"> - + + + @@ -93,7 +98,5 @@ - - diff --git a/src/KeyKeeper/Views/MainWindow.axaml.cs b/src/KeyKeeper/Views/MainWindow.axaml.cs index ee8549a..c8bb345 100644 --- a/src/KeyKeeper/Views/MainWindow.axaml.cs +++ b/src/KeyKeeper/Views/MainWindow.axaml.cs @@ -3,8 +3,10 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Platform.Storage; +using KeyKeeper.Models; using KeyKeeper.PasswordStore; using KeyKeeper.PasswordStore.Crypto; +using KeyKeeper.Services; using KeyKeeper.ViewModels; using System; using System.Collections.Generic; @@ -15,8 +17,11 @@ namespace KeyKeeper.Views { public partial class MainWindow : Window { - public MainWindow() + private IRecentFilesService recentFilesService; + + public MainWindow(IRecentFilesService recentFilesService) { + this.recentFilesService = recentFilesService; InitializeComponent(); } @@ -40,6 +45,9 @@ namespace KeyKeeper.Views Key = compositeKey, LockTimeoutSeconds = 800 }); + + recentFilesService.Remember(path); + IPassStore passStore = passStoreAccessor; OpenRepositoryWindow(passStore); } @@ -69,11 +77,20 @@ namespace KeyKeeper.Views var file = files[0]; if (file.TryGetLocalPath() is string path) { + recentFilesService.Remember(path); OpenRepositoryWindow(new PassStoreFileAccessor(path, false, null)); } } } + private void RecentVaultsListItem_DoubleTapped(object sender, RoutedEventArgs e) + { + if (sender is Control c && c.DataContext is RecentFile recentFile) + { + OpenRepositoryWindow(new PassStoreFileAccessor(recentFile.Path, false, null)); + } + } + private void OpenRepositoryWindow(IPassStore passStore) { var repositoryWindow = new RepositoryWindow(new RepositoryWindowViewModel(passStore)) From 26cfa7e8e1b56d591717a3dd52ca637b9e4654a7 Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 22:40:01 +0300 Subject: [PATCH 2/8] minor changes in RecentFilesService.cs --- src/KeyKeeper/Services/RecentFilesService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/KeyKeeper/Services/RecentFilesService.cs b/src/KeyKeeper/Services/RecentFilesService.cs index 7ddc7e4..ea12e0d 100644 --- a/src/KeyKeeper/Services/RecentFilesService.cs +++ b/src/KeyKeeper/Services/RecentFilesService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.ObjectModel; +using System.IO; using System.Linq; using KeyKeeper.Models; @@ -7,13 +8,20 @@ namespace KeyKeeper.Services; internal class RecentFilesService : IRecentFilesService { + private const string RecentFilesFilename = "recent-files.json"; + // files are stored in reverse chronological order public ObservableCollection RecentFiles { get; } private readonly int maxEntries = 8; + private readonly string recentFilesPath; public RecentFilesService() { RecentFiles = new ObservableCollection(); + var appDataDirectory = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "KeyKeeper"); + recentFilesPath = Path.Combine(appDataDirectory, RecentFilesFilename); } public void Remember(string filename) From 651f1106e40c5c16d7701a95be0135546c1e1b9d Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 22:47:28 +0300 Subject: [PATCH 3/8] add Load method --- src/KeyKeeper/Services/RecentFilesService.cs | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/KeyKeeper/Services/RecentFilesService.cs b/src/KeyKeeper/Services/RecentFilesService.cs index ea12e0d..5b253db 100644 --- a/src/KeyKeeper/Services/RecentFilesService.cs +++ b/src/KeyKeeper/Services/RecentFilesService.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Text.Json; using KeyKeeper.Models; namespace KeyKeeper.Services; @@ -24,6 +26,33 @@ internal class RecentFilesService : IRecentFilesService recentFilesPath = Path.Combine(appDataDirectory, RecentFilesFilename); } + public void Load() + { + RecentFiles.Clear(); + + if (!File.Exists(recentFilesPath)) + { + return; + } + + try + { + var content = File.ReadAllText(recentFilesPath); + var loadedFiles = JsonSerializer.Deserialize>(content) ?? new List(); + + foreach (var recentFile in loadedFiles + .OrderByDescending(file => file.LastOpened) + .Take(maxEntries)) + { + RecentFiles.Add(recentFile); + } + } + catch + { + // ignore broken data and continue with empty recent files + } + } + public void Remember(string filename) { RemoveIfExists(filename); From 6d333e94f8388a16aa6f0a78777659e81a8a26dd Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 22:49:44 +0300 Subject: [PATCH 4/8] add Save method --- src/KeyKeeper/Services/RecentFilesService.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/KeyKeeper/Services/RecentFilesService.cs b/src/KeyKeeper/Services/RecentFilesService.cs index 5b253db..183d0b2 100644 --- a/src/KeyKeeper/Services/RecentFilesService.cs +++ b/src/KeyKeeper/Services/RecentFilesService.cs @@ -11,6 +11,7 @@ namespace KeyKeeper.Services; internal class RecentFilesService : IRecentFilesService { private const string RecentFilesFilename = "recent-files.json"; + private static readonly JsonSerializerOptions jsonOptions = new() { WriteIndented = true }; // files are stored in reverse chronological order public ObservableCollection RecentFiles { get; } @@ -53,6 +54,18 @@ internal class RecentFilesService : IRecentFilesService } } + public void Save() + { + var directory = Path.GetDirectoryName(recentFilesPath); + if (!string.IsNullOrEmpty(directory)) + { + Directory.CreateDirectory(directory); + } + + var payload = JsonSerializer.Serialize(RecentFiles, jsonOptions); + File.WriteAllText(recentFilesPath, payload); + } + public void Remember(string filename) { RemoveIfExists(filename); From 3f757e708afe211aad79806c183a17829c734116 Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 22:52:46 +0300 Subject: [PATCH 5/8] add Save to methods --- src/KeyKeeper/Services/RecentFilesService.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/KeyKeeper/Services/RecentFilesService.cs b/src/KeyKeeper/Services/RecentFilesService.cs index 183d0b2..aa569c2 100644 --- a/src/KeyKeeper/Services/RecentFilesService.cs +++ b/src/KeyKeeper/Services/RecentFilesService.cs @@ -74,16 +74,19 @@ internal class RecentFilesService : IRecentFilesService { RecentFiles.RemoveAt(RecentFiles.Count - 1); } + Save(); } public void Forget(string filename) { RemoveIfExists(filename); + Save(); } public void ForgetAll() { RecentFiles.Clear(); + Save(); } public void RemoveIfExists(string filename) From f2a5c342f6e0e2d5fbb1ff31f1992d526cda4931 Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 23:03:01 +0300 Subject: [PATCH 6/8] add new methods to IRecentFilesService.cs --- src/KeyKeeper/Services/IRecentFilesService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/KeyKeeper/Services/IRecentFilesService.cs b/src/KeyKeeper/Services/IRecentFilesService.cs index 8621da8..25894f0 100644 --- a/src/KeyKeeper/Services/IRecentFilesService.cs +++ b/src/KeyKeeper/Services/IRecentFilesService.cs @@ -7,10 +7,11 @@ public interface IRecentFilesService { // files are stored in reverse chronological order ObservableCollection RecentFiles { get; } - + + void Load(); + void Save(); + void Remember(string filename); void Forget(string filename); void ForgetAll(); - - // TODO load and store } \ No newline at end of file From 128cbae4317442fc9f07a781a779551544bb9f5b Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 23:08:39 +0300 Subject: [PATCH 7/8] update RecentVaultsListItem_DoubleTapped in MainWindow.axaml.cs --- src/KeyKeeper/Views/MainWindow.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/KeyKeeper/Views/MainWindow.axaml.cs b/src/KeyKeeper/Views/MainWindow.axaml.cs index c8bb345..527ae1a 100644 --- a/src/KeyKeeper/Views/MainWindow.axaml.cs +++ b/src/KeyKeeper/Views/MainWindow.axaml.cs @@ -87,6 +87,7 @@ namespace KeyKeeper.Views { if (sender is Control c && c.DataContext is RecentFile recentFile) { + recentFilesService.Remember(recentFile.Path); OpenRepositoryWindow(new PassStoreFileAccessor(recentFile.Path, false, null)); } } From 552c8577997a1e660e442bab05234b0b1b5c98c2 Mon Sep 17 00:00:00 2001 From: InspectorIT Date: Fri, 27 Mar 2026 23:12:29 +0300 Subject: [PATCH 8/8] update OnFrameworkInitializationCompleted in App.axaml.cs --- src/KeyKeeper/App.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/KeyKeeper/App.axaml.cs b/src/KeyKeeper/App.axaml.cs index 0dba5a8..e61b9f4 100644 --- a/src/KeyKeeper/App.axaml.cs +++ b/src/KeyKeeper/App.axaml.cs @@ -25,6 +25,7 @@ public partial class App : Application // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins DisableAvaloniaDataAnnotationValidation(); var recentFilesService = new RecentFilesService(); + recentFilesService.Load(); desktop.MainWindow = new MainWindow(recentFilesService) { DataContext = new MainWindowViewModel(recentFilesService),