House rules: numbers through one formatter, no made-up server errors, and the rules fail the build
Build / Build (push) Successful in 33s
Build / Build (push) Successful in 33s
- Every number shown on a page goes through NumbersExtensions: counts, percentages, the nerd readouts and the values written into a style. - A failure the client builds itself (offline, timeout, retries run out, an answer it can't read) is a 400 that names its kind; only a server's own 5XX passes through. - Login, sign-up, recovery and password confirmations leave autocomplete to the browser, so it can offer the saved login. - The Daisy components start with empty lists and attributes, and a decorative image still renders alt="". - Arasaka.HouseRules.Analyzers checks all of this in every build, together with PrivaPub.ClientModels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4YtEJrgxG12Pc39Fai8Bd
This commit is contained in:
1 parent
697b2c8897
commit
3827d903a3
37 files changed
+165
-111
No files matched your search
@@ -34,7 +34,7 @@
|
||||
}
|
||||
@if (AskPassword)
|
||||
{
|
||||
<DInput TValue="string" Type="password" class="w-full" @bind-Value="password" Immediate="true" autocomplete="current-password" maxlength="200"
|
||||
<DInput TValue="string" Type="password" class="w-full" @bind-Value="password" Immediate="true" maxlength="200"
|
||||
placeholder="@(Staff ? Localizer["Your password"] : Localizer["Current password"])"
|
||||
aria-label="@(Staff ? Localizer["Your password"] : Localizer["Current password"])"
|
||||
data-admin-password="@(Staff ? "" : null)" data-own-password="@(Staff ? null : "")" />
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
@if (ticket is null)
|
||||
{
|
||||
<p class="text-sm">@Localizer["It holds every persona's private keys and private posts: keep it where only you can read it. Your password, to get a link:"]</p>
|
||||
<DInput TValue="string" type="password" @bind-Value="password" Immediate="true" autocomplete="current-password" maxlength="200"
|
||||
<DInput TValue="string" type="password" @bind-Value="password" Immediate="true" maxlength="200"
|
||||
placeholder="@Localizer["Password"]" data-backup-password />
|
||||
<ErrorViewer AdditionalError="@dialogError" />
|
||||
}
|
||||
@@ -182,7 +182,7 @@
|
||||
{
|
||||
<p>@Localizer["{0} logins were deleted since; a restore deletes them again.", restoring.RootsDeletedSince]</p>
|
||||
}
|
||||
<DInput TValue="string" type="password" @bind-Value="password" Immediate="true" autocomplete="current-password" maxlength="200"
|
||||
<DInput TValue="string" type="password" @bind-Value="password" Immediate="true" maxlength="200"
|
||||
placeholder="@Localizer["Password"]" data-backup-password />
|
||||
<DInput TValue="string" @bind-Value="typedHost" Immediate="true" autocomplete="off" maxlength="253"
|
||||
placeholder="@Localizer["Type {0} to confirm", backups.Host ?? ""]" data-backup-host />
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<DStats class="w-full overflow-x-auto neo-raised neo-xs" data-statistics-overview data-scroll-x>
|
||||
<DStat>
|
||||
<Heading>@Localizer["Deliveries that arrived"]</Heading>
|
||||
<ChildContent>@(overview.DeliverySuccess is { } success ? $"{success * 100:0.#}%" : "—")</ChildContent>
|
||||
<ChildContent>@(overview.DeliverySuccess is { } success ? (success * 100).ToDefaultPercent() : "—")</ChildContent>
|
||||
<Desc>@Localizer["p50 {0} ms, p95 {1} ms", Ms(overview.DeliveryLatencyMs.P50), Ms(overview.DeliveryLatencyMs.P95)]</Desc>
|
||||
</DStat>
|
||||
<DStat>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@Localizer["Shown only to people within {0} km of here, never sent to other servers.", form.RangeKm.ToString("0")]</span>
|
||||
<span>@Localizer["Shown only to people within {0} km of here, never sent to other servers.", form.RangeKm.ToDefaultCount()]</span>
|
||||
}
|
||||
</span>
|
||||
<DRange TValue="double" Size="DSize.Xs" Min="1" Max="50" Step="1" @bind-Value="form.RangeKm" />
|
||||
|
||||
@@ -804,7 +804,7 @@
|
||||
var option = poll.Options[i];
|
||||
var share = poll.VotesCount > 0 && option.VotesCount is { } votes ? votes * 100d / poll.VotesCount : 0;
|
||||
<div class="relative rounded-md overflow-hidden neo-inset neo-2xs px-2 py-1">
|
||||
<div class="absolute inset-y-0 left-0 bg-primary/20" style="width:@(share.ToString("0.#", System.Globalization.CultureInfo.InvariantCulture))%"></div>
|
||||
<div class="absolute inset-y-0 left-0 bg-primary/20" style="width:@(share.ToCssNumber())%"></div>
|
||||
<span class="relative flex justify-between gap-2">
|
||||
<span>
|
||||
@option.Title
|
||||
@@ -813,7 +813,7 @@
|
||||
<i class="ion-md-checkmark-circle text-primary" title="@Localizer["Your vote"]" aria-label="@Localizer["Your vote"]"></i>
|
||||
}
|
||||
</span>
|
||||
<span>@share.ToString("0")%</span>
|
||||
<span>@share.ToWholePercent()</span>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@@ -1318,7 +1318,7 @@
|
||||
|
||||
// where a cropped picture keeps its subject: Mastodon's focus runs from -1 to 1, left to right and bottom to top
|
||||
static string FocalPoint(MediaAttachment media) => media.Meta?.Focus is { } focus
|
||||
? string.Create(System.Globalization.CultureInfo.InvariantCulture, $"object-position: {(focus.X + 1) * 50:0.#}% {(1 - focus.Y) * 50:0.#}%")
|
||||
? string.Create(System.Globalization.CultureInfo.InvariantCulture, $"object-position: {((focus.X + 1) * 50).ToCssNumber()}% {((1 - focus.Y) * 50).ToCssNumber()}%")
|
||||
: null;
|
||||
|
||||
static bool IsWebLink(string url) => Uri.TryCreate(url, UriKind.Absolute, out var uri) && uri.Scheme is "https" or "http";
|
||||
@@ -1474,7 +1474,7 @@
|
||||
yield return NerdRow.Of(Localizer["server"], server.NodeName is { Length: > 0 } name && name != server.Host ? $"{server.Host} ({name})" : server.Host);
|
||||
yield return NerdRow.Of(Localizer["software"], $"{server.Software} {server.Version}".Trim());
|
||||
yield return NerdRow.Of(Localizer["Users"], server.Users is { } users
|
||||
? server.ActiveMonth is { } active ? $"{users:N0} ({active:N0} {Localizer["active this month"]})" : users.ToString("N0")
|
||||
? server.ActiveMonth is { } active ? $"{users.ToDefaultCount()} ({active.ToDefaultCount()} {Localizer["active this month"]})" : users.ToDefaultCount()
|
||||
: null);
|
||||
yield return NerdRow.Of(Localizer["local posts"], server.LocalPosts);
|
||||
yield return NerdRow.Of(Localizer["registrations"], server.OpenRegistrations is { } open ? open ? Localizer["open"] : Localizer["closed"] : null);
|
||||
@@ -1553,7 +1553,7 @@
|
||||
_ => Localizer["Near you"]
|
||||
});
|
||||
yield return NerdRow.Of("lat, lng", $"{Coordinate(location.Latitude)}, {Coordinate(location.Longitude)}");
|
||||
yield return NerdRow.Of(Localizer["precision"], $"±{location.PrecisionKm:0} km");
|
||||
yield return NerdRow.Of(Localizer["precision"], $"±{location.PrecisionKm.ToDefaultCount()} km");
|
||||
if (location.Source is LocationSource.Server or LocationSource.Country && ServerGeo is { } geo)
|
||||
{
|
||||
yield return NerdRow.Of(Localizer["network"], geo.Network);
|
||||
@@ -1561,7 +1561,7 @@
|
||||
yield return NerdRow.Of(Localizer["located at"], geo.LocatedAt);
|
||||
}
|
||||
if (InstanceService.Known(InstanceService.SelfHost)?.Geo is { Latitude: { } selfLatitude, Longitude: { } selfLongitude } && location.Source is not LocationSource.NearYou)
|
||||
yield return new(Localizer["from PrivaPub"], $"{NerdRow.Kilometres(selfLatitude, selfLongitude, location.Latitude, location.Longitude):N0} km");
|
||||
yield return new(Localizer["from PrivaPub"], $"{NerdRow.Kilometres(selfLatitude, selfLongitude, location.Latitude, location.Longitude).ToDefaultCount()} km");
|
||||
}
|
||||
|
||||
static IEnumerable<NerdRow> MediaRows(MediaAttachment media)
|
||||
@@ -1570,7 +1570,7 @@
|
||||
yield return NerdRow.Of("size", media.Meta?.Original?.Size);
|
||||
yield return NerdRow.Of("aspect", media.Meta?.Original?.Aspect);
|
||||
yield return NerdRow.Of("length", media.Meta?.Length);
|
||||
yield return NerdRow.Of("focus", media.Meta?.Focus is { } focus ? $"{focus.X:0.##}, {focus.Y:0.##}" : null);
|
||||
yield return NerdRow.Of("focus", media.Meta?.Focus is { } focus ? $"{focus.X.ToReadout()}, {focus.Y.ToReadout()}" : null);
|
||||
yield return NerdRow.Of("blurhash", media.Blurhash);
|
||||
yield return NerdRow.Of("served by", Uri.TryCreate(media.Url, UriKind.Absolute, out var served) ? served.Host : null);
|
||||
yield return NerdRow.Of("origin", Uri.TryCreate(media.RemoteUrl, UriKind.Absolute, out var origin) ? origin.Host : null);
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
[Parameter] public bool CloseOnContentClick { get; set; }
|
||||
[Parameter] public string TriggerClass { get; set; }
|
||||
/// <summary>Attributes of the trigger itself (an aria-label, a data- hook); the outer element takes the unmatched ones.</summary>
|
||||
[Parameter] public IReadOnlyDictionary<string, object> TriggerAttributes { get; set; }
|
||||
[Parameter] public IReadOnlyDictionary<string, object> TriggerAttributes { get; set; } = new Dictionary<string, object>();
|
||||
[Parameter] public string ContentClass { get; set; }
|
||||
/// <summary>Below md, the content is a sheet at the bottom of the screen instead of hanging from the trigger.</summary>
|
||||
[Parameter] public bool SheetOnNarrow { get; set; }
|
||||
|
||||
@@ -10,7 +10,7 @@ public abstract class DComponentBase : ComponentBase
|
||||
{
|
||||
[Parameter] public string Class { get; set; }
|
||||
|
||||
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }
|
||||
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
protected static string Cx(params string[] classes) => DCx.Join(classes);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="@Class">
|
||||
@if (Src is not null)
|
||||
{
|
||||
<img src="@Src" alt="@Alt" />
|
||||
<img src="@Src" alt="@(Alt ?? "")" />
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -19,7 +19,7 @@
|
||||
@code {
|
||||
[Parameter] public string Src { get; set; }
|
||||
// "" on purpose (the documented exception to "no string defaults"): an empty alt marks a decorative image.
|
||||
[Parameter] public string Alt { get; set; } = "";
|
||||
[Parameter] public string Alt { get; set; }
|
||||
/// <summary>Shown in the frame when there is no Src, e.g. initials with Placeholder.</summary>
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
[Parameter] public DAvatarStatus Status { get; set; }
|
||||
|
||||
@@ -4,19 +4,16 @@
|
||||
across it shows the others. Up to 10 images of the same size, from Images or as <img> children; cap its width in
|
||||
Class (max-w-60), or it fills the container. *@
|
||||
<figure @attributes="AdditionalAttributes" class="@Cx("hover-gallery", Class)">
|
||||
@if (Images is not null)
|
||||
@foreach (var src in Images.Take(10))
|
||||
{
|
||||
foreach (var src in Images.Take(10))
|
||||
{
|
||||
<img src="@src" alt="@Alt" />
|
||||
}
|
||||
<img src="@src" alt="@(Alt ?? "")" />
|
||||
}
|
||||
@ChildContent
|
||||
</figure>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<string> Images { get; set; }
|
||||
[Parameter] public IEnumerable<string> Images { get; set; } = [];
|
||||
// "" on purpose (the documented exception to "no string defaults"): an empty alt marks a decorative image.
|
||||
[Parameter] public string Alt { get; set; } = "";
|
||||
[Parameter] public string Alt { get; set; }
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -5,19 +5,16 @@
|
||||
duration-* in Class (default 10s). *@
|
||||
<span @attributes="AdditionalAttributes" class="@Cx("text-rotate", Class)">
|
||||
<span class="@(Centered ? "justify-items-center" : null)">
|
||||
@if (Items is not null)
|
||||
@foreach (var item in Items.Take(6))
|
||||
{
|
||||
foreach (var item in Items.Take(6))
|
||||
{
|
||||
<span>@item</span>
|
||||
}
|
||||
<span>@item</span>
|
||||
}
|
||||
@ChildContent
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<string> Items { get; set; }
|
||||
[Parameter] public IEnumerable<string> Items { get; set; } = [];
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>Centre the lines horizontally.</summary>
|
||||
[Parameter] public bool Centered { get; set; }
|
||||
|
||||
@@ -7,19 +7,16 @@
|
||||
<div @attributes="InputAttributes" class="@Cx("filter", CssClass)">
|
||||
<input type="radio" name="@GroupName" class="@Cx("btn", "filter-reset", SizeClass)" aria-label="@ResetText"
|
||||
disabled="@Disabled" @onchange="() => CurrentValue = default" />
|
||||
@if (Options is not null)
|
||||
@foreach (var option in Options)
|
||||
{
|
||||
foreach (var option in Options)
|
||||
{
|
||||
<input type="radio" name="@GroupName" class="@Cx("btn", SizeClass)" aria-label="@(TextOf?.Invoke(option) ?? option?.ToString())"
|
||||
checked="@IsSelected(option)" disabled="@Disabled"
|
||||
@onchange="() => CurrentValue = option" />
|
||||
}
|
||||
<input type="radio" name="@GroupName" class="@Cx("btn", SizeClass)" aria-label="@(TextOf?.Invoke(option) ?? option?.ToString())"
|
||||
checked="@IsSelected(option)" disabled="@Disabled"
|
||||
@onchange="() => CurrentValue = option" />
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<TValue> Options { get; set; }
|
||||
[Parameter] public IEnumerable<TValue> Options { get; set; } = [];
|
||||
/// <summary>An option's button text; ToString() when null.</summary>
|
||||
[Parameter] public Func<TValue, string> TextOf { get; set; }
|
||||
[Parameter] public string ResetText { get; set; } = "×";
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
/// <summary>The choices can't be made now (the caller is busy with the last one).</summary>
|
||||
[Parameter] public bool ChoicesDisabled { get; set; }
|
||||
/// <summary>Passed to the dropdown: its class, a data- hook.</summary>
|
||||
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }
|
||||
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
bool isOpen;
|
||||
string query;
|
||||
|
||||
@@ -251,9 +251,9 @@
|
||||
yield return NerdRow.Of(Localizer["axial tilt"], $"{Fixed(sun.Obliquity)}°");
|
||||
yield return NerdRow.Of(Localizer["ecliptic longitude"], $"{Fixed(sun.EclipticLongitude)}°");
|
||||
yield return NerdRow.Of(Localizer["Earth–Sun distance"],
|
||||
$"{sun.Distance.ToString("0.00000", CultureInfo.InvariantCulture)} AU ({sun.Distance * 149_597_870.7:N0} km)");
|
||||
$"{sun.Distance.ToFixed(5)} AU ({(sun.Distance * 149_597_870.7).ToDefaultCount()} km)");
|
||||
yield return NerdRow.Of(Localizer["irradiance"],
|
||||
$"{sun.Irradiance * 1361:0} W/m² ({Math.Round((sun.Irradiance - 1) * 100, 1).ToString("+0.0;-0.0;0.0", CultureInfo.InvariantCulture)}%)");
|
||||
$"{(sun.Irradiance * 1361).ToDefaultCount()} W/m² ({Math.Round((sun.Irradiance - 1) * 100, 1).ToSignedPercent()})");
|
||||
yield return NerdRow.Of(Localizer["Place"], sun.Place is { } place ? $"{place} ({Fixed(sun.PlaceLatitude)}, {Fixed(sun.PlaceLongitude)})" : null);
|
||||
yield return NerdRow.Of(Localizer["elevation"], sun.Elevation is { } elevation ? $"{Fixed(elevation)}°" : null);
|
||||
yield return NerdRow.Of(Localizer["day length"], sun.DayLength is { } hours ? NerdRow.Duration(TimeSpan.FromHours(hours)) : null);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Globalization;
|
||||
|
||||
using decePubClient.Extensions;
|
||||
|
||||
namespace decePubClient.Components.Nerd;
|
||||
|
||||
/// <summary>One line of a NerdTip; a row without a value is not shown.</summary>
|
||||
@@ -9,7 +11,7 @@ public sealed record NerdRow(string Label, string Value)
|
||||
{
|
||||
null => null,
|
||||
DateTime time => Time(time),
|
||||
double number => number.ToString("0.###", CultureInfo.InvariantCulture),
|
||||
double number => number.ToReadout(),
|
||||
bool flag => flag ? "yes" : "no",
|
||||
IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture),
|
||||
_ => value.ToString()
|
||||
@@ -27,8 +29,8 @@ public sealed record NerdRow(string Label, string Value)
|
||||
span = span.Duration();
|
||||
return sign + (span.TotalSeconds switch
|
||||
{
|
||||
< 1 => $"{span.TotalMilliseconds:0} ms",
|
||||
< 60 => $"{span.TotalSeconds.ToString("0.0", CultureInfo.InvariantCulture)} s",
|
||||
< 1 => $"{span.TotalMilliseconds.ToFixed(0)} ms",
|
||||
< 60 => $"{span.TotalSeconds.ToFixed(1)} s",
|
||||
< 3600 => $"{span.Minutes} min {span.Seconds} s",
|
||||
< 86400 => $"{(int)span.TotalHours} h {span.Minutes} min",
|
||||
_ => $"{(int)span.TotalDays} d {span.Hours} h"
|
||||
@@ -40,8 +42,8 @@ public sealed record NerdRow(string Label, string Value)
|
||||
{
|
||||
null => null,
|
||||
< 1024 => $"{bytes} B",
|
||||
< 1024 * 1024 => $"{(bytes.Value / 1024d).ToString("0.0", CultureInfo.InvariantCulture)} KiB",
|
||||
_ => $"{(bytes.Value / 1048576d).ToString("0.0", CultureInfo.InvariantCulture)} MiB"
|
||||
< 1024 * 1024 => $"{(bytes.Value / 1024d).ToFixed(1)} KiB",
|
||||
_ => $"{(bytes.Value / 1048576d).ToFixed(1)} MiB"
|
||||
};
|
||||
|
||||
/// <summary>The great-circle distance between two points in kilometres (haversine, Earth radius 6371 km).</summary>
|
||||
|
||||
@@ -58,9 +58,9 @@
|
||||
{
|
||||
<EditForm Model="change" OnValidSubmit="ChangeEmail" class="flex flex-col gap-2" data-account-email-change>
|
||||
<DataAnnotationsValidator />
|
||||
<DInput TValue="string" Type="email" @bind-Value="change.Email" Immediate="true" autocomplete="email" inputmode="email" autocapitalize="none"
|
||||
<DInput TValue="string" Type="email" @bind-Value="change.Email" Immediate="true" inputmode="email" autocapitalize="none"
|
||||
spellcheck="false" placeholder="@Localizer["New email address"]" data-account-email-new />
|
||||
<DInput TValue="string" Type="password" @bind-Value="change.Password" Immediate="true" autocomplete="current-password"
|
||||
<DInput TValue="string" Type="password" @bind-Value="change.Password" Immediate="true"
|
||||
placeholder="@Localizer["Current password"]" data-account-email-password />
|
||||
<ErrorViewer />
|
||||
<div class="flex flex-wrap justify-end gap-2 pointer-coarse:gap-2">
|
||||
@@ -75,7 +75,7 @@
|
||||
{
|
||||
<EditForm Model="emailRemoval" OnValidSubmit="RemoveEmail" class="flex flex-col gap-2" data-account-email-remove>
|
||||
<DataAnnotationsValidator />
|
||||
<DInput TValue="string" Type="password" @bind-Value="emailRemoval.Password" Immediate="true" autocomplete="current-password"
|
||||
<DInput TValue="string" Type="password" @bind-Value="emailRemoval.Password" Immediate="true"
|
||||
placeholder="@Localizer["Current password"]" data-account-email-remove-password />
|
||||
<ErrorViewer />
|
||||
<div class="flex flex-wrap justify-end gap-2 pointer-coarse:gap-2">
|
||||
@@ -107,7 +107,7 @@
|
||||
<DataAnnotationsValidator />
|
||||
<b class="text-sm">@Localizer["Change the password"]</b>
|
||||
<p class="text-xs opacity-70">@Localizer["Every other device signed in to this login is signed out."]</p>
|
||||
<DInput TValue="string" type="password" @bind-Value="password.OldPassword" Immediate="true" autocomplete="current-password"
|
||||
<DInput TValue="string" type="password" @bind-Value="password.OldPassword" Immediate="true"
|
||||
placeholder="@Localizer["Current password"]" data-account-current />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<DInput TValue="string" type="password" class="flex-1 min-w-40" @bind-Value="password.NewPassword" Immediate="true" autocomplete="new-password"
|
||||
@@ -135,7 +135,7 @@
|
||||
<DataAnnotationsValidator />
|
||||
<b class="text-sm text-error">@Localizer["Delete this login"]</b>
|
||||
<p class="text-sm">@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."]</p>
|
||||
<DInput TValue="string" type="password" @bind-Value="removal.Password" Immediate="true" autocomplete="current-password"
|
||||
<DInput TValue="string" type="password" @bind-Value="removal.Password" Immediate="true"
|
||||
placeholder="@Localizer["Current password"]" data-account-delete-password />
|
||||
<DCheckbox @bind-Value="understood" Size="DSize.Sm" LabelText="@Localizer["I understand: delete this login and every persona of it"]" data-account-delete-understood />
|
||||
<ErrorViewer />
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
<EditForm Model="disable" OnValidSubmit="Disable" class="flex flex-col gap-2" data-totp-disable>
|
||||
<DataAnnotationsValidator />
|
||||
<DInput TValue="string" Type="password" @bind-Value="disable.Password" Immediate="true" autocomplete="current-password"
|
||||
<DInput TValue="string" Type="password" @bind-Value="disable.Password" Immediate="true"
|
||||
placeholder="@Localizer["Current password"]" data-totp-disable-password />
|
||||
@if (disableWithRecoveryCode)
|
||||
{
|
||||
@@ -133,7 +133,7 @@
|
||||
{
|
||||
<EditForm Model="setupPassword" OnValidSubmit="Setup" class="flex flex-col gap-2" data-totp-setup>
|
||||
<DataAnnotationsValidator />
|
||||
<DInput TValue="string" Type="password" @bind-Value="setupPassword.Password" Immediate="true" autocomplete="current-password"
|
||||
<DInput TValue="string" Type="password" @bind-Value="setupPassword.Password" Immediate="true"
|
||||
placeholder="@Localizer["Current password"]" data-totp-setup-password />
|
||||
<ErrorViewer />
|
||||
<div class="flex flex-wrap justify-end gap-2 pointer-coarse:gap-2">
|
||||
|
||||
Reference in new issue
Block a user