diff --git a/src/App.axaml.cs b/src/App.axaml.cs index f5c0559a1..a106ca0e0 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -575,6 +575,7 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop) _launcher = new ViewModels.Launcher(startupRepo); desktop.MainWindow = new Views.Launcher() { DataContext = _launcher }; + Models.UserActivityTracker.Instance.Initialize(); desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; #if !DISABLE_UPDATE_DETECTION diff --git a/src/Models/UserActivityTracker.cs b/src/Models/UserActivityTracker.cs new file mode 100644 index 000000000..78e28eb23 --- /dev/null +++ b/src/Models/UserActivityTracker.cs @@ -0,0 +1,84 @@ +using System; +using System.Threading; + +namespace SourceGit.Models +{ + public class UserActivityTracker + { + private const int DefaultMinIdleSecondsBeforeAutoFetch = 15; + + private static readonly Lazy s_instance = new(() => new UserActivityTracker()); + private bool _isWindowActive = false; + private DateTime _lastActivity = DateTime.MinValue; + private readonly Lock _lockObject = new(); + private readonly int _minIdleSecondsBeforeAutoFetch = DefaultMinIdleSecondsBeforeAutoFetch; + + private void OnUserActivity(object sender, EventArgs e) => UpdateLastActivity(); + + private void OnWindowActivated(object sender, EventArgs e) + { + lock (_lockObject) + { + _isWindowActive = true; + _lastActivity = DateTime.Now; + } + } + + private void OnWindowDeactivated(object sender, EventArgs e) + { + lock (_lockObject) + _isWindowActive = false; + } + + public void Initialize() + { + lock (_lockObject) + { + _lastActivity = DateTime.Now; + _isWindowActive = true; + } + + if (App.Current?.ApplicationLifetime is not Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop) + return; + + if (desktop.MainWindow == null) + return; + + desktop.MainWindow.Activated += OnWindowActivated; + desktop.MainWindow.Deactivated += OnWindowDeactivated; + desktop.MainWindow.KeyDown += OnUserActivity; + desktop.MainWindow.PointerPressed += OnUserActivity; + desktop.MainWindow.PointerMoved += OnUserActivity; + desktop.MainWindow.PointerWheelChanged += OnUserActivity; + } + + public bool ShouldPerformAutoFetch(DateTime lastFetchTime, int intervalMinutes) + { + var now = DateTime.Now; + + if (now < lastFetchTime.AddMinutes(intervalMinutes)) + return false; + + lock (_lockObject) + { + if (!_isWindowActive) + return true; + + var timeSinceLastActivity = now - _lastActivity; + + if (timeSinceLastActivity.TotalSeconds >= _minIdleSecondsBeforeAutoFetch) + return true; + } + + return false; + } + + public void UpdateLastActivity() + { + lock (_lockObject) + _lastActivity = DateTime.Now; + } + + public static UserActivityTracker Instance => s_instance.Value; + } +} diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index e51347457..5b29d19c6 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -3074,9 +3074,7 @@ private async void AutoFetchImpl(object sender) if (File.Exists(lockFile)) return; - var now = DateTime.Now; - var desire = _lastFetchTime.AddMinutes(_settings.AutoFetchInterval); - if (desire > now) + if (!Models.UserActivityTracker.Instance.ShouldPerformAutoFetch(_lastFetchTime, _settings.AutoFetchInterval)) return; var remotes = new List();