mirror of https://github.com/ErsatzTV/ErsatzTV.git
6 changed files with 227 additions and 13 deletions
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
using ErsatzTV.Core; |
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules; |
||||
|
||||
public record CopyProgramSchedule |
||||
(int ProgramScheduleId, string Name) : IRequest<Either<BaseError, ProgramScheduleViewModel>>; |
||||
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using ErsatzTV.Infrastructure.Extensions; |
||||
using Microsoft.EntityFrameworkCore; |
||||
using Microsoft.EntityFrameworkCore.ChangeTracking; |
||||
using static ErsatzTV.Application.ProgramSchedules.Mapper; |
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules; |
||||
|
||||
public class |
||||
CopyProgramScheduleHandler : IRequestHandler<CopyProgramSchedule, Either<BaseError, ProgramScheduleViewModel>> |
||||
{ |
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||
|
||||
public CopyProgramScheduleHandler(IDbContextFactory<TvContext> dbContextFactory) => |
||||
_dbContextFactory = dbContextFactory; |
||||
|
||||
public async Task<Either<BaseError, ProgramScheduleViewModel>> Handle( |
||||
CopyProgramSchedule request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
try |
||||
{ |
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
Validation<BaseError, ProgramSchedule> validation = await Validate(dbContext, request); |
||||
return await validation.Apply(p => PerformCopy(dbContext, p, request, cancellationToken)); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
return BaseError.New(ex.Message); |
||||
} |
||||
} |
||||
|
||||
private async Task<ProgramScheduleViewModel> PerformCopy( |
||||
TvContext dbContext, |
||||
ProgramSchedule schedule, |
||||
CopyProgramSchedule request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
var clone = new ProgramSchedule(); |
||||
await dbContext.AddAsync(clone, cancellationToken); |
||||
|
||||
clone.Name = request.Name; |
||||
clone.RandomStartPoint = schedule.RandomStartPoint; |
||||
clone.ShuffleScheduleItems = schedule.ShuffleScheduleItems; |
||||
clone.TreatCollectionsAsShows = schedule.TreatCollectionsAsShows; |
||||
clone.KeepMultiPartEpisodesTogether = schedule.KeepMultiPartEpisodesTogether; |
||||
|
||||
// no playouts, no alternates
|
||||
clone.Playouts = new List<Playout>(); |
||||
clone.ProgramScheduleAlternates = new List<ProgramScheduleAlternate>(); |
||||
|
||||
// clone all items
|
||||
clone.Items = new List<ProgramScheduleItem>(); |
||||
foreach (ProgramScheduleItem item in schedule.Items) |
||||
{ |
||||
PropertyValues itemValues = dbContext.Entry(item).CurrentValues.Clone(); |
||||
itemValues["Id"] = 0; |
||||
|
||||
ProgramScheduleItem itemClone = item switch |
||||
{ |
||||
ProgramScheduleItemFlood => new ProgramScheduleItemFlood(), |
||||
ProgramScheduleItemDuration => new ProgramScheduleItemDuration(), |
||||
ProgramScheduleItemMultiple => new ProgramScheduleItemMultiple(), |
||||
_ => new ProgramScheduleItemOne() |
||||
}; |
||||
|
||||
await dbContext.AddAsync(itemClone, cancellationToken); |
||||
dbContext.Entry(itemClone).CurrentValues.SetValues(itemValues); |
||||
|
||||
itemClone.ProgramScheduleId = 0; |
||||
itemClone.ProgramSchedule = clone; |
||||
} |
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken); |
||||
|
||||
return ProjectToViewModel(clone); |
||||
} |
||||
|
||||
private static async Task<Validation<BaseError, ProgramSchedule>> Validate( |
||||
TvContext dbContext, |
||||
CopyProgramSchedule request) => |
||||
(await ScheduleMustExist(dbContext, request), await ValidateName(dbContext, request)) |
||||
.Apply((programSchedule, _) => programSchedule); |
||||
|
||||
private static Task<Validation<BaseError, ProgramSchedule>> ScheduleMustExist( |
||||
TvContext dbContext, |
||||
CopyProgramSchedule request) => |
||||
dbContext.ProgramSchedules |
||||
.AsNoTracking() |
||||
.Include(ps => ps.Items) |
||||
.SelectOneAsync(p => p.Id, p => p.Id == request.ProgramScheduleId) |
||||
.Map(o => o.ToValidation<BaseError>("Schedule does not exist.")); |
||||
|
||||
private static async Task<Validation<BaseError, string>> ValidateName( |
||||
TvContext dbContext, |
||||
CopyProgramSchedule request) |
||||
{ |
||||
List<string> allNames = await dbContext.ProgramSchedules |
||||
.Map(ps => ps.Name) |
||||
.ToListAsync(); |
||||
|
||||
Validation<BaseError, string> result1 = request.NotEmpty(c => c.Name) |
||||
.Bind(_ => request.NotLongerThan(50)(c => c.Name)); |
||||
|
||||
var result2 = Optional(request.Name) |
||||
.Where(name => !allNames.Contains(name)) |
||||
.ToValidation<BaseError>("Schedule name must be unique"); |
||||
|
||||
return (result1, result2).Apply((_, _) => request.Name); |
||||
} |
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
@using ErsatzTV.Application.ProgramSchedules |
||||
@implements IDisposable |
||||
@inject IMediator Mediator |
||||
@inject ISnackbar Snackbar |
||||
@inject ILogger<CopyScheduleDialog> Logger |
||||
|
||||
<MudDialog> |
||||
<DialogContent> |
||||
<EditForm Model="@_dummyModel" OnSubmit="@(_ => Submit())"> |
||||
<MudContainer Class="mb-6"> |
||||
<MudText> |
||||
Enter a name for the new Schedule |
||||
</MudText> |
||||
</MudContainer> |
||||
<MudTextField T="string" Label="New Schedule Name" |
||||
@bind-Text="@_newName" |
||||
Class="mb-6 mx-4"> |
||||
</MudTextField> |
||||
</EditForm> |
||||
</DialogContent> |
||||
<DialogActions> |
||||
<MudButton OnClick="Cancel" ButtonType="ButtonType.Reset">Cancel</MudButton> |
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="Submit"> |
||||
Copy Schedule |
||||
</MudButton> |
||||
</DialogActions> |
||||
</MudDialog> |
||||
|
||||
@code { |
||||
private readonly CancellationTokenSource _cts = new(); |
||||
|
||||
[CascadingParameter] |
||||
MudDialogInstance MudDialog { get; set; } |
||||
|
||||
[Parameter] |
||||
public int ProgramScheduleId { get; set; } |
||||
|
||||
private record DummyModel; |
||||
|
||||
private readonly DummyModel _dummyModel = new(); |
||||
|
||||
private string _newName; |
||||
|
||||
public void Dispose() |
||||
{ |
||||
_cts.Cancel(); |
||||
_cts.Dispose(); |
||||
} |
||||
|
||||
private bool CanSubmit() => !string.IsNullOrWhiteSpace(_newName); |
||||
|
||||
private async Task Submit() |
||||
{ |
||||
if (!CanSubmit()) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
Either<BaseError, ProgramScheduleViewModel> maybeResult = |
||||
await Mediator.Send(new CopyProgramSchedule(ProgramScheduleId, _newName), _cts.Token); |
||||
|
||||
maybeResult.Match( |
||||
schedule => { MudDialog.Close(DialogResult.Ok(schedule)); }, |
||||
error => |
||||
{ |
||||
Snackbar.Add(error.Value, Severity.Error); |
||||
Logger.LogError("Error copying Schedule: {Error}", error.Value); |
||||
MudDialog.Close(DialogResult.Cancel()); |
||||
}); |
||||
} |
||||
|
||||
private void Cancel(MouseEventArgs e) => MudDialog.Cancel(); |
||||
} |
||||
Loading…
Reference in new issue