universal media edit page created

This commit is contained in:
2024-09-22 23:11:21 +02:00
Unverified
parent 9a96679de1
commit de0eac4a0c
27 changed files with 626 additions and 180 deletions

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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)]

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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>

View File

@@ -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">⚠&#xFE0E;</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>

View File

@@ -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
}

View File

@@ -0,0 +1,5 @@
/* CLASSES */
.icon-size {
font-size: 80px;
}

View File

@@ -80,7 +80,7 @@ public partial class MediaFormComponent : ComponentBase
private async Task SavePoster()
{
void SuccessAction()
void SuccessAction(MediaPosterResponse data)
{
_actualPosterBase64 = _posterBase64;
_actualPosterMediaType = _posterMediaType;

View File

@@ -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>

View File

@@ -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>

View File

@@ -2,10 +2,10 @@
<PageTitle>WatchIt administrator panel</PageTitle>
<div class="container-fluid">
@if (_loaded)
{
<div class="container-fluid">
@if (_authenticated)
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"/>
}
}
else
{
<LoadingComponent/>
}
</div>
}

View File

@@ -66,7 +66,9 @@ public partial class AuthPage
#region METHODS
protected override async Task OnInitializedAsync()
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (await AuthenticationService.GetAuthenticationStatusAsync())
{
@@ -88,6 +90,8 @@ public partial class AuthPage
await MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess);
_loaded = true;
StateHasChanged();
}
}
private async Task Login()

View 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>
}

View 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
}

View File

@@ -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>

View File

@@ -24,8 +24,7 @@ public partial class MediaPage : ComponentBase
#region PARAMETERS
[Parameter]
public long Id { get; set; }
[Parameter] public long Id { get; set; }
#endregion

View File

@@ -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>
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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>