@using System.ComponentModel.DataAnnotations @using Microsoft.Extensions.Localization @inherits AliasVault.Client.Main.Pages.MainBase @inject IJSRuntime JSRuntime @inject CredentialService CredentialService @inject AliasVault.Client.Services.QuickCreateStateService QuickCreateStateService @inject LanguageService LanguageService @implements IAsyncDisposable @if (IsPopupVisible) {

@Localizer["CreateNewAliasTitle"]

} @code { private IStringLocalizer Localizer => LocalizerFactory.Create("Components.Main.Widgets.CreateNewIdentityWidget", "AliasVault.Client"); private bool IsPopupVisible = false; private bool IsCreating = false; private CreateModel Model = new(); private string PopupStyle { get; set; } = string.Empty; private ElementReference buttonRef; private IJSObjectReference? Module; /// async ValueTask IAsyncDisposable.DisposeAsync() { await KeyboardShortcutService.UnregisterShortcutAsync("gc"); LanguageService.LanguageChanged -= OnLanguageChanged; if (Module is not null) { await Module.DisposeAsync(); } } /// protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await KeyboardShortcutService.RegisterShortcutAsync("gc", ShowPopup); LanguageService.LanguageChanged += OnLanguageChanged; Module = await JSRuntime.InvokeAsync("import", "./js/modules/newIdentityWidget.js"); } } /// /// Handles language change events and triggers component refresh. /// /// The new language code. private void OnLanguageChanged(string languageCode) { InvokeAsync(StateHasChanged); } /// /// When the URL input is focused, place cursor at the end of the default URL to allow for easy typing. /// private void OnFocusUrlInput(FocusEventArgs e) { if (Model.ServiceUrl != CredentialService.DefaultServiceUrl) { return; } // Use a small delay to ensure the focus is set after the browser's default behavior. Task.Delay(1).ContinueWith(_ => { JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('serviceUrl').setSelectionRange({CredentialService.DefaultServiceUrl.Length}, {CredentialService.DefaultServiceUrl.Length})"); }); } /// /// Toggle the popup. /// private async Task TogglePopup() { IsPopupVisible = !IsPopupVisible; if (IsPopupVisible) { await ShowPopup(); } } /// /// Show the popup. /// private async Task ShowPopup() { IsPopupVisible = true; // Clear the input fields Model = new(); Model.ServiceUrl = CredentialService.DefaultServiceUrl; await UpdatePopupStyle(); await Task.Delay(100); // Give time for the DOM to update await JsInteropService.FocusElementById("serviceName"); } /// /// Close the popup. /// private void ClosePopup() { IsPopupVisible = false; } /// /// Update the popup style so that it is positioned correctly. /// private async Task UpdatePopupStyle() { var windowWidth = await JSRuntime.InvokeAsync("getWindowWidth"); var buttonRect = await JSRuntime.InvokeAsync("getElementRect", buttonRef); // Constrain the popup width to 400px minus some padding. var popupWidth = Math.Min(400, windowWidth - 20); PopupStyle = $"width: {popupWidth}px; top: {buttonRect.Bottom}px;"; StateHasChanged(); } /// /// Create the new identity. /// private async Task CreateIdentity() { if (IsCreating) { return; } IsCreating = true; GlobalLoadingSpinner.Show(Localizer["CreatingNewAliasMessage"]); StateHasChanged(); var credential = new Credential(); credential.Alias = new Alias(); credential.Alias.Email = "@" + CredentialService.GetDefaultEmailDomain(); credential.Service = new Service(); credential.Service.Name = Model.ServiceName; if (Model.ServiceUrl != CredentialService.DefaultServiceUrl) { credential.Service.Url = Model.ServiceUrl; } credential.Passwords = new List { new() }; await CredentialService.GenerateRandomIdentityAsync(credential); var id = await CredentialService.InsertEntryAsync(credential); if (id == Guid.Empty) { // Error saving. IsCreating = false; GlobalLoadingSpinner.Hide(); GlobalNotificationService.AddErrorMessage(Localizer["CreateCredentialErrorMessage"], true); return; } // No error, add success message. GlobalNotificationService.AddSuccessMessage(Localizer["CredentialCreatedSuccessMessage"]); NavigationManager.NavigateTo("/credentials/" + id); IsCreating = false; GlobalLoadingSpinner.Hide(); StateHasChanged(); ClosePopup(); } /// /// Open the advanced mode for creating a new identity. /// private void OpenAdvancedMode() { // Store the form data in the state service to prefill in the advanced mode form. QuickCreateStateService.ServiceName = Model.ServiceName; QuickCreateStateService.ServiceUrl = Model.ServiceUrl; NavigationManager.NavigateTo("/credentials/create"); ClosePopup(); } /// /// Bounding client rectangle returned from JavaScript. /// private sealed class BoundingClientRect { public double Left { get; set; } public double Top { get; set; } public double Right { get; set; } public double Bottom { get; set; } public double Width { get; set; } public double Height { get; set; } } /// /// Local model for the form with support for validation. /// private sealed class CreateModel { /// /// The service name. /// [Required] [Display(Name = "Service Name")] public string ServiceName { get; set; } = string.Empty; /// /// The service URL. /// [Display(Name = "Service URL")] public string ServiceUrl { get; set; } = string.Empty; } }