mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-05-24 09:16:33 +03:00
merge branch 'feature/session-timeout'
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using KeyKeeper.PasswordStore;
|
||||
using static KeyKeeper.PasswordStore.FileFormatConstants;
|
||||
|
||||
@@ -7,8 +8,13 @@ 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;
|
||||
private DateTime _timerStart;
|
||||
private string _lockTimerDisplay = string.Empty;
|
||||
|
||||
public Func<string, Task> ShowErrorPopup;
|
||||
|
||||
@@ -18,6 +24,12 @@ public partial class RepositoryWindowViewModel : ViewModelBase
|
||||
set { currentPage = value; OnPropertyChanged(nameof(CurrentPage)); }
|
||||
}
|
||||
|
||||
public string LockTimerDisplay
|
||||
{
|
||||
get => _lockTimerDisplay;
|
||||
private set { _lockTimerDisplay = value; OnPropertyChanged(nameof(LockTimerDisplay)); }
|
||||
}
|
||||
|
||||
public RepositoryWindowViewModel(IPassStore store)
|
||||
{
|
||||
passStore = store;
|
||||
@@ -32,15 +44,72 @@ public partial class RepositoryWindowViewModel : ViewModelBase
|
||||
SwitchToLocked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сбрасывает таймер блокировки (вызывается при любой активности пользователя).
|
||||
/// </summary>
|
||||
public void ResetLockTimer()
|
||||
{
|
||||
if (_lockTimer != null && _lockTimer.IsEnabled)
|
||||
_timerStart = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private void SwitchToUnlocked()
|
||||
{
|
||||
var directory = passStore.GetGroupByType(GROUP_TYPE_DEFAULT)
|
||||
?? passStore.GetRootDirectory();
|
||||
CurrentPage = new UnlockedRepositoryViewModel(passStore, directory);
|
||||
StartLockTimer();
|
||||
}
|
||||
|
||||
private void SwitchToLocked()
|
||||
{
|
||||
StopLockTimer();
|
||||
CurrentPage = new LockedRepositoryViewModel(passStore, this);
|
||||
}
|
||||
|
||||
public void StartLockTimer()
|
||||
{
|
||||
StopLockTimer();
|
||||
_timerStart = DateTime.UtcNow;
|
||||
_lockTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
_lockTimer.Tick += OnLockTimerTick;
|
||||
_lockTimer.Start();
|
||||
UpdateTimerDisplay();
|
||||
}
|
||||
|
||||
public void StopLockTimer()
|
||||
{
|
||||
if (_lockTimer != null)
|
||||
{
|
||||
_lockTimer.Tick -= OnLockTimerTick;
|
||||
_lockTimer.Stop();
|
||||
_lockTimer = null;
|
||||
}
|
||||
LockTimerDisplay = string.Empty;
|
||||
}
|
||||
|
||||
private void OnLockTimerTick(object? sender, EventArgs e)
|
||||
{
|
||||
var elapsed = DateTime.UtcNow - _timerStart;
|
||||
var remaining = LockTimeout - elapsed;
|
||||
|
||||
if (remaining <= TimeSpan.Zero)
|
||||
{
|
||||
StopLockTimer();
|
||||
passStore.Lock();
|
||||
UpdateLockStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateTimerDisplay(remaining);
|
||||
}
|
||||
|
||||
private void UpdateTimerDisplay(TimeSpan? remaining = null)
|
||||
{
|
||||
var r = remaining ?? LockTimeout;
|
||||
LockTimerDisplay = $"{r:mm\\:ss}";
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,29 @@
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0,0,0,20"/>
|
||||
|
||||
<!-- Таймер блокировки под надписью KeyKeeper -->
|
||||
<Border Background="#CC000000"
|
||||
CornerRadius="6"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0,0,0,12"
|
||||
IsVisible="{Binding $parent[Window].DataContext.LockTimerDisplay,
|
||||
Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6" VerticalAlignment="Center">
|
||||
<!-- Иконка замка (Material Design lock outline) -->
|
||||
<Path Fill="White"
|
||||
VerticalAlignment="Center"
|
||||
Width="13" Height="13"
|
||||
Stretch="Uniform"
|
||||
Data="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>
|
||||
<TextBlock Text="{Binding $parent[Window].DataContext.LockTimerDisplay}"
|
||||
Foreground="White"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Рамочка -->
|
||||
<!-- <Border BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
|
||||
@@ -28,6 +28,15 @@ public partial class RepositoryWindow : Window
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
AddHandler(PointerMovedEvent, OnUserActivity, RoutingStrategies.Tunnel);
|
||||
AddHandler(PointerPressedEvent, OnUserActivity, RoutingStrategies.Tunnel);
|
||||
AddHandler(KeyDownEvent, OnUserActivity, RoutingStrategies.Tunnel);
|
||||
}
|
||||
|
||||
private void OnUserActivity(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is RepositoryWindowViewModel vm)
|
||||
vm.ResetLockTimer();
|
||||
}
|
||||
|
||||
private async void RepositoryWindow_Closing(object? sender, WindowClosingEventArgs e)
|
||||
@@ -77,8 +86,13 @@ public partial class RepositoryWindow : Window
|
||||
if (DataContext is RepositoryWindowViewModel vm_ && vm_.CurrentPage is UnlockedRepositoryViewModel vm)
|
||||
{
|
||||
EntryEditWindow dialog = new();
|
||||
|
||||
vm_.StopLockTimer();
|
||||
|
||||
await dialog.ShowDialog(this);
|
||||
|
||||
vm_.StartLockTimer();
|
||||
|
||||
if (dialog.EditedEntry != null)
|
||||
vm.AddEntry(dialog.EditedEntry);
|
||||
}
|
||||
@@ -104,8 +118,13 @@ public partial class RepositoryWindow : Window
|
||||
|
||||
EntryEditWindow dialog = new();
|
||||
dialog.SetEntry(selectedEntry);
|
||||
|
||||
vm_.StopLockTimer();
|
||||
|
||||
await dialog.ShowDialog(this);
|
||||
|
||||
vm_.StartLockTimer();
|
||||
|
||||
if (dialog.EditedEntry != null)
|
||||
{
|
||||
vm.UpdateEntry(dialog.EditedEntry);
|
||||
@@ -151,7 +170,9 @@ public partial class RepositoryWindow : Window
|
||||
{
|
||||
EntryEditWindow dialog = new();
|
||||
dialog.SetEntry(pwd);
|
||||
vm.StopLockTimer();
|
||||
await dialog.ShowDialog(this);
|
||||
vm.StartLockTimer();
|
||||
if (dialog.EditedEntry != null)
|
||||
{
|
||||
pageVm.UpdateEntry(dialog.EditedEntry);
|
||||
|
||||
Reference in New Issue
Block a user