profile picture and basic info editors added
This commit is contained in:
@@ -2,9 +2,12 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace WatchIt.Common.Model.Accounts;
|
namespace WatchIt.Common.Model.Accounts;
|
||||||
|
|
||||||
public class AccountRequest : Account
|
public class AccountProfileInfoRequest
|
||||||
{
|
{
|
||||||
#region PROPERTIES
|
#region PROPERTIES
|
||||||
|
|
||||||
|
[JsonPropertyName("description")]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("gender_id")]
|
[JsonPropertyName("gender_id")]
|
||||||
public short? GenderId { get; set; }
|
public short? GenderId { get; set; }
|
||||||
@@ -13,12 +16,24 @@ public class AccountRequest : Account
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region CONSTRUCTORS
|
||||||
|
|
||||||
|
public AccountProfileInfoRequest() { }
|
||||||
|
|
||||||
|
public AccountProfileInfoRequest(AccountResponse accountResponse)
|
||||||
|
{
|
||||||
|
Description = accountResponse.Description;
|
||||||
|
GenderId = accountResponse.Gender?.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region PUBLIC METHODS
|
#region PUBLIC METHODS
|
||||||
|
|
||||||
public void UpdateAccount(Database.Model.Account.Account account)
|
public void UpdateAccount(Database.Model.Account.Account account)
|
||||||
{
|
{
|
||||||
account.Username = Username;
|
|
||||||
account.Email = Email;
|
|
||||||
account.Description = Description;
|
account.Description = Description;
|
||||||
account.GenderId = GenderId;
|
account.GenderId = GenderId;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace WatchIt.Common.Model.Accounts;
|
||||||
|
|
||||||
|
public class AccountProfilePictureRequest : AccountProfilePicture
|
||||||
|
{
|
||||||
|
#region CONSTRUCTORS
|
||||||
|
|
||||||
|
public AccountProfilePictureRequest() {}
|
||||||
|
|
||||||
|
[SetsRequiredMembers]
|
||||||
|
public AccountProfilePictureRequest(Picture image)
|
||||||
|
{
|
||||||
|
Image = image.Image;
|
||||||
|
MimeType = image.MimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
public Database.Model.Account.AccountProfilePicture CreateMediaPosterImage() => new Database.Model.Account.AccountProfilePicture
|
||||||
|
{
|
||||||
|
Image = Image,
|
||||||
|
MimeType = MimeType,
|
||||||
|
};
|
||||||
|
|
||||||
|
public void UpdateMediaPosterImage(Database.Model.Account.AccountProfilePicture item)
|
||||||
|
{
|
||||||
|
item.Image = Image;
|
||||||
|
item.MimeType = MimeType;
|
||||||
|
item.UploadDate = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ public class AccountsController(IAccountsControllerService accountsControllerSer
|
|||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
public async Task<ActionResult> Authenticate([FromBody]AuthenticateRequest body) => await accountsControllerService.Authenticate(body);
|
public async Task<ActionResult> Authenticate([FromBody]AuthenticateRequest body) => await accountsControllerService.Authenticate(body);
|
||||||
|
|
||||||
[HttpPost("authenticate-refresh")]
|
[HttpPost("authenticate_refresh")]
|
||||||
[Authorize(AuthenticationSchemes = "refresh")]
|
[Authorize(AuthenticationSchemes = "refresh")]
|
||||||
[ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
@@ -39,25 +39,38 @@ public class AccountsController(IAccountsControllerService accountsControllerSer
|
|||||||
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
|
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
|
||||||
public async Task<ActionResult> Logout() => await accountsControllerService.Logout();
|
public async Task<ActionResult> Logout() => await accountsControllerService.Logout();
|
||||||
|
|
||||||
[HttpGet("{id}/profile-picture")]
|
[HttpGet("{id}/profile_picture")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[ProducesResponseType(typeof(AccountProfilePictureResponse), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(AccountProfilePictureResponse), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> GetAccountProfilePicture([FromRoute(Name = "id")]long id) => await accountsControllerService.GetAccountProfilePicture(id);
|
public async Task<ActionResult> GetAccountProfilePicture([FromRoute(Name = "id")]long id) => await accountsControllerService.GetAccountProfilePicture(id);
|
||||||
|
|
||||||
|
[HttpPut("profile_picture")]
|
||||||
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||||
|
[ProducesResponseType(typeof(AccountProfilePictureResponse), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
|
public async Task<ActionResult> PutAccountProfilePicture([FromBody]AccountProfilePictureRequest body) => await accountsControllerService.PutAccountProfilePicture(body);
|
||||||
|
|
||||||
|
[HttpDelete("profile_picture")]
|
||||||
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
|
public async Task<ActionResult> DeleteAccountProfilePicture() => await accountsControllerService.DeleteAccountProfilePicture();
|
||||||
|
|
||||||
[HttpGet("{id}/info")]
|
[HttpGet("{id}/info")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> GetAccountInfo([FromRoute]long id) => await accountsControllerService.GetAccountInfo(id);
|
public async Task<ActionResult> GetAccountInfo([FromRoute]long id) => await accountsControllerService.GetAccountInfo(id);
|
||||||
|
|
||||||
[HttpPut("info")]
|
[HttpPut("profile_info")]
|
||||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||||
[ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> PutAccountInfo([FromBody]AccountRequest data) => await accountsControllerService.PutAccountInfo(data);
|
public async Task<ActionResult> PutAccountProfileInfo([FromBody]AccountProfileInfoRequest data) => await accountsControllerService.PutAccountProfileInfo(data);
|
||||||
|
|
||||||
[HttpGet("{id}/movies")]
|
[HttpGet("{id}/movies")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
|
|||||||
@@ -142,6 +142,48 @@ public class AccountsControllerService(
|
|||||||
AccountProfilePictureResponse picture = new AccountProfilePictureResponse(account.ProfilePicture);
|
AccountProfilePictureResponse picture = new AccountProfilePictureResponse(account.ProfilePicture);
|
||||||
return RequestResult.Ok(picture);
|
return RequestResult.Ok(picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<RequestResult> PutAccountProfilePicture(AccountProfilePictureRequest data)
|
||||||
|
{
|
||||||
|
Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId());
|
||||||
|
Database.Model.Account.AccountProfilePicture? picture = account.ProfilePicture;
|
||||||
|
|
||||||
|
if (picture is null)
|
||||||
|
{
|
||||||
|
picture = data.CreateMediaPosterImage();
|
||||||
|
await database.AccountProfilePictures.AddAsync(picture);
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
|
account.ProfilePictureId = picture.Id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.UpdateMediaPosterImage(picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
|
AccountProfilePictureResponse returnData = new AccountProfilePictureResponse(picture);
|
||||||
|
return RequestResult.Ok(returnData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RequestResult> DeleteAccountProfilePicture()
|
||||||
|
{
|
||||||
|
Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId());
|
||||||
|
Database.Model.Account.AccountProfilePicture? picture = account.ProfilePicture;
|
||||||
|
|
||||||
|
if (picture is not null)
|
||||||
|
{
|
||||||
|
account.ProfilePictureId = null;
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
|
database.AccountProfilePictures.Attach(picture);
|
||||||
|
database.AccountProfilePictures.Remove(picture);
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RequestResult.NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<RequestResult> GetAccountInfo(long id)
|
public async Task<RequestResult> GetAccountInfo(long id)
|
||||||
{
|
{
|
||||||
@@ -151,11 +193,11 @@ public class AccountsControllerService(
|
|||||||
return RequestResult.NotFound();
|
return RequestResult.NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountResponse response = new AccountResponse(account);
|
AccountResponse profileInfoResponse = new AccountResponse(account);
|
||||||
return RequestResult.Ok(response);
|
return RequestResult.Ok(profileInfoResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestResult> PutAccountInfo(AccountRequest data)
|
public async Task<RequestResult> PutAccountProfileInfo(AccountProfileInfoRequest data)
|
||||||
{
|
{
|
||||||
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == userService.GetUserId());
|
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == userService.GetUserId());
|
||||||
if (account is null)
|
if (account is null)
|
||||||
@@ -164,6 +206,8 @@ public class AccountsControllerService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data.UpdateAccount(account);
|
data.UpdateAccount(account);
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
return RequestResult.Ok();
|
return RequestResult.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ public interface IAccountsControllerService
|
|||||||
Task<RequestResult> AuthenticateRefresh();
|
Task<RequestResult> AuthenticateRefresh();
|
||||||
Task<RequestResult> Logout();
|
Task<RequestResult> Logout();
|
||||||
Task<RequestResult> GetAccountProfilePicture(long id);
|
Task<RequestResult> GetAccountProfilePicture(long id);
|
||||||
|
Task<RequestResult> PutAccountProfilePicture(AccountProfilePictureRequest data);
|
||||||
|
Task<RequestResult> DeleteAccountProfilePicture();
|
||||||
Task<RequestResult> GetAccountInfo(long id);
|
Task<RequestResult> GetAccountInfo(long id);
|
||||||
Task<RequestResult> PutAccountInfo(AccountRequest data);
|
Task<RequestResult> PutAccountProfileInfo(AccountProfileInfoRequest data);
|
||||||
Task<RequestResult> GetAccountRatedMovies(long id, MovieRatedQueryParameters query);
|
Task<RequestResult> GetAccountRatedMovies(long id, MovieRatedQueryParameters query);
|
||||||
Task<RequestResult> GetAccountRatedSeries(long id, SeriesRatedQueryParameters query);
|
Task<RequestResult> GetAccountRatedSeries(long id, SeriesRatedQueryParameters query);
|
||||||
Task<RequestResult> GetAccountRatedPersons(long id, PersonRatedQueryParameters query);
|
Task<RequestResult> GetAccountRatedPersons(long id, PersonRatedQueryParameters query);
|
||||||
|
|||||||
@@ -4,14 +4,10 @@ using WatchIt.Database;
|
|||||||
|
|
||||||
namespace WatchIt.WebAPI.Validators.Accounts;
|
namespace WatchIt.WebAPI.Validators.Accounts;
|
||||||
|
|
||||||
public class AccountRequestValidator : AbstractValidator<AccountRequest>
|
public class AccountProfileInfoRequestValidator : AbstractValidator<AccountProfileInfoRequest>
|
||||||
{
|
{
|
||||||
public AccountRequestValidator(DatabaseContext database)
|
public AccountProfileInfoRequestValidator(DatabaseContext database)
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Username).NotEmpty()
|
|
||||||
.MaximumLength(50);
|
|
||||||
RuleFor(x => x.Email).EmailAddress()
|
|
||||||
.MaximumLength(320);
|
|
||||||
RuleFor(x => x.Description).MaximumLength(1000);
|
RuleFor(x => x.Description).MaximumLength(1000);
|
||||||
When(x => x.GenderId.HasValue, () =>
|
When(x => x.GenderId.HasValue, () =>
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using WatchIt.Common.Model.Accounts;
|
||||||
|
|
||||||
|
namespace WatchIt.WebAPI.Validators.Accounts;
|
||||||
|
|
||||||
|
public class AccountProfilePictureRequestValidator : AbstractValidator<AccountProfilePictureRequest>
|
||||||
|
{
|
||||||
|
public AccountProfilePictureRequestValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Image).NotEmpty();
|
||||||
|
RuleFor(x => x.MimeType).Matches(@"\w+/.+").WithMessage("Incorrect mimetype");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
|
|||||||
|
|
||||||
public async Task GetAccountProfilePicture(long id, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null)
|
public async Task GetAccountProfilePicture(long id, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null)
|
||||||
{
|
{
|
||||||
string url = GetUrl(EndpointsConfiguration.Accounts.GetProfilePicture, id);
|
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountProfilePicture, id);
|
||||||
HttpRequest request = new HttpRequest(HttpMethodType.Get, url);
|
HttpRequest request = new HttpRequest(HttpMethodType.Get, url);
|
||||||
|
|
||||||
HttpResponse response = await httpClientService.SendRequestAsync(request);
|
HttpResponse response = await httpClientService.SendRequestAsync(request);
|
||||||
@@ -80,6 +80,34 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
|
|||||||
.RegisterActionFor404NotFound(notFoundAction)
|
.RegisterActionFor404NotFound(notFoundAction)
|
||||||
.ExecuteAction();
|
.ExecuteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task PutAccountProfilePicture(AccountProfilePictureRequest data, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null)
|
||||||
|
{
|
||||||
|
string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountProfilePicture);
|
||||||
|
|
||||||
|
HttpRequest request = new HttpRequest(HttpMethodType.Put, url)
|
||||||
|
{
|
||||||
|
Body = data
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpResponse response = await httpClientService.SendRequestAsync(request);
|
||||||
|
response.RegisterActionFor2XXSuccess(successAction)
|
||||||
|
.RegisterActionFor400BadRequest(badRequestAction)
|
||||||
|
.RegisterActionFor401Unauthorized(unauthorizedAction)
|
||||||
|
.ExecuteAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAccountProfilePicture(Action? successAction = null, Action? unauthorizedAction = null)
|
||||||
|
{
|
||||||
|
string url = GetUrl(EndpointsConfiguration.Accounts.DeleteAccountProfilePicture);
|
||||||
|
|
||||||
|
HttpRequest request = new HttpRequest(HttpMethodType.Delete, url);
|
||||||
|
|
||||||
|
HttpResponse response = await httpClientService.SendRequestAsync(request);
|
||||||
|
response.RegisterActionFor2XXSuccess(successAction)
|
||||||
|
.RegisterActionFor401Unauthorized(unauthorizedAction)
|
||||||
|
.ExecuteAction();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task GetAccountInfo(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null)
|
public async Task GetAccountInfo(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null)
|
||||||
{
|
{
|
||||||
@@ -92,9 +120,9 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
|
|||||||
.ExecuteAction();
|
.ExecuteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PutAccountInfo(AccountRequest data, Action<AccountResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null)
|
public async Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null)
|
||||||
{
|
{
|
||||||
string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountInfo);
|
string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountProfileInfo);
|
||||||
HttpRequest request = new HttpRequest(HttpMethodType.Put, url)
|
HttpRequest request = new HttpRequest(HttpMethodType.Put, url)
|
||||||
{
|
{
|
||||||
Body = data,
|
Body = data,
|
||||||
@@ -104,7 +132,6 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
|
|||||||
response.RegisterActionFor2XXSuccess(successAction)
|
response.RegisterActionFor2XXSuccess(successAction)
|
||||||
.RegisterActionFor400BadRequest(badRequestAction)
|
.RegisterActionFor400BadRequest(badRequestAction)
|
||||||
.RegisterActionFor401Unauthorized(unauthorizedAction)
|
.RegisterActionFor401Unauthorized(unauthorizedAction)
|
||||||
.RegisterActionFor404NotFound(notFoundAction)
|
|
||||||
.ExecuteAction();
|
.ExecuteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ public interface IAccountsClientService
|
|||||||
Task AuthenticateRefresh(Action<AuthenticateResponse>? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
Task AuthenticateRefresh(Action<AuthenticateResponse>? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
||||||
Task Logout(Action? successAction = null);
|
Task Logout(Action? successAction = null);
|
||||||
Task GetAccountProfilePicture(long id, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null);
|
Task GetAccountProfilePicture(long id, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null);
|
||||||
|
Task PutAccountProfilePicture(AccountProfilePictureRequest data, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null);
|
||||||
|
Task DeleteAccountProfilePicture(Action? successAction = null, Action? unauthorizedAction = null);
|
||||||
Task GetAccountInfo(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null);
|
Task GetAccountInfo(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null);
|
||||||
Task PutAccountInfo(AccountRequest data, Action<AccountResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null);
|
Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null);
|
||||||
Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null);
|
Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null);
|
||||||
Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null);
|
Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null);
|
||||||
Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null);
|
Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null);
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ public class Accounts
|
|||||||
public string Authenticate { get; set; }
|
public string Authenticate { get; set; }
|
||||||
public string AuthenticateRefresh { get; set; }
|
public string AuthenticateRefresh { get; set; }
|
||||||
public string Logout { get; set; }
|
public string Logout { get; set; }
|
||||||
public string GetProfilePicture { get; set; }
|
public string GetAccountProfilePicture { get; set; }
|
||||||
|
public string PutAccountProfilePicture { get; set; }
|
||||||
|
public string DeleteAccountProfilePicture { get; set; }
|
||||||
public string GetAccountInfo { get; set; }
|
public string GetAccountInfo { get; set; }
|
||||||
public string PutAccountInfo { get; set; }
|
public string PutAccountProfileInfo { get; set; }
|
||||||
public string GetAccountRatedMovies { get; set; }
|
public string GetAccountRatedMovies { get; set; }
|
||||||
public string GetAccountRatedSeries { get; set; }
|
public string GetAccountRatedSeries { get; set; }
|
||||||
public string GetAccountRatedPersons { get; set; }
|
public string GetAccountRatedPersons { get; set; }
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link rel="stylesheet" href="css/general.css?version=0.3.0.5"/>
|
<link rel="stylesheet" href="css/general.css?version=0.3.0.5"/>
|
||||||
<link rel="stylesheet" href="css/panel.css?version=0.3.0.3"/>
|
<link rel="stylesheet" href="css/panel.css?version=0.3.0.5"/>
|
||||||
<link rel="stylesheet" href="css/main_button.css?version=0.3.0.0"/>
|
<link rel="stylesheet" href="css/main_button.css?version=0.3.0.0"/>
|
||||||
<link rel="stylesheet" href="css/gaps.css?version=0.3.0.1"/>
|
<link rel="stylesheet" href="css/gaps.css?version=0.3.0.1"/>
|
||||||
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.4.0.13"/>
|
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.4.0.16"/>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
|
||||||
<!-- BOOTSTRAP -->
|
<!-- BOOTSTRAP -->
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
<div class="vstack gap-3">
|
<div class="vstack gap-3">
|
||||||
<PictureComponent Picture="@(_pictureSelected)" Placeholder="@(PicturePlaceholder)" AlternativeText="poster" Width="@(ContentWidth)" AspectRatio="PictureComponent.PictureComponentAspectRatio.Default"/>
|
<PictureComponent Picture="@(_pictureSelected)" Placeholder="@(PicturePlaceholder)" AlternativeText="loaded_picture" Circle="@(Circle)" Width="@(ContentWidth)"/>
|
||||||
<InputFile class="form-control content-width" OnChange="Load" disabled="@(!Id.HasValue)" autocomplete="off"/>
|
<InputFile class="form-control content-width" OnChange="Load" disabled="@(!Id.HasValue)" autocomplete="off"/>
|
||||||
@if (_pictureChanged || _pictureSaved is not null)
|
@if (_pictureChanged || _pictureSaved is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ public partial class PictureEditorPanelComponent : ComponentBase
|
|||||||
[Parameter] public long? Id { get; set; }
|
[Parameter] public long? Id { get; set; }
|
||||||
[Parameter] public int ContentWidth { get; set; } = 300;
|
[Parameter] public int ContentWidth { get; set; } = 300;
|
||||||
[Parameter] public required string PicturePlaceholder { get; set; }
|
[Parameter] public required string PicturePlaceholder { get; set; }
|
||||||
|
[Parameter] public bool Circle { get; set; }
|
||||||
[Parameter] public string Class { get; set; } = string.Empty;
|
[Parameter] public string Class { get; set; } = string.Empty;
|
||||||
[Parameter] public required Func<long, Action<Picture>, Task> PictureGetTask { get; set; }
|
[Parameter] public required Func<long, Action<Picture>, Task> PictureGetTask { get; set; }
|
||||||
[Parameter] public required Func<long, Picture, Action<Picture>, Task> PicturePutTask { get; set; }
|
[Parameter] public required Func<long, Picture, Action<Picture>, Task> PicturePutTask { get; set; }
|
||||||
[Parameter] public required Func<long, Action, Task> PictureDeleteTask { get; set; }
|
[Parameter] public required Func<long, Action, Task> PictureDeleteTask { get; set; }
|
||||||
|
[Parameter] public Action<Picture?>? OnPictureChanged { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -92,6 +94,7 @@ public partial class PictureEditorPanelComponent : ComponentBase
|
|||||||
_pictureSelected = data;
|
_pictureSelected = data;
|
||||||
_pictureChanged = false;
|
_pictureChanged = false;
|
||||||
_pictureSaving = false;
|
_pictureSaving = false;
|
||||||
|
OnPictureChanged?.Invoke(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pictureSaving = true;
|
_pictureSaving = true;
|
||||||
@@ -112,6 +115,7 @@ public partial class PictureEditorPanelComponent : ComponentBase
|
|||||||
_pictureSelected = null;
|
_pictureSelected = null;
|
||||||
_pictureChanged = false;
|
_pictureChanged = false;
|
||||||
_pictureDeleting = false;
|
_pictureDeleting = false;
|
||||||
|
OnPictureChanged?.Invoke(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pictureDeleting = true;
|
_pictureDeleting = true;
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
<img class="rounded-circle object-fit-cover @(Class)" alt="avatar" height="@(Size)" src="@(_picture is null ? "assets/user_placeholder.png" : _picture.ToString())"/>
|
<PictureComponent Class="@(Class)"
|
||||||
|
Height="Size"
|
||||||
|
Circle="true"
|
||||||
|
Shadow="false"
|
||||||
|
Placeholder="assets/user_placeholder.png"
|
||||||
|
AlternativeText="avatar"
|
||||||
|
Picture="@(_picture)"/>
|
||||||
@@ -30,6 +30,18 @@ public partial class AccountPictureComponent : ComponentBase
|
|||||||
private AccountProfilePictureResponse? _picture;
|
private AccountProfilePictureResponse? _picture;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PUBLIC METHODS
|
||||||
|
|
||||||
|
public async Task Reload()
|
||||||
|
{
|
||||||
|
await AccountsClientService.GetAccountProfilePicture(Id, data => _picture = data, notFoundAction: () => _picture = null);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -39,18 +51,7 @@ public partial class AccountPictureComponent : ComponentBase
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
List<Task> endTasks = new List<Task>();
|
await Reload();
|
||||||
|
|
||||||
// STEP 0
|
|
||||||
endTasks.AddRange(
|
|
||||||
[
|
|
||||||
AccountsClientService.GetAccountProfilePicture(Id, data => _picture = data)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// END
|
|
||||||
await Task.WhenAll(endTasks);
|
|
||||||
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<img id="imgObject" class="rounded-2 shadow object-fit-cover @(Class)" src="@(Picture is not null ? Picture.ToString() : Placeholder)" alt="@(AlternativeText)" @attributes="@(_attributes)" style="aspect-ratio: @(AspectRatio.ToString());"/>
|
<img class="@(Circle ? "rounded-circle" : "rounded-2") @(Shadow ? "shadow" : string.Empty) object-fit-cover @(Class)" src="@(Picture is not null ? Picture.ToString() : Placeholder)" alt="@(AlternativeText)" @attributes="@(_attributes)" style="aspect-ratio: @(AspectRatio.ToString());"/>
|
||||||
@@ -14,6 +14,8 @@ public partial class PictureComponent : ComponentBase
|
|||||||
[Parameter] public string Class { get; set; } = string.Empty;
|
[Parameter] public string Class { get; set; } = string.Empty;
|
||||||
[Parameter] public int? Height { get; set; }
|
[Parameter] public int? Height { get; set; }
|
||||||
[Parameter] public int? Width { get; set; }
|
[Parameter] public int? Width { get; set; }
|
||||||
|
[Parameter] public bool Circle { get; set; }
|
||||||
|
[Parameter] public bool Shadow { get; set; } = true;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -40,6 +42,11 @@ public partial class PictureComponent : ComponentBase
|
|||||||
{
|
{
|
||||||
_attributes.Add("width", Width.Value);
|
_attributes.Add("width", Width.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Circle)
|
||||||
|
{
|
||||||
|
AspectRatio = PictureComponentAspectRatio.Square;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -71,6 +78,7 @@ public partial class PictureComponent : ComponentBase
|
|||||||
|
|
||||||
public static readonly PictureComponentAspectRatio Default = new PictureComponentAspectRatio();
|
public static readonly PictureComponentAspectRatio Default = new PictureComponentAspectRatio();
|
||||||
public static readonly PictureComponentAspectRatio Photo = new PictureComponentAspectRatio(16, 9);
|
public static readonly PictureComponentAspectRatio Photo = new PictureComponentAspectRatio(16, 9);
|
||||||
|
public static readonly PictureComponentAspectRatio Square = new PictureComponentAspectRatio(1, 1);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
@using WatchIt.Common.Model.Genders
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="panel @(Class)">
|
||||||
|
@if (_loaded)
|
||||||
|
{
|
||||||
|
<div class="vstack gap-3">
|
||||||
|
<h4 class="fw-bold">Basic profile info</h4>
|
||||||
|
<EditForm Model="@(_accountProfileInfo)">
|
||||||
|
<AntiforgeryToken/>
|
||||||
|
<div class="container-grid">
|
||||||
|
<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="_accountProfileInfo!.Description"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group my-1">
|
||||||
|
<label for="desc" class="col-2 col-form-label">Gender</label>
|
||||||
|
<div class="col-10">
|
||||||
|
<InputSelect TValue="short?" id="desc" class="form-control" @bind-Value="_accountProfileInfo!.GenderId">
|
||||||
|
<option value="@(default(short?))">No choice</option>
|
||||||
|
@foreach (GenderResponse gender in _genders)
|
||||||
|
{
|
||||||
|
<option value="@(gender.Id)">@(gender.Name)</option>
|
||||||
|
}
|
||||||
|
</InputSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="col align-self-center">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_error))
|
||||||
|
{
|
||||||
|
<span class="text-danger">@(_error)</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-secondary" disabled="@(_saving)" @onclick="@(Save)">
|
||||||
|
@if (!_saving)
|
||||||
|
{
|
||||||
|
<span>Save</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span>Saving...</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<LoadingComponent Color="@(LoadingComponent.LoadingComponentColors.Light)"/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Common.Model.Accounts;
|
||||||
|
using WatchIt.Common.Model.Genders;
|
||||||
|
using WatchIt.Website.Services.Client.Accounts;
|
||||||
|
using WatchIt.Website.Services.Client.Genders;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Components.Pages.UserEditPage.Panels;
|
||||||
|
|
||||||
|
public partial class ProfileEditFormPanelComponent : ComponentBase
|
||||||
|
{
|
||||||
|
#region SERVICES
|
||||||
|
|
||||||
|
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
|
||||||
|
[Inject] private IGendersClientService GendersClientService { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public long Id { get; set; }
|
||||||
|
[Parameter] public string Class { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private bool _loaded;
|
||||||
|
private bool _saving;
|
||||||
|
private string? _error;
|
||||||
|
|
||||||
|
private IEnumerable<GenderResponse> _genders = [];
|
||||||
|
|
||||||
|
private AccountProfileInfoRequest _accountProfileInfo = new AccountProfileInfoRequest();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
await Task.WhenAll(
|
||||||
|
[
|
||||||
|
GendersClientService.GetAllGenders(successAction: data => _genders = data),
|
||||||
|
AccountsClientService.GetAccountInfo(Id, data => _accountProfileInfo = new AccountProfileInfoRequest(data))
|
||||||
|
]);
|
||||||
|
|
||||||
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
void Success()
|
||||||
|
{
|
||||||
|
_error = null;
|
||||||
|
_saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BadRequest(IDictionary<string, string[]> errors)
|
||||||
|
{
|
||||||
|
_error = errors.SelectMany(x => x.Value).FirstOrDefault() ?? "Unknown error";
|
||||||
|
_saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthError()
|
||||||
|
{
|
||||||
|
_error = "Authentication error";
|
||||||
|
_saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_saving = true;
|
||||||
|
await AccountsClientService.PutAccountProfileInfo(_accountProfileInfo, Success, BadRequest, AuthError);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div class="panel panel-section-header">
|
||||||
|
<div class="d-flex">
|
||||||
|
<h3 class="fw-bold m-0">Profile settings</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<div class="panel" role="button" @onclick="@(() => NavigationManager.NavigateTo("/user"))">
|
||||||
|
<div class="d-flex gap-3 align-items-center">
|
||||||
|
<AccountPictureComponent @ref="_accountPicture" Id="@(User.Id)" Size="50"/>
|
||||||
|
<div class="d-flex-inline flex-column">
|
||||||
|
<h2 id="username" class="fw-bold m-0">@(User.Username)</h2>
|
||||||
|
<span id="secondaryText" class="text-secondary">User settings</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Website.Components.Common.Subcomponents;
|
||||||
|
using WatchIt.Website.Services.Authentication;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Components.Pages.UserEditPage.Panels;
|
||||||
|
|
||||||
|
public partial class UserEditPageHeaderPanelComponent : ComponentBase
|
||||||
|
{
|
||||||
|
#region SERVICES
|
||||||
|
|
||||||
|
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public required User User { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private AccountPictureComponent _accountPicture = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PUBLIC METHODS
|
||||||
|
|
||||||
|
public async Task ReloadPicture() => await _accountPicture.Reload();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/* IDS */
|
||||||
|
|
||||||
|
#username {
|
||||||
|
margin-top: -8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#secondaryText {
|
||||||
|
color: lightgray !important;
|
||||||
|
}
|
||||||
@@ -1,26 +1,26 @@
|
|||||||
<div id="base" class="vstack">
|
<div id="base" class="vstack">
|
||||||
<AccountPictureComponent Class="shadow position-absolute z-1 start-50 translate-middle" Id="@(AccountData.Id)" Size="240"/>
|
<AccountPictureComponent Class="shadow position-absolute z-1 start-50 translate-middle" Id="@(AccountProfileInfoData.Id)" Size="240"/>
|
||||||
<div class="panel z-0">
|
<div class="panel z-0">
|
||||||
<div class="vstack gap-3">
|
<div class="vstack gap-3">
|
||||||
<div id="space" class="container-grid"></div>
|
<div id="space" class="container-grid"></div>
|
||||||
<div class="d-flex justify-content-center">
|
<div class="d-flex justify-content-center">
|
||||||
<h3 class="fw-bold">@(AccountData.Username)</h3>
|
<h3 class="fw-bold m-0">@(AccountProfileInfoData.Username)</h3>
|
||||||
</div>
|
</div>
|
||||||
@if (!string.IsNullOrWhiteSpace(AccountData.Description))
|
@if (!string.IsNullOrWhiteSpace(AccountProfileInfoData.Description))
|
||||||
{
|
{
|
||||||
<span>
|
<span class="text-center w-100 mb-2">
|
||||||
@(AccountData.Description)
|
@(AccountProfileInfoData.Description)
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
<div class="d-flex flex-wrap justify-content-center metadata-pill-container">
|
<div class="d-flex flex-wrap justify-content-center metadata-pill-container">
|
||||||
<div class="metadata-pill"><strong>Email:</strong> @(AccountData.Email)</div>
|
<div class="metadata-pill"><strong>Email:</strong> @(AccountProfileInfoData.Email)</div>
|
||||||
@if (!string.IsNullOrWhiteSpace(AccountData.Gender?.Name))
|
@if (!string.IsNullOrWhiteSpace(AccountProfileInfoData.Gender?.Name))
|
||||||
{
|
{
|
||||||
<div class="metadata-pill"><strong>Gender:</strong> @(AccountData.Gender?.Name)</div>
|
<div class="metadata-pill"><strong>Gender:</strong> @(AccountProfileInfoData.Gender?.Name)</div>
|
||||||
}
|
}
|
||||||
<div class="metadata-pill"><strong>Account created:</strong> @(AccountData.CreationDate.ToShortDateString())</div>
|
<div class="metadata-pill"><strong>Account created:</strong> @(AccountProfileInfoData.CreationDate.ToShortDateString())</div>
|
||||||
<div class="metadata-pill"><strong>Last active:</strong> @(AccountData.LastActive.ToShortDateString())</div>
|
<div class="metadata-pill"><strong>Last active:</strong> @(AccountProfileInfoData.LastActive.ToShortDateString())</div>
|
||||||
@if (AccountData.IsAdmin)
|
@if (AccountProfileInfoData.IsAdmin)
|
||||||
{
|
{
|
||||||
<div class="metadata-pill"><strong>Admin</strong></div>
|
<div class="metadata-pill"><strong>Admin</strong></div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public partial class UserPageHeaderPanelComponent : ComponentBase
|
|||||||
|
|
||||||
#region PARAMETERS
|
#region PARAMETERS
|
||||||
|
|
||||||
[Parameter] public required AccountResponse AccountData { get; set; }
|
[Parameter] public required AccountResponse AccountProfileInfoData { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public partial class UserPageHeaderPanelComponent : ComponentBase
|
|||||||
// STEP 0
|
// STEP 0
|
||||||
endTasks.AddRange(
|
endTasks.AddRange(
|
||||||
[
|
[
|
||||||
AccountsClientService.GetAccountProfilePicture(AccountData.Id, data => _accountProfilePicture = data),
|
AccountsClientService.GetAccountProfilePicture(AccountProfileInfoData.Id, data => _accountProfilePicture = data),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// END
|
// END
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<Dropdown RightAligned>
|
<Dropdown RightAligned>
|
||||||
<Button Color="Color.Default" Clicked="@(() => NavigationManager.NavigateTo("/user"))">
|
<Button Color="Color.Default" Clicked="@(() => NavigationManager.NavigateTo("/user"))">
|
||||||
<div class="d-flex gap-2 align-items-center">
|
<div class="d-flex gap-2 align-items-center">
|
||||||
<AccountPictureComponent Id="@(_user.Id)" Size="30"/>
|
<AccountPictureComponent @ref="_profilePicture" Id="@(_user.Id)" Size="30"/>
|
||||||
<span>@(_user.Username)</span>
|
<span>@(_user.Username)</span>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row pb-3">
|
<div class="row pt-3 pb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@Body
|
@Body
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Net;
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using WatchIt.Common.Model.Accounts;
|
using WatchIt.Common.Model.Accounts;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Website.Components.Common.Subcomponents;
|
||||||
using WatchIt.Website.Services.Authentication;
|
using WatchIt.Website.Services.Authentication;
|
||||||
using WatchIt.Website.Services.Tokens;
|
using WatchIt.Website.Services.Tokens;
|
||||||
using WatchIt.Website.Services.Client.Accounts;
|
using WatchIt.Website.Services.Client.Accounts;
|
||||||
@@ -27,6 +28,8 @@ public partial class MainLayout : LayoutComponentBase
|
|||||||
|
|
||||||
#region FIELDS
|
#region FIELDS
|
||||||
|
|
||||||
|
private AccountPictureComponent? _profilePicture;
|
||||||
|
|
||||||
private bool _loaded;
|
private bool _loaded;
|
||||||
|
|
||||||
private User? _user;
|
private User? _user;
|
||||||
@@ -53,6 +56,20 @@ public partial class MainLayout : LayoutComponentBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PUBLIC METHODS
|
||||||
|
|
||||||
|
public async Task ReloadProfilePicture()
|
||||||
|
{
|
||||||
|
if (_profilePicture is not null)
|
||||||
|
{
|
||||||
|
await _profilePicture.Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,82 @@
|
|||||||
@page "/user/edit"
|
@using System.Text
|
||||||
|
@using WatchIt.Common.Model
|
||||||
|
@using WatchIt.Website.Components.Pages.UserEditPage.Panels
|
||||||
|
|
||||||
|
@page "/user/edit"
|
||||||
|
|
||||||
|
@{
|
||||||
|
StringBuilder sb = new StringBuilder(" - WatchIt");
|
||||||
|
|
||||||
|
if (_user is null) sb.Insert(0, "Loading...");
|
||||||
|
else sb.Insert(0, "User settings");
|
||||||
|
|
||||||
|
<PageTitle>@(sb.ToString())</PageTitle>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container-grid">
|
||||||
|
@if (_user is not null)
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<UserEditPageHeaderPanelComponent @ref="@(_header)" User="@(_user)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-default">
|
||||||
|
<div class="col">
|
||||||
|
<Tabs Pills
|
||||||
|
RenderMode="TabsRenderMode.LazyLoad"
|
||||||
|
Class="panel panel-menu panel-background-menu justify-content-center"
|
||||||
|
SelectedTab="profile">
|
||||||
|
<Items>
|
||||||
|
<Tab Name="profile">Profile</Tab>
|
||||||
|
<Tab Name="account">Account</Tab>
|
||||||
|
</Items>
|
||||||
|
<Content>
|
||||||
|
<TabPanel Name="profile">
|
||||||
|
<div class="mt-default">
|
||||||
|
<div class="container-grid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ProfileEditHeaderPanelComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-default gx-default">
|
||||||
|
<div class="col-auto">
|
||||||
|
<PictureEditorPanelComponent Id="@(_user.Id)"
|
||||||
|
Class="h-100"
|
||||||
|
PicturePlaceholder="assets/user_placeholder.png"
|
||||||
|
Circle="true"
|
||||||
|
PictureGetTask="@((id, action) => AccountsClientService.GetAccountProfilePicture(id, action))"
|
||||||
|
PicturePutTask="@((_, picture, action) => AccountsClientService.PutAccountProfilePicture(new AccountProfilePictureRequest(picture), action))"
|
||||||
|
PictureDeleteTask="@((_, action) => AccountsClientService.DeleteAccountProfilePicture(action))"
|
||||||
|
OnPictureChanged="@(async (_) => await PictureChanged())"/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<ProfileEditFormPanelComponent Id="@(_user.Id)"
|
||||||
|
Class="h-100"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel Name="account">
|
||||||
|
|
||||||
|
</TabPanel>
|
||||||
|
</Content>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="m-5">
|
||||||
|
<LoadingComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@@ -1,7 +1,68 @@
|
|||||||
|
using System.Net;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Common.Model;
|
||||||
|
using WatchIt.Website.Components.Pages.UserEditPage.Panels;
|
||||||
|
using WatchIt.Website.Layout;
|
||||||
|
using WatchIt.Website.Services.Authentication;
|
||||||
|
using WatchIt.Website.Services.Client.Accounts;
|
||||||
|
|
||||||
namespace WatchIt.Website.Pages;
|
namespace WatchIt.Website.Pages;
|
||||||
|
|
||||||
public partial class UserEditPage : ComponentBase
|
public partial class UserEditPage : ComponentBase
|
||||||
{
|
{
|
||||||
|
#region SERVICES
|
||||||
|
|
||||||
|
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
[Inject] private IAuthenticationService AuthenticationService { get; set; } = default!;
|
||||||
|
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[CascadingParameter] public MainLayout Layout { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private User? _user;
|
||||||
|
|
||||||
|
private UserEditPageHeaderPanelComponent _header = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
Layout.BackgroundPhoto = null;
|
||||||
|
|
||||||
|
_user = await AuthenticationService.GetUserAsync();
|
||||||
|
if (_user is null)
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo($"/auth?redirect_to={WebUtility.UrlEncode("/user/edit")}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PictureChanged() => await Task.WhenAll(
|
||||||
|
[
|
||||||
|
_header.ReloadPicture(),
|
||||||
|
Layout.ReloadProfilePicture()
|
||||||
|
]);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
{
|
{
|
||||||
<div class="row mt-header">
|
<div class="row mt-header">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<UserPageHeaderPanelComponent AccountData="@(_accountData)"/>
|
<UserPageHeaderPanelComponent AccountProfileInfoData="@(_accountData)"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-over-panel-menu">
|
<div class="row mt-over-panel-menu">
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Net;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using WatchIt.Common.Model.Accounts;
|
using WatchIt.Common.Model.Accounts;
|
||||||
using WatchIt.Website.Layout;
|
using WatchIt.Website.Layout;
|
||||||
@@ -75,7 +76,7 @@ public partial class UserPage : ComponentBase
|
|||||||
{
|
{
|
||||||
if (user is null)
|
if (user is null)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo("/auth");
|
NavigationManager.NavigateTo($"/auth?redirect_to={WebUtility.UrlEncode("/user")}");
|
||||||
_redirection = true;
|
_redirection = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,13 @@
|
|||||||
"Base": "/accounts",
|
"Base": "/accounts",
|
||||||
"Register": "/register",
|
"Register": "/register",
|
||||||
"Authenticate": "/authenticate",
|
"Authenticate": "/authenticate",
|
||||||
"AuthenticateRefresh": "/authenticate-refresh",
|
"AuthenticateRefresh": "/authenticate_refresh",
|
||||||
"Logout": "/logout",
|
"Logout": "/logout",
|
||||||
"GetProfilePicture": "/{0}/profile-picture",
|
"GetAccountProfilePicture": "/{0}/profile_picture",
|
||||||
|
"PutAccountProfilePicture": "/profile_picture",
|
||||||
|
"DeleteAccountProfilePicture": "/profile_picture",
|
||||||
"GetAccountInfo": "/{0}/info",
|
"GetAccountInfo": "/{0}/info",
|
||||||
"PutAccountInfo": "/info",
|
"PutAccountProfileInfo": "/profile_info",
|
||||||
"GetAccountRatedMovies": "/{0}/movies",
|
"GetAccountRatedMovies": "/{0}/movies",
|
||||||
"GetAccountRatedSeries": "/{0}/series",
|
"GetAccountRatedSeries": "/{0}/series",
|
||||||
"GetAccountRatedPersons": "/{0}/persons"
|
"GetAccountRatedPersons": "/{0}/persons"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem 1.5rem !important;
|
padding: 1rem 1.5rem !important;
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-menu > li > a {
|
.panel-menu > li > a {
|
||||||
@@ -52,6 +52,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* SECTION HEADER */
|
||||||
|
|
||||||
|
.panel-section-header {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* BACKGROUNDS */
|
/* BACKGROUNDS */
|
||||||
|
|
||||||
.panel-background-gold {
|
.panel-background-gold {
|
||||||
|
|||||||
Reference in New Issue
Block a user