diff --git a/src/KeyKeeper/ViewModels/RepositoryWindowViewModel.cs b/src/KeyKeeper/ViewModels/RepositoryWindowViewModel.cs index d576097..48fab08 100644 --- a/src/KeyKeeper/ViewModels/RepositoryWindowViewModel.cs +++ b/src/KeyKeeper/ViewModels/RepositoryWindowViewModel.cs @@ -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 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(); } + /// + /// Сбрасывает таймер блокировки (вызывается при любой активности пользователя). + /// + 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); } -} \ No newline at end of file + + 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}"; + } +} diff --git a/src/KeyKeeper/Views/RepositoryWindow.axaml b/src/KeyKeeper/Views/RepositoryWindow.axaml index e407c8d..126c690 100644 --- a/src/KeyKeeper/Views/RepositoryWindow.axaml +++ b/src/KeyKeeper/Views/RepositoryWindow.axaml @@ -4,7 +4,7 @@ xmlns:i="using:Avalonia.Interactivity" xmlns:kkp="using:KeyKeeper.Views" x:Class="KeyKeeper.Views.RepositoryWindow" - Closing="RepositoryWindow_Closing" + Closing="RepositoryWindow_Closing" Title="KeyKeeper - Password store" CanResize="False" Width="800" @@ -12,36 +12,59 @@ Background="White" x:DataType="vm:RepositoryWindowViewModel"> - - - - - + + + + - - - + + + + + + + + + + + - @@ -52,93 +75,93 @@ HorizontalAlignment="Left" Margin="0,20,0,0"/> - -