mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-05-19 06:46:32 +03:00
add lock timer setting
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");
|
private static readonly string FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeyKeeper", "settings.json");
|
||||||
|
|
||||||
public static bool ExitOnRepositoryClose { get; set; } = false;
|
public static bool ExitOnRepositoryClose { get; set; } = false;
|
||||||
|
public static int LockTimerMinutes { get; set; } = 5;
|
||||||
|
|
||||||
// Сохранение в файл
|
// Сохранение в файл
|
||||||
public static void Save()
|
public static void Save()
|
||||||
@@ -17,7 +18,7 @@ public static class AppSettings
|
|||||||
if (!string.IsNullOrEmpty(directory))
|
if (!string.IsNullOrEmpty(directory))
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
var data = new { ExitOnRepositoryClose };
|
var data = new { ExitOnRepositoryClose, LockTimerMinutes };
|
||||||
string json = JsonSerializer.Serialize(data);
|
string json = JsonSerializer.Serialize(data);
|
||||||
File.WriteAllText(FilePath, json);
|
File.WriteAllText(FilePath, json);
|
||||||
}
|
}
|
||||||
@@ -34,6 +35,7 @@ public static class AppSettings
|
|||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
ExitOnRepositoryClose = data.ExitOnRepositoryClose;
|
ExitOnRepositoryClose = data.ExitOnRepositoryClose;
|
||||||
|
LockTimerMinutes = data.LockTimerMinutes ?? 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { /* Если файл поврежден, просто используем значения по умолчанию */ }
|
catch { /* Если файл поврежден, просто используем значения по умолчанию */ }
|
||||||
@@ -43,5 +45,6 @@ public static class AppSettings
|
|||||||
private class SettingsData
|
private class SettingsData
|
||||||
{
|
{
|
||||||
public bool ExitOnRepositoryClose { get; set; }
|
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.Controls;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
@@ -49,14 +50,60 @@ public class SettingsWindow : Window
|
|||||||
exitOnCloseCheckBox.IsCheckedChanged += (s, e) =>
|
exitOnCloseCheckBox.IsCheckedChanged += (s, e) =>
|
||||||
{
|
{
|
||||||
AppSettings.ExitOnRepositoryClose = exitOnCloseCheckBox.IsChecked ?? false;
|
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(titleText);
|
||||||
mainStack.Children.Add(exitOnCloseCheckBox);
|
mainStack.Children.Add(exitOnCloseCheckBox);
|
||||||
|
mainStack.Children.Add(lockTimerDurationRow);
|
||||||
|
|
||||||
// Назначаем стек основным контентом окна
|
// Назначаем стек основным контентом окна
|
||||||
this.Content = mainStack;
|
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
|
public partial class RepositoryWindowViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan LockTimeout = TimeSpan.FromMinutes(5);
|
|
||||||
|
|
||||||
private object currentPage;
|
private object currentPage;
|
||||||
private IPassStore passStore;
|
private IPassStore passStore;
|
||||||
private DispatcherTimer? _lockTimer;
|
private DispatcherTimer? _lockTimer;
|
||||||
@@ -94,7 +92,7 @@ public partial class RepositoryWindowViewModel : ViewModelBase
|
|||||||
private void OnLockTimerTick(object? sender, EventArgs e)
|
private void OnLockTimerTick(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var elapsed = DateTime.UtcNow - _timerStart;
|
var elapsed = DateTime.UtcNow - _timerStart;
|
||||||
var remaining = LockTimeout - elapsed;
|
var remaining = TimeSpan.FromMinutes(AppSettings.LockTimerMinutes) - elapsed;
|
||||||
|
|
||||||
if (remaining <= TimeSpan.Zero)
|
if (remaining <= TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
@@ -109,7 +107,7 @@ public partial class RepositoryWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
private void UpdateTimerDisplay(TimeSpan? remaining = null)
|
private void UpdateTimerDisplay(TimeSpan? remaining = null)
|
||||||
{
|
{
|
||||||
var r = remaining ?? LockTimeout;
|
var r = remaining ?? TimeSpan.FromMinutes(AppSettings.LockTimerMinutes);
|
||||||
LockTimerDisplay = $"{r:mm\\:ss}";
|
LockTimerDisplay = $"{r:mm\\:ss}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using KeyKeeper.Services;
|
|||||||
using KeyKeeper.ViewModels;
|
using KeyKeeper.ViewModels;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -39,19 +40,31 @@ namespace KeyKeeper.Views
|
|||||||
var path = createVaultDialog.FilePath;
|
var path = createVaultDialog.FilePath;
|
||||||
var password = createVaultDialog.Password;
|
var password = createVaultDialog.Password;
|
||||||
var compositeKey = new CompositeKey(password, null);
|
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;
|
recentFilesService.Remember(path);
|
||||||
OpenRepositoryWindow(passStore);
|
|
||||||
|
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 = "Открыть хранилище паролей",
|
Title = "Открыть хранилище паролей",
|
||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
FileTypeFilter = new[]
|
FileTypeFilter =
|
||||||
{
|
[
|
||||||
new FilePickerFileType("KeyKeeper files")
|
new FilePickerFileType("KeyKeeper files")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.kkp" }
|
Patterns = ["*.kkp"]
|
||||||
},
|
},
|
||||||
new FilePickerFileType("All files")
|
new FilePickerFileType("All files")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.*" }
|
Patterns = ["*.*"]
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (files.Count > 0)
|
if (files.Count > 0)
|
||||||
@@ -80,17 +93,69 @@ namespace KeyKeeper.Views
|
|||||||
if (file.TryGetLocalPath() is string path)
|
if (file.TryGetLocalPath() is string path)
|
||||||
{
|
{
|
||||||
recentFilesService.Remember(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)
|
if (sender is Control c && c.DataContext is RecentFile recentFile)
|
||||||
{
|
{
|
||||||
recentFilesService.Remember(recentFile.Path);
|
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)
|
if (AppSettings.ExitOnRepositoryClose)
|
||||||
{
|
{
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.Show();
|
Show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
repositoryWindow.Show();
|
repositoryWindow.Show();
|
||||||
|
|||||||
Reference in New Issue
Block a user