diff --git a/src/Languages/lang_en.json b/src/Languages/lang_en.json index efc443e2cc..d128bb58e9 100644 --- a/src/Languages/lang_en.json +++ b/src/Languages/lang_en.json @@ -1,4 +1,5 @@ { + "{0} of {1} operations completed": "{0} of {1} operations completed", "{0} is now {1}": "{0} is now {1}", "Enabled": "Enabled", "Disabled": "Disabled", diff --git a/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs index 442f77989a..6adf890334 100644 --- a/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs @@ -22,6 +22,7 @@ using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; using UniGetUI.PackageEngine.PackageLoader; +using UniGetUI.PackageOperations; namespace UniGetUI.Avalonia.ViewModels; @@ -67,6 +68,41 @@ public partial class MainWindowViewModel : ViewModelBase [ObservableProperty] private bool _operationsPanelExpanded = true; + private readonly List _operationBatch = new(); + + public string OperationsHeaderText + { + get + { + int total = _operationBatch.Count; + if (total == 0) + return CoreTools.Translate("Operations"); + int completed = _operationBatch.Count(o => + o.Status is OperationStatus.Succeeded or OperationStatus.Failed or OperationStatus.Canceled); + return CoreTools.Translate("{0} of {1} operations completed", completed, total); + } + } + + private void AddToBatch(AbstractOperation op) + { + if (_operationBatch.Count > 0 + && _operationBatch.All(o => o.Status is not (OperationStatus.InQueue or OperationStatus.Running))) + { + foreach (var old in _operationBatch) + old.StatusChanged -= OnOperationStatusChanged; + _operationBatch.Clear(); + } + + if (!_operationBatch.Contains(op)) + { + _operationBatch.Add(op); + op.StatusChanged += OnOperationStatusChanged; + } + } + + private void OnOperationStatusChanged(object? sender, OperationStatus e) + => Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(OperationsHeaderText))); + [RelayCommand] private void ToggleOperationsPanel() => OperationsPanelExpanded = !OperationsPanelExpanded; @@ -221,8 +257,14 @@ public MainWindowViewModel() AvaloniaAutoUpdater.CheckForOrphanedUpdateAttempt(); // Keep OperationsPanelVisible in sync with the live operations list - Operations.CollectionChanged += (_, _) => + Operations.CollectionChanged += (_, e) => + { + if (e.NewItems is not null) + foreach (OperationViewModel vm in e.NewItems) + AddToBatch(vm.Operation); OperationsPanelVisible = Operations.Count > 0; + OnPropertyChanged(nameof(OperationsHeaderText)); + }; if (OperatingSystem.IsWindows() && CoreTools.IsAdministrator() && !Settings.Get(Settings.K.AlreadyWarnedAboutAdmin)) { diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml b/src/UniGetUI.Avalonia/Views/MainWindow.axaml index 4f180a7b87..9182ff5275 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml @@ -112,7 +112,7 @@ diff --git a/src/UniGetUI/Pages/MainView.xaml b/src/UniGetUI/Pages/MainView.xaml index 845fea0d7b..8b2259e85c 100644 --- a/src/UniGetUI/Pages/MainView.xaml +++ b/src/UniGetUI/Pages/MainView.xaml @@ -407,6 +407,16 @@ + + MainApp.Operations._operationList.CollectionChanged += (_, e) => + { + if (e.NewItems is not null) + foreach (OperationControl c in e.NewItems) + AddToBatch(c.Operation); UpdateOperationsLayout(); + }; if (!Settings.Get(Settings.K.ShownTelemetryBanner)) { @@ -478,6 +484,38 @@ private void UpdateOperationsLayout() OperationSplitterMenuButton.Visibility = Visibility.Collapsed; } ResizingOPLayout = false; + UpdateOperationCount(); + } + + private readonly List _operationBatch = new(); + + private void AddToBatch(AbstractOperation op) + { + if (_operationBatch.Count > 0 + && _operationBatch.All(o => o.Status is not (OperationStatus.InQueue or OperationStatus.Running))) + { + foreach (var old in _operationBatch) + old.StatusChanged -= OnAnyOperationStatusChanged; + _operationBatch.Clear(); + } + + if (!_operationBatch.Contains(op)) + { + _operationBatch.Add(op); + op.StatusChanged += OnAnyOperationStatusChanged; + } + } + + private void OnAnyOperationStatusChanged(object? sender, OperationStatus e) + => DispatcherQueue.TryEnqueue(UpdateOperationCount); + + private void UpdateOperationCount() + { + int total = _operationBatch.Count; + int completed = _operationBatch.Count(o => + o.Status is OperationStatus.Succeeded or OperationStatus.Failed or OperationStatus.Canceled); + OperationCountLabel.Text = + total > 0 ? CoreTools.Translate("{0} of {1} operations completed", completed, total) : ""; } private void OperationScrollView_SizeChanged(object sender, SizeChangedEventArgs e)