Merge pull request #67 from mateuszskoczek/features/universal_media_edit_page
Features/universal media edit page
This commit is contained in:
@@ -4,9 +4,21 @@ namespace WatchIt.Common.Model.Media;
|
||||
|
||||
public abstract class MediaPoster
|
||||
{
|
||||
#region PROPERTIES
|
||||
|
||||
[JsonPropertyName("image")]
|
||||
public required byte[] Image { get; set; }
|
||||
|
||||
[JsonPropertyName("mime_type")]
|
||||
public required string MimeType { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public override string ToString() => $"data:{MimeType};base64,{Convert.ToBase64String(Image)}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,9 +1,24 @@
|
||||
using WatchIt.Database.Model.Media;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using WatchIt.Database.Model.Media;
|
||||
|
||||
namespace WatchIt.Common.Model.Media;
|
||||
|
||||
public class MediaPosterRequest : MediaPoster
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public MediaPosterRequest() {}
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public MediaPosterRequest(MediaPosterResponse response)
|
||||
{
|
||||
Image = response.Image;
|
||||
MimeType = response.MimeType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public MediaPosterImage CreateMediaPosterImage() => new MediaPosterImage
|
||||
{
|
||||
Image = Image,
|
||||
@@ -14,6 +29,6 @@ public class MediaPosterRequest : MediaPoster
|
||||
{
|
||||
item.Image = Image;
|
||||
item.MimeType = MimeType;
|
||||
item.UploadDate = DateTime.Now;
|
||||
item.UploadDate = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ public class MediaController(IMediaControllerService mediaControllerService)
|
||||
|
||||
[HttpPut("{id}/poster")]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(MediaPosterResponse), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
|
||||
|
||||
@@ -267,7 +267,8 @@ public class MediaControllerService(DatabaseContext database, IUserService userS
|
||||
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
return RequestResult.Ok();
|
||||
MediaPosterResponse returnData = new MediaPosterResponse(media.MediaPosterImage);
|
||||
return RequestResult.Ok(returnData);
|
||||
}
|
||||
|
||||
public async Task<RequestResult> DeleteMediaPoster(long mediaId)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentValidation;
|
||||
using WatchIt.Common.Model.Series;
|
||||
|
||||
namespace WatchIt.WebAPI.Validators.Movies;
|
||||
|
||||
public class SeriesRequestValidator : AbstractValidator<SeriesRequest>
|
||||
{
|
||||
public SeriesRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Title).NotEmpty().MaximumLength(250);
|
||||
RuleFor(x => x.OriginalTitle).MaximumLength(250);
|
||||
RuleFor(x => x.Description).MaximumLength(1000);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,6 @@ public interface IMediaWebAPIService
|
||||
Task GetPhotoMediaRandomBackground(long mediaId, Action<MediaPhotoResponse>? successAction = null, Action? notFoundAction = null);
|
||||
Task GetPhotoRandomBackground(Action<MediaPhotoResponse>? successAction = null, Action? notFoundAction = null);
|
||||
Task GetPoster(long mediaId, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null);
|
||||
Task PutPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
||||
Task PutPoster(long mediaId, MediaPosterRequest data, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
||||
Task DeletePoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura
|
||||
.ExecuteAction();
|
||||
}
|
||||
|
||||
public async Task PutPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null)
|
||||
public async Task PutPoster(long mediaId, MediaPosterRequest data, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null)
|
||||
{
|
||||
string url = GetUrl(EndpointsConfiguration.Media.PutPoster, mediaId);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="app.css?version=0.1.0.16"/>
|
||||
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.1.0.25"/>
|
||||
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.1.0.29"/>
|
||||
|
||||
<!-- BOOTSTRAP -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="rounded-3 panel panel-regular p-4">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="text-danger icon-size">⚠︎</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h3 class="text-danger">An error occured while loading a page</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrWhiteSpace(ErrorMessage))
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="d-flex justify-content-center">
|
||||
<p>@ErrorMessage</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace WatchIt.Website.Components;
|
||||
|
||||
public partial class ErrorComponent : ComponentBase
|
||||
{
|
||||
#region PARAMETERS
|
||||
|
||||
[Parameter] public string? ErrorMessage { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/* CLASSES */
|
||||
|
||||
.icon-size {
|
||||
font-size: 80px;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-auto rounded-3 panel panel-regular m-1 p-3">
|
||||
<img class="rounded-2 shadow mb-1" src="@(_posterBase64 is not null ? $"data:{_posterMediaType};base64,{_posterBase64}" : "assets/poster.png")" alt="poster" width="300" height="500"/>
|
||||
<br/>
|
||||
<InputFile id="posterInput" class="form-control my-1" OnChange="LoadPoster" disabled=@(!Id.HasValue) autocomplete="off"/>
|
||||
|
||||
@if (_posterChanged || !string.IsNullOrWhiteSpace(_actualPosterBase64))
|
||||
{
|
||||
<div id="posterButtons" class="container-fluid mt-2 p-0">
|
||||
<div class="row gx-1">
|
||||
@if (_posterChanged)
|
||||
{
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-secondary btn-block btn-stretch-x" @onclick="SavePoster" disabled=@(!Id.HasValue || _posterLoading) autocomplete="off">Save poster</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="CancelPoster" disabled=@(!Id.HasValue || _posterLoading) autocomplete="off">Drop changes</button>
|
||||
</div>
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(_actualPosterBase64))
|
||||
{
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="DeletePoster" disabled=@(!Id.HasValue || _posterLoading) autocomplete="off">Delete poster</button>
|
||||
</div>
|
||||
}
|
||||
@if (_posterLoading)
|
||||
{
|
||||
<div class="col-auto">
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<div class="spinner-border" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="col rounded-3 panel panel-regular m-1 p-3">
|
||||
<EditForm Model="Data" FormName="MediaInfo">
|
||||
<AntiforgeryToken/>
|
||||
<div class="form-group row my-1">
|
||||
<label for="title" class="col-2 col-form-label">Title*</label>
|
||||
<div class="col-10">
|
||||
<InputText id="title" class="form-control" @bind-Value="Data!.Title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row my-1">
|
||||
<label for="originalTitle" class="col-2 col-form-label">Original title</label>
|
||||
<div class="col-10">
|
||||
<InputText id="originalTitle" class="form-control" @bind-Value="Data!.OriginalTitle"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row my-1">
|
||||
<label for="description" class="col-2 col-form-label">Description</label>
|
||||
<div class="col-10">
|
||||
<InputTextArea id="description" class="form-control" rows="14" @bind-Value="Data!.Description"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row my-1">
|
||||
<label for="releaseDate" class="col-2 col-form-label">Release date</label>
|
||||
<div class="col-10">
|
||||
<InputDate TValue="DateOnly?" id="releaseDate" class="form-control" @bind-Value="Data!.ReleaseDate"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row my-1">
|
||||
<label for="length" class="col-2 col-form-label">Length</label>
|
||||
<div class="col-10">
|
||||
<InputNumber TValue="short?" id="length" class="form-control" @bind-Value="Data!.Length"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-1 mt-3">
|
||||
<div class="col d-flex align-items-center">
|
||||
@if (SaveDataErrors is not null && SaveDataErrors.Any())
|
||||
{
|
||||
<p class="text-danger m-0">@(SaveDataErrors.ElementAt(0))</p>
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(SaveDataInfo))
|
||||
{
|
||||
<p class="text-success m-0">@(SaveDataInfo)</p>
|
||||
}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-secondary" @onclick="SaveDataAction">Save data</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
#posterInput, #posterButtons {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,125 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using WatchIt.Common.Model.Media;
|
||||
using WatchIt.Website.Services.WebAPI.Media;
|
||||
|
||||
namespace WatchIt.Website.Components;
|
||||
|
||||
public partial class MediaFormComponent : ComponentBase
|
||||
{
|
||||
#region SERVICES
|
||||
|
||||
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
||||
[Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
[Parameter] public Media Data { get; set; }
|
||||
[Parameter] public long? Id { get; set; }
|
||||
[Parameter] public Func<Task> SaveDataAction { get; set; }
|
||||
[Parameter] public IEnumerable<string>? SaveDataErrors { get; set; }
|
||||
[Parameter] public string? SaveDataInfo { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private string? _actualPosterBase64 = null;
|
||||
private string? _actualPosterMediaType = null;
|
||||
private bool _posterChanged = false;
|
||||
private string? _posterBase64 = null;
|
||||
private string? _posterMediaType = null;
|
||||
private bool _posterLoading = false;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await MediaWebAPIService.GetPoster(Id.Value, (data) =>
|
||||
{
|
||||
_actualPosterBase64 = Encoding.UTF8.GetString(data.Image);
|
||||
_actualPosterMediaType = data.MimeType;
|
||||
_posterBase64 = _actualPosterBase64;
|
||||
_posterMediaType = _actualPosterMediaType;
|
||||
});
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadPoster(InputFileChangeEventArgs args)
|
||||
{
|
||||
if (args.File.ContentType.StartsWith("image"))
|
||||
{
|
||||
Stream stream = args.File.OpenReadStream(5242880);
|
||||
byte[] array;
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
await stream.CopyToAsync(ms);
|
||||
array = ms.ToArray();
|
||||
}
|
||||
|
||||
_posterMediaType = args.File.ContentType;
|
||||
_posterBase64 = Convert.ToBase64String(array);
|
||||
_posterChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SavePoster()
|
||||
{
|
||||
void SuccessAction()
|
||||
{
|
||||
_actualPosterBase64 = _posterBase64;
|
||||
_actualPosterMediaType = _posterMediaType;
|
||||
_posterChanged = false;
|
||||
_posterLoading = false;
|
||||
}
|
||||
|
||||
MediaPosterRequest data = new MediaPosterRequest
|
||||
{
|
||||
Image = Encoding.UTF8.GetBytes(_posterBase64),
|
||||
MimeType = _posterMediaType
|
||||
};
|
||||
|
||||
_posterLoading = true;
|
||||
await MediaWebAPIService.PutPoster(Id.Value, data, SuccessAction);
|
||||
}
|
||||
|
||||
private async Task DeletePoster()
|
||||
{
|
||||
void SuccessAction()
|
||||
{
|
||||
_actualPosterBase64 = null;
|
||||
_actualPosterMediaType = null;
|
||||
_posterChanged = false;
|
||||
_posterBase64 = null;
|
||||
_posterMediaType = null;
|
||||
_posterLoading = false;
|
||||
}
|
||||
|
||||
_posterLoading = true;
|
||||
await MediaWebAPIService.DeletePoster(Id.Value, SuccessAction);
|
||||
}
|
||||
|
||||
private void CancelPoster()
|
||||
{
|
||||
_posterBase64 = _actualPosterBase64;
|
||||
_posterMediaType = _actualPosterMediaType;
|
||||
_posterChanged = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/* IDS */
|
||||
|
||||
#posterInput, #posterButtons {
|
||||
width: 300px;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col rounded-3 panel panel-regular m-1">
|
||||
<div>
|
||||
<h2 class="text-danger">Sorry. You have no permission to view this site.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,7 +3,7 @@
|
||||
@if (_loaded)
|
||||
{
|
||||
<div class="container-xl">
|
||||
<div class="row align-items-center m-1 my-2 rounded-3 header panel panel-header z-3">
|
||||
<div class="row align-items-center m-1 my-2 mb-3 rounded-3 header panel panel-header z-3">
|
||||
<div class="col-2">
|
||||
<a class="logo" href="/">
|
||||
WatchIt
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col z-0">
|
||||
<div class="col z-0 p-1">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
<PageTitle>WatchIt administrator panel</PageTitle>
|
||||
|
||||
@if (_loaded)
|
||||
{
|
||||
<div class="container-fluid">
|
||||
@if (_authenticated)
|
||||
<div class="container-fluid">
|
||||
@if (_loaded)
|
||||
{
|
||||
if (_authenticated)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col rounded-3 panel panel-regular m-1">
|
||||
@@ -13,10 +13,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a class="col rounded-3 panel panel-regular m-1" href="/movies/new">
|
||||
<a class="col rounded-3 panel panel-regular m-1" href="/media/new/movies">
|
||||
<p class="text-center text-decorations-none">New movie</p>
|
||||
</a>
|
||||
|
||||
<div class="col rounded-3 panel panel-regular m-1">
|
||||
<p class="text-center">New TV series</p>
|
||||
</div>
|
||||
@@ -27,7 +26,11 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<NoPermissionComponent/>
|
||||
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<LoadingComponent/>
|
||||
}
|
||||
</div>
|
||||
@@ -66,28 +66,32 @@ public partial class AuthPage
|
||||
|
||||
#region METHODS
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (await AuthenticationService.GetAuthenticationStatusAsync())
|
||||
if (firstRender)
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
if (await AuthenticationService.GetAuthenticationStatusAsync())
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
Action<MediaPhotoResponse> backgroundSuccess = (data) =>
|
||||
{
|
||||
string imageBase64 = Convert.ToBase64String(data.Image);
|
||||
string firstColor = BitConverter.ToString(data.Background.FirstGradientColor)
|
||||
.Replace("-", string.Empty);
|
||||
string secondColor = BitConverter.ToString(data.Background.SecondGradientColor)
|
||||
.Replace("-", string.Empty);
|
||||
Action<MediaPhotoResponse> backgroundSuccess = (data) =>
|
||||
{
|
||||
string imageBase64 = Convert.ToBase64String(data.Image);
|
||||
string firstColor = BitConverter.ToString(data.Background.FirstGradientColor)
|
||||
.Replace("-", string.Empty);
|
||||
string secondColor = BitConverter.ToString(data.Background.SecondGradientColor)
|
||||
.Replace("-", string.Empty);
|
||||
|
||||
_background = $"data:{data.MimeType};base64,{imageBase64}";
|
||||
_firstGradientColor = $"#{firstColor}";
|
||||
_secondGradientColor = $"#{secondColor}";
|
||||
};
|
||||
await MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess);
|
||||
_background = $"data:{data.MimeType};base64,{imageBase64}";
|
||||
_firstGradientColor = $"#{firstColor}";
|
||||
_secondGradientColor = $"#{secondColor}";
|
||||
};
|
||||
await MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess);
|
||||
|
||||
_loaded = true;
|
||||
_loaded = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Login()
|
||||
|
||||
230
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor
Normal file
230
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor
Normal file
@@ -0,0 +1,230 @@
|
||||
@using WatchIt.Common.Model.Movies
|
||||
@using WatchIt.Common.Model.Series
|
||||
|
||||
@page "/media/{id:long}/edit"
|
||||
@page "/media/new/{type}"
|
||||
|
||||
|
||||
<PageTitle>
|
||||
WatchIt -
|
||||
@if (_loaded)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_error) && _user?.IsAdmin == true)
|
||||
{
|
||||
if (_media is not null)
|
||||
{
|
||||
@($"Edit \"")@(_media.Title)@("\"")
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_movieRequest is null)
|
||||
{
|
||||
@("New TV series")
|
||||
}
|
||||
else
|
||||
{
|
||||
@("New movie")
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@("Error")
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@("Loading")
|
||||
}
|
||||
</PageTitle>
|
||||
|
||||
<div class="container-fluid">
|
||||
@if (_loaded)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_error))
|
||||
{
|
||||
if (_user?.IsAdmin == true)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="rounded-3 panel panel-regular p-2">
|
||||
<h2 class="m-0 mx-2 mb-1 p-0">@(_media is not null ? "Edit" : "Create new") @(_movieRequest is not null ? "movie" : "series")@(_media is not null ? $" \"{_media.Title}\"" : string.Empty)</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3 gx-3">
|
||||
<div class="col-auto">
|
||||
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||
<div class="container-fluid p-0">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<img class="rounded-2 shadow object-fit-cover" src="@(_mediaPosterRequest is not null ? _mediaPosterRequest.ToString() : "assets/poster.png")" alt="poster" width="300" height="500"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<InputFile class="form-control" OnChange="LoadPoster" disabled="@(_media is null)" autocomplete="off" style="width: 300px;"/>
|
||||
</div>
|
||||
</div>
|
||||
@if (_mediaPosterChanged || _mediaPosterSaved is not null)
|
||||
{
|
||||
<div class="row mt-4 gx-1" style="width: 300px;">
|
||||
@if (_mediaPosterChanged)
|
||||
{
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-secondary btn-block btn-stretch-x" @onclick="SavePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
||||
@if (!_mediaPosterSaving)
|
||||
{
|
||||
<span class="sr-only">Save poster</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="sr-only">Saving...</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="CancelPoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">Drop changes</button>
|
||||
</div>
|
||||
}
|
||||
else if (_mediaPosterSaved is not null)
|
||||
{
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="DeletePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
||||
@if (!_mediaPosterSaving)
|
||||
{
|
||||
<span class="sr-only">Delete poster</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="sr-only">Deleting...</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||
<EditForm Model="_mediaRequest">
|
||||
<AntiforgeryToken/>
|
||||
<div class="container-fluid p-0">
|
||||
<div class="row form-group mb-1">
|
||||
<label for="title" class="col-2 col-form-label">Title*</label>
|
||||
<div class="col-10">
|
||||
<InputText id="title" class="form-control" @bind-Value="_mediaRequest!.Title"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row form-group my-1">
|
||||
<label for="og-title" class="col-2 col-form-label">Original title</label>
|
||||
<div class="col-10">
|
||||
<InputText id="og-title" class="form-control" @bind-Value="_mediaRequest!.OriginalTitle"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row form-group my-1">
|
||||
<label for="desc" class="col-2 col-form-label">Description</label>
|
||||
<div class="col-10">
|
||||
<InputTextArea id="desc" class="form-control" @bind-Value="_mediaRequest!.Description"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row form-group my-1">
|
||||
<label for="rel-date" class="col-2 col-form-label">Release date</label>
|
||||
<div class="col-4">
|
||||
<InputDate TValue="DateOnly?" id="rel-date" class="form-control" @bind-Value="_mediaRequest!.ReleaseDate"/>
|
||||
</div>
|
||||
<label for="length" class="col-2 col-form-label">Length</label>
|
||||
<div class="col-4">
|
||||
<InputNumber TValue="short?" id="length" class="form-control" @bind-Value="_mediaRequest!.Length"/>
|
||||
</div>
|
||||
</div>
|
||||
@if (_mediaRequest is MovieRequest)
|
||||
{
|
||||
<div class="row form-group mt-1">
|
||||
<label for="budget" class="col-2 col-form-label">Budget</label>
|
||||
<div class="col-10">
|
||||
<InputNumber TValue="decimal?" id="budget" class="form-control" @bind-Value="((MovieRequest)_mediaRequest)!.Budget"/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row form-group mt-1">
|
||||
<label class="col-2 col-form-label">Has ended</label>
|
||||
<div class="col-10 col-form-label">
|
||||
<div class="d-flex gap-3">
|
||||
<InputRadioGroup TValue="bool" @bind-Value="((SeriesRequest)_mediaRequest)!.HasEnded">
|
||||
<div class="d-flex gap-2">
|
||||
<InputRadio TValue="bool" Value="true"/>
|
||||
Yes
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<InputRadio TValue="bool" Value="false"/>
|
||||
No
|
||||
</div>
|
||||
</InputRadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<div class="d-flex justify-content-end align-items-center gap-3">
|
||||
@if (!string.IsNullOrWhiteSpace(_basicDataError))
|
||||
{
|
||||
<div class="text-danger">@_basicDataError</div>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" @onclick="SaveBasicData">
|
||||
@if (!_basicDataSaving)
|
||||
{
|
||||
<span class="sr-only">Save</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="sr-only">Saving...</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<ErrorComponent ErrorMessage="@_error"/>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<LoadingComponent/>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@if (_background is not null)
|
||||
{
|
||||
<style>
|
||||
body {
|
||||
background-image: url('@($"data:{_background.MimeType};base64,{Convert.ToBase64String(_background.Image)}")') !important;
|
||||
}
|
||||
|
||||
.logo, .main-button {
|
||||
background-image: linear-gradient(45deg, @($"#{BitConverter.ToString(_background.Background.FirstGradientColor).Replace("-", string.Empty)}"), @($"#{BitConverter.ToString(_background.Background.SecondGradientColor).Replace("-", string.Empty)}")) !important;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
254
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs
Normal file
254
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using WatchIt.Common.Model.Media;
|
||||
using WatchIt.Common.Model.Movies;
|
||||
using WatchIt.Common.Model.Series;
|
||||
using WatchIt.Website.Services.Utility.Authentication;
|
||||
using WatchIt.Website.Services.WebAPI.Media;
|
||||
using WatchIt.Website.Services.WebAPI.Movies;
|
||||
using WatchIt.Website.Services.WebAPI.Series;
|
||||
|
||||
namespace WatchIt.Website.Pages;
|
||||
|
||||
public partial class MediaEditPage : ComponentBase
|
||||
{
|
||||
#region SERVICES
|
||||
|
||||
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
||||
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
||||
[Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!;
|
||||
[Inject] public IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!;
|
||||
[Inject] public ISeriesWebAPIService SeriesWebAPIService { get; set; } = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PARAMETERS
|
||||
|
||||
[Parameter] public long? Id { get; set; }
|
||||
[Parameter] public string? Type { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private bool _loaded = false;
|
||||
private string? _error;
|
||||
|
||||
private User? _user;
|
||||
|
||||
private MediaPhotoResponse? _background;
|
||||
|
||||
private MediaResponse? _media;
|
||||
private MovieRequest? _movieRequest;
|
||||
private SeriesRequest? _seriesRequest;
|
||||
private Media? _mediaRequest => _movieRequest is not null ? _movieRequest : _seriesRequest;
|
||||
private bool _basicDataSaving;
|
||||
private string? _basicDataError;
|
||||
|
||||
private MediaPosterResponse? _mediaPosterSaved;
|
||||
private MediaPosterRequest? _mediaPosterRequest;
|
||||
private bool _mediaPosterChanged;
|
||||
private bool _mediaPosterSaving;
|
||||
private bool _mediaPosterDeleting;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
#region Main
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
List<Task> step1Tasks = new List<Task>();
|
||||
List<Task> step2Tasks = new List<Task>();
|
||||
List<Task> endTasks = new List<Task>();
|
||||
|
||||
// STEP 0
|
||||
step1Tasks.AddRange(
|
||||
[
|
||||
Task.Run(async () => _user = await AuthenticationService.GetUserAsync())
|
||||
]);
|
||||
|
||||
// STEP 1
|
||||
await Task.WhenAll(step1Tasks);
|
||||
if (_user is not null && _user.IsAdmin)
|
||||
{
|
||||
step2Tasks.AddRange(
|
||||
[
|
||||
InitializeMedia()
|
||||
]);
|
||||
}
|
||||
|
||||
// STEP 2
|
||||
await Task.WhenAll(step2Tasks);
|
||||
if (_user is not null && _user.IsAdmin && _media is not null)
|
||||
{
|
||||
endTasks.AddRange(
|
||||
[
|
||||
MediaWebAPIService.GetPhotoMediaRandomBackground(Id.Value, data => _background = data),
|
||||
MediaWebAPIService.GetPoster(Id.Value, data =>
|
||||
{
|
||||
_mediaPosterSaved = data;
|
||||
_mediaPosterRequest = new MediaPosterRequest(data);
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
await Task.WhenAll(endTasks);
|
||||
|
||||
_loaded = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeMedia()
|
||||
{
|
||||
if (Id.HasValue)
|
||||
{
|
||||
await MediaWebAPIService.GetMedia(Id.Value, data => _media = data, () => NavigationManager.NavigateTo("/media/new/movie"));
|
||||
if (_media.Type == MediaType.Movie)
|
||||
{
|
||||
await MoviesWebAPIService.GetMovie(Id.Value, data => _movieRequest = new MovieRequest(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
await SeriesWebAPIService.GetSeries(Id.Value, data => _seriesRequest = new SeriesRequest(data));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(Type) && Type == "series")
|
||||
{
|
||||
_seriesRequest = new SeriesRequest
|
||||
{
|
||||
Title = string.Empty
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_movieRequest = new MovieRequest
|
||||
{
|
||||
Title = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Poster
|
||||
|
||||
private async Task LoadPoster(InputFileChangeEventArgs args)
|
||||
{
|
||||
if (args.File.ContentType.StartsWith("image"))
|
||||
{
|
||||
Stream stream = args.File.OpenReadStream(5242880);
|
||||
byte[] array;
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
await stream.CopyToAsync(ms);
|
||||
array = ms.ToArray();
|
||||
}
|
||||
|
||||
_mediaPosterRequest = new MediaPosterRequest()
|
||||
{
|
||||
Image = array,
|
||||
MimeType = args.File.ContentType
|
||||
};
|
||||
_mediaPosterChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SavePoster()
|
||||
{
|
||||
void Success(MediaPosterResponse data)
|
||||
{
|
||||
_mediaPosterSaved = data;
|
||||
_mediaPosterRequest = new MediaPosterRequest(data);
|
||||
_mediaPosterChanged = false;
|
||||
_mediaPosterSaving = false;
|
||||
}
|
||||
|
||||
_mediaPosterSaving = true;
|
||||
await MediaWebAPIService.PutPoster(Id.Value, _mediaPosterRequest, Success);
|
||||
}
|
||||
|
||||
private void CancelPoster()
|
||||
{
|
||||
_mediaPosterRequest = _mediaPosterSaved is not null ? new MediaPosterRequest(_mediaPosterSaved) : null;
|
||||
_mediaPosterChanged = false;
|
||||
}
|
||||
|
||||
private async Task DeletePoster()
|
||||
{
|
||||
void Success()
|
||||
{
|
||||
_mediaPosterSaved = null;
|
||||
_mediaPosterRequest = null;
|
||||
_mediaPosterChanged = false;
|
||||
_mediaPosterDeleting = false;
|
||||
}
|
||||
|
||||
_mediaPosterDeleting = true;
|
||||
await MediaWebAPIService.DeletePoster(Id.Value, Success);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Basic data
|
||||
|
||||
private async Task SaveBasicData()
|
||||
{
|
||||
void SuccessPost(long id)
|
||||
{
|
||||
_basicDataSaving = false;
|
||||
NavigationManager.NavigateTo($"/media/{id}/edit", true);
|
||||
}
|
||||
|
||||
void BadRequest(IDictionary<string, string[]> errors)
|
||||
{
|
||||
_basicDataError = errors.SelectMany(x => x.Value).FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(_basicDataError))
|
||||
{
|
||||
_basicDataSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
_basicDataSaving = true;
|
||||
if (_media is null)
|
||||
{
|
||||
if (_movieRequest is not null)
|
||||
{
|
||||
await MoviesWebAPIService.PostMovie(_movieRequest, data => SuccessPost(data.Id), BadRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SeriesWebAPIService.PostSeries(_seriesRequest, data => SuccessPost(data.Id), BadRequest);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_movieRequest is not null)
|
||||
{
|
||||
await MoviesWebAPIService.PutMovie(Id.Value, _movieRequest, () => _basicDataSaving = false, BadRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SeriesWebAPIService.PutSeries(Id.Value, _seriesRequest, () => _basicDataSaving = false, BadRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -24,14 +24,14 @@ else
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid p-1 gy-2 gx-2">
|
||||
<div class="container-fluid">
|
||||
@if (_loaded)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_error))
|
||||
{
|
||||
<div class="row mt-9">
|
||||
<div class="col-auto">
|
||||
<img class="rounded-2 shadow" src="@(_poster is not null ? $"data:{_poster.MimeType};base64,{Encoding.UTF8.GetString(_poster.Image)}" : "assets/poster.png")" alt="poster" width="200" height="333"/>
|
||||
<img class="rounded-2 shadow object-fit-cover" src="@(_poster is not null ? _poster.ToString() : "assets/poster.png")" alt="poster" width="200" height="333"/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="d-flex h-100">
|
||||
@@ -187,18 +187,12 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col rounded-3 panel panel-regular m-1">
|
||||
<div>
|
||||
<h2 class="text-danger">@_error</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ErrorComponent ErrorMessage="@_error"/>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<LoadingPageComponent/>
|
||||
<LoadingComponent/>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -24,8 +24,7 @@ public partial class MediaPage : ComponentBase
|
||||
|
||||
#region PARAMETERS
|
||||
|
||||
[Parameter]
|
||||
public long Id { get; set; }
|
||||
[Parameter] public long Id { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
@page "/movies/new"
|
||||
@page "/movies/{id:long}/edit"
|
||||
|
||||
<PageTitle>WatchIt - New movie</PageTitle>
|
||||
|
||||
@if (_loaded)
|
||||
{
|
||||
<div class="container-fluid">
|
||||
@if (_authenticated)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col rounded-3 panel panel-regular m-1">
|
||||
<h2>@(Id is null ? "Create new movie" : $"Edit movie \"{_movieInfo.Title}\"")</h2>
|
||||
</div>
|
||||
</div>
|
||||
<MediaFormComponent Data=@(_movieData) SaveDataAction=SaveData Id=@(Id) SaveDataErrors=@(_movieDataErrors) SaveDataInfo=@(_movieDataInfo)/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<NoPermissionComponent/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using WatchIt.Common.Model.Movies;
|
||||
using WatchIt.Website.Services.Utility.Authentication;
|
||||
using WatchIt.Website.Services.WebAPI.Movies;
|
||||
|
||||
namespace WatchIt.Website.Pages;
|
||||
|
||||
public partial class MovieEditPage : ComponentBase
|
||||
{
|
||||
#region SERVICES
|
||||
|
||||
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
||||
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
||||
[Inject] public IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PARAMETERS
|
||||
|
||||
[Parameter]
|
||||
public long? Id { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private bool _loaded = false;
|
||||
private bool _authenticated = false;
|
||||
|
||||
private MovieResponse? _movieInfo = null;
|
||||
|
||||
private MovieRequest _movieData = new MovieRequest { Title = string.Empty };
|
||||
private IEnumerable<string>? _movieDataErrors = null;
|
||||
private string? _movieDataInfo = null;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
User? user = await AuthenticationService.GetUserAsync();
|
||||
if (user is not null && user.IsAdmin)
|
||||
{
|
||||
_authenticated = true;
|
||||
|
||||
await LoadData();
|
||||
}
|
||||
_loaded = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadData()
|
||||
{
|
||||
if (Id is not null)
|
||||
{
|
||||
await MoviesWebAPIService.GetMovie(Id.Value, GetSuccessAction, NoIdAction);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void GetSuccessAction(MovieResponse data)
|
||||
{
|
||||
_movieInfo = data;
|
||||
_movieData = new MovieRequest(_movieInfo);
|
||||
}
|
||||
void NoIdAction() => NavigationManager.NavigateTo("/movies/new", true); // await for all
|
||||
}
|
||||
|
||||
private async Task SaveData()
|
||||
{
|
||||
_movieDataErrors = null;
|
||||
_movieDataInfo = null;
|
||||
if (Id is null)
|
||||
{
|
||||
await MoviesWebAPIService.PostMovie(_movieData, PostSuccessAction, BadRequestAction, NoPermissionsAction, NoPermissionsAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MoviesWebAPIService.PutMovie(Id.Value, _movieData, PutSuccessAction, BadRequestAction, NoPermissionsAction, NoPermissionsAction);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void PutSuccessAction() => _movieDataInfo = "Data saved";
|
||||
void PostSuccessAction(MovieResponse data) => NavigationManager.NavigateTo($"/movies/{data.Id}/edit", true);
|
||||
void BadRequestAction(IDictionary<string, string[]> errors) => _movieDataErrors = errors.SelectMany(x => x.Value);
|
||||
void NoPermissionsAction() => NavigationManager.Refresh(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using WatchIt.Website.Services.Utility.Tokens;
|
||||
using WatchIt.Website.Services.WebAPI.Accounts;
|
||||
using WatchIt.Website.Services.WebAPI.Media;
|
||||
using WatchIt.Website.Services.WebAPI.Movies;
|
||||
using WatchIt.Website.Services.WebAPI.Series;
|
||||
|
||||
namespace WatchIt.Website;
|
||||
|
||||
@@ -62,6 +63,7 @@ public static class Program
|
||||
builder.Services.AddScoped<IAccountsWebAPIService, AccountsWebAPIService>();
|
||||
builder.Services.AddSingleton<IMediaWebAPIService, MediaWebAPIService>();
|
||||
builder.Services.AddSingleton<IMoviesWebAPIService, MoviesWebAPIService>();
|
||||
builder.Services.AddSingleton<ISeriesWebAPIService, SeriesWebAPIService>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Accounts\WatchIt.Website.Services.WebAPI.Accounts.csproj" />
|
||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Media\WatchIt.Website.Services.WebAPI.Media.csproj" />
|
||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Movies\WatchIt.Website.Services.WebAPI.Movies.csproj" />
|
||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Series\WatchIt.Website.Services.WebAPI.Series.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user