From a728c5e31ea104e2cd5734f90da291983098ff63 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:24:08 +0000 Subject: [PATCH] add smart collection editor to support renaming (#2253) --- CHANGELOG.md | 3 + .../Commands/UpdateSmartCollection.cs | 2 +- .../Commands/UpdateSmartCollectionHandler.cs | 1 + .../Queries/GetSmartCollectionById.cs | 3 + .../Queries/GetSmartCollectionByIdHandler.cs | 20 ++++++ ErsatzTV/Pages/CollectionEditor.razor | 4 +- ErsatzTV/Pages/Collections.razor | 11 ++- ErsatzTV/Pages/Search.razor | 1 + ErsatzTV/Pages/SmartCollectionEditor.razor | 71 +++++++++++++++++++ .../SmartCollectionEditViewModel.cs | 8 +++ 10 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionById.cs create mode 100644 ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs create mode 100644 ErsatzTV/Pages/SmartCollectionEditor.razor create mode 100644 ErsatzTV/ViewModels/SmartCollectionEditViewModel.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ae3fd406..30a1038de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add `Use Chapters As Media Items` option to filler preset - This option allows scheduling individual chapters as filler - The chapters are shuffled or otherwise sorted together just like normal filler would be +- Add smart collection edit page to allow renaming smart collections + - Previous edit link behavior (performing search using smart collection query) now uses magnifying glass icon ### Fixed - Fix app startup with MySql/MariaDB @@ -62,6 +64,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Move search debug logging to its own log category; add `Searching Minimum Log Level` to `Settings` > `Logging` - Classic schedules: always schedule the full `Duration` amount instead of stopping mid-duration - This allows duration items to be scheduled beyond midnight + - e.g. fixed start time 22:00 with 4 hour duration will schedule until 02:00 instead of stopping at midnight ## [25.3.1] - 2025-07-24 ### Fixed diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollection.cs index 40db0287d..8980550e5 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollection.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollection.cs @@ -2,4 +2,4 @@ namespace ErsatzTV.Application.MediaCollections; -public record UpdateSmartCollection(int Id, string Query) : IRequest>; +public record UpdateSmartCollection(int Id, string Name, string Query) : IRequest>; diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs index 5176aae4b..88adc00b3 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs @@ -46,6 +46,7 @@ public class UpdateSmartCollectionHandler : IRequestHandler ApplyUpdateRequest(TvContext dbContext, SmartCollection c, UpdateSmartCollection request) { c.Query = request.Query; + c.Name = request.Name; // rebuild playouts if (await dbContext.SaveChangesAsync() > 0) diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionById.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionById.cs new file mode 100644 index 000000000..c4b17895f --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionById.cs @@ -0,0 +1,3 @@ +namespace ErsatzTV.Application.MediaCollections; + +public record GetSmartCollectionById(int Id) : IRequest>; diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs new file mode 100644 index 000000000..f4a13130a --- /dev/null +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs @@ -0,0 +1,20 @@ +using ErsatzTV.Infrastructure.Data; +using ErsatzTV.Infrastructure.Extensions; +using Microsoft.EntityFrameworkCore; +using static ErsatzTV.Application.MediaCollections.Mapper; + +namespace ErsatzTV.Application.MediaCollections; + +public class GetSmartCollectionByIdHandler(IDbContextFactory dbContextFactory) : + IRequestHandler> +{ + public async Task> Handle( + GetSmartCollectionById request, + CancellationToken cancellationToken) + { + await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); + return await dbContext.SmartCollections + .SelectOneAsync(c => c.Id, c => c.Id == request.Id) + .MapT(ProjectToViewModel); + } +} diff --git a/ErsatzTV/Pages/CollectionEditor.razor b/ErsatzTV/Pages/CollectionEditor.razor index 83e1884ac..b65ad50c4 100644 --- a/ErsatzTV/Pages/CollectionEditor.razor +++ b/ErsatzTV/Pages/CollectionEditor.razor @@ -9,7 +9,7 @@ - @(IsEdit ? "Save Collection" : "Add Collection") + @(IsEdit ? "Save Collection" : "Add Collection")
@@ -19,7 +19,7 @@
Name
- +
diff --git a/ErsatzTV/Pages/Collections.razor b/ErsatzTV/Pages/Collections.razor index 8f1f61cc2..8938ea6a1 100644 --- a/ErsatzTV/Pages/Collections.razor +++ b/ErsatzTV/Pages/Collections.razor @@ -114,7 +114,7 @@ - + @@ -125,12 +125,17 @@ @context.Name
- + + + + + - + diff --git a/ErsatzTV/Pages/Search.razor b/ErsatzTV/Pages/Search.razor index 2027198bd..ad9434802 100644 --- a/ErsatzTV/Pages/Search.razor +++ b/ErsatzTV/Pages/Search.razor @@ -950,6 +950,7 @@ { var request = new UpdateSmartCollection( collectionResult.Item1.Id, + collectionResult.Item1.Name, _query); Either updateResult = await Mediator.Send(request, CancellationToken); diff --git a/ErsatzTV/Pages/SmartCollectionEditor.razor b/ErsatzTV/Pages/SmartCollectionEditor.razor new file mode 100644 index 000000000..9667a3b9b --- /dev/null +++ b/ErsatzTV/Pages/SmartCollectionEditor.razor @@ -0,0 +1,71 @@ +@page "/media/smart-collections/{Id:int}/edit" +@using ErsatzTV.Application.MediaCollections +@implements IDisposable +@inject NavigationManager NavigationManager +@inject ILogger Logger +@inject ISnackbar Snackbar +@inject IMediator Mediator + + + + + Save Smart Collection + + +
+ + Smart Collection + + +
+ Name +
+ +
+
+
+
+ +@code { + private readonly CancellationTokenSource _cts = new(); + + [Parameter] + public int Id { get; set; } + + private readonly SmartCollectionEditViewModel _model = new(); + private MudForm _form; + private bool _success; + + public void Dispose() + { + _cts.Cancel(); + _cts.Dispose(); + } + + protected override async Task OnParametersSetAsync() + { + Option maybeSmartCollection = await Mediator.Send(new GetSmartCollectionById(Id), _cts.Token); + foreach (var smartCollection in maybeSmartCollection) + { + _model.Id = smartCollection.Id; + _model.Name = smartCollection.Name; + _model.Query = smartCollection.Query; + } + } + + private async Task HandleSubmitAsync() + { + await _form.Validate(); + if (_success) + { + Either result = await Mediator.Send(new UpdateSmartCollection(Id, _model.Name, _model.Query), _cts.Token); + result.Match( + _ => NavigationManager.NavigateTo("media/collections"), + error => + { + Snackbar.Add(error.Value, Severity.Error); + Logger.LogError("Error saving smart collection: {Error}", error.Value); + }); + } + } +} \ No newline at end of file diff --git a/ErsatzTV/ViewModels/SmartCollectionEditViewModel.cs b/ErsatzTV/ViewModels/SmartCollectionEditViewModel.cs new file mode 100644 index 000000000..a27ee1a51 --- /dev/null +++ b/ErsatzTV/ViewModels/SmartCollectionEditViewModel.cs @@ -0,0 +1,8 @@ +namespace ErsatzTV.ViewModels; + +public class SmartCollectionEditViewModel +{ + public int Id { get; set; } + public string Name { get; set; } + public string Query { get; set; } +}