mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-05-18 22:36:30 +03:00
merge branch 'feature/configurable-lock-timer'
This commit is contained in:
@@ -9,6 +9,7 @@ public static class AppSettings
|
||||
private static readonly string FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeyKeeper", "settings.json");
|
||||
|
||||
public static bool ExitOnRepositoryClose { get; set; } = false;
|
||||
public static int LockTimerMinutes { get; set; } = 5;
|
||||
|
||||
// Сохранение в файл
|
||||
public static void Save()
|
||||
@@ -17,7 +18,7 @@ public static class AppSettings
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
var data = new { ExitOnRepositoryClose };
|
||||
var data = new { ExitOnRepositoryClose, LockTimerMinutes };
|
||||
string json = JsonSerializer.Serialize(data);
|
||||
File.WriteAllText(FilePath, json);
|
||||
}
|
||||
@@ -34,6 +35,7 @@ public static class AppSettings
|
||||
if (data != null)
|
||||
{
|
||||
ExitOnRepositoryClose = data.ExitOnRepositoryClose;
|
||||
LockTimerMinutes = data.LockTimerMinutes ?? 5;
|
||||
}
|
||||
}
|
||||
catch { /* Если файл поврежден, просто используем значения по умолчанию */ }
|
||||
@@ -43,5 +45,6 @@ public static class AppSettings
|
||||
private class SettingsData
|
||||
{
|
||||
public bool ExitOnRepositoryClose { get; set; }
|
||||
public int? LockTimerMinutes { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Avalonia;
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
@@ -49,14 +50,60 @@ public class SettingsWindow : Window
|
||||
exitOnCloseCheckBox.IsCheckedChanged += (s, e) =>
|
||||
{
|
||||
AppSettings.ExitOnRepositoryClose = exitOnCloseCheckBox.IsChecked ?? false;
|
||||
AppSettings.Save();
|
||||
};
|
||||
|
||||
// Настройка таймера блокировки
|
||||
var lockTimerDurationRow = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Spacing = 12,
|
||||
};
|
||||
|
||||
var lockTimerDurationLabel1 = new TextBlock
|
||||
{
|
||||
Text = "Lock the vault after",
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
|
||||
var lockTimerDurationInput = new NumericUpDown
|
||||
{
|
||||
Value = AppSettings.LockTimerMinutes,
|
||||
Increment = 1,
|
||||
Minimum = 1,
|
||||
Maximum = 90,
|
||||
ClipValueToMinMax = true,
|
||||
Width = 120,
|
||||
};
|
||||
|
||||
var lockTimerDurationLabel2 = new TextBlock
|
||||
{
|
||||
Text = "minutes of inactivity",
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
|
||||
lockTimerDurationInput.ValueChanged += (_, _) =>
|
||||
{
|
||||
Console.WriteLine($"Set timer to {lockTimerDurationInput.Value} minutes");
|
||||
AppSettings.LockTimerMinutes = (int)(lockTimerDurationInput.Value ?? 5m);
|
||||
};
|
||||
|
||||
lockTimerDurationRow.Children.Add(lockTimerDurationLabel1);
|
||||
lockTimerDurationRow.Children.Add(lockTimerDurationInput);
|
||||
lockTimerDurationRow.Children.Add(lockTimerDurationLabel2);
|
||||
|
||||
// Добавляем элементы в стек
|
||||
mainStack.Children.Add(titleText);
|
||||
mainStack.Children.Add(exitOnCloseCheckBox);
|
||||
mainStack.Children.Add(lockTimerDurationRow);
|
||||
|
||||
// Назначаем стек основным контентом окна
|
||||
this.Content = mainStack;
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
Console.WriteLine("Saving application settings");
|
||||
AppSettings.Save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace KeyKeeper.ViewModels;
|
||||
|
||||
public partial class RepositoryWindowViewModel : ViewModelBase
|
||||
{
|
||||
private static readonly TimeSpan LockTimeout = TimeSpan.FromMinutes(5);
|
||||
|
||||
private object currentPage;
|
||||
private IPassStore passStore;
|
||||
private DispatcherTimer? _lockTimer;
|
||||
@@ -94,7 +92,7 @@ public partial class RepositoryWindowViewModel : ViewModelBase
|
||||
private void OnLockTimerTick(object? sender, EventArgs e)
|
||||
{
|
||||
var elapsed = DateTime.UtcNow - _timerStart;
|
||||
var remaining = LockTimeout - elapsed;
|
||||
var remaining = TimeSpan.FromMinutes(AppSettings.LockTimerMinutes) - elapsed;
|
||||
|
||||
if (remaining <= TimeSpan.Zero)
|
||||
{
|
||||
@@ -109,7 +107,7 @@ public partial class RepositoryWindowViewModel : ViewModelBase
|
||||
|
||||
private void UpdateTimerDisplay(TimeSpan? remaining = null)
|
||||
{
|
||||
var r = remaining ?? LockTimeout;
|
||||
var r = remaining ?? TimeSpan.FromMinutes(AppSettings.LockTimerMinutes);
|
||||
LockTimerDisplay = $"{r:mm\\:ss}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using KeyKeeper.Services;
|
||||
using KeyKeeper.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -39,19 +40,31 @@ namespace KeyKeeper.Views
|
||||
var path = createVaultDialog.FilePath;
|
||||
var password = createVaultDialog.Password;
|
||||
var compositeKey = new CompositeKey(password, null);
|
||||
var passStoreAccessor = new PassStoreFileAccessor(
|
||||
filename: path,
|
||||
create: true,
|
||||
createOptions: new StoreCreationOptions()
|
||||
{
|
||||
Key = compositeKey,
|
||||
LockTimeoutSeconds = 800
|
||||
});
|
||||
|
||||
recentFilesService.Remember(path);
|
||||
try
|
||||
{
|
||||
var passStoreAccessor = new PassStoreFileAccessor(
|
||||
filename: path,
|
||||
create: true,
|
||||
createOptions: new StoreCreationOptions()
|
||||
{
|
||||
Key = compositeKey,
|
||||
LockTimeoutSeconds = 800
|
||||
});
|
||||
|
||||
IPassStore passStore = passStoreAccessor;
|
||||
OpenRepositoryWindow(passStore);
|
||||
recentFilesService.Remember(path);
|
||||
|
||||
IPassStore passStore = passStoreAccessor;
|
||||
OpenRepositoryWindow(passStore);
|
||||
} catch (IOException exception)
|
||||
{
|
||||
Console.WriteLine($"I/O error when creating \"{path}\": {exception}");
|
||||
await new ErrorDialog("Cannot create the password store", "File error").ShowDialog(this);
|
||||
} catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine($"Unknown error when creating \"{path}\": {exception}");
|
||||
await new ErrorDialog("Cannot create the password store", "Unknown error").ShowDialog(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,17 +74,17 @@ namespace KeyKeeper.Views
|
||||
{
|
||||
Title = "Открыть хранилище паролей",
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new[]
|
||||
{
|
||||
FileTypeFilter =
|
||||
[
|
||||
new FilePickerFileType("KeyKeeper files")
|
||||
{
|
||||
Patterns = new[] { "*.kkp" }
|
||||
Patterns = ["*.kkp"]
|
||||
},
|
||||
new FilePickerFileType("All files")
|
||||
{
|
||||
Patterns = new[] { "*.*" }
|
||||
Patterns = ["*.*"]
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (files.Count > 0)
|
||||
@@ -80,17 +93,69 @@ namespace KeyKeeper.Views
|
||||
if (file.TryGetLocalPath() is string path)
|
||||
{
|
||||
recentFilesService.Remember(path);
|
||||
OpenRepositoryWindow(new PassStoreFileAccessor(path, false, null));
|
||||
IPassStore? passStore;
|
||||
try
|
||||
{
|
||||
passStore = new PassStoreFileAccessor(path, false, null);
|
||||
} catch (PassStoreFileException exc)
|
||||
{
|
||||
await new ErrorDialog($"This password store file has a problem: {exc.Message}", "File format error").ShowDialog(this);
|
||||
Console.WriteLine($"Format error when opening \"{path}\": {exc}");
|
||||
recentFilesService.Forget(path);
|
||||
return;
|
||||
} catch (FileNotFoundException)
|
||||
{
|
||||
await new ErrorDialog("This password store no longer exists", "File error").ShowDialog(this);
|
||||
recentFilesService.Forget(path);
|
||||
return;
|
||||
} catch (IOException exc)
|
||||
{
|
||||
Console.WriteLine($"I/O error when opening \"{path}\": {exc}");
|
||||
await new ErrorDialog("Cannot open this password store", "File error").ShowDialog(this);
|
||||
return;
|
||||
} catch (Exception exc)
|
||||
{
|
||||
Console.WriteLine($"Unknown error when opening \"{path}\": {exc}");
|
||||
await new ErrorDialog("Cannot open this password store", "File error").ShowDialog(this);
|
||||
return;
|
||||
}
|
||||
OpenRepositoryWindow(passStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecentVaultsListItem_DoubleTapped(object sender, RoutedEventArgs e)
|
||||
private async void RecentVaultsListItem_DoubleTapped(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Control c && c.DataContext is RecentFile recentFile)
|
||||
{
|
||||
recentFilesService.Remember(recentFile.Path);
|
||||
OpenRepositoryWindow(new PassStoreFileAccessor(recentFile.Path, false, null));
|
||||
IPassStore? passStore;
|
||||
try
|
||||
{
|
||||
passStore = new PassStoreFileAccessor(recentFile.Path, false, null);
|
||||
} catch (PassStoreFileException exc)
|
||||
{
|
||||
await new ErrorDialog($"This password store file has a problem: {exc.Message}", "File format error").ShowDialog(this);
|
||||
Console.WriteLine($"Format error when opening \"{recentFile.Path}\" from recents: {exc}");
|
||||
recentFilesService.Forget(recentFile.Path);
|
||||
return;
|
||||
} catch (FileNotFoundException)
|
||||
{
|
||||
await new ErrorDialog("This password store no longer exists", "File error").ShowDialog(this);
|
||||
recentFilesService.Forget(recentFile.Path);
|
||||
return;
|
||||
} catch (IOException exc)
|
||||
{
|
||||
Console.WriteLine($"I/O error when opening \"{recentFile.Path}\" from recents: {exc}");
|
||||
await new ErrorDialog("Cannot open this password store", "File error").ShowDialog(this);
|
||||
return;
|
||||
} catch (Exception exc)
|
||||
{
|
||||
Console.WriteLine($"Unknown error when opening \"{recentFile.Path}\" from recents: {exc}");
|
||||
await new ErrorDialog("Cannot open this password store", "File error").ShowDialog(this);
|
||||
return;
|
||||
}
|
||||
OpenRepositoryWindow(passStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +169,11 @@ namespace KeyKeeper.Views
|
||||
{
|
||||
if (AppSettings.ExitOnRepositoryClose)
|
||||
{
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Show();
|
||||
Show();
|
||||
}
|
||||
};
|
||||
repositoryWindow.Show();
|
||||
|
||||
Reference in New Issue
Block a user