From 88e8e330aa3762c316fb7ed7115e2c54ae9b4fa0 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Sun, 3 Nov 2024 14:21:30 +0100 Subject: [PATCH 1/6] some fixes --- .../Panels/HorizontalListPanelComponent.razor | 2 +- .../HorizontalListPanelComponent.razor.cs | 1 + .../HorizontalListItemComponent.razor | 13 +++-- .../HorizontalListItemComponent.razor.cs | 2 +- .../WatchIt.Website/Layout/MainLayout.razor | 1 + .../WatchIt.Website/Pages/MediaEditPage.razor | 13 ++++- .../WatchIt.Website/Pages/UserPage.razor | 9 ++-- WatchIt.slnx | 50 ------------------- 8 files changed, 31 insertions(+), 60 deletions(-) delete mode 100644 WatchIt.slnx diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor index a5485a0..f1e5efe 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor @@ -16,7 +16,7 @@ { @{int iCopy = i;} - diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor.cs index 971d659..326590d 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/HorizontalListPanelComponent.razor.cs @@ -17,6 +17,7 @@ public partial class HorizontalListPanelComponent : ComponentBase [Parameter] public required Func NameSource { get; set; } [Parameter] public required string PosterPlaceholder { get; set; } [Parameter] public required Func, Task> GetPictureAction { get; set; } + [Parameter] public bool HidePlace { get; set; } #endregion diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor index 89c1a89..4030e10 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor @@ -2,11 +2,16 @@
-
-
@(Place)
-
+ @if (Place.HasValue) + { +
+
@(Place)
+
+ }
-
@(Name)
+
+
@(Name)
+
diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor.cs index f80bcf4..73b4506 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/HorizontalListItemComponent.razor.cs @@ -7,7 +7,7 @@ public partial class HorizontalListItemComponent : ComponentBase { #region PARAMETERS - [Parameter] public required int Place { get; set; } + [Parameter] public int? Place { get; set; } [Parameter] public required string Name { get; set; } [Parameter] public required string PosterPlaceholder { get; set; } [Parameter] public required Func, Task> GetPosterAction { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor index 5d34a04..f4c1daa 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor @@ -62,6 +62,7 @@ Your profile + User settings @if (_user.IsAdmin) { diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor index 86a3d61..f0aa505 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor @@ -51,7 +51,18 @@
diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor index 1e1d3cf..12503ca 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor @@ -74,7 +74,8 @@ IdSource="@(item => item.Id)" NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)" PosterPlaceholder="/assets/media_poster.png" - GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/> + GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))" + HidePlace="@(true)"/> + GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))" + HidePlace="@(true)"/> + GetPictureAction="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))" + HidePlace="@(true)"/> diff --git a/WatchIt.slnx b/WatchIt.slnx deleted file mode 100644 index ed0e24e..0000000 --- a/WatchIt.slnx +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 3604c066e77abb4650d520ee161e433fcdd941bf Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Sun, 3 Nov 2024 23:01:34 +0100 Subject: [PATCH 2/6] profile picture and basic info editors added --- ...equest.cs => AccountProfileInfoRequest.cs} | 21 ++++- .../Accounts/AccountProfilePictureRequest.cs | 33 +++++++ .../AccountsController.cs | 23 +++-- .../AccountsControllerService.cs | 50 ++++++++++- .../IAccountsControllerService.cs | 4 +- ... => AccountProfileInfoRequestValidator.cs} | 8 +- .../AccountProfilePictureRequestValidator.cs | 13 +++ .../Accounts/AccountsClientService.cs | 35 +++++++- .../Accounts/IAccountsClientService.cs | 4 +- .../Model/Accounts.cs | 6 +- WatchIt.Website/WatchIt.Website/App.razor | 4 +- .../Panels/PictureEditorPanelComponent.razor | 2 +- .../PictureEditorPanelComponent.razor.cs | 4 + .../AccountPictureComponent.razor | 8 +- .../AccountPictureComponent.razor.cs | 25 +++--- .../Subcomponents/PictureComponent.razor | 2 +- .../Subcomponents/PictureComponent.razor.cs | 8 ++ .../ProfileEditFormPanelComponent.razor | 60 +++++++++++++ .../ProfileEditFormPanelComponent.razor.cs | 86 +++++++++++++++++++ .../ProfileEditHeaderPanelComponent.razor | 5 ++ .../ProfileEditHeaderPanelComponent.razor.css | 0 .../UserEditPageHeaderPanelComponent.razor | 9 ++ .../UserEditPageHeaderPanelComponent.razor.cs | 38 ++++++++ ...UserEditPageHeaderPanelComponent.razor.css | 9 ++ .../Panels/UserPageHeaderPanelComponent.razor | 22 ++--- .../UserPageHeaderPanelComponent.razor.cs | 4 +- .../WatchIt.Website/Layout/MainLayout.razor | 4 +- .../Layout/MainLayout.razor.cs | 17 ++++ .../WatchIt.Website/Pages/UserEditPage.razor | 83 +++++++++++++++++- .../Pages/UserEditPage.razor.cs | 61 +++++++++++++ .../WatchIt.Website/Pages/UserPage.razor | 2 +- .../WatchIt.Website/Pages/UserPage.razor.cs | 3 +- .../WatchIt.Website/appsettings.json | 8 +- .../WatchIt.Website/wwwroot/css/panel.css | 10 ++- 34 files changed, 607 insertions(+), 64 deletions(-) rename WatchIt.Common/WatchIt.Common.Model/Accounts/{AccountRequest.cs => AccountProfileInfoRequest.cs} (50%) create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePictureRequest.cs rename WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/{AccountRequestValidator.cs => AccountProfileInfoRequestValidator.cs} (52%) create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfilePictureRequestValidator.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor.css create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.css diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileInfoRequest.cs similarity index 50% rename from WatchIt.Common/WatchIt.Common.Model/Accounts/AccountRequest.cs rename to WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileInfoRequest.cs index 30b1d7b..e26f615 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountRequest.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileInfoRequest.cs @@ -2,9 +2,12 @@ using System.Text.Json.Serialization; namespace WatchIt.Common.Model.Accounts; -public class AccountRequest : Account +public class AccountProfileInfoRequest { #region PROPERTIES + + [JsonPropertyName("description")] + public string? Description { get; set; } [JsonPropertyName("gender_id")] 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 public void UpdateAccount(Database.Model.Account.Account account) { - account.Username = Username; - account.Email = Email; account.Description = Description; account.GenderId = GenderId; } diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePictureRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePictureRequest.cs new file mode 100644 index 0000000..1fbea18 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePictureRequest.cs @@ -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; + } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs index 3bcb30d..02fa953 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -27,7 +27,7 @@ public class AccountsController(IAccountsControllerService accountsControllerSer [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] public async Task Authenticate([FromBody]AuthenticateRequest body) => await accountsControllerService.Authenticate(body); - [HttpPost("authenticate-refresh")] + [HttpPost("authenticate_refresh")] [Authorize(AuthenticationSchemes = "refresh")] [ProducesResponseType(typeof(AuthenticateResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] @@ -39,25 +39,38 @@ public class AccountsController(IAccountsControllerService accountsControllerSer [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] public async Task Logout() => await accountsControllerService.Logout(); - [HttpGet("{id}/profile-picture")] + [HttpGet("{id}/profile_picture")] [AllowAnonymous] [ProducesResponseType(typeof(AccountProfilePictureResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task 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 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 DeleteAccountProfilePicture() => await accountsControllerService.DeleteAccountProfilePicture(); + [HttpGet("{id}/info")] [AllowAnonymous] [ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetAccountInfo([FromRoute]long id) => await accountsControllerService.GetAccountInfo(id); - [HttpPut("info")] + [HttpPut("profile_info")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - [ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task PutAccountInfo([FromBody]AccountRequest data) => await accountsControllerService.PutAccountInfo(data); + public async Task PutAccountProfileInfo([FromBody]AccountProfileInfoRequest data) => await accountsControllerService.PutAccountProfileInfo(data); [HttpGet("{id}/movies")] [AllowAnonymous] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs index a4151d7..da1d7a9 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs @@ -142,6 +142,48 @@ public class AccountsControllerService( AccountProfilePictureResponse picture = new AccountProfilePictureResponse(account.ProfilePicture); return RequestResult.Ok(picture); } + + public async Task 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 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 GetAccountInfo(long id) { @@ -151,11 +193,11 @@ public class AccountsControllerService( return RequestResult.NotFound(); } - AccountResponse response = new AccountResponse(account); - return RequestResult.Ok(response); + AccountResponse profileInfoResponse = new AccountResponse(account); + return RequestResult.Ok(profileInfoResponse); } - public async Task PutAccountInfo(AccountRequest data) + public async Task PutAccountProfileInfo(AccountProfileInfoRequest data) { Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == userService.GetUserId()); if (account is null) @@ -164,6 +206,8 @@ public class AccountsControllerService( } data.UpdateAccount(account); + await database.SaveChangesAsync(); + return RequestResult.Ok(); } diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs index 500b256..4fb51a0 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs @@ -14,8 +14,10 @@ public interface IAccountsControllerService Task AuthenticateRefresh(); Task Logout(); Task GetAccountProfilePicture(long id); + Task PutAccountProfilePicture(AccountProfilePictureRequest data); + Task DeleteAccountProfilePicture(); Task GetAccountInfo(long id); - Task PutAccountInfo(AccountRequest data); + Task PutAccountProfileInfo(AccountProfileInfoRequest data); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query); Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query); diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileInfoRequestValidator.cs similarity index 52% rename from WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountRequestValidator.cs rename to WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileInfoRequestValidator.cs index a5b3265..aa20074 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountRequestValidator.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileInfoRequestValidator.cs @@ -4,14 +4,10 @@ using WatchIt.Database; namespace WatchIt.WebAPI.Validators.Accounts; -public class AccountRequestValidator : AbstractValidator +public class AccountProfileInfoRequestValidator : AbstractValidator { - 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); When(x => x.GenderId.HasValue, () => { diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfilePictureRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfilePictureRequestValidator.cs new file mode 100644 index 0000000..873766c --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfilePictureRequestValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; +using WatchIt.Common.Model.Accounts; + +namespace WatchIt.WebAPI.Validators.Accounts; + +public class AccountProfilePictureRequestValidator : AbstractValidator +{ + public AccountProfilePictureRequestValidator() + { + RuleFor(x => x.Image).NotEmpty(); + RuleFor(x => x.MimeType).Matches(@"\w+/.+").WithMessage("Incorrect mimetype"); + } +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs index c246c03..4438636 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs @@ -71,7 +71,7 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig public async Task GetAccountProfilePicture(long id, Action? successAction = null, Action>? 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); HttpResponse response = await httpClientService.SendRequestAsync(request); @@ -80,6 +80,34 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); } + + public async Task PutAccountProfilePicture(AccountProfilePictureRequest data, Action? successAction = null, Action>? 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? successAction = null, Action? notFoundAction = null) { @@ -92,9 +120,9 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig .ExecuteAction(); } - public async Task PutAccountInfo(AccountRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null) + public async Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null) { - string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountInfo); + string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountProfileInfo); HttpRequest request = new HttpRequest(HttpMethodType.Put, url) { Body = data, @@ -104,7 +132,6 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor400BadRequest(badRequestAction) .RegisterActionFor401Unauthorized(unauthorizedAction) - .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); } diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs index 6f16640..bb45c37 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs @@ -12,8 +12,10 @@ public interface IAccountsClientService Task AuthenticateRefresh(Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); Task Logout(Action? successAction = null); Task GetAccountProfilePicture(long id, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); + Task PutAccountProfilePicture(AccountProfilePictureRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); + Task DeleteAccountProfilePicture(Action? successAction = null, Action? unauthorizedAction = null); Task GetAccountInfo(long id, Action? successAction = null, Action? notFoundAction = null); - Task PutAccountInfo(AccountRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null); + Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs index 5731d34..02ac602 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs @@ -7,9 +7,11 @@ public class Accounts public string Authenticate { get; set; } public string AuthenticateRefresh { 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 PutAccountInfo { get; set; } + public string PutAccountProfileInfo { get; set; } public string GetAccountRatedMovies { get; set; } public string GetAccountRatedSeries { get; set; } public string GetAccountRatedPersons { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/App.razor b/WatchIt.Website/WatchIt.Website/App.razor index 5ceca05..66ac768 100644 --- a/WatchIt.Website/WatchIt.Website/App.razor +++ b/WatchIt.Website/WatchIt.Website/App.razor @@ -10,10 +10,10 @@ - + - + diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor index 2264955..37613f4 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor @@ -2,7 +2,7 @@ @if (_loaded) {
- + @if (_pictureChanged || _pictureSaved is not null) { diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor.cs index 3bd56b1..1949048 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Panels/PictureEditorPanelComponent.razor.cs @@ -11,10 +11,12 @@ public partial class PictureEditorPanelComponent : ComponentBase [Parameter] public long? Id { get; set; } [Parameter] public int ContentWidth { get; set; } = 300; [Parameter] public required string PicturePlaceholder { get; set; } + [Parameter] public bool Circle { get; set; } [Parameter] public string Class { get; set; } = string.Empty; [Parameter] public required Func, Task> PictureGetTask { get; set; } [Parameter] public required Func, Task> PicturePutTask { get; set; } [Parameter] public required Func PictureDeleteTask { get; set; } + [Parameter] public Action? OnPictureChanged { get; set; } #endregion @@ -92,6 +94,7 @@ public partial class PictureEditorPanelComponent : ComponentBase _pictureSelected = data; _pictureChanged = false; _pictureSaving = false; + OnPictureChanged?.Invoke(data); } _pictureSaving = true; @@ -112,6 +115,7 @@ public partial class PictureEditorPanelComponent : ComponentBase _pictureSelected = null; _pictureChanged = false; _pictureDeleting = false; + OnPictureChanged?.Invoke(null); } _pictureDeleting = true; diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor index ee75eef..6b532d5 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor @@ -1 +1,7 @@ -avatar \ No newline at end of file + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor.cs index 21e0402..42d51f8 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/AccountPictureComponent.razor.cs @@ -30,6 +30,18 @@ public partial class AccountPictureComponent : ComponentBase private AccountProfilePictureResponse? _picture; #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) { - List endTasks = new List(); - - // STEP 0 - endTasks.AddRange( - [ - AccountsClientService.GetAccountProfilePicture(Id, data => _picture = data) - ]); - - // END - await Task.WhenAll(endTasks); - - StateHasChanged(); + await Reload(); } } diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor index 398c4d6..358a074 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor @@ -1 +1 @@ -@(AlternativeText) \ No newline at end of file +@(AlternativeText) \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor.cs index 213b9c2..08800d4 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Common/Subcomponents/PictureComponent.razor.cs @@ -14,6 +14,8 @@ public partial class PictureComponent : ComponentBase [Parameter] public string Class { get; set; } = string.Empty; [Parameter] public int? Height { get; set; } [Parameter] public int? Width { get; set; } + [Parameter] public bool Circle { get; set; } + [Parameter] public bool Shadow { get; set; } = true; #endregion @@ -40,6 +42,11 @@ public partial class PictureComponent : ComponentBase { _attributes.Add("width", Width.Value); } + + if (Circle) + { + AspectRatio = PictureComponentAspectRatio.Square; + } } #endregion @@ -71,6 +78,7 @@ public partial class PictureComponent : ComponentBase public static readonly PictureComponentAspectRatio Default = new PictureComponentAspectRatio(); public static readonly PictureComponentAspectRatio Photo = new PictureComponentAspectRatio(16, 9); + public static readonly PictureComponentAspectRatio Square = new PictureComponentAspectRatio(1, 1); #endregion diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor new file mode 100644 index 0000000..438fa9c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor @@ -0,0 +1,60 @@ +@using WatchIt.Common.Model.Genders + + + +
+ @if (_loaded) + { +
+

Basic profile info

+ + +
+
+ +
+ +
+
+
+ +
+ + + @foreach (GenderResponse gender in _genders) + { + + } + +
+
+
+
+ @if (!string.IsNullOrWhiteSpace(_error)) + { + @(_error) + } +
+
+ +
+
+
+
+
+ } + else + { + + } +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs new file mode 100644 index 0000000..59806dd --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs @@ -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 _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 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 +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor new file mode 100644 index 0000000..6385348 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor @@ -0,0 +1,5 @@ +
+
+

Profile settings

+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor new file mode 100644 index 0000000..2b2c701 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor @@ -0,0 +1,9 @@ +
+
+ +
+

@(User.Username)

+ User settings +
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs new file mode 100644 index 0000000..9b44909 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs @@ -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 +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.css new file mode 100644 index 0000000..7044add --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.css @@ -0,0 +1,9 @@ +/* IDS */ + +#username { + margin-top: -8px !important; +} + +#secondaryText { + color: lightgray !important; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserPage/Panels/UserPageHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserPage/Panels/UserPageHeaderPanelComponent.razor index 86e41f1..28059b2 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserPage/Panels/UserPageHeaderPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserPage/Panels/UserPageHeaderPanelComponent.razor @@ -1,26 +1,26 @@
- +
-

@(AccountData.Username)

+

@(AccountProfileInfoData.Username)

- @if (!string.IsNullOrWhiteSpace(AccountData.Description)) + @if (!string.IsNullOrWhiteSpace(AccountProfileInfoData.Description)) { - - @(AccountData.Description) + + @(AccountProfileInfoData.Description) }
-
+
@Body
diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs index 10a09d8..a73056b 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs @@ -2,6 +2,7 @@ using System.Net; using Microsoft.AspNetCore.Components; using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Photos; +using WatchIt.Website.Components.Common.Subcomponents; using WatchIt.Website.Services.Authentication; using WatchIt.Website.Services.Tokens; using WatchIt.Website.Services.Client.Accounts; @@ -27,6 +28,8 @@ public partial class MainLayout : LayoutComponentBase #region FIELDS + private AccountPictureComponent? _profilePicture; + private bool _loaded; private User? _user; @@ -53,6 +56,20 @@ public partial class MainLayout : LayoutComponentBase } #endregion + + + + #region PUBLIC METHODS + + public async Task ReloadProfilePicture() + { + if (_profilePicture is not null) + { + await _profilePicture.Reload(); + } + } + + #endregion diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor index 64a2378..c9e0aac 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor @@ -1 +1,82 @@ -@page "/user/edit" \ No newline at end of file +@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"); + + @(sb.ToString()) +} + + + +
+ @if (_user is not null) + { +
+
+ +
+
+
+
+ + + Profile + Account + + + +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + +
+
+
+
+ } + else + { +
+
+
+ +
+
+
+ } +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs index d5a489f..b3ada08 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs @@ -1,7 +1,68 @@ +using System.Net; 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; 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 } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor index 12503ca..ff65c65 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor @@ -48,7 +48,7 @@ {
- +
diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs index 2a2985c..4de839e 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor.cs @@ -1,3 +1,4 @@ +using System.Net; using Microsoft.AspNetCore.Components; using WatchIt.Common.Model.Accounts; using WatchIt.Website.Layout; @@ -75,7 +76,7 @@ public partial class UserPage : ComponentBase { if (user is null) { - NavigationManager.NavigateTo("/auth"); + NavigationManager.NavigateTo($"/auth?redirect_to={WebUtility.UrlEncode("/user")}"); _redirection = true; return; } diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index 9d0355d..2d4a0f1 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -19,11 +19,13 @@ "Base": "/accounts", "Register": "/register", "Authenticate": "/authenticate", - "AuthenticateRefresh": "/authenticate-refresh", + "AuthenticateRefresh": "/authenticate_refresh", "Logout": "/logout", - "GetProfilePicture": "/{0}/profile-picture", + "GetAccountProfilePicture": "/{0}/profile_picture", + "PutAccountProfilePicture": "/profile_picture", + "DeleteAccountProfilePicture": "/profile_picture", "GetAccountInfo": "/{0}/info", - "PutAccountInfo": "/info", + "PutAccountProfileInfo": "/profile_info", "GetAccountRatedMovies": "/{0}/movies", "GetAccountRatedSeries": "/{0}/series", "GetAccountRatedPersons": "/{0}/persons" diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/css/panel.css b/WatchIt.Website/WatchIt.Website/wwwroot/css/panel.css index 0e200f3..6dcb155 100644 --- a/WatchIt.Website/WatchIt.Website/wwwroot/css/panel.css +++ b/WatchIt.Website/WatchIt.Website/wwwroot/css/panel.css @@ -28,7 +28,7 @@ gap: 1rem; 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 { @@ -52,6 +52,14 @@ +/* SECTION HEADER */ + +.panel-section-header { + padding: 0.75rem 1.5rem; +} + + + /* BACKGROUNDS */ .panel-background-gold { From 314fceb1208c1e1a3e99684b72e7d64e33644360 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Sun, 3 Nov 2024 23:27:45 +0100 Subject: [PATCH 3/6] profile background editor panel added --- .../Panels/ProfileBackgroundEditorPanelComponent.razor | 5 +++++ .../Panels/ProfileBackgroundEditorPanelComponent.razor.cs | 7 +++++++ WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor | 5 +++++ 3 files changed, 17 insertions(+) create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor new file mode 100644 index 0000000..8d8a88c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor @@ -0,0 +1,5 @@ +
+
+

Profile background

+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs new file mode 100644 index 0000000..904e6a4 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs @@ -0,0 +1,7 @@ +using Microsoft.AspNetCore.Components; + +namespace WatchIt.Website.Components.Pages.UserEditPage.Panels; + +public partial class ProfileBackgroundEditorPanelComponent : ComponentBase +{ +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor index c9e0aac..8031ff4 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor @@ -58,6 +58,11 @@ Class="h-100"/>
+
+
+ +
+
From 26a5e1e5587ae75e619c02305392c173eec5af8e Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Tue, 5 Nov 2024 20:04:15 +0100 Subject: [PATCH 4/6] profile editing finished --- .../AccountProfileBackgroundRequest.cs | 24 ++++ .../AccountsController.cs | 33 +++++ .../AccountsControllerService.cs | 56 ++++++++ .../IAccountsControllerService.cs | 3 + ...ccountProfileBackgroundRequestValidator.cs | 14 ++ .../Accounts/AccountsClientService.cs | 41 ++++++ .../Accounts/IAccountsClientService.cs | 4 + .../Model/Accounts.cs | 3 + WatchIt.Website/WatchIt.Website/App.razor | 2 +- ...rofileBackgroundEditorPanelComponent.razor | 122 +++++++++++++++++- ...ileBackgroundEditorPanelComponent.razor.cs | 121 +++++++++++++++++ ...leBackgroundEditorPanelComponent.razor.css | 23 ++++ .../WatchIt.Website/Pages/UserEditPage.razor | 5 +- .../Pages/UserEditPage.razor.cs | 15 ++- .../WatchIt.Website/Pages/UserPage.razor | 12 +- .../WatchIt.Website/Pages/UserPage.razor.cs | 10 +- .../WatchIt.Website/appsettings.json | 3 + 17 files changed, 475 insertions(+), 16 deletions(-) create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileBackgroundRequestValidator.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.css diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs new file mode 100644 index 0000000..4bcb781 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; + +namespace WatchIt.Common.Model.Accounts; + +public class AccountProfileBackgroundRequest +{ + #region PROPERTIES + + public required Guid Id { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + [SetsRequiredMembers] + public AccountProfileBackgroundRequest(Guid id) + { + Id = id; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs index 02fa953..714d4e4 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Photos; using WatchIt.Common.Model.Series; using WatchIt.WebAPI.Services.Controllers.Accounts; @@ -14,6 +15,8 @@ namespace WatchIt.WebAPI.Controllers; [Route("accounts")] public class AccountsController(IAccountsControllerService accountsControllerService) : ControllerBase { + #region Basic + [HttpPost("register")] [AllowAnonymous] [ProducesResponseType(typeof(RegisterResponse), StatusCodes.Status201Created)] @@ -39,6 +42,10 @@ public class AccountsController(IAccountsControllerService accountsControllerSer [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] public async Task Logout() => await accountsControllerService.Logout(); + #endregion + + #region Profile picture + [HttpGet("{id}/profile_picture")] [AllowAnonymous] [ProducesResponseType(typeof(AccountProfilePictureResponse), StatusCodes.Status200OK)] @@ -59,6 +66,32 @@ public class AccountsController(IAccountsControllerService accountsControllerSer [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] public async Task DeleteAccountProfilePicture() => await accountsControllerService.DeleteAccountProfilePicture(); + #endregion + + #region Profile background + + [HttpGet("{id}/profile_background")] + [AllowAnonymous] + [ProducesResponseType(typeof(PhotoResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetAccountProfileBackground([FromRoute(Name = "id")]long id) => await accountsControllerService.GetAccountProfileBackground(id); + + [HttpPut("profile_background")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(PhotoResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + public async Task PutAccountProfileBackground([FromBody]AccountProfileBackgroundRequest body) => await accountsControllerService.PutAccountProfileBackground(body); + + [HttpDelete("profile_background")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + public async Task DeleteAccountProfileBackground() => await accountsControllerService.DeleteAccountProfileBackground(); + + #endregion + [HttpGet("{id}/info")] [AllowAnonymous] [ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs index da1d7a9..e4a6879 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs @@ -8,6 +8,7 @@ using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Media; using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Photos; using WatchIt.Common.Model.Series; using WatchIt.Database; using WatchIt.Database.Model.Account; @@ -32,6 +33,8 @@ public class AccountsControllerService( { #region PUBLIC METHODS + #region Basic + public async Task Register(RegisterRequest data) { string leftSalt = StringExtensions.CreateRandom(20); @@ -125,6 +128,10 @@ public class AccountsControllerService( return RequestResult.NoContent(); } + #endregion + + #region Profile picture + public async Task GetAccountProfilePicture(long id) { Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id); @@ -185,6 +192,55 @@ public class AccountsControllerService( return RequestResult.NoContent(); } + #endregion + + #region Profile background + + public async Task GetAccountProfileBackground(long id) + { + Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id); + if (account is null) + { + return RequestResult.BadRequest() + .AddValidationError("id", "Account with this id does not exists"); + } + + if (account.BackgroundPicture is null) + { + return RequestResult.NotFound(); + } + + PhotoResponse response = new PhotoResponse(account.BackgroundPicture); + return RequestResult.Ok(response); + } + + public async Task PutAccountProfileBackground(AccountProfileBackgroundRequest data) + { + Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId()); + + account.BackgroundPictureId = data.Id; + + await database.SaveChangesAsync(); + + PhotoResponse returnData = new PhotoResponse(account.BackgroundPicture!); + return RequestResult.Ok(returnData); + } + + public async Task DeleteAccountProfileBackground() + { + Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId()); + + if (account.BackgroundPicture is not null) + { + account.BackgroundPictureId = null; + await database.SaveChangesAsync(); + } + + return RequestResult.NoContent(); + } + + #endregion + public async Task GetAccountInfo(long id) { Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id); diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs index 4fb51a0..5213ab4 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs @@ -16,6 +16,9 @@ public interface IAccountsControllerService Task GetAccountProfilePicture(long id); Task PutAccountProfilePicture(AccountProfilePictureRequest data); Task DeleteAccountProfilePicture(); + Task GetAccountProfileBackground(long id); + Task PutAccountProfileBackground(AccountProfileBackgroundRequest data); + Task DeleteAccountProfileBackground(); Task GetAccountInfo(long id); Task PutAccountProfileInfo(AccountProfileInfoRequest data); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query); diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileBackgroundRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileBackgroundRequestValidator.cs new file mode 100644 index 0000000..205be53 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountProfileBackgroundRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using WatchIt.Common.Model.Accounts; +using WatchIt.Database; + +namespace WatchIt.WebAPI.Validators.Accounts; + +public class AccountProfileBackgroundRequestValidator : AbstractValidator +{ + public AccountProfileBackgroundRequestValidator(DatabaseContext database) + { + RuleFor(x => x.Id).MustBeIn(database.MediaPhotoImages.Where(x => x.MediaPhotoImageBackground != null), x => x.Id) + .WithMessage("Image has to be background"); + } +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs index 4438636..1dab7b6 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs @@ -1,6 +1,7 @@ using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Photos; using WatchIt.Common.Model.Series; using WatchIt.Common.Services.HttpClient; using WatchIt.Website.Services.Configuration; @@ -108,6 +109,46 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig .RegisterActionFor401Unauthorized(unauthorizedAction) .ExecuteAction(); } + + public async Task GetAccountProfileBackground(long id, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountProfileBackground, id); + HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + + HttpResponse response = await httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor400BadRequest(badRequestAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task PutAccountProfileBackground(AccountProfileBackgroundRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountProfileBackground); + + 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 DeleteAccountProfileBackground(Action? successAction = null, Action? unauthorizedAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.DeleteAccountProfileBackground); + + 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? successAction = null, Action? notFoundAction = null) { diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs index bb45c37..48110fe 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs @@ -1,6 +1,7 @@ using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Persons; +using WatchIt.Common.Model.Photos; using WatchIt.Common.Model.Series; namespace WatchIt.Website.Services.Client.Accounts; @@ -14,6 +15,9 @@ public interface IAccountsClientService Task GetAccountProfilePicture(long id, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); Task PutAccountProfilePicture(AccountProfilePictureRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task DeleteAccountProfilePicture(Action? successAction = null, Action? unauthorizedAction = null); + Task GetAccountProfileBackground(long id, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); + Task PutAccountProfileBackground(AccountProfileBackgroundRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); + Task DeleteAccountProfileBackground(Action? successAction = null, Action? unauthorizedAction = null); Task GetAccountInfo(long id, Action? successAction = null, Action? notFoundAction = null); Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs index 02ac602..f035f6d 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs @@ -10,6 +10,9 @@ public class Accounts public string GetAccountProfilePicture { get; set; } public string PutAccountProfilePicture { get; set; } public string DeleteAccountProfilePicture { get; set; } + public string GetAccountProfileBackground { get; set; } + public string PutAccountProfileBackground { get; set; } + public string DeleteAccountProfileBackground { get; set; } public string GetAccountInfo { get; set; } public string PutAccountProfileInfo { get; set; } public string GetAccountRatedMovies { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/App.razor b/WatchIt.Website/WatchIt.Website/App.razor index 66ac768..f460abd 100644 --- a/WatchIt.Website/WatchIt.Website/App.razor +++ b/WatchIt.Website/WatchIt.Website/App.razor @@ -13,7 +13,7 @@ - + diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor index 8d8a88c..0a2e598 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor @@ -1,5 +1,121 @@ +@using Blazorise.Components +@using WatchIt.Common.Model.Photos + + +
-
-

Profile background

-
+ @if (_loaded) + { +
+
+

Profile background

+ @if (_editMode) + { + + } + else + { + + + } +
+ @if (_editMode) + { +
+
+
+
+ + Sorry... @not_found_context was not found + +
+
+ +
+
+
+ @if (_mediaPhotos is null) + { + Select media first + } + else if (!_mediaPhotos.Any()) + { + No backgrounds for this media + } + else + { +
+ @foreach (PhotoResponse photo in _mediaPhotos) + { +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+ } +
+ } +
+ } + else + { + if (_selectedPhoto is not null) + { + + } + else + { + You don't have selected background. Click "Edit" to choose one. + } + } +
+ } + else + { + + }
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs index 904e6a4..594ba75 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.cs @@ -1,7 +1,128 @@ using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; +using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Photos; +using WatchIt.Website.Services.Authentication; +using WatchIt.Website.Services.Client.Accounts; +using WatchIt.Website.Services.Client.Media; namespace WatchIt.Website.Components.Pages.UserEditPage.Panels; public partial class ProfileBackgroundEditorPanelComponent : ComponentBase { + #region SERVICES + + [Inject] private IMediaClientService MediaClientService { get; set; } = default!; + [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public required long Id { get; set; } + [Parameter] public Action? OnBackgroundChanged { get; set; } + + #endregion + + + + #region FIELDS + + private bool _loaded; + + private bool _editMode; + private long? _selectedMedia; + private IEnumerable? _mediaPhotos; + private bool _backgroundsLoading; + private bool _saveLoading; + + private bool _removeLoading; + + private IEnumerable _mediaList = default!; + + private PhotoResponse? _selectedPhoto; + private MediaResponse? _selectedPhotoMedia; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await Task.WhenAll( + [ + MediaClientService.GetAllMedia(successAction: data => _mediaList = data), + AccountsClientService.GetAccountProfileBackground(Id, data => _selectedPhoto = data) + ]); + + if (_selectedPhoto is not null) + { + _selectedPhotoMedia = _mediaList.First(x => x.Id == _selectedPhoto.MediaId); + } + + _loaded = true; + StateHasChanged(); + } + } + + private async Task Save(Guid id) + { + _saveLoading = true; + await AccountsClientService.PutAccountProfileBackground(new AccountProfileBackgroundRequest(id), data => + { + OnBackgroundChanged?.Invoke(data); + _selectedPhoto = data; + _selectedPhotoMedia = _mediaList.First(x => x.Id == _selectedMedia!.Value); + _saveLoading = false; + Cancel(); + }); + } + + private void Cancel() + { + _editMode = false; + _selectedMedia = default; + _saveLoading = false; + _backgroundsLoading = false; + _mediaPhotos = default; + } + + private void Edit() + { + _editMode = true; + } + + private async Task Remove() + { + _removeLoading = true; + await AccountsClientService.DeleteAccountProfileBackground(() => + { + OnBackgroundChanged?.Invoke(null); + _selectedPhoto = null; + _selectedPhotoMedia = null; + _removeLoading = false; + }); + } + + private async Task LoadBackgrounds() + { + _backgroundsLoading = true; + PhotoQueryParameters query = new PhotoQueryParameters + { + IsBackground = true + }; + await MediaClientService.GetMediaPhotos(_selectedMedia!.Value, query, successAction: data => + { + _mediaPhotos = data; + _backgroundsLoading = false; + }); + } + + #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.css new file mode 100644 index 0000000..910ae4d --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor.css @@ -0,0 +1,23 @@ +/* IDS */ + +#scrollPhotos { + overflow-x: scroll; + + border-color: grey; + border-width: 1px; + border-style: solid; + border-radius: 10px; +} + +#backgroundIndicator { + height: 30px; + width: 30px; +} + + + +/* CLASSES */ + +.photo-container { + width: 350px; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor index 8031ff4..ae3c2c8 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor @@ -1,7 +1,9 @@ @using System.Text @using WatchIt.Common.Model +@using WatchIt.Common.Model.Photos @using WatchIt.Website.Components.Pages.UserEditPage.Panels + @page "/user/edit" @{ @@ -60,7 +62,8 @@
- +
diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs index b3ada08..8e4c5ea 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs @@ -1,6 +1,7 @@ using System.Net; using Microsoft.AspNetCore.Components; using WatchIt.Common.Model; +using WatchIt.Common.Model.Photos; using WatchIt.Website.Components.Pages.UserEditPage.Panels; using WatchIt.Website.Layout; using WatchIt.Website.Services.Authentication; @@ -50,11 +51,12 @@ public partial class UserEditPage : ComponentBase if (_user is null) { NavigationManager.NavigateTo($"/auth?redirect_to={WebUtility.UrlEncode("/user/edit")}"); + return; } - else - { - StateHasChanged(); - } + StateHasChanged(); + + await AccountsClientService.GetAccountProfileBackground(_user.Id, data => Layout.BackgroundPhoto = data); + StateHasChanged(); } } @@ -63,6 +65,11 @@ public partial class UserEditPage : ComponentBase _header.ReloadPicture(), Layout.ReloadProfilePicture() ]); + + private void BackgroundChanged(PhotoResponse? background) + { + Layout.BackgroundPhoto = background; + } #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor index ff65c65..722e501 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserPage.razor @@ -69,7 +69,7 @@ step1Tasks = new List(); List endTasks = new List(); // INIT Layout.BackgroundPhoto = null; // STEP 0 - endTasks.AddRange( + step1Tasks.AddRange( [ GetUserData() ]); + // STEP 1 + await Task.WhenAll(step1Tasks); + endTasks.AddRange( + [ + AccountsClientService.GetAccountProfileBackground(_accountData.Id, data => Layout.BackgroundPhoto = data) + ]); + // END await Task.WhenAll(endTasks); diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index 2d4a0f1..b6820ca 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -24,6 +24,9 @@ "GetAccountProfilePicture": "/{0}/profile_picture", "PutAccountProfilePicture": "/profile_picture", "DeleteAccountProfilePicture": "/profile_picture", + "GetAccountProfileBackground": "/{0}/profile_background", + "PutAccountProfileBackground": "/profile_background", + "DeleteAccountProfileBackground": "/profile_background", "GetAccountInfo": "/{0}/info", "PutAccountProfileInfo": "/profile_info", "GetAccountRatedMovies": "/{0}/movies", From 4cbc44f9be1de6f675ee745832165e40f75cbf0c Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 6 Nov 2024 00:11:45 +0100 Subject: [PATCH 5/6] username change panel added --- .../AccountProfileBackgroundRequest.cs | 2 + .../Accounts/AccountUsernameRequest.cs | 27 ++++++ .../AccountsController.cs | 11 +++ .../AccountsControllerService.cs | 21 +++- .../IAccountsControllerService.cs | 1 + .../AccountUsernameRequestValidator.cs | 16 ++++ .../Accounts/AccountsClientService.cs | 15 +++ .../Accounts/IAccountsClientService.cs | 1 + .../Model/Accounts.cs | 1 + .../AccountEditHeaderPanelComponent.razor | 5 + .../Panels/NewUsernamePanelComponent.razor | 46 +++++++++ .../Panels/NewUsernamePanelComponent.razor.cs | 95 +++++++++++++++++++ ...rofileBackgroundEditorPanelComponent.razor | 2 +- .../ProfileEditFormPanelComponent.razor | 6 +- .../ProfileEditHeaderPanelComponent.razor.css | 0 .../UserEditPageHeaderPanelComponent.razor | 4 +- .../UserEditPageHeaderPanelComponent.razor.cs | 21 ++++ .../WatchIt.Website/Layout/MainLayout.razor | 2 +- .../Layout/MainLayout.razor.cs | 15 +-- .../WatchIt.Website/Pages/UserEditPage.razor | 55 +++++------ .../WatchIt.Website/appsettings.json | 1 + 21 files changed, 305 insertions(+), 42 deletions(-) create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AccountUsernameRequest.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountUsernameRequestValidator.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/AccountEditHeaderPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs delete mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor.css diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs index 4bcb781..6f8ce7b 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfileBackgroundRequest.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; namespace WatchIt.Common.Model.Accounts; @@ -6,6 +7,7 @@ public class AccountProfileBackgroundRequest { #region PROPERTIES + [JsonPropertyName("id")] public required Guid Id { get; set; } #endregion diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountUsernameRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountUsernameRequest.cs new file mode 100644 index 0000000..5219594 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountUsernameRequest.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Accounts; + +public class AccountUsernameRequest +{ + #region PROPERTIES + + [JsonPropertyName("new_username")] + public string NewUsername { get; set; } + + [JsonPropertyName("password")] + public string Password { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public void UpdateAccount(Database.Model.Account.Account account) + { + account.Username = NewUsername; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs index 714d4e4..1b93fe9 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -92,6 +92,8 @@ public class AccountsController(IAccountsControllerService accountsControllerSer #endregion + #region Info + [HttpGet("{id}/info")] [AllowAnonymous] [ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)] @@ -105,6 +107,15 @@ public class AccountsController(IAccountsControllerService accountsControllerSer [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task PutAccountProfileInfo([FromBody]AccountProfileInfoRequest data) => await accountsControllerService.PutAccountProfileInfo(data); + [HttpPatch("username")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task PatchAccountUsername([FromBody]AccountUsernameRequest data) => await accountsControllerService.PatchAccountUsername(data); + + #endregion + [HttpGet("{id}/movies")] [AllowAnonymous] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs index e4a6879..67d41c1 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs @@ -240,7 +240,9 @@ public class AccountsControllerService( } #endregion - + + #region Info + public async Task GetAccountInfo(long id) { Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id); @@ -266,6 +268,23 @@ public class AccountsControllerService( return RequestResult.Ok(); } + + public async Task PatchAccountUsername(AccountUsernameRequest data) + { + Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId()); + + if (!ComputeHash(data.Password, account.LeftSalt, account.RightSalt).SequenceEqual(account.Password)) + { + return RequestResult.Unauthorized(); + } + + data.UpdateAccount(account); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + #endregion public async Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query) { diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs index 5213ab4..481603e 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs @@ -21,6 +21,7 @@ public interface IAccountsControllerService Task DeleteAccountProfileBackground(); Task GetAccountInfo(long id); Task PutAccountProfileInfo(AccountProfileInfoRequest data); + Task PatchAccountUsername(AccountUsernameRequest data); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query); Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query); diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountUsernameRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountUsernameRequestValidator.cs new file mode 100644 index 0000000..6c0ad30 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountUsernameRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; +using WatchIt.Common.Model.Accounts; +using WatchIt.Database; + +namespace WatchIt.WebAPI.Validators.Accounts; + +public class AccountUsernameRequestValidator : AbstractValidator +{ + public AccountUsernameRequestValidator(DatabaseContext database) + { + RuleFor(x => x.NewUsername).MinimumLength(5) + .MaximumLength(50) + .CannotBeIn(database.Accounts, x => x.Username) + .WithMessage("Username is already used"); + } +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs index 1dab7b6..d73728e 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs @@ -176,6 +176,21 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig .ExecuteAction(); } + public async Task PatchAccountUsername(AccountUsernameRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.PatchAccountUsername); + HttpRequest request = new HttpRequest(HttpMethodType.Patch, url) + { + Body = data, + }; + + HttpResponse response = await httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor400BadRequest(badRequestAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .ExecuteAction(); + } + public async Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null) { string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedMovies, id); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs index 48110fe..4a86d8f 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs @@ -20,6 +20,7 @@ public interface IAccountsClientService Task DeleteAccountProfileBackground(Action? successAction = null, Action? unauthorizedAction = null); Task GetAccountInfo(long id, Action? successAction = null, Action? notFoundAction = null); Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); + Task PatchAccountUsername(AccountUsernameRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs index f035f6d..04e8c7f 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs @@ -15,6 +15,7 @@ public class Accounts public string DeleteAccountProfileBackground { get; set; } public string GetAccountInfo { get; set; } public string PutAccountProfileInfo { get; set; } + public string PatchAccountUsername { get; set; } public string GetAccountRatedMovies { get; set; } public string GetAccountRatedSeries { get; set; } public string GetAccountRatedPersons { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/AccountEditHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/AccountEditHeaderPanelComponent.razor new file mode 100644 index 0000000..a051670 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/AccountEditHeaderPanelComponent.razor @@ -0,0 +1,5 @@ +
+
+

Account settings

+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor new file mode 100644 index 0000000..62a6a9b --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor @@ -0,0 +1,46 @@ +
+
+

Change username

+ @if (_data is not null) + { + + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ @if (!string.IsNullOrWhiteSpace(_error)) + { + @(_error) + } + else if (_saved) + { + New username saved! + } +
+
+ +
+
+
+
+ } + else + { + + } +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs new file mode 100644 index 0000000..0a797fd --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs @@ -0,0 +1,95 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; +using WatchIt.Website.Services.Authentication; +using WatchIt.Website.Services.Client.Accounts; + +namespace WatchIt.Website.Components.Pages.UserEditPage.Panels; + +public partial class NewUsernamePanelComponent : ComponentBase +{ + #region SERVICES + + [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; + [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; + [Inject] private NavigationManager NavigationManager { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public required long Id { get; set; } + + #endregion + + + + #region FIELDS + + private AccountUsernameRequest? _data; + private string? _error; + private bool _saving; + private bool _saved; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + User? user = await AuthenticationService.GetUserAsync(); + + if (user is null) + { + return; + } + + await AccountsClientService.GetAccountInfo(user.Id, data => + { + _data = new AccountUsernameRequest + { + NewUsername = data.Username + }; + StateHasChanged(); + }); + } + } + + private async Task Save() + { + void Success() + { + _saved = true; + _saving = false; + _data = new AccountUsernameRequest + { + NewUsername = _data!.NewUsername + }; + NavigationManager.Refresh(true); + } + + void BadRequest(IDictionary errors) + { + _error = errors.SelectMany(x => x.Value).FirstOrDefault() ?? "Unknown error"; + _saving = false; + } + + void Unauthorized() + { + _error = "Incorrect password"; + _saving = false; + } + + _saving = true; + _saved = false; + _error = null; + await AccountsClientService.PatchAccountUsername(_data!, Success, BadRequest, Unauthorized); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor index 0a2e598..6281e1a 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileBackgroundEditorPanelComponent.razor @@ -90,7 +90,7 @@
- +
diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor index 438fa9c..8f9869a 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor @@ -39,12 +39,12 @@
diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditHeaderPanelComponent.razor.css deleted file mode 100644 index e69de29..0000000 diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor index 2b2c701..2fd8305 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor @@ -1,8 +1,8 @@
- +
-

@(User.Username)

+

@(_username ?? "Loading...")

User settings
diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs index 9b44909..83f7d8e 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components; using WatchIt.Website.Components.Common.Subcomponents; using WatchIt.Website.Services.Authentication; +using WatchIt.Website.Services.Client.Accounts; namespace WatchIt.Website.Components.Pages.UserEditPage.Panels; @@ -8,6 +9,7 @@ public partial class UserEditPageHeaderPanelComponent : ComponentBase { #region SERVICES + [Inject] public IAccountsClientService AccountsClientService { get; set; } = default!; [Inject] public NavigationManager NavigationManager { get; set; } = default!; #endregion @@ -25,6 +27,7 @@ public partial class UserEditPageHeaderPanelComponent : ComponentBase #region FIELDS private AccountPictureComponent _accountPicture = default!; + private string? _username; #endregion @@ -35,4 +38,22 @@ public partial class UserEditPageHeaderPanelComponent : ComponentBase public async Task ReloadPicture() => await _accountPicture.Reload(); #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await AccountsClientService.GetAccountInfo(User.Id, data => + { + _username = data.Username; + StateHasChanged(); + }); + } + } + + #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor index 3b64a23..736e311 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor @@ -56,7 +56,7 @@ diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs index a73056b..00657c8 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs @@ -21,6 +21,7 @@ public partial class MainLayout : LayoutComponentBase [Inject] public IAuthenticationService AuthenticationService { get; set; } = default!; [Inject] public IMediaClientService MediaClientService { get; set; } = default!; [Inject] public IPhotosClientService PhotosClientService { get; set; } = default!; + [Inject] public IAccountsClientService AccountsClientService { get; set; } = default!; #endregion @@ -33,6 +34,7 @@ public partial class MainLayout : LayoutComponentBase private bool _loaded; private User? _user; + private AccountResponse? _accountData; private PhotoResponse? _defaultBackgroundPhoto; private bool _searchbarVisible; @@ -81,17 +83,16 @@ public partial class MainLayout : LayoutComponentBase { if (firstRender) { - List endTasks = new List(); - - // STEP 0 - endTasks.AddRange( + await Task.WhenAll( [ Task.Run(async () => _user = await AuthenticationService.GetUserAsync()), PhotosClientService.GetPhotoRandomBackground(data => _defaultBackgroundPhoto = data) ]); - - // END - await Task.WhenAll(endTasks); + + if (_user is not null) + { + await AccountsClientService.GetAccountInfo(_user.Id, data => _accountData = data); + } _loaded = true; StateHasChanged(); diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor index ae3c2c8..77dadc7 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor @@ -37,40 +37,41 @@ -
-
-
-
- -
+
+
+
+
-
-
- -
-
- -
+
+
+
+
-
-
- -
+
+ +
+
+
+
+
- +
+ + +
diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index b6820ca..a4eb5eb 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -29,6 +29,7 @@ "DeleteAccountProfileBackground": "/profile_background", "GetAccountInfo": "/{0}/info", "PutAccountProfileInfo": "/profile_info", + "PatchAccountUsername": "/username", "GetAccountRatedMovies": "/{0}/movies", "GetAccountRatedSeries": "/{0}/series", "GetAccountRatedPersons": "/{0}/persons" From 2a0d130914c0888acfb3ca8bb58e706a98accd5d Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 6 Nov 2024 14:56:02 +0100 Subject: [PATCH 6/6] email and password change panels added --- .../Accounts/AccountEmailRequest.cs | 27 ++++++ .../Accounts/AccountPasswordRequest.cs | 19 +++++ .../WatchIt.Database.Model/Account/Account.cs | 6 +- .../AccountsController.cs | 14 ++++ .../AccountsControllerService.cs | 50 +++++++++-- .../IAccountsControllerService.cs | 2 + .../Accounts/AccountEmailRequestValidator.cs | 15 ++++ .../AccountPasswordRequestValidator.cs | 17 ++++ .../Accounts/AccountsClientService.cs | 30 +++++++ .../Accounts/IAccountsClientService.cs | 2 + .../Model/Accounts.cs | 2 + .../Panels/NewEmailPanelComponent.razor | 46 ++++++++++ .../Panels/NewEmailPanelComponent.razor.cs | 83 +++++++++++++++++++ .../Panels/NewPasswordPanelComponent.razor | 45 ++++++++++ .../Panels/NewPasswordPanelComponent.razor.cs | 60 ++++++++++++++ .../Panels/NewUsernamePanelComponent.razor.cs | 21 ++--- .../ProfileEditFormPanelComponent.razor | 2 +- .../ProfileEditFormPanelComponent.razor.cs | 9 +- .../UserEditPageHeaderPanelComponent.razor | 4 +- .../UserEditPageHeaderPanelComponent.razor.cs | 22 +---- .../WatchIt.Website/Pages/UserEditPage.razor | 16 ++-- .../Pages/UserEditPage.razor.cs | 13 ++- .../WatchIt.Website/appsettings.json | 2 + 23 files changed, 441 insertions(+), 66 deletions(-) create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AccountEmailRequest.cs create mode 100644 WatchIt.Common/WatchIt.Common.Model/Accounts/AccountPasswordRequest.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountEmailRequestValidator.cs create mode 100644 WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountPasswordRequestValidator.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor.cs create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor create mode 100644 WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor.cs diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountEmailRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountEmailRequest.cs new file mode 100644 index 0000000..bdf5bab --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountEmailRequest.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Accounts; + +public class AccountEmailRequest +{ + #region PROPERTIES + + [JsonPropertyName("new_email")] + public string NewEmail { get; set; } + + [JsonPropertyName("password")] + public string Password { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public void UpdateAccount(Database.Model.Account.Account account) + { + account.Email = NewEmail; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountPasswordRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountPasswordRequest.cs new file mode 100644 index 0000000..e0048c1 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountPasswordRequest.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Accounts; + +public class AccountPasswordRequest +{ + #region PROPERTIES + + [JsonPropertyName("old_password")] + public string OldPassword { get; set; } + + [JsonPropertyName("new_password")] + public string NewPassword { get; set; } + + [JsonPropertyName("new_password_confirmation")] + public string NewPasswordConfirmation { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs index c24a5ec..f7f5db4 100644 --- a/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs +++ b/WatchIt.Database/WatchIt.Database.Model/WatchIt.Database.Model/Account/Account.cs @@ -15,9 +15,9 @@ public class Account public short? GenderId { get; set; } public Guid? ProfilePictureId { get; set; } public Guid? BackgroundPictureId { get; set; } - public required byte[] Password { get; set; } - public required string LeftSalt { get; set; } - public required string RightSalt { get; set; } + public byte[] Password { get; set; } + public string LeftSalt { get; set; } + public string RightSalt { get; set; } public bool IsAdmin { get; set; } = false; public DateTime CreationDate { get; set; } public DateTime LastActive { get; set; } diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs index 1b93fe9..56ea67a 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/AccountsController.cs @@ -114,6 +114,20 @@ public class AccountsController(IAccountsControllerService accountsControllerSer [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task PatchAccountUsername([FromBody]AccountUsernameRequest data) => await accountsControllerService.PatchAccountUsername(data); + [HttpPatch("email")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task PatchAccountEmail([FromBody]AccountEmailRequest data) => await accountsControllerService.PatchAccountEmail(data); + + [HttpPatch("password")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public async Task PatchAccountPassword([FromBody]AccountPasswordRequest data) => await accountsControllerService.PatchAccountPassword(data); + #endregion [HttpGet("{id}/movies")] diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs index 67d41c1..cb6a052 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/AccountsControllerService.cs @@ -37,18 +37,13 @@ public class AccountsControllerService( public async Task Register(RegisterRequest data) { - string leftSalt = StringExtensions.CreateRandom(20); - string rightSalt = StringExtensions.CreateRandom(20); - byte[] hash = ComputeHash(data.Password, leftSalt, rightSalt); - Account account = new Account { Username = data.Username, Email = data.Email, - Password = hash, - LeftSalt = leftSalt, - RightSalt = rightSalt, }; + + SetPassword(account, data.Password); await database.Accounts.AddAsync(account); await database.SaveChangesAsync(); @@ -283,6 +278,36 @@ public class AccountsControllerService( return RequestResult.Ok(); } + + public async Task PatchAccountEmail(AccountEmailRequest data) + { + Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId()); + + if (!ComputeHash(data.Password, account.LeftSalt, account.RightSalt).SequenceEqual(account.Password)) + { + return RequestResult.Unauthorized(); + } + + data.UpdateAccount(account); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + public async Task PatchAccountPassword(AccountPasswordRequest data) + { + Account account = await database.Accounts.FirstAsync(x => x.Id == userService.GetUserId()); + + if (!ComputeHash(data.OldPassword, account.LeftSalt, account.RightSalt).SequenceEqual(account.Password)) + { + return RequestResult.Unauthorized(); + } + + SetPassword(account, data.NewPassword); + await database.SaveChangesAsync(); + + return RequestResult.Ok(); + } #endregion @@ -338,5 +363,16 @@ public class AccountsControllerService( protected byte[] ComputeHash(string password, string leftSalt, string rightSalt) => SHA512.HashData(Encoding.UTF8.GetBytes($"{leftSalt}{password}{rightSalt}")); + private void SetPassword(Account account, string password) + { + string leftSalt = StringExtensions.CreateRandom(20); + string rightSalt = StringExtensions.CreateRandom(20); + byte[] hash = ComputeHash(password, leftSalt, rightSalt); + + account.Password = hash; + account.LeftSalt = leftSalt; + account.RightSalt = rightSalt; + } + #endregion } \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs index 481603e..0728d3e 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Accounts/IAccountsControllerService.cs @@ -22,6 +22,8 @@ public interface IAccountsControllerService Task GetAccountInfo(long id); Task PutAccountProfileInfo(AccountProfileInfoRequest data); Task PatchAccountUsername(AccountUsernameRequest data); + Task PatchAccountEmail(AccountEmailRequest data); + Task PatchAccountPassword(AccountPasswordRequest data); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query); Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query); diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountEmailRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountEmailRequestValidator.cs new file mode 100644 index 0000000..6bc7e63 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountEmailRequestValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; +using WatchIt.Common.Model.Accounts; +using WatchIt.Database; + +namespace WatchIt.WebAPI.Validators.Accounts; + +public class AccountEmailRequestValidator : AbstractValidator +{ + public AccountEmailRequestValidator(DatabaseContext database) + { + RuleFor(x => x.NewEmail).EmailAddress() + .CannotBeIn(database.Accounts, x => x.Email) + .WithMessage("Email was already used"); + } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountPasswordRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountPasswordRequestValidator.cs new file mode 100644 index 0000000..ffa2096 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Accounts/AccountPasswordRequestValidator.cs @@ -0,0 +1,17 @@ +using FluentValidation; +using WatchIt.Common.Model.Accounts; +using WatchIt.Database; + +namespace WatchIt.WebAPI.Validators.Accounts; + +public class AccountPasswordRequestValidator : AbstractValidator +{ + public AccountPasswordRequestValidator(DatabaseContext database) + { + RuleFor(x => x.NewPassword).MinimumLength(8) + .Must(x => x.Any(char.IsUpper)).WithMessage("Password must contain at least one uppercase letter.") + .Must(x => x.Any(char.IsLower)).WithMessage("Password must contain at least one lowercase letter.") + .Must(x => x.Any(char.IsDigit)).WithMessage("Password must contain at least one digit.") + .Equal(x => x.NewPasswordConfirmation); + } +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs index d73728e..49e0344 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/AccountsClientService.cs @@ -191,6 +191,36 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig .ExecuteAction(); } + public async Task PatchAccountEmail(AccountEmailRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.PatchAccountEmail); + HttpRequest request = new HttpRequest(HttpMethodType.Patch, url) + { + Body = data, + }; + + HttpResponse response = await httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor400BadRequest(badRequestAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .ExecuteAction(); + } + + public async Task PatchAccountPassword(AccountPasswordRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null) + { + string url = GetUrl(EndpointsConfiguration.Accounts.PatchAccountPassword); + HttpRequest request = new HttpRequest(HttpMethodType.Patch, url) + { + Body = data, + }; + + HttpResponse response = await httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor400BadRequest(badRequestAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .ExecuteAction(); + } + public async Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null) { string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedMovies, id); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs index 4a86d8f..84b48f5 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Client/Accounts/IAccountsClientService.cs @@ -21,6 +21,8 @@ public interface IAccountsClientService Task GetAccountInfo(long id, Action? successAction = null, Action? notFoundAction = null); Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task PatchAccountUsername(AccountUsernameRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); + Task PatchAccountEmail(AccountEmailRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); + Task PatchAccountPassword(AccountPasswordRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action>? successAction = null, Action? notFoundAction = null); diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs index 04e8c7f..fae7f58 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Configuration/Model/Accounts.cs @@ -16,6 +16,8 @@ public class Accounts public string GetAccountInfo { get; set; } public string PutAccountProfileInfo { get; set; } public string PatchAccountUsername { get; set; } + public string PatchAccountEmail { get; set; } + public string PatchAccountPassword { get; set; } public string GetAccountRatedMovies { get; set; } public string GetAccountRatedSeries { get; set; } public string GetAccountRatedPersons { get; set; } diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor new file mode 100644 index 0000000..612abe7 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor @@ -0,0 +1,46 @@ +
+
+

Change email

+ @if (_data is not null) + { + + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ @if (!string.IsNullOrWhiteSpace(_error)) + { + @(_error) + } + else if (_saved) + { + New email saved! + } +
+
+ +
+
+
+
+ } + else + { + + } +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor.cs new file mode 100644 index 0000000..77043e6 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewEmailPanelComponent.razor.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; +using WatchIt.Website.Services.Client.Accounts; + +namespace WatchIt.Website.Components.Pages.UserEditPage.Panels; + +public partial class NewEmailPanelComponent : ComponentBase +{ + #region SERVICES + + [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; + [Inject] private NavigationManager NavigationManager { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public required AccountResponse AccountData { get; set; } + + #endregion + + + + #region FIELDS + + private AccountEmailRequest? _data; + private string? _error; + private bool _saving; + private bool _saved; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + _data = new AccountEmailRequest + { + NewEmail = AccountData.Email, + }; + StateHasChanged(); + } + } + + private async Task Save() + { + void Success() + { + _saved = true; + _saving = false; + _data = new AccountEmailRequest + { + NewEmail = _data!.NewEmail + }; + NavigationManager.Refresh(true); + } + + void BadRequest(IDictionary errors) + { + _error = errors.SelectMany(x => x.Value).FirstOrDefault() ?? "Unknown error"; + _saving = false; + } + + void Unauthorized() + { + _error = "Incorrect password"; + _saving = false; + } + + _saving = true; + _saved = false; + _error = null; + await AccountsClientService.PatchAccountEmail(_data!, Success, BadRequest, Unauthorized); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor new file mode 100644 index 0000000..1a869ac --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor @@ -0,0 +1,45 @@ +
+
+

Change password

+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ @if (!string.IsNullOrWhiteSpace(_error)) + { + @(_error) + } + else if (_saved) + { + New email saved! + } +
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor.cs new file mode 100644 index 0000000..7655c8c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewPasswordPanelComponent.razor.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; +using WatchIt.Website.Services.Client.Accounts; + +namespace WatchIt.Website.Components.Pages.UserEditPage.Panels; + +public partial class NewPasswordPanelComponent : ComponentBase +{ + #region SERVICES + + [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; + [Inject] private NavigationManager NavigationManager { get; set; } = default!; + + #endregion + + + + #region FIELDS + + private AccountPasswordRequest _data = new AccountPasswordRequest(); + private string? _error; + private bool _saving; + private bool _saved; + + #endregion + + + + #region PRIVATE METHODS + + private async Task Save() + { + void Success() + { + _saved = true; + _saving = false; + _data = new AccountPasswordRequest(); + NavigationManager.Refresh(true); + } + + void BadRequest(IDictionary errors) + { + _error = errors.SelectMany(x => x.Value).FirstOrDefault() ?? "Unknown error"; + _saving = false; + } + + void Unauthorized() + { + _error = "Incorrect password"; + _saving = false; + } + + _saving = true; + _saved = false; + _error = null; + await AccountsClientService.PatchAccountPassword(_data, Success, BadRequest, Unauthorized); + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs index 0a797fd..39eeb7f 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/NewUsernamePanelComponent.razor.cs @@ -9,7 +9,6 @@ public partial class NewUsernamePanelComponent : ComponentBase { #region SERVICES - [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; [Inject] private NavigationManager NavigationManager { get; set; } = default!; @@ -19,7 +18,7 @@ public partial class NewUsernamePanelComponent : ComponentBase #region PARAMETERS - [Parameter] public required long Id { get; set; } + [Parameter] public required AccountResponse AccountData { get; set; } #endregion @@ -42,21 +41,11 @@ public partial class NewUsernamePanelComponent : ComponentBase { if (firstRender) { - User? user = await AuthenticationService.GetUserAsync(); - - if (user is null) + _data = new AccountUsernameRequest { - return; - } - - await AccountsClientService.GetAccountInfo(user.Id, data => - { - _data = new AccountUsernameRequest - { - NewUsername = data.Username - }; - StateHasChanged(); - }); + NewUsername = AccountData.Username, + }; + StateHasChanged(); } } diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor index 8f9869a..b5a326d 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor @@ -23,7 +23,7 @@ @foreach (GenderResponse gender in _genders) { - + }
diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs index 59806dd..7407687 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/ProfileEditFormPanelComponent.razor.cs @@ -20,7 +20,7 @@ public partial class ProfileEditFormPanelComponent : ComponentBase #region PARAMETERS - [Parameter] public long Id { get; set; } + [Parameter] public required AccountResponse AccountData { get; set; } [Parameter] public string Class { get; set; } = string.Empty; #endregion @@ -47,11 +47,8 @@ public partial class ProfileEditFormPanelComponent : ComponentBase { if (firstRender) { - await Task.WhenAll( - [ - GendersClientService.GetAllGenders(successAction: data => _genders = data), - AccountsClientService.GetAccountInfo(Id, data => _accountProfileInfo = new AccountProfileInfoRequest(data)) - ]); + _accountProfileInfo = new AccountProfileInfoRequest(AccountData); + await GendersClientService.GetAllGenders(successAction: data => _genders = data); _loaded = true; StateHasChanged(); diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor index 2fd8305..f9a9c48 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor @@ -1,8 +1,8 @@
- +
-

@(_username ?? "Loading...")

+

@(AccountData.Username)

User settings
diff --git a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs index 83f7d8e..7700867 100644 --- a/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Components/Pages/UserEditPage/Panels/UserEditPageHeaderPanelComponent.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model.Accounts; using WatchIt.Website.Components.Common.Subcomponents; using WatchIt.Website.Services.Authentication; using WatchIt.Website.Services.Client.Accounts; @@ -18,7 +19,7 @@ public partial class UserEditPageHeaderPanelComponent : ComponentBase #region PARAMETERS - [Parameter] public required User User { get; set; } + [Parameter] public required AccountResponse AccountData { get; set; } #endregion @@ -27,7 +28,6 @@ public partial class UserEditPageHeaderPanelComponent : ComponentBase #region FIELDS private AccountPictureComponent _accountPicture = default!; - private string? _username; #endregion @@ -38,22 +38,4 @@ public partial class UserEditPageHeaderPanelComponent : ComponentBase public async Task ReloadPicture() => await _accountPicture.Reload(); #endregion - - - - #region PRIVATE METHODS - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await AccountsClientService.GetAccountInfo(User.Id, data => - { - _username = data.Username; - StateHasChanged(); - }); - } - } - - #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor index 77dadc7..1ef2c1b 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor @@ -9,7 +9,7 @@ @{ StringBuilder sb = new StringBuilder(" - WatchIt"); - if (_user is null) sb.Insert(0, "Loading..."); + if (_accountData is null) sb.Insert(0, "Loading..."); else sb.Insert(0, "User settings"); @(sb.ToString()) @@ -18,11 +18,11 @@
- @if (_user is not null) + @if (_accountData is not null) {
- +
@@ -45,7 +45,7 @@
-
-
-
@@ -70,7 +70,9 @@
- + + +
diff --git a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs index 8e4c5ea..4b29a60 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/UserEditPage.razor.cs @@ -1,6 +1,7 @@ using System.Net; using Microsoft.AspNetCore.Components; using WatchIt.Common.Model; +using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Photos; using WatchIt.Website.Components.Pages.UserEditPage.Panels; using WatchIt.Website.Layout; @@ -31,7 +32,7 @@ public partial class UserEditPage : ComponentBase #region FIELDS - private User? _user; + private AccountResponse? _accountData; private UserEditPageHeaderPanelComponent _header = default!; @@ -47,15 +48,19 @@ public partial class UserEditPage : ComponentBase { Layout.BackgroundPhoto = null; - _user = await AuthenticationService.GetUserAsync(); - if (_user is null) + User? user = await AuthenticationService.GetUserAsync(); + if (user is null) { NavigationManager.NavigateTo($"/auth?redirect_to={WebUtility.UrlEncode("/user/edit")}"); return; } StateHasChanged(); - await AccountsClientService.GetAccountProfileBackground(_user.Id, data => Layout.BackgroundPhoto = data); + await Task.WhenAll( + [ + AccountsClientService.GetAccountInfo(user.Id, data => _accountData = data), + AccountsClientService.GetAccountProfileBackground(user.Id, data => Layout.BackgroundPhoto = data) + ]); StateHasChanged(); } } diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index a4eb5eb..943633b 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -30,6 +30,8 @@ "GetAccountInfo": "/{0}/info", "PutAccountProfileInfo": "/profile_info", "PatchAccountUsername": "/username", + "PatchAccountEmail": "/email", + "PatchAccountPassword": "/password", "GetAccountRatedMovies": "/{0}/movies", "GetAccountRatedSeries": "/{0}/series", "GetAccountRatedPersons": "/{0}/persons"