113 lines
5.3 KiB
Plaintext
113 lines
5.3 KiB
Plaintext
@inherits LocalizableComponentBase
|
|||
|
|
|
||
|
|
@* Asks before an act that takes words or a password: what it does, then the fields it needs, then a confirm button. The
|
||
|
|
staff's acts (Staff) take a note only the staff read (data-moderate-note), a warning the persona is told
|
||
|
|
(data-moderate-warning) and, to delete, the administrator's own password (data-admin-password); the dialog is
|
||
|
|
data-moderate-confirm="{act}". A login's own deletions take its password (data-own-password) in data-own-confirm="{act}".
|
||
|
|
OnConfirm does the act: a failure stays in the dialog (a wrong password is typed again), a success closes it. *@
|
||
|
|
<DModal Placement="DModalPlacement.ResponsiveBottom" IsOpen="true" IsOpenChanged="Closed" CloseText="@Localizer["Close"]"
|
||
|
|
data-moderate-confirm="@(Staff ? Act : null)" data-own-confirm="@(Staff ? null : Act)">
|
||
|
|
<Heading>@Question</Heading>
|
||
|
|
<ChildContent>
|
||
|
|
<div class="flex flex-col gap-3 mt-2 text-sm">
|
||
|
|
@if (Text is { Length: > 0 })
|
||
|
|
{
|
||
|
|
<p class="whitespace-pre-line">@Text</p>
|
||
|
|
}
|
||
|
|
@if (AskNote)
|
||
|
|
{
|
||
|
|
<label class="flex flex-col gap-1">
|
||
|
|
<span class="font-semibold">@Localizer["A note for the staff (optional)"]</span>
|
||
|
|
<DTextarea TValue="string" class="w-full" @bind-Value="note" Immediate="true" rows="2" autocomplete="off" maxlength="1000"
|
||
|
|
aria-label="@Localizer["A note for the staff (optional)"]" data-moderate-note />
|
||
|
|
<span class="text-xs opacity-70">@Localizer["Only the staff read it, in the moderation log."]</span>
|
||
|
|
</label>
|
||
|
|
}
|
||
|
|
@if (AskWarning)
|
||
|
|
{
|
||
|
|
<label class="flex flex-col gap-1">
|
||
|
|
<span class="font-semibold">@Localizer["What the persona is told (optional)"]</span>
|
||
|
|
<DTextarea TValue="string" class="w-full" @bind-Value="warning" Immediate="true" rows="3" autocomplete="off" maxlength="1000"
|
||
|
|
aria-label="@Localizer["What the persona is told (optional)"]" data-moderate-warning />
|
||
|
|
<span class="text-xs opacity-70">@(WarningHint ?? Localizer["It reaches the persona as a warning from this server's staff."])</span>
|
||
|
|
</label>
|
||
|
|
}
|
||
|
|
@if (AskPassword)
|
||
|
|
{
|
||
|
|
<DInput TValue="string" Type="password" class="w-full" @bind-Value="password" Immediate="true" autocomplete="current-password" 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 : "")" />
|
||
|
|
}
|
||
|
|
<ErrorViewer AdditionalError="@error" />
|
||
|
|
</div>
|
||
|
|
</ChildContent>
|
||
|
|
<Actions>
|
||
|
|
<DButton class="max-md:flex-1" OnClick="Cancel" data-act-cancel>@Localizer["Cancel"]</DButton>
|
||
|
|
<DButton class="max-md:flex-1" Color="@(Danger ? DColor.Error : DColor.Primary)"
|
||
|
|
Disabled="@(IsDefaultDisabled || isActing || (AskPassword && string.IsNullOrEmpty(password)))" OnClick="Confirm" data-act-submit>
|
||
|
|
@if (isActing)
|
||
|
|
{
|
||
|
|
<DLoading Size="DSize.Sm" LabelText="@Localizer["loading"]" />
|
||
|
|
}
|
||
|
|
<span>@ConfirmText</span>
|
||
|
|
</DButton>
|
||
|
|
</Actions>
|
||
|
|
</DModal>
|
||
|
|
|
||
|
|
@code {
|
||
|
|
/// <summary>The question the dialog asks, its heading.</summary>
|
||
|
|
[Parameter, EditorRequired] public string Question { get; set; }
|
||
|
|
/// <summary>What the act does, in words.</summary>
|
||
|
|
[Parameter] public string Text { get; set; }
|
||
|
|
[Parameter, EditorRequired] public string ConfirmText { get; set; }
|
||
|
|
/// <summary>The confirm button shows as a danger (it deletes or cuts something off).</summary>
|
||
|
|
[Parameter] public bool Danger { get; set; }
|
||
|
|
/// <summary>The act's name in the dialog's data attribute (silence, persona-delete, ...).</summary>
|
||
|
|
[Parameter, EditorRequired] public string Act { get; set; }
|
||
|
|
/// <summary>A staff act (data-moderate-confirm, the administrator's password) or the login's own (data-own-confirm).</summary>
|
||
|
|
[Parameter] public bool Staff { get; set; }
|
||
|
|
[Parameter] public bool AskNote { get; set; }
|
||
|
|
[Parameter] public bool AskWarning { get; set; }
|
||
|
|
/// <summary>Who reads the warning, when it isn't simply the persona (an account elsewhere is told nothing).</summary>
|
||
|
|
[Parameter] public string WarningHint { get; set; }
|
||
|
|
[Parameter] public bool AskPassword { get; set; }
|
||
|
|
/// <summary>Does the act with what was typed; an invalid result stays in the dialog, a valid one closes it.</summary>
|
||
|
|
[Parameter, EditorRequired] public Func<Answer, Task<WebResult>> OnConfirm { get; set; }
|
||
|
|
[Parameter] public EventCallback OnClosed { get; set; }
|
||
|
|
|
||
|
|
/// <summary>What was typed: the note and the warning trimmed, null when empty; the password as typed.</summary>
|
||
|
|
public sealed record Answer(string Note, string Warning, string Password);
|
||
|
|
|
||
|
|
string note;
|
||
|
|
string warning;
|
||
|
|
string password;
|
||
|
|
string error;
|
||
|
|
bool isActing;
|
||
|
|
|
||
|
|
protected override void OnInitialized() => IsLoading = false;
|
||
|
|
|
||
|
|
async Task Confirm()
|
||
|
|
{
|
||
|
|
error = default;
|
||
|
|
isActing = true;
|
||
|
|
var result = await OnConfirm(new Answer(Trimmed(note), Trimmed(warning), password));
|
||
|
|
isActing = false;
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
// a cancelled request says nothing; anything else is said here, where it can be fixed
|
||
|
|
if (result.StatusCode is not 410)
|
||
|
|
error = result.ErrorMessage;
|
||
|
|
password = default;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
await OnClosed.InvokeAsync();
|
||
|
|
}
|
||
|
|
|
||
|
|
static string Trimmed(string text) => string.IsNullOrWhiteSpace(text) ? null : text.Trim();
|
||
|
|
|
||
|
|
Task Cancel() => OnClosed.InvokeAsync();
|
||
|
|
|
||
|
|
Task Closed(bool open) => open ? Task.CompletedTask : OnClosed.InvokeAsync();
|
||
|
|
}
|