@inherits LocalizableComponentBase @* The login itself (the root, /clientapi/user): its email address, changed only with the password and a code sent to the new one, since a forgotten password is recovered through it; its password changed (every other session ends, this device's goes on with a new JWT); every session of it ended at once; or the whole of it deleted, every persona with it. Deleting it deletes everything public its personas had, and PrivaPub tells the servers that hold it. *@
@Localizer["Email address"] @if (IsLoading) { } else if (account is not null) { @if (account.Email is { Length: > 0 } email) {

@email @if (account.IsEmailValidated) { @Localizer["verified"] } else { @Localizer["not verified"] }

@if (!account.IsEmailValidated) {

@Localizer["Only a verified address can recover a forgotten password."]

} } else {

@Localizer["No email address yet: without one, a forgotten password can't be recovered."]

} @if (account.PendingEmail is { Length: > 0 } pending && !isChangingEmail) { @if (codeSentence is not null) {

@codeSentence

}

@Localizer["Enter the code sent to {0}.", pending]

@Localizer["Verify"]
} @if (isChangingEmail) {
@Localizer["Cancel"] @Localizer["Send the code"]
} else if (isRemovingEmail) {
@Localizer["Cancel"] @Localizer["Remove the address"]
} else {
@if (account.Email is { Length: > 0 }) { @Localizer["Remove the address"] } @(account.Email is { Length: > 0 } ? Localizer["Change"] : Localizer["Add an address"])
} }
@Localizer["Change the password"]

@Localizer["Every other device signed in to this login is signed out."]

@Localizer["Change the password"]
@Localizer["Sign out everywhere"]

@Localizer["Every device signed in to this login signs out, this one too, with every persona: for a lost device, or a session that isn't yours."]

@Localizer["Sign out everywhere"]
@Localizer["Delete this login"]

@Localizer["Every persona of this login goes with it, and everything public they had, here and on the servers that hold it. It cannot be undone."]

@Localizer["Delete it for good"]
@code { [Inject] AuthService AuthService { get; set; } [Inject] ConfirmService Confirm { get; set; } /// The login as PrivaPub describes it; null until read, or when it couldn't be (the email block stays empty). ViewAccount account; EmailChangeForm change = new(); EmailCodeForm code = new(); PasswordForm emailRemoval = new(); UserPasswordForm password = new(); RemoveSelfForm removal = new(); /// PrivaPub's answer to the change asked here, the same whether or not the address is free. string codeSentence; bool isChangingEmail; bool isRemovingEmail; bool understood; bool isSaving; protected override async Task OnInitializedAsync() { if (CascadingState.IsOffline) { WhenOnline(LoadData); StartOnlinePolling(); IsLoading = false; return; } await LoadData(); IsLoading = false; } async Task LoadData() => account = await AuthService.Account(); void OpenEmailChange() { change = new(); isRemovingEmail = false; isChangingEmail = true; } void OpenEmailRemoval() { emailRemoval = new(); isChangingEmail = false; isRemovingEmail = true; } void CloseEmailForms() { isChangingEmail = false; isRemovingEmail = false; } async Task ChangeEmail() { isSaving = true; change.Email = change.Email.Trim(); var result = await AuthService.ChangeEmail(change); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } codeSentence = result.Data is string { Length: > 0 } said ? said : Localizer["If that address can be used, a code is on its way to it. It works for one hour."]; // as PrivaPub keeps it while it waits for its code account.PendingEmail = change.Email.ToLowerInvariant(); change = new(); code = new(); isChangingEmail = false; } async Task VerifyEmail() { isSaving = true; var result = await AuthService.VerifyEmail(code.Code.Trim()); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); // five wrong codes, or an hour, and the address no longer waits account = await AuthService.Account() ?? account; return; } account = result.Data as ViewAccount ?? await AuthService.Account(); code = new(); codeSentence = default; Toast.AddResponseSuccess(Localizer["Email address verified."]); } async Task RemoveEmail() { if (!await Confirm.Ask(Localizer["Remove the email address?"], Localizer["Without one, a forgotten password can't be recovered."], Localizer["Remove the address"], true)) return; isSaving = true; var result = await AuthService.RemoveEmail(emailRemoval.Password); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } account = result.Data as ViewAccount ?? await AuthService.Account(); emailRemoval = new(); codeSentence = default; isRemovingEmail = false; Toast.AddResponseSuccess(Localizer["Email address removed."]); } async Task ChangePassword() { isSaving = true; // the session goes on with the JWT PrivaPub answers with, and the persona with a new token var result = await AuthService.ChangePassword(password); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } password = new(); Toast.AddResponseSuccess(Localizer["Password changed."]); } async Task LogoutEverywhere() { if (!await Confirm.Ask(Localizer["Sign out everywhere?"], Localizer["Every device and every persona of this login signs out, this one too. Signing in again takes the password."], Localizer["Sign out everywhere"], true)) return; isSaving = true; var result = await AuthService.LogoutEverywhere(); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } await CascadingState.SignOut(); Navigation.NavigateTo("login"); } async Task Delete() { isSaving = true; var result = await AuthService.DeleteAccount(removal); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } await CascadingState.SignOut(); Navigation.NavigateTo("login"); } }