Add identity generator age setting to AliasVault.Client (#1379)

This commit is contained in:
Leendert de Borst
2025-11-21 09:55:06 +01:00
committed by Leendert de Borst
parent 02575d7366
commit aab69ab1b4
4 changed files with 128 additions and 28 deletions

View File

@@ -3,6 +3,7 @@
@inject LanguageService LanguageService
@using Microsoft.Extensions.Localization
@using AliasVault.Client.Services
@using AliasVault.Client.Services.JsInterop.Models
<LayoutPageTitle>@Localizer["PageTitle"]</LayoutPageTitle>
@@ -12,6 +13,22 @@
Description="@Localizer["PageDescription"]">
</PageHeader>
<h3 class="mb-4 text-lg font-medium text-gray-900 dark:text-white">@Localizer["AppLanguageTitle"]</h3>
<div class="mb-4">
<label for="appLanguage" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["AppLanguageLabel"]</label>
<select @bind="AppLanguage" @bind:after="UpdateAppLanguage" id="appLanguage" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
@foreach (var language in LanguageService.GetSupportedLanguages())
{
<option value="@language.Key">@LanguageService.GetLanguageFlag(language.Key) @language.Value</option>
}
</select>
<span class="block text-sm font-normal text-gray-500 truncate dark:text-gray-400">
@Localizer["AppLanguageDescription"]
</span>
</div>
</div>
<div class="p-4 mb-4 mx-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
<h3 class="mb-4 text-lg font-medium text-gray-900 dark:text-white">@Localizer["EmailSettingsTitle"]</h3>
@@ -50,23 +67,6 @@
</div>
</div>
<div class="p-4 mb-4 mx-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
<h3 class="mb-4 text-lg font-medium text-gray-900 dark:text-white">@Localizer["AppLanguageTitle"]</h3>
<div class="mb-4">
<label for="appLanguage" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["AppLanguageLabel"]</label>
<select @bind="AppLanguage" @bind:after="UpdateAppLanguage" id="appLanguage" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
@foreach (var language in LanguageService.GetSupportedLanguages())
{
<option value="@language.Key">@LanguageService.GetLanguageFlag(language.Key) @language.Value</option>
}
</select>
<span class="block text-sm font-normal text-gray-500 truncate dark:text-gray-400">
@Localizer["AppLanguageDescription"]
</span>
</div>
</div>
<div class="p-4 mx-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
<h3 class="mb-4 text-lg font-medium text-gray-900 dark:text-white">@Localizer["AliasSettingsTitle"]</h3>
@@ -75,6 +75,7 @@
<select @bind="DefaultIdentityLanguage" @bind:after="UpdateDefaultIdentityLanguage" id="defaultIdentityLanguage" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="en">@Localizer["EnglishOption"]</option>
<option value="nl">@Localizer["DutchOption"]</option>
<option value="de">@Localizer["GermanOption"]</option>
</select>
<span class="block text-sm font-normal text-gray-500 truncate dark:text-gray-400">
@Localizer["AliasGenerationLanguageDescription"]
@@ -92,6 +93,21 @@
@Localizer["AliasGenerationGenderDescription"]
</span>
</div>
<div class="mb-4">
<label for="defaultIdentityAgeRange" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["AliasGenerationAgeRangeLabel"]</label>
<select @bind="DefaultIdentityAgeRange" @bind:after="UpdateDefaultIdentityAgeRange" id="defaultIdentityAgeRange" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
@foreach (var ageRange in AvailableAgeRanges)
{
<option value="@ageRange.Value">
@(ageRange.Value == "random" ? Localizer["RandomOption"] : ageRange.Label)
</option>
}
</select>
<span class="block text-sm font-normal text-gray-500 truncate dark:text-gray-400">
@Localizer["AliasGenerationAgeRangeDescription"]
</span>
</div>
</div>
<div class="p-4 mx-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
@@ -134,8 +150,10 @@
private bool AutoEmailRefresh { get; set; }
private string DefaultIdentityLanguage { get; set; } = string.Empty;
private string DefaultIdentityGender { get; set; } = string.Empty;
private string DefaultIdentityAgeRange { get; set; } = string.Empty;
private string AppLanguage { get; set; } = string.Empty;
private int ClipboardClearSeconds { get; set; }
private List<AgeRangeOption> AvailableAgeRanges { get; set; } = new();
/// <inheritdoc />
protected override async Task OnInitializedAsync()
@@ -143,6 +161,9 @@
await base.OnInitializedAsync();
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["BreadcrumbTitle"] });
// Load available age ranges from JavaScript utility
AvailableAgeRanges = await JsInteropService.GetAvailableAgeRangesAsync();
DefaultEmailDomain = DbService.Settings.DefaultEmailDomain;
if (DefaultEmailDomain == string.Empty || Config.HiddenPrivateEmailDomains.Contains(DefaultEmailDomain))
{
@@ -158,6 +179,7 @@
AutoEmailRefresh = DbService.Settings.AutoEmailRefresh;
DefaultIdentityLanguage = DbService.Settings.DefaultIdentityLanguage;
DefaultIdentityGender = DbService.Settings.DefaultIdentityGender;
DefaultIdentityAgeRange = DbService.Settings.DefaultIdentityAgeRange;
AppLanguage = DbService.Settings.AppLanguage;
ClipboardClearSeconds = DbService.Settings.ClipboardClearSeconds;
}
@@ -198,6 +220,15 @@
StateHasChanged();
}
/// <summary>
/// Updates the default identity age range setting.
/// </summary>
private async Task UpdateDefaultIdentityAgeRange()
{
await DbService.Settings.SetDefaultIdentityAgeRange(DefaultIdentityAgeRange);
StateHasChanged();
}
/// <summary>
/// Updates the app language setting.
/// </summary>

