email and password change panels added

This commit is contained in:
2024-11-06 14:56:02 +01:00
Unverified
parent 4cbc44f9be
commit 2a0d130914
23 changed files with 441 additions and 66 deletions

View File

@@ -191,6 +191,36 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
.ExecuteAction();
}
public async Task PatchAccountEmail(AccountEmailRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? 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<IDictionary<string, string[]>>? 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<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedMovies, id);

View File

@@ -21,6 +21,8 @@ public interface IAccountsClientService
Task GetAccountInfo(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null);
Task PutAccountProfileInfo(AccountProfileInfoRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null);
Task PatchAccountUsername(AccountUsernameRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null);
Task PatchAccountEmail(AccountEmailRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null);
Task PatchAccountPassword(AccountPasswordRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null);
Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null);
Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null);
Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null);

View File

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

View File

@@ -0,0 +1,46 @@
<div class="panel">
<div class="vstack gap-3">
<h4 class="fw-bold">Change email</h4>
@if (_data is not null)
{
<EditForm Model="@(_data)">
<AntiforgeryToken/>
<div class="container-grid">
<div class="row form-group my-1">
<label for="email" class="col-2 col-form-label">New email</label>
<div class="col-10">
<InputText id="email" class="form-control" @bind-Value="_data!.NewEmail"/>
</div>
</div>
<div class="row form-group my-1">
<label for="password" class="col-2 col-form-label">Password</label>
<div class="col-10">
<InputText id="password" type="password" class="form-control" @bind-Value="_data!.Password"/>
</div>
</div>
<div class="row mt-2">
<div class="col align-self-center">
@if (!string.IsNullOrWhiteSpace(_error))
{
<span class="text-danger">@(_error)</span>
}
else if (_saved)
{
<span class="text-success">New email saved!</span>
}
</div>
<div class="col-auto">
<button type="submit" class="btn btn-secondary" disabled="@(_saving)" @onclick="@(Save)">
<LoadingButtonContentComponent IsLoading="@(_saving)" Content="Save" LoadingContent="Saving..."/>
</button>
</div>
</div>
</div>
</EditForm>
}
else
{
<LoadingComponent Color="@(LoadingComponent.LoadingComponentColors.Light)"/>
}
</div>
</div>

View File

@@ -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<string, string[]> 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
}

View File

@@ -0,0 +1,45 @@
<div class="panel">
<div class="vstack gap-3">
<h4 class="fw-bold">Change password</h4>
<EditForm Model="@(_data)">
<AntiforgeryToken/>
<div class="container-grid">
<div class="row form-group my-1">
<label for="oldPassword" class="col-2 col-form-label">Old password</label>
<div class="col-10">
<InputText id="oldPassword" class="form-control" @bind-Value="_data.OldPassword"/>
</div>
</div>
<div class="row form-group my-1">
<label for="newPassword" class="col-2 col-form-label">New password</label>
<div class="col-10">
<InputText id="newPassword" type="password" class="form-control" @bind-Value="_data.NewPassword"/>
</div>
</div>
<div class="row form-group my-1">
<label for="newPasswordConf" class="col-2 col-form-label">Confirm new password</label>
<div class="col-10">
<InputText id="newPasswordConf" type="password" class="form-control" @bind-Value="_data.NewPasswordConfirmation"/>
</div>
</div>
<div class="row mt-2">
<div class="col align-self-center">
@if (!string.IsNullOrWhiteSpace(_error))
{
<span class="text-danger">@(_error)</span>
}
else if (_saved)
{
<span class="text-success">New email saved!</span>
}
</div>
<div class="col-auto">
<button type="submit" class="btn btn-secondary" disabled="@(_saving)" @onclick="@(Save)">
<LoadingButtonContentComponent IsLoading="@(_saving)" Content="Save" LoadingContent="Saving..."/>
</button>
</div>
</div>
</div>
</EditForm>
</div>
</div>

View File

@@ -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<string, string[]> 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
}

View File

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

View File

@@ -23,7 +23,7 @@
<option value="@(default(short?))">No choice</option>
@foreach (GenderResponse gender in _genders)
{
<option value="@(gender.Id)">@(gender.Name)</option>
<option value="@(gender.Id)">@(gender.Name)</option>
}
</InputSelect>
</div>

View File

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

View File

@@ -1,8 +1,8 @@
<div class="panel" role="button" @onclick="@(() => NavigationManager.NavigateTo("/user"))">
<div class="d-flex gap-3 align-items-center">
<AccountPictureComponent @ref="_accountPicture" Id="@(User.Id)" Size="60"/>
<AccountPictureComponent @ref="_accountPicture" Id="@(AccountData.Id)" Size="60"/>
<div class="d-flex-inline flex-column">
<h2 id="username" class="fw-bold m-0">@(_username ?? "Loading...")</h2>
<h2 id="username" class="fw-bold m-0">@(AccountData.Username)</h2>
<span id="secondaryText" class="text-secondary">User settings</span>
</div>
</div>

View File

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

View File

@@ -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");
<PageTitle>@(sb.ToString())</PageTitle>
@@ -18,11 +18,11 @@
<div class="container-grid">
@if (_user is not null)
@if (_accountData is not null)
{
<div class="row">
<div class="col">
<UserEditPageHeaderPanelComponent @ref="@(_header)" User="@(_user)"/>
<UserEditPageHeaderPanelComponent @ref="@(_header)" AccountData="@(_accountData)"/>
</div>
</div>
<div class="row mt-default">
@@ -45,7 +45,7 @@
</div>
<div class="row mt-default gx-default">
<div class="col-auto">
<PictureEditorPanelComponent Id="@(_user.Id)"
<PictureEditorPanelComponent Id="@(_accountData.Id)"
Class="h-100"
PicturePlaceholder="assets/user_placeholder.png"
Circle="true"
@@ -55,13 +55,13 @@
OnPictureChanged="@(async (_) => await PictureChanged())"/>
</div>
<div class="col">
<ProfileEditFormPanelComponent Id="@(_user.Id)"
<ProfileEditFormPanelComponent AccountData="@(_accountData)"
Class="h-100"/>
</div>
</div>
<div class="row mt-default">
<div class="col">
<ProfileBackgroundEditorPanelComponent Id="@(_user.Id)"
<ProfileBackgroundEditorPanelComponent Id="@(_accountData.Id)"
OnBackgroundChanged="BackgroundChanged"/>
</div>
</div>
@@ -70,7 +70,9 @@
<TabPanel Name="account">
<div class="vstack mt-default gap-default">
<AccountEditHeaderPanelComponent/>
<NewUsernamePanelComponent Id="@(_user.Id)"/>
<NewUsernamePanelComponent AccountData="@(_accountData)"/>
<NewEmailPanelComponent AccountData="@(_accountData)"/>
<NewPasswordPanelComponent/>
</div>
</TabPanel>
</Content>

View File

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

View File

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