mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-05-05 22:36:31 +03:00
refactor 'recent files'
rename RecentVault to RecentFile and move it to Models create RecentFilesService to read and manipulate the recent file list inject RecentFilesService into MainWindowViewModel
This commit is contained in:
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using KeyKeeper.ViewModels;
|
||||
using KeyKeeper.Views;
|
||||
using KeyKeeper.Services;
|
||||
|
||||
namespace KeyKeeper;
|
||||
|
||||
@@ -25,7 +26,7 @@ public partial class App : Application
|
||||
DisableAvaloniaDataAnnotationValidation();
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainWindowViewModel(),
|
||||
DataContext = new MainWindowViewModel(new RecentFilesService()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
25
src/KeyKeeper/Models/RecentFile.cs
Normal file
25
src/KeyKeeper/Models/RecentFile.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace KeyKeeper.Models;
|
||||
|
||||
public struct RecentFile
|
||||
{
|
||||
public string Path { get; set; } = string.Empty;
|
||||
public DateTime LastOpened { get; set; }
|
||||
|
||||
public string DisplayPath => Path;
|
||||
|
||||
public RecentFile(string path, DateTime lastOpened)
|
||||
{
|
||||
Path = path;
|
||||
LastOpened = lastOpened;
|
||||
}
|
||||
|
||||
public RecentFile(string path): this(path, DateTime.Now)
|
||||
{}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return DisplayPath;
|
||||
}
|
||||
}
|
||||
16
src/KeyKeeper/Services/IRecentFilesService.cs
Normal file
16
src/KeyKeeper/Services/IRecentFilesService.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using KeyKeeper.Models;
|
||||
|
||||
namespace KeyKeeper.Services;
|
||||
|
||||
public interface IRecentFilesService
|
||||
{
|
||||
// files are stored in reverse chronological order
|
||||
ObservableCollection<RecentFile> RecentFiles { get; }
|
||||
|
||||
void Remember(string filename);
|
||||
void Forget(string filename);
|
||||
void ForgetAll();
|
||||
|
||||
// TODO load and store
|
||||
}
|
||||
50
src/KeyKeeper/Services/RecentFilesService.cs
Normal file
50
src/KeyKeeper/Services/RecentFilesService.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using KeyKeeper.Models;
|
||||
|
||||
namespace KeyKeeper.Services;
|
||||
|
||||
internal class RecentFilesService : IRecentFilesService
|
||||
{
|
||||
// files are stored in reverse chronological order
|
||||
public ObservableCollection<RecentFile> RecentFiles { get; }
|
||||
private readonly int maxEntries = 8;
|
||||
|
||||
public RecentFilesService()
|
||||
{
|
||||
RecentFiles = new ObservableCollection<RecentFile>();
|
||||
}
|
||||
|
||||
public void Remember(string filename)
|
||||
{
|
||||
RemoveIfExists(filename);
|
||||
RecentFiles.Insert(0, new RecentFile(filename));
|
||||
if (RecentFiles.Count > maxEntries)
|
||||
{
|
||||
RecentFiles.RemoveAt(RecentFiles.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void Forget(string filename)
|
||||
{
|
||||
RemoveIfExists(filename);
|
||||
}
|
||||
|
||||
public void ForgetAll()
|
||||
{
|
||||
RecentFiles.Clear();
|
||||
}
|
||||
|
||||
public void RemoveIfExists(string filename)
|
||||
{
|
||||
for (int i = 0; i < RecentFiles.Count; i++)
|
||||
{
|
||||
if (RecentFiles[i].Path == filename)
|
||||
{
|
||||
RecentFiles.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,32 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using KeyKeeper.Views;
|
||||
using Avalonia.Controls;
|
||||
using System.Threading.Tasks;
|
||||
using KeyKeeper.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
using KeyKeeper.Models;
|
||||
|
||||
namespace KeyKeeper.ViewModels;
|
||||
public partial class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
public string Greeting { get; } = "Welcome to KeyKeeper!";
|
||||
public ObservableCollection<RecentFile> RecentFiles => recentFilesService.RecentFiles;
|
||||
|
||||
private IRecentFilesService recentFilesService;
|
||||
|
||||
public MainWindowViewModel(IRecentFilesService recentFilesService)
|
||||
{
|
||||
this.recentFilesService = recentFilesService;
|
||||
}
|
||||
|
||||
public void OpenVault(string filename)
|
||||
{
|
||||
recentFilesService.Remember(filename);
|
||||
}
|
||||
|
||||
public void CreateVault(string filename)
|
||||
{
|
||||
recentFilesService.Remember(filename);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenSettings()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:KeyKeeper.ViewModels"
|
||||
xmlns:m="using:KeyKeeper.Models"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:KeyKeeper.Views"
|
||||
@@ -15,15 +16,15 @@
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid>
|
||||
<TextBlock Text="{Binding Greeting}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20">
|
||||
<Button Content="Настройки"
|
||||
Command="{Binding OpenSettingsCommand}"
|
||||
Width="100"
|
||||
Height="30"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding Greeting}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20">
|
||||
<Button Content="Настройки"
|
||||
Command="{Binding OpenSettingsCommand}"
|
||||
Width="100"
|
||||
Height="30"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -76,9 +77,10 @@
|
||||
Height="120"
|
||||
Background="Transparent"
|
||||
BorderThickness="1"
|
||||
BorderBrush="LightGray">
|
||||
BorderBrush="LightGray"
|
||||
ItemsSource="{Binding RecentFiles}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:RecentVault">
|
||||
<DataTemplate x:DataType="m:RecentFile">
|
||||
<TextBlock Text="{Binding DisplayPath}"
|
||||
Margin="5"/>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -3,6 +3,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using KeyKeeper.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -15,68 +16,48 @@ namespace KeyKeeper.Views
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
LoadRecentVaults();
|
||||
}
|
||||
|
||||
private void LoadRecentVaults()
|
||||
{
|
||||
var recentFiles = new List<RecentVault>
|
||||
{
|
||||
new RecentVault { Path = "C:\\Users\\User\\Documents\\passwords.kdbx", LastOpened = DateTime.Now.AddDays(-1) },
|
||||
new RecentVault { Path = "C:\\Users\\User\\Desktop\\work_passwords.kdbx", LastOpened = DateTime.Now.AddDays(-3) }
|
||||
};
|
||||
|
||||
RecentVaultsList.ItemsSource = recentFiles;
|
||||
}
|
||||
|
||||
private async void CreateNewVault_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel == null) return;
|
||||
|
||||
var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
|
||||
var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
|
||||
{
|
||||
Title = "Создать новое хранилище паролей",
|
||||
SuggestedFileName = "passwords.kdbx",
|
||||
DefaultExtension = "kdbx",
|
||||
SuggestedFileName = "passwords.kkp",
|
||||
DefaultExtension = "kkp",
|
||||
FileTypeChoices = new[]
|
||||
{
|
||||
new FilePickerFileType("KeyKeeper Database")
|
||||
new FilePickerFileType("Хранилище KeyKeeper")
|
||||
{
|
||||
Patterns = new[] { "*.kdbx" }
|
||||
Patterns = new[] { "*.kkp" }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
// Здесь будет логика создания нового хранилища
|
||||
ShowMessage($"Создание нового хранилища: {file.Name}");
|
||||
if (file.TryGetLocalPath() is string path)
|
||||
{
|
||||
ShowMessage($"Создание нового хранилища: {path}");
|
||||
(DataContext as MainWindowViewModel)!.CreateVault(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void OpenExistingVault_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel == null) return;
|
||||
|
||||
// Открываем диалог выбора файла
|
||||
var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
Title = "Открыть хранилище паролей",
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new[]
|
||||
{
|
||||
new FilePickerFileType("KeyKeeper Database")
|
||||
new FilePickerFileType("Хранилище KeyKeeper")
|
||||
{
|
||||
Patterns = new[] { "*.kdbx", "*.kkdb" }
|
||||
Patterns = new[] { "*.kkp" }
|
||||
},
|
||||
new FilePickerFileType("KeePass Database")
|
||||
{
|
||||
Patterns = new[] { "*.kdbx" }
|
||||
},
|
||||
new FilePickerFileType("All Files")
|
||||
new FilePickerFileType("Все файлы")
|
||||
{
|
||||
Patterns = new[] { "*.*" }
|
||||
}
|
||||
@@ -86,7 +67,11 @@ namespace KeyKeeper.Views
|
||||
if (files.Count > 0)
|
||||
{
|
||||
var file = files[0];
|
||||
ShowMessage($"Открытие хранилища: {file.Name}");
|
||||
if (file.TryGetLocalPath() is string path)
|
||||
{
|
||||
ShowMessage($"Открытие хранилища: {path}");
|
||||
(DataContext as MainWindowViewModel)!.OpenVault(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,17 +88,4 @@ namespace KeyKeeper.Views
|
||||
messageBox.ShowDialog(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class RecentVault
|
||||
{
|
||||
public string Path { get; set; } = string.Empty;
|
||||
public DateTime LastOpened { get; set; }
|
||||
|
||||
public string DisplayPath => System.IO.Path.GetFileName(Path);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return DisplayPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user