mirror of https://github.com/ErsatzTV/ErsatzTV.git
17 changed files with 214 additions and 94 deletions
@ -0,0 +1,8 @@ |
|||||||
|
using ErsatzTV.Core; |
||||||
|
using LanguageExt; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaCollections.Commands |
||||||
|
{ |
||||||
|
public record AddMovieToSimpleMediaCollection |
||||||
|
(int MediaCollectionId, int MovieId) : MediatR.IRequest<Either<BaseError, Unit>>; |
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using ErsatzTV.Core; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Core.Interfaces.Repositories; |
||||||
|
using LanguageExt; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaCollections.Commands |
||||||
|
{ |
||||||
|
public class |
||||||
|
AddMovieToSimpleMediaCollectionHandler : MediatR.IRequestHandler<AddMovieToSimpleMediaCollection, |
||||||
|
Either<BaseError, Unit>> |
||||||
|
{ |
||||||
|
private readonly IMediaCollectionRepository _mediaCollectionRepository; |
||||||
|
private readonly IMovieRepository _movieRepository; |
||||||
|
|
||||||
|
public AddMovieToSimpleMediaCollectionHandler( |
||||||
|
IMediaCollectionRepository mediaCollectionRepository, |
||||||
|
IMovieRepository movieRepository) |
||||||
|
{ |
||||||
|
_mediaCollectionRepository = mediaCollectionRepository; |
||||||
|
_movieRepository = movieRepository; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<Either<BaseError, Unit>> Handle( |
||||||
|
AddMovieToSimpleMediaCollection request, |
||||||
|
CancellationToken cancellationToken) => |
||||||
|
Validate(request) |
||||||
|
.MapT(ApplyAddMoviesRequest) |
||||||
|
.Bind(v => v.ToEitherAsync()); |
||||||
|
|
||||||
|
private async Task<Unit> ApplyAddMoviesRequest(RequestParameters parameters) |
||||||
|
{ |
||||||
|
parameters.Collection.Movies.Add(parameters.MovieToAdd); |
||||||
|
await _mediaCollectionRepository.Update(parameters.Collection); |
||||||
|
|
||||||
|
return Unit.Default; |
||||||
|
} |
||||||
|
|
||||||
|
private async Task<Validation<BaseError, RequestParameters>> |
||||||
|
Validate(AddMovieToSimpleMediaCollection request) => |
||||||
|
(await SimpleMediaCollectionMustExist(request), await ValidateMovies(request)) |
||||||
|
.Apply( |
||||||
|
(simpleMediaCollectionToUpdate, movieToAdd) => |
||||||
|
new RequestParameters(simpleMediaCollectionToUpdate, movieToAdd)); |
||||||
|
|
||||||
|
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist( |
||||||
|
AddMovieToSimpleMediaCollection updateSimpleMediaCollection) => |
||||||
|
_mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId) |
||||||
|
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist.")); |
||||||
|
|
||||||
|
private Task<Validation<BaseError, MovieMediaItem>> ValidateMovies( |
||||||
|
AddMovieToSimpleMediaCollection request) => |
||||||
|
LoadMovie(request) |
||||||
|
.Map(v => v.ToValidation<BaseError>("MovieMediaItem does not exist")); |
||||||
|
|
||||||
|
private Task<Option<MovieMediaItem>> LoadMovie(AddMovieToSimpleMediaCollection request) => |
||||||
|
_movieRepository.GetMovie(request.MovieId); |
||||||
|
|
||||||
|
private record RequestParameters(SimpleMediaCollection Collection, MovieMediaItem MovieToAdd); |
||||||
|
} |
||||||
|
} |
||||||
@ -1,9 +0,0 @@ |
|||||||
using System.Collections.Generic; |
|
||||||
using ErsatzTV.Core; |
|
||||||
using LanguageExt; |
|
||||||
|
|
||||||
namespace ErsatzTV.Application.MediaCollections.Commands |
|
||||||
{ |
|
||||||
public record AddMoviesToSimpleMediaCollection |
|
||||||
(int MediaCollectionId, List<int> MovieIds) : MediatR.IRequest<Either<BaseError, Unit>>; |
|
||||||
} |
|
||||||
@ -1,79 +0,0 @@ |
|||||||
using System.Collections.Generic; |
|
||||||
using System.Linq; |
|
||||||
using System.Threading; |
|
||||||
using System.Threading.Tasks; |
|
||||||
using ErsatzTV.Core; |
|
||||||
using ErsatzTV.Core.Domain; |
|
||||||
using ErsatzTV.Core.Interfaces.Repositories; |
|
||||||
using LanguageExt; |
|
||||||
using static LanguageExt.Prelude; |
|
||||||
|
|
||||||
namespace ErsatzTV.Application.MediaCollections.Commands |
|
||||||
{ |
|
||||||
public class |
|
||||||
AddMoviesToSimpleMediaCollectionHandler : MediatR.IRequestHandler<AddMoviesToSimpleMediaCollection, |
|
||||||
Either<BaseError, Unit>> |
|
||||||
{ |
|
||||||
private readonly IMediaCollectionRepository _mediaCollectionRepository; |
|
||||||
private readonly IMediaItemRepository _mediaItemRepository; |
|
||||||
|
|
||||||
public AddMoviesToSimpleMediaCollectionHandler( |
|
||||||
IMediaCollectionRepository mediaCollectionRepository, |
|
||||||
IMediaItemRepository mediaItemRepository) |
|
||||||
{ |
|
||||||
_mediaCollectionRepository = mediaCollectionRepository; |
|
||||||
_mediaItemRepository = mediaItemRepository; |
|
||||||
} |
|
||||||
|
|
||||||
public Task<Either<BaseError, Unit>> Handle( |
|
||||||
AddMoviesToSimpleMediaCollection request, |
|
||||||
CancellationToken cancellationToken) => |
|
||||||
Validate(request) |
|
||||||
.MapT(ApplyAddMoviesRequest) |
|
||||||
.Bind(v => v.ToEitherAsync()); |
|
||||||
|
|
||||||
private async Task<Unit> ApplyAddMoviesRequest(RequestParameters parameters) |
|
||||||
{ |
|
||||||
foreach (MovieMediaItem item in parameters.MoviesToAdd.Where( |
|
||||||
item => parameters.Collection.Movies.All(i => i.Id != item.Id))) |
|
||||||
{ |
|
||||||
parameters.Collection.Movies.Add(item); |
|
||||||
} |
|
||||||
|
|
||||||
await _mediaCollectionRepository.Update(parameters.Collection); |
|
||||||
|
|
||||||
return Unit.Default; |
|
||||||
} |
|
||||||
|
|
||||||
private async Task<Validation<BaseError, RequestParameters>> |
|
||||||
Validate(AddMoviesToSimpleMediaCollection request) => |
|
||||||
(await SimpleMediaCollectionMustExist(request), await ValidateMovies(request)) |
|
||||||
.Apply( |
|
||||||
(simpleMediaCollectionToUpdate, moviesToAdd) => |
|
||||||
new RequestParameters(simpleMediaCollectionToUpdate, moviesToAdd)); |
|
||||||
|
|
||||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist( |
|
||||||
AddMoviesToSimpleMediaCollection updateSimpleMediaCollection) => |
|
||||||
_mediaCollectionRepository.GetSimpleMediaCollection(updateSimpleMediaCollection.MediaCollectionId) |
|
||||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist.")); |
|
||||||
|
|
||||||
private Task<Validation<BaseError, List<MovieMediaItem>>> ValidateMovies( |
|
||||||
AddMoviesToSimpleMediaCollection request) => |
|
||||||
LoadAllMovies(request) |
|
||||||
.Map(v => v.ToValidation<BaseError>("MovieMediaItem does not exist")); |
|
||||||
|
|
||||||
private async Task<Option<List<MovieMediaItem>>> LoadAllMovies(AddMoviesToSimpleMediaCollection request) |
|
||||||
{ |
|
||||||
var items = (await request.MovieIds.Map(async id => await _mediaItemRepository.Get(id)).Sequence()) |
|
||||||
.ToList(); |
|
||||||
if (items.Any(i => i.IsNone)) |
|
||||||
{ |
|
||||||
return None; |
|
||||||
} |
|
||||||
|
|
||||||
return items.Somes().OfType<MovieMediaItem>().ToList(); |
|
||||||
} |
|
||||||
|
|
||||||
private record RequestParameters(SimpleMediaCollection Collection, List<MovieMediaItem> MoviesToAdd); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,10 @@ |
|||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Movies |
||||||
|
{ |
||||||
|
internal static class Mapper |
||||||
|
{ |
||||||
|
internal static MovieViewModel ProjectToViewModel(MovieMediaItem movie) => |
||||||
|
new(movie.Metadata.Title, movie.Metadata.Year?.ToString(), movie.Metadata.Plot, movie.Poster); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
namespace ErsatzTV.Application.Movies |
||||||
|
{ |
||||||
|
public record MovieViewModel(string Title, string Year, string Plot, string Poster); |
||||||
|
} |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
using LanguageExt; |
||||||
|
using MediatR; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Movies.Queries |
||||||
|
{ |
||||||
|
public record GetMovieById(int Id) : IRequest<Option<MovieViewModel>>; |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using ErsatzTV.Core.Interfaces.Repositories; |
||||||
|
using LanguageExt; |
||||||
|
using MediatR; |
||||||
|
using static ErsatzTV.Application.Movies.Mapper; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Movies.Queries |
||||||
|
{ |
||||||
|
public class GetMovieByIdHandler : IRequestHandler<GetMovieById, Option<MovieViewModel>> |
||||||
|
{ |
||||||
|
private readonly IMovieRepository _movieRepository; |
||||||
|
|
||||||
|
public GetMovieByIdHandler(IMovieRepository movieRepository) => |
||||||
|
_movieRepository = movieRepository; |
||||||
|
|
||||||
|
public Task<Option<MovieViewModel>> Handle( |
||||||
|
GetMovieById request, |
||||||
|
CancellationToken cancellationToken) => |
||||||
|
_movieRepository.GetMovie(request.Id).MapT(ProjectToViewModel); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
@page "/media/movies/{MovieId:int}" |
||||||
|
@using ErsatzTV.Application.Movies |
||||||
|
@using ErsatzTV.Application.Movies.Queries |
||||||
|
@using ErsatzTV.Application.MediaCollections |
||||||
|
@using ErsatzTV.Application.MediaCollections.Commands |
||||||
|
@inject IMediator Mediator |
||||||
|
@inject IDialogService Dialog |
||||||
|
@inject NavigationManager NavigationManager |
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Large"> |
||||||
|
<MudBreadcrumbs Items="_breadcrumbs" Class="mb-6"></MudBreadcrumbs> |
||||||
|
<MudCard Class="mb-6"> |
||||||
|
<div style="display: flex; flex-direction: row;"> |
||||||
|
@if (!string.IsNullOrWhiteSpace(_movie.Poster)) |
||||||
|
{ |
||||||
|
<MudPaper Style="flex-shrink: 0;"> |
||||||
|
<MudCardMedia Image="@($"/posters/{_movie.Poster}")" Style="height: 440px; width: 304px;"/> |
||||||
|
</MudPaper> |
||||||
|
} |
||||||
|
<MudCardContent Class="mx-3 my-3"> |
||||||
|
<div style="display: flex; flex-direction: column; height: 100%"> |
||||||
|
<MudText Typo="Typo.h3">@_movie.Title</MudText> |
||||||
|
<MudText Typo="Typo.subtitle1" Class="mb-6 mud-text-secondary">@_movie.Year</MudText> |
||||||
|
<MudText Style="flex-grow: 1">@_movie.Plot</MudText> |
||||||
|
<div> |
||||||
|
<MudButton Variant="Variant.Filled" |
||||||
|
Color="Color.Primary" |
||||||
|
StartIcon="@Icons.Material.Filled.Add" |
||||||
|
OnClick="@AddToCollection"> |
||||||
|
Add To Collection |
||||||
|
</MudButton> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</MudCardContent> |
||||||
|
</div> |
||||||
|
</MudCard> |
||||||
|
</MudContainer> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
[Parameter] |
||||||
|
public int MovieId { get; set; } |
||||||
|
|
||||||
|
private MovieViewModel _movie; |
||||||
|
|
||||||
|
private List<BreadcrumbItem> _breadcrumbs; |
||||||
|
|
||||||
|
protected override Task OnParametersSetAsync() => RefreshData(); |
||||||
|
|
||||||
|
private async Task RefreshData() |
||||||
|
{ |
||||||
|
await Mediator.Send(new GetMovieById(MovieId)) |
||||||
|
.IfSomeAsync(vm => _movie = vm); |
||||||
|
|
||||||
|
_breadcrumbs = new List<BreadcrumbItem> |
||||||
|
{ |
||||||
|
new("Movies", "/media/movies"), |
||||||
|
new($"{_movie.Title} ({_movie.Year})", null, true) |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private async Task AddToCollection() |
||||||
|
{ |
||||||
|
var parameters = new DialogParameters { { "EntityType", "movie" }, { "EntityName", _movie.Title } }; |
||||||
|
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; |
||||||
|
|
||||||
|
IDialogReference dialog = Dialog.Show<AddToCollectionDialog>("Add To Collection", parameters, options); |
||||||
|
DialogResult result = await dialog.Result; |
||||||
|
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection) |
||||||
|
{ |
||||||
|
await Mediator.Send(new AddMovieToSimpleMediaCollection(collection.Id, MovieId)); |
||||||
|
NavigationManager.NavigateTo($"/media/collections/{collection.Id}"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue