R6 moderation in decePub: users and personas, accounts, report actions, deletions, suspended personas
Administration's Users & personas (administrators): logins searched and opened into their personas and groups; roles, bans, two-factor reset and deletion of a login; silence, suspension and deletion of a persona; a group deleted. Accounts (moderators): local personas and remote accounts searched or listed as moderated, silenced and suspended with a staff note and a warning. Reports resolve with an action. ActDialog asks for the note, the warning or the password and is the act's confirmation. A login deletes its own persona and the groups it owns with its password. Suspended personas can't be chosen; one refused a token mid-session moves the session to another, or to none, once. Warnings show in notifications, limited accounts on their profile, and moderators remove a post from its card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
This commit is contained in:
1 parent
4c88980374
commit
c572471b88
30 files changed
+2604
-67
No files matched your search
@@ -1,26 +1,58 @@
|
||||
@inherits LocalizableComponentBase
|
||||
|
||||
@* The root's personas (/clientapi/avatar/private): each one used in turn, and a new one made. Personas are never linked
|
||||
to one another: no server, PrivaPub's included, shows that they share a root. *@
|
||||
@* The root's personas (/clientapi/avatar/private): each one used in turn, a new one made, and one deleted with the
|
||||
login's password, asked first (everything public it had goes, with the groups it owns). One the server's staff
|
||||
suspended or silenced says so; a suspended one can't be used until the suspension is lifted, but may be deleted.
|
||||
Personas are never linked to one another: no server, PrivaPub's included, shows that they share a root. *@
|
||||
<div class="flex flex-col gap-4 w-full" data-personas>
|
||||
<div class="flex flex-col gap-2">
|
||||
@if (CascadingState.Personas.Count == 0)
|
||||
{
|
||||
<p class="text-sm" data-personas-none>@Localizer["This login has no persona to act as yet: make one below."]</p>
|
||||
}
|
||||
@foreach (var persona in CascadingState.Personas)
|
||||
{
|
||||
<div class="flex items-center gap-2 min-w-0" data-persona="@persona.UserName">
|
||||
<div class="flex flex-wrap items-center gap-2 pointer-coarse:gap-2 min-w-0" data-persona="@persona.UserName" data-persona-state="@StateOf(persona)">
|
||||
<img class="size-8 rounded-full object-cover" src="@(persona.PictureURL ?? "imgs/icon-192.png")" alt="" />
|
||||
<span class="flex-1 min-w-0 truncate"><b>@(persona.Name ?? persona.UserName)</b> <span class="text-xs opacity-70">@persona.Handle</span></span>
|
||||
@if (persona.AccountState is ViewAvatarState.Suspended)
|
||||
{
|
||||
<DBadge Size="DSize.Sm" Variant="DBadgeVariant.Soft" Color="DColor.Error" title="@Localizer["Suspended by this server's staff: it can't act until that is lifted."]">
|
||||
@Localizer["Suspended"]
|
||||
</DBadge>
|
||||
}
|
||||
else if (persona.AccountState is ViewAvatarState.Silenced)
|
||||
{
|
||||
<DBadge Size="DSize.Sm" Variant="DBadgeVariant.Soft" Color="DColor.Warning" title="@Localizer["Silenced by this server's staff: its posts stay out of public timelines, trends and search."]">
|
||||
@Localizer["Silenced"]
|
||||
</DBadge>
|
||||
}
|
||||
@if (persona.Id == CascadingState.PersonaId)
|
||||
{
|
||||
<DBadge Size="DSize.Sm" Variant="DBadgeVariant.Soft" Color="DColor.Primary">@Localizer["in use"]</DBadge>
|
||||
}
|
||||
else
|
||||
{
|
||||
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || switching is not null)" OnClick="() => Use(persona)">@Localizer["Use"]</DButton>
|
||||
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || switching is not null || !CascadingState.CanUse(persona))" OnClick="() => Use(persona)">
|
||||
@Localizer["Use"]
|
||||
</DButton>
|
||||
}
|
||||
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || switching is not null)" OnClick="() => deleting = persona"
|
||||
title="@Localizer["Delete this persona"]" aria-label="@Localizer["Delete this persona"]" data-persona-delete-own>
|
||||
<i class="ion-md-trash text-error" aria-hidden="true"></i><span class="hidden sm:inline">@Localizer["Delete"]</span>
|
||||
</DButton>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (deleting is not null)
|
||||
{
|
||||
<ActDialog Question="@Localizer["Delete {0}?", deleting.Name ?? "@" + deleting.UserName]"
|
||||
Text="@Localizer["Everything public it had goes, here and on the servers that hold it, with the groups it owns. Its name stays taken. It cannot be undone."]"
|
||||
ConfirmText="@Localizer["Delete for good"]" Danger="true" Act="persona-delete" AskPassword="true" OnConfirm="Delete"
|
||||
OnClosed="() => deleting = null" />
|
||||
}
|
||||
|
||||
<EditForm Model="form" OnValidSubmit="Create" class="flex flex-col gap-2 p-3 rounded-xl neo-inset neo-2xs" data-persona-new>
|
||||
<DataAnnotationsValidator />
|
||||
<b class="text-sm">@Localizer["A new persona"]</b>
|
||||
@@ -47,6 +79,15 @@
|
||||
InsertAvatarForm form = new();
|
||||
bool isSaving;
|
||||
string switching;
|
||||
// the persona whose deletion the dialog asks about
|
||||
ViewAvatar deleting;
|
||||
|
||||
static string StateOf(ViewAvatar persona) => persona.AccountState switch
|
||||
{
|
||||
ViewAvatarState.Suspended => "suspended",
|
||||
ViewAvatarState.Silenced => "silenced",
|
||||
_ => "normal"
|
||||
};
|
||||
|
||||
protected override void OnInitialized() => IsLoading = false;
|
||||
|
||||
@@ -59,6 +100,19 @@
|
||||
Toast.AddResponseError(result);
|
||||
}
|
||||
|
||||
async Task<WebResult> Delete(ActDialog.Answer answer)
|
||||
{
|
||||
var persona = deleting;
|
||||
var result = await AuthService.DeletePersona(persona.Id, answer.Password);
|
||||
if (result.IsInvalid)
|
||||
return result;
|
||||
|
||||
// the session drops it, and acts as another persona when it was the current one (none left: make one below)
|
||||
await CascadingState.PersonaDeleted(persona.Id);
|
||||
Toast.AddResponseSuccess(Localizer["{0} is deleted.", persona.Name ?? "@" + persona.UserName]);
|
||||
return result;
|
||||
}
|
||||
|
||||
async Task Create()
|
||||
{
|
||||
isSaving = true;
|
||||
|
||||
Reference in new issue
Block a user