View File

@@ -74,11 +74,11 @@
<!-- Alias Settings Section -->
<data name="AliasSettingsTitle">
<value>Alias Settings</value>
<comment>Title for alias settings section</comment>
<value>Identity Generator Settings</value>
<comment>Title for identity generator settings section</comment>
</data>
<data name="AliasGenerationLanguageLabel">
<value>Alias generation language</value>
<value>Language</value>
<comment>Label for alias generation language setting</comment>
</data>
<data name="AliasGenerationLanguageDescription">
@@ -93,8 +93,12 @@
<value>Dutch</value>
<comment>Dutch language option</comment>
</data>
<data name="GermanOption">
<value>German</value>
<comment>German language option</comment>
</data>
<data name="AliasGenerationGenderLabel">
<value>Alias generation gender</value>
<value>Gender</value>
<comment>Label for alias generation gender setting</comment>
</data>
<data name="AliasGenerationGenderDescription">
@@ -113,6 +117,14 @@
<value>Female</value>
<comment>Female gender option</comment>
</data>
<data name="AliasGenerationAgeRangeLabel">
<value>Age range</value>
<comment>Label for alias generation age range setting</comment>
</data>
<data name="AliasGenerationAgeRangeDescription">
<value>Set the default age range for generating new identities. This affects the birthdate assigned to the generated identity.</value>
<comment>Description for alias generation age range setting</comment>
</data>
<!-- Clipboard Settings Section -->
<data name="ClipboardSettingsTitle">

View File

@@ -58,8 +58,11 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService
do
{
// Convert age range to birthdate options using shared JS utility
var birthdateOptions = await jsInteropService.ConvertAgeRangeToBirthdateOptionsAsync(dbService.Settings.DefaultIdentityAgeRange);
// Generate a random identity using the TypeScript library
var identity = await jsInteropService.GenerateRandomIdentityAsync(dbService.Settings.DefaultIdentityLanguage, dbService.Settings.DefaultIdentityGender);
var identity = await jsInteropService.GenerateRandomIdentityAsync(dbService.Settings.DefaultIdentityLanguage, dbService.Settings.DefaultIdentityGender, birthdateOptions);
// Generate random values for the Identity properties
credential.Username = identity.NickName;

View File

@@ -360,13 +360,61 @@ public sealed class JsInteropService(IJSRuntime jsRuntime)
return await jsRuntime.InvokeAsync<byte[]>("cryptoInterop.decryptBytes", base64Ciphertext, encryptionKey);
}
/// <summary>
/// Gets all available age range options from the shared JavaScript utility.
/// </summary>
/// <returns>Array of age range options.</returns>
public async Task<List<AgeRangeOption>> GetAvailableAgeRangesAsync()
{
try
{
if (_identityGeneratorModule == null)
{
await InitializeAsync();
}
var result = await _identityGeneratorModule!.InvokeAsync<List<AgeRangeOption>>("getAvailableAgeRanges");
return result ?? new List<AgeRangeOption>();
}
catch (JSException ex)
{
await Console.Error.WriteLineAsync($"JavaScript error getting age ranges: {ex.Message}");
return new List<AgeRangeOption>();
}
}
/// <summary>
/// Converts an age range string to birthdate options using the shared JavaScript utility.
/// </summary>
/// <param name="ageRange">Age range string (e.g., "21-25", "30-35", or "random").</param>
/// <returns>Birthdate options object or null if random.</returns>
public async Task<object?> ConvertAgeRangeToBirthdateOptionsAsync(string ageRange)
{
try
{
if (_identityGeneratorModule == null)
{
await InitializeAsync();
}
var result = await _identityGeneratorModule!.InvokeAsync<object?>("convertAgeRangeToBirthdateOptions", ageRange);
return result;
}
catch (JSException ex)
{
await Console.Error.WriteLineAsync($"JavaScript error converting age range: {ex.Message}");
return null;
}
}
/// <summary>
/// Generates a random identity using the specified language.
/// </summary>
/// <param name="language">The language to use for generating the identity (e.g. "en", "nl").</param>
/// <param name="gender">The gender preference for generating the identity (optional, defaults to random).</param>
/// <param name="language">The language to use for generating the identity (e.g. "en", "nl", "de").</param>
/// <param name="gender">The gender preference for generating the identity (defaults to "random").</param>
/// <param name="birthdateOptions">Optional birthdate options (targetYear and yearDeviation).</param>
/// <returns>An AliasVaultIdentity containing the generated identity information.</returns>
public async Task<AliasVaultIdentity> GenerateRandomIdentityAsync(string language, string? gender = null)
public async Task<AliasVaultIdentity> GenerateRandomIdentityAsync(string language, string? gender = null, object? birthdateOptions = null)
{
try
{
@@ -376,9 +424,15 @@ public sealed class JsInteropService(IJSRuntime jsRuntime)
}
var generatorInstance = await _identityGeneratorModule!.InvokeAsync<IJSObjectReference>("CreateIdentityGenerator", language);
var result = string.IsNullOrEmpty(gender) || gender == "random"
? await generatorInstance.InvokeAsync<AliasVaultIdentity>("generateRandomIdentity")
: await generatorInstance.InvokeAsync<AliasVaultIdentity>("generateRandomIdentity", gender);
// Use "random" as default if gender is null or empty
var genderValue = "random";
if (!string.IsNullOrEmpty(gender))
{
genderValue = gender;
}
var result = await generatorInstance.InvokeAsync<AliasVaultIdentity>("generateRandomIdentity", genderValue, birthdateOptions);
return result;
}