From c727286d225a77fcf36c2c6504c15e604536f030 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Wed, 13 Jul 2022 16:22:45 -0600 Subject: [PATCH] Move ProcessQueue biz logic into viewmodel --- .../AvaloniaUI/Assets/glass-with-glow_16.png | Bin 0 -> 482 bytes .../AvaloniaUI/AsyncNotifyPropertyChanged2.cs | 14 -- .../AvaloniaUI/AvaloniaUtils.cs | 5 - .../ViewModels/ProcessQueueViewModel.cs | 114 ++++++++- .../Views/MainWindow/MainWindow.axaml | 4 +- .../Views/ProcessQueueControl2.axaml | 16 +- .../Views/ProcessQueueControl2.axaml.cs | 238 ++++-------------- .../ProductsDisplay2.Display.xaml.cs | 1 + .../LibationWinForms/LibationWinForms.csproj | 1 + .../PublishProfiles/FolderProfile.pubxml | 2 +- 10 files changed, 180 insertions(+), 215 deletions(-) create mode 100644 Source/LibationWinForms/AvaloniaUI/Assets/glass-with-glow_16.png delete mode 100644 Source/LibationWinForms/AvaloniaUI/AsyncNotifyPropertyChanged2.cs diff --git a/Source/LibationWinForms/AvaloniaUI/Assets/glass-with-glow_16.png b/Source/LibationWinForms/AvaloniaUI/Assets/glass-with-glow_16.png new file mode 100644 index 0000000000000000000000000000000000000000..05e40becffe3a7977ba6e9a7962e3ec6d6432159 GIT binary patch literal 482 zcmV<80UiE{P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0e4A6K~y+Ttx>^B z0#Ov5H_d`u|G#ZW7Xf(Qq&VeGu96e^BE9#myfjgZ}_dL&ggKmMBDYob_1MPx3 z@amcCy0X{n#VoT(j~QlBuaTVKwNNNX(@eH>&|?O2zUk->JlATqWfVmspU Avalonia.Threading.Dispatcher.UIThread.Post(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))); - } -} diff --git a/Source/LibationWinForms/AvaloniaUI/AvaloniaUtils.cs b/Source/LibationWinForms/AvaloniaUI/AvaloniaUtils.cs index 18e28559..30ef7dd2 100644 --- a/Source/LibationWinForms/AvaloniaUI/AvaloniaUtils.cs +++ b/Source/LibationWinForms/AvaloniaUI/AvaloniaUtils.cs @@ -1,15 +1,10 @@ using Avalonia.Media; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace LibationWinForms.AvaloniaUI { internal static class AvaloniaUtils { - public static IBrush GetBrushFromResources(string name) => GetBrushFromResources(name, Brushes.Transparent); public static IBrush GetBrushFromResources(string name, IBrush defaultBrush) diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/ProcessQueueViewModel.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/ProcessQueueViewModel.cs index acde7ab6..ffff1d8e 100644 --- a/Source/LibationWinForms/AvaloniaUI/ViewModels/ProcessQueueViewModel.cs +++ b/Source/LibationWinForms/AvaloniaUI/ViewModels/ProcessQueueViewModel.cs @@ -1,7 +1,11 @@ -using Avalonia.Threading; +using ApplicationServices; +using Avalonia.Threading; using ReactiveUI; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; namespace LibationWinForms.AvaloniaUI.ViewModels { @@ -15,7 +19,52 @@ namespace LibationWinForms.AvaloniaUI.ViewModels set => this.RaiseAndSetIfChanged(ref _items, value); } + private TrackedQueue2 Queue => Items; + public ProcessBook2 SelectedItem { get; set; } + public Task QueueRunner { get; private set; } + public bool Running => !QueueRunner?.IsCompleted ?? false; + + public ProcessQueueViewModel() + { + Queue.QueuededCountChanged += Queue_QueuededCountChanged; + Queue.CompletedCountChanged += Queue_CompletedCountChanged; + } + + private int _completedCount; + private int _errorCount; + private int _queuedCount; + private string _runningTime; + private bool _progressBarVisible; + + public int CompletedCount { get => _completedCount; private set { this.RaiseAndSetIfChanged(ref _completedCount, value); this.RaisePropertyChanged(nameof(AnyCompleted)); } } + public int QueuedCount { get => _queuedCount; private set { this.RaiseAndSetIfChanged(ref _queuedCount, value); this.RaisePropertyChanged(nameof(AnyQueued)); } } + public int ErrorCount { get => _errorCount; private set { this.RaiseAndSetIfChanged(ref _errorCount, value); this.RaisePropertyChanged(nameof(AnyErrors)); } } + public string RunningTime { get => _runningTime; set { this.RaiseAndSetIfChanged(ref _runningTime, value); } } + public bool ProgressBarVisible { get => _progressBarVisible; set { this.RaiseAndSetIfChanged(ref _progressBarVisible, value); } } + + public bool AnyCompleted => CompletedCount > 0; + public bool AnyQueued => QueuedCount > 0; + public bool AnyErrors => ErrorCount > 0; + + public double Progress => 100d * Queue.Completed.Count / Queue.Count; + + + + private void Queue_CompletedCountChanged(object sender, int e) + { + int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail); + int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success); + + ErrorCount = errCount; + CompletedCount = completeCount; + this.RaisePropertyChanged(nameof(Progress)); + } + private void Queue_QueuededCountChanged(object sender, int cueCount) + { + QueuedCount = cueCount; + this.RaisePropertyChanged(nameof(Progress)); + } public void WriteLine(string text) { @@ -26,6 +75,69 @@ namespace LibationWinForms.AvaloniaUI.ViewModels LogMessage = text.Trim() })); } + + public void AddToQueue(IEnumerable pbook) + { + Dispatcher.UIThread.Post(() => + { + Queue.Enqueue(pbook); + if (!Running) + QueueRunner = QueueLoop(); + }); + } + + DateTime StartingTime; + private async Task QueueLoop() + { + try + { + Serilog.Log.Logger.Information("Begin processing queue"); + + RunningTime = string.Empty; + ProgressBarVisible = true; + StartingTime = DateTime.Now; + + using var counterTimer = new System.Threading.Timer(CounterTimer_Tick, null, 0, 500); + + while (Queue.MoveNext()) + { + var nextBook = Queue.Current; + + Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook?.LibraryBook); + + var result = await nextBook.ProcessOneAsync(); + + Serilog.Log.Logger.Information("Completed processing queued item: {item_LibraryBook}\r\nResult: {result}", nextBook?.LibraryBook, result); + + if (result == ProcessBookResult.ValidationFail) + Queue.ClearCurrent(); + else if (result == ProcessBookResult.FailedAbort) + Queue.ClearQueue(); + else if (result == ProcessBookResult.FailedSkip) + nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error); + } + Serilog.Log.Logger.Information("Completed processing queue"); + + Queue_CompletedCountChanged(this, 0); + ProgressBarVisible = false; + } + catch (Exception ex) + { + Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items"); + } + } + + private void CounterTimer_Tick(object? state) + { + string timeToStr(TimeSpan time) + { + string minsSecs = $"{time:mm\\:ss}"; + if (time.TotalHours >= 1) + return $"{time.TotalHours:F0}:{minsSecs}"; + return minsSecs; + } + RunningTime = timeToStr(DateTime.Now - StartingTime); + } } public class LogEntry diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml index fcdb7ff1..85355abf 100644 --- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml +++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml @@ -8,7 +8,9 @@ xmlns:prgid="clr-namespace:LibationWinForms.AvaloniaUI.Views.ProductsGrid" xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls" mc:Ignorable="d" d:DesignWidth="2000" d:DesignHeight="700" - x:Class="LibationWinForms.AvaloniaUI.Views.MainWindow" Title="MainWindow"> + x:Class="LibationWinForms.AvaloniaUI.Views.MainWindow" + Title="MainWindow" + Icon="/AvaloniaUI/Assets/glass-with-glow_16.png"> diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml b/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml index c5d0be64..e3cd8f8b 100644 --- a/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml +++ b/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml @@ -91,24 +91,24 @@ - + - - + + - - + + - - + + - 00:00:25 + diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml.cs index 5c2200c5..fd0d7eb9 100644 --- a/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml.cs +++ b/Source/LibationWinForms/AvaloniaUI/Views/ProcessQueueControl2.axaml.cs @@ -26,37 +26,7 @@ namespace LibationWinForms.AvaloniaUI.Views private TrackedQueue2 Queue => _viewModel.Items; private readonly ProcessQueue.LogMe Logger; - private int QueuedCount - { - set - { - queueNumberLbl_Text.Text = value.ToString(); - queueNumberLbl_Text.IsVisible = value > 0; - queueNumberLbl_Icon.IsVisible = value > 0; - } - } - private int ErrorCount - { - set - { - errorNumberLbl_Text.Text = value.ToString(); - errorNumberLbl_Text.IsVisible = value > 0; - errorNumberLbl_Icon.IsVisible = value > 0; - } - } - - private int CompletedCount - { - set - { - completedNumberLbl_Text.Text = value.ToString(); - completedNumberLbl_Text.IsVisible = value > 0; - completedNumberLbl_Icon.IsVisible = value > 0; - } - } - - public Task QueueRunner { get; private set; } - public bool Running => !QueueRunner?.IsCompleted ?? false; + public ProcessQueueControl2() { @@ -71,22 +41,6 @@ namespace LibationWinForms.AvaloniaUI.Views ProcessBookControl2.PositionButtonClicked += ProcessBookControl2_ButtonClicked; ProcessBookControl2.CancelButtonClicked += ProcessBookControl2_CancelButtonClicked; - queueNumberLbl_Icon = this.FindControl(nameof(queueNumberLbl_Icon)); - errorNumberLbl_Icon = this.FindControl(nameof(errorNumberLbl_Icon)); - completedNumberLbl_Icon = this.FindControl(nameof(completedNumberLbl_Icon)); - - queueNumberLbl_Text = this.FindControl(nameof(queueNumberLbl_Text)); - errorNumberLbl_Text = this.FindControl(nameof(errorNumberLbl_Text)); - completedNumberLbl_Text = this.FindControl(nameof(completedNumberLbl_Text)); - - runningTimeLbl = this.FindControl(nameof(runningTimeLbl)); - - toolStripProgressBar1 = this.FindControl(nameof(toolStripProgressBar1)); - - - Queue.QueuededCountChanged += Queue_QueuededCountChanged; - Queue.CompletedCountChanged += Queue_CompletedCountChanged; - #region Design Mode Testing if (Design.IsDesignMode) { @@ -140,11 +94,6 @@ namespace LibationWinForms.AvaloniaUI.Views return; } #endregion - - runningTimeLbl.Text = string.Empty; - QueuedCount = 0; - ErrorCount = 0; - CompletedCount = 0; } private void InitializeComponent() @@ -152,61 +101,6 @@ namespace LibationWinForms.AvaloniaUI.Views AvaloniaXamlLoader.Load(this); } - private async void ProcessBookControl2_CancelButtonClicked(ProcessBook2 item) - { - if (item is not null) - await item.CancelAsync(); - Queue.RemoveQueued(item); - } - - private void ProcessBookControl2_ButtonClicked(ProcessBook2 item, QueuePosition queueButton) - { - Queue.MoveQueuePosition(item, queueButton); - } - - private void RepeaterClick(object sender, PointerPressedEventArgs e) - { - if ((e.Source as TextBlock)?.DataContext is ProcessBook2 item) - { - _viewModel.SelectedItem = item; - _selectedIndex = _viewModel.Items.IndexOf(item); - } - } - - private void RepeaterOnKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.F5) - { - //_viewModel.ResetItems(); - } - } - public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) - { - Queue.ClearQueue(); - if (Queue.Current is not null) - await Queue.Current.CancelAsync(); - } - - public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) - { - Queue.ClearCompleted(); - - if (!Running) - runningTimeLbl.Text = string.Empty; - } - - public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) - { - _viewModel.LogEntries.Clear(); - } - - private void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) - { - string logText = string.Join("\r\n", _viewModel.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}")); - System.Windows.Forms.Clipboard.SetDataObject(logText, false, 5, 150); - } - - private bool isBookInQueue(LibraryBook libraryBook) => Queue.Any(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId); @@ -233,7 +127,7 @@ namespace LibationWinForms.AvaloniaUI.Views } Serilog.Log.Logger.Information("Queueing {count} books", procs.Count); - AddToQueue(procs); + _viewModel.AddToQueue(procs); } public void AddDownloadDecrypt(IEnumerable entries) @@ -251,7 +145,7 @@ namespace LibationWinForms.AvaloniaUI.Views } Serilog.Log.Logger.Information("Queueing {count} books", procs.Count); - AddToQueue(procs); + _viewModel.AddToQueue(procs); } public void AddConvertMp3(IEnumerable entries) @@ -268,75 +162,63 @@ namespace LibationWinForms.AvaloniaUI.Views } Serilog.Log.Logger.Information("Queueing {count} books", procs.Count); - AddToQueue(procs); - } - private void AddToQueue(IEnumerable pbook) - { - Dispatcher.UIThread.Post(() => - { - Queue.Enqueue(pbook); - if (!Running) - QueueRunner = QueueLoop(); - }); + _viewModel.AddToQueue(procs); } - DateTime StartingTime; - private async Task QueueLoop() - { - try - { - Serilog.Log.Logger.Information("Begin processing queue"); - - StartingTime = DateTime.Now; - - using var counterTimer = new System.Threading.Timer(CounterTimer_Tick, null, 0, 500); - - while (Queue.MoveNext()) - { - var nextBook = Queue.Current; - - Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook?.LibraryBook); - - var result = await nextBook.ProcessOneAsync(); - - Serilog.Log.Logger.Information("Completed processing queued item: {item_LibraryBook}\r\nResult: {result}", nextBook?.LibraryBook, result); - - if (result == ProcessBookResult.ValidationFail) - Queue.ClearCurrent(); - else if (result == ProcessBookResult.FailedAbort) - Queue.ClearQueue(); - else if (result == ProcessBookResult.FailedSkip) - nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error); - } - Serilog.Log.Logger.Information("Completed processing queue"); - - Queue_CompletedCountChanged(this, 0); - } - catch (Exception ex) - { - Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items"); - } - } #region Control event handlers - private void Queue_CompletedCountChanged(object sender, int e) + private async void ProcessBookControl2_CancelButtonClicked(ProcessBook2 item) { - int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail); - int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success); + if (item is not null) + await item.CancelAsync(); + Queue.RemoveQueued(item); + } - ErrorCount = errCount; - CompletedCount = completeCount; - UpdateProgressBar(); - } - private void Queue_QueuededCountChanged(object sender, int cueCount) + private void ProcessBookControl2_ButtonClicked(ProcessBook2 item, QueuePosition queueButton) { - QueuedCount = cueCount; - UpdateProgressBar(); + Queue.MoveQueuePosition(item, queueButton); } - private void UpdateProgressBar() + + private void RepeaterClick(object sender, PointerPressedEventArgs e) { - double percent = 100d * Queue.Completed.Count / Queue.Count; - toolStripProgressBar1.Value = percent; + if ((e.Source as TextBlock)?.DataContext is ProcessBook2 item) + { + _viewModel.SelectedItem = item; + _selectedIndex = _viewModel.Items.IndexOf(item); + } + } + + private void RepeaterOnKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.F5) + { + //_viewModel.ResetItems(); + } + } + public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + Queue.ClearQueue(); + if (Queue.Current is not null) + await Queue.Current.CancelAsync(); + } + + public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + Queue.ClearCompleted(); + + if (!_viewModel.Running) + _viewModel.RunningTime = string.Empty; + } + + public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + _viewModel.LogEntries.Clear(); + } + + private void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + string logText = string.Join("\r\n", _viewModel.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}")); + System.Windows.Forms.Clipboard.SetDataObject(logText, false, 5, 150); } private async void cancelAllBtn_Click(object sender, EventArgs e) @@ -350,22 +232,8 @@ namespace LibationWinForms.AvaloniaUI.Views { Queue.ClearCompleted(); - if (!Running) - runningTimeLbl.Text = string.Empty; - } - - private void CounterTimer_Tick(object? state) - { - string timeToStr(TimeSpan time) - { - string minsSecs = $"{time:mm\\:ss}"; - if (time.TotalHours >= 1) - return $"{time.TotalHours:F0}:{minsSecs}"; - return minsSecs; - } - - if (Running) - Dispatcher.UIThread.Post(() => runningTimeLbl.Text = timeToStr(DateTime.Now - StartingTime)); + if (!_viewModel.Running) + _viewModel.RunningTime = string.Empty; } #endregion diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProductsGrid/ProductsDisplay2.Display.xaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/ProductsGrid/ProductsDisplay2.Display.xaml.cs index 5c934399..5fac228a 100644 --- a/Source/LibationWinForms/AvaloniaUI/Views/ProductsGrid/ProductsDisplay2.Display.xaml.cs +++ b/Source/LibationWinForms/AvaloniaUI/Views/ProductsGrid/ProductsDisplay2.Display.xaml.cs @@ -46,6 +46,7 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid } else { + //List is already displayed. Replace all items with new ones, refilter, and re-sort string existingFilter = _viewModel?.GridEntries?.Filter; bindingList.ReplaceList(ProductsDisplayViewModel.CreateGridEntries(dbBooks)); bindingList.Filter = existingFilter; diff --git a/Source/LibationWinForms/LibationWinForms.csproj b/Source/LibationWinForms/LibationWinForms.csproj index d08592d7..befda771 100644 --- a/Source/LibationWinForms/LibationWinForms.csproj +++ b/Source/LibationWinForms/LibationWinForms.csproj @@ -46,6 +46,7 @@ + diff --git a/Source/LibationWinForms/Properties/PublishProfiles/FolderProfile.pubxml b/Source/LibationWinForms/Properties/PublishProfiles/FolderProfile.pubxml index 806982d7..3ed14ad3 100644 --- a/Source/LibationWinForms/Properties/PublishProfiles/FolderProfile.pubxml +++ b/Source/LibationWinForms/Properties/PublishProfiles/FolderProfile.pubxml @@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Release - x64 + Any CPU ..\bin\publish\ FileSystem net6.0-windows