diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index d544a77f..1d428237 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -163,6 +163,7 @@ namespace ApplicationServices using var context = DbContexts.GetContext(); var udi = libraryBook.UserDefinedItem; + udi.Tags = newTags; // Attach() NoTracking entities before SaveChanges() context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; @@ -186,6 +187,7 @@ namespace ApplicationServices using var context = DbContexts.GetContext(); var udi = libraryBook.UserDefinedItem; + udi.BookStatus = liberatedStatus; // Attach() NoTracking entities before SaveChanges() context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; @@ -209,6 +211,7 @@ namespace ApplicationServices using var context = DbContexts.GetContext(); var udi = libraryBook.UserDefinedItem; + udi.PdfStatus = liberatedStatus; // Attach() NoTracking entities before SaveChanges() context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; diff --git a/DataLayer/EfClasses/UserDefinedItem.cs b/DataLayer/EfClasses/UserDefinedItem.cs index 18bb4fd5..5bd52beb 100644 --- a/DataLayer/EfClasses/UserDefinedItem.cs +++ b/DataLayer/EfClasses/UserDefinedItem.cs @@ -44,8 +44,12 @@ namespace DataLayer get => _tags; set { - _tags = sanitize(value); - ItemChanged?.Invoke(this, nameof(Tags)); + var newTags = sanitize(value); + if (_tags != newTags) + { + _tags = newTags; + ItemChanged?.Invoke(this, nameof(Tags)); + } } } diff --git a/LibationWinForms/Form1.cs b/LibationWinForms/Form1.cs index 8a1a1fb5..134a2123 100644 --- a/LibationWinForms/Form1.cs +++ b/LibationWinForms/Form1.cs @@ -384,15 +384,14 @@ namespace LibationWinForms #region liberate menu private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e) - => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(updateGridRow); + => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(); private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e) - => await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(updateGridRow); + => await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(); private async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, EventArgs e) => await BookLiberation.ProcessorAutomationController.ConvertAllBooksAsync(); - private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshLiberatedStatus(libraryBook); #endregion #region Export menu diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index d6f414bf..b2184d6e 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -12,6 +12,9 @@ using Dinah.Core.Drawing; namespace LibationWinForms { + /// + /// The View Model for a LibraryBook + /// internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable { #region implementation properties @@ -26,12 +29,15 @@ namespace LibationWinForms private Book Book => LibraryBook.Book; private Image _cover; + private Action _refilter; - public GridEntry(LibraryBook libraryBook) + public GridEntry(LibraryBook libraryBook, Action refilterOnChanged = null) { LibraryBook = libraryBook; + _refilter = refilterOnChanged; _memberValues = CreateMemberValueDictionary(); + //Get cover art. If it's default, subscribe to PictureCached { (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(new FileManager.PictureDefinition(Book.PictureId, FileManager.PictureSize._80x80)); @@ -58,7 +64,7 @@ namespace LibationWinForms Description = GetDescriptionDisplay(Book); } - //DisplayTags and Liberate properties are live. + UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged; } private void PictureStorage_PictureCached(object sender, FileManager.PictureCachedEventArgs e) @@ -70,7 +76,48 @@ namespace LibationWinForms } } - #region Data Source properties + #region detect changes to the model and update the view + + /// + /// This event handler receives notifications from the model that it has changed. + /// Save to the database and notify the view that it's changed. + /// + private void UserDefinedItem_ItemChanged(object sender, string itemName) + { + var udi = sender as UserDefinedItem; + + if (udi.Book.AudibleProductId != LibraryBook.Book.AudibleProductId) + return; + + switch (itemName) + { + case nameof(udi.Tags): + { + LibraryCommands.UpdateTags(LibraryBook.Book, udi.Tags); + NotifyPropertyChanged(nameof(DisplayTags)); + } + break; + case nameof(udi.BookStatus): + { + var status = udi.BookStatus == LiberatedStatus.PartialDownload ? LiberatedStatus.NotLiberated : udi.BookStatus; + LibraryCommands.UpdateBook(LibraryBook.Book, status); + NotifyPropertyChanged(nameof(Liberate)); + } + break; + case nameof(udi.PdfStatus): + { + LibraryCommands.UpdatePdf(LibraryBook.Book, udi.PdfStatus); + NotifyPropertyChanged(nameof(Liberate)); + } + break; + } + + _refilter?.Invoke(); + } + + #endregion + + #region Model properties exposed to the view public Image Cover { get @@ -95,8 +142,22 @@ namespace LibationWinForms public string Category { get; } public string Misc { get; } public string Description { get; } - public string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); - public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book)); + public string DisplayTags + { + get=> Book.UserDefinedItem.Tags; + set => Book.UserDefinedItem.Tags = value; + } + public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate + { + get => (LibraryCommands.Liberated_Status(LibraryBook.Book), LibraryCommands.Pdf_Status(LibraryBook.Book)); + + set + { + LibraryBook.Book.UserDefinedItem.BookStatus = value.BookStatus; + LibraryBook.Book.UserDefinedItem.PdfStatus = value.PdfStatus; + } + } + #endregion #region Data Sorting @@ -199,5 +260,12 @@ namespace LibationWinForms } #endregion + + ~GridEntry() + { + UserDefinedItem.ItemChanged -= UserDefinedItem_ItemChanged; + FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached; + } } + } diff --git a/LibationWinForms/ProductsGrid.cs b/LibationWinForms/ProductsGrid.cs index 46b721a4..043c0a1e 100644 --- a/LibationWinForms/ProductsGrid.cs +++ b/LibationWinForms/ProductsGrid.cs @@ -86,7 +86,7 @@ namespace LibationWinForms } // else: liberate - await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId)); + await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook); } private void Details_Click(GridEntry liveGridEntry) @@ -95,15 +95,10 @@ namespace LibationWinForms if (bookDetailsForm.ShowDialog() != DialogResult.OK) return; - var qtyChanges = LibraryCommands.UpdateUserDefinedItem(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags, bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); - if (qtyChanges == 0) - return; + liveGridEntry.DisplayTags = bookDetailsForm.NewTags; + liveGridEntry.Liberate = (bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); - //Re-apply filters - Filter(); - - //Update whole GridEntry row - liveGridEntry.NotifyPropertyChanged(); + BackupCountsChanged?.Invoke(this, EventArgs.Empty); } #endregion @@ -132,7 +127,7 @@ namespace LibationWinForms } var orderedGridEntries = lib - .Select(lb => new GridEntry(lb)).ToList() + .Select(lb => new GridEntry(lb, Filter)).ToList() // default load order .OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate))) //// more advanced example: sort by author, then series, then title @@ -150,19 +145,6 @@ namespace LibationWinForms BackupCountsChanged?.Invoke(this, EventArgs.Empty); } - public void RefreshRow(string productId) - { - var liveGridEntry = getGridEntry((ge) => ge.AudibleProductId == productId); - - // update GridEntry Liberate cell - liveGridEntry?.NotifyPropertyChanged(nameof(liveGridEntry.Liberate)); - - // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change - Filter(); - - BackupCountsChanged?.Invoke(this, EventArgs.Empty); - } - #endregion #region Filter @@ -195,12 +177,7 @@ namespace LibationWinForms #endregion #region DataGridView Macro - - private GridEntry getGridEntry(Func predicate) - => ((SortableBindingList)gridEntryBindingSource.DataSource).FirstOrDefault(predicate); - private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); - #endregion } }