229 lines
6.7 KiB
Plaintext
229 lines
6.7 KiB
Plaintext
@inherits LocalizableComponentBase
|
|||
|
|
|
||
|
|
@* The accounts the current persona muted or blocked (/api/v1/mutes, /api/v1/blocks), and the servers it blocked
|
||
|
|
(/api/v1/domain_blocks), each one undone here. A server blocked hides everything from it and removes the persona's
|
||
|
|
followers there. *@
|
||
|
|
@if (IsLoading)
|
||
|
|
{
|
||
|
|
<p class="flex w-full items-center justify-center gap-2">
|
||
|
|
<DLoading Variant="DLoadingVariant.Dots" LabelText="@Localizer["loading"]" />
|
||
|
|
@Localizer["loading"]
|
||
|
|
</p>
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
<div class="flex flex-col gap-5 w-full">
|
||
|
|
<section class="flex flex-col gap-2" data-mutes>
|
||
|
|
<h3 class="font-semibold text-sm">@Localizer["Muted accounts"]</h3>
|
||
|
|
@if (mutes.Count == 0)
|
||
|
|
{
|
||
|
|
<p class="text-sm opacity-70">@Localizer["Nobody is muted."]</p>
|
||
|
|
}
|
||
|
|
@foreach (var account in mutes)
|
||
|
|
{
|
||
|
|
<div class="flex items-center gap-2 min-w-0" data-account-id="@account.Id">
|
||
|
|
<img class="size-8 rounded-full object-cover" src="@account.Avatar" alt="" />
|
||
|
|
<a class="flex-1 min-w-0 truncate link link-hover" href="@($"account/{Uri.EscapeDataString(account.Id)}")">
|
||
|
|
<b>@account.Name</b> <span class="text-xs opacity-70">@@@account.Acct</span>
|
||
|
|
</a>
|
||
|
|
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || busy.Contains(account.Id))" OnClick="() => Undo(account, AccountAction.Unmute)">
|
||
|
|
@Localizer["Unmute"]
|
||
|
|
</DButton>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
@if (mutesMaxId is not null)
|
||
|
|
{
|
||
|
|
<DButton Size="DSize.Sm" class="self-start" Disabled="IsDefaultDisabled" OnClick="MoreMutes">@Localizer["Load more"]</DButton>
|
||
|
|
}
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section class="flex flex-col gap-2" data-blocks>
|
||
|
|
<h3 class="font-semibold text-sm">@Localizer["Blocked accounts"]</h3>
|
||
|
|
@if (blocks.Count == 0)
|
||
|
|
{
|
||
|
|
<p class="text-sm opacity-70">@Localizer["Nobody is blocked."]</p>
|
||
|
|
}
|
||
|
|
@foreach (var account in blocks)
|
||
|
|
{
|
||
|
|
<div class="flex items-center gap-2 min-w-0" data-account-id="@account.Id">
|
||
|
|
<img class="size-8 rounded-full object-cover" src="@account.Avatar" alt="" />
|
||
|
|
<a class="flex-1 min-w-0 truncate link link-hover" href="@($"account/{Uri.EscapeDataString(account.Id)}")">
|
||
|
|
<b>@account.Name</b> <span class="text-xs opacity-70">@@@account.Acct</span>
|
||
|
|
</a>
|
||
|
|
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || busy.Contains(account.Id))" OnClick="() => Undo(account, AccountAction.Unblock)">
|
||
|
|
@Localizer["Unblock"]
|
||
|
|
</DButton>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
@if (blocksMaxId is not null)
|
||
|
|
{
|
||
|
|
<DButton Size="DSize.Sm" class="self-start" Disabled="IsDefaultDisabled" OnClick="MoreBlocks">@Localizer["Load more"]</DButton>
|
||
|
|
}
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section class="flex flex-col gap-2" data-domain-blocks>
|
||
|
|
<h3 class="font-semibold text-sm">@Localizer["Blocked servers"]</h3>
|
||
|
|
<EditForm Model="draft" OnValidSubmit="BlockDomain" class="flex flex-wrap gap-2 items-center">
|
||
|
|
<DInput TValue="string" class="flex-1 min-w-48" @bind-Value="draft.Domain" Immediate="true" autocomplete="off" maxlength="253"
|
||
|
|
placeholder="@Localizer["A server, e.g. example.social"]" data-domain-new>
|
||
|
|
<Prefix><i class="ion-md-globe"></i></Prefix>
|
||
|
|
</DInput>
|
||
|
|
<DButton Type="submit" Size="DSize.Sm" Color="DColor.Error" Disabled="@(IsDefaultDisabled || string.IsNullOrWhiteSpace(draft.Domain) || busy.Contains(Domain(draft.Domain)))">
|
||
|
|
@Localizer["Block server"]
|
||
|
|
</DButton>
|
||
|
|
</EditForm>
|
||
|
|
@if (domains.Count == 0)
|
||
|
|
{
|
||
|
|
<p class="text-sm opacity-70">@Localizer["No server is blocked."]</p>
|
||
|
|
}
|
||
|
|
@foreach (var domain in domains)
|
||
|
|
{
|
||
|
|
<div class="flex items-center gap-2 min-w-0" data-domain="@domain">
|
||
|
|
<i class="ion-md-globe opacity-70"></i>
|
||
|
|
<span class="flex-1 min-w-0 truncate">@domain</span>
|
||
|
|
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || busy.Contains(domain))" OnClick="() => UnblockDomain(domain)">
|
||
|
|
@Localizer["Unblock"]
|
||
|
|
</DButton>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
|
||
|
|
@code {
|
||
|
|
[Inject] AccountService AccountService { get; set; }
|
||
|
|
|
||
|
|
sealed class DomainDraft
|
||
|
|
{
|
||
|
|
public string Domain { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
List<Account> mutes = [];
|
||
|
|
List<Account> blocks = [];
|
||
|
|
List<string> domains = [];
|
||
|
|
string mutesMaxId;
|
||
|
|
string blocksMaxId;
|
||
|
|
DomainDraft draft = new();
|
||
|
|
// the accounts and servers whose undo is on its way
|
||
|
|
HashSet<string> busy = [];
|
||
|
|
string loadedPersonaId;
|
||
|
|
|
||
|
|
protected override async Task OnParametersSetAsync()
|
||
|
|
{
|
||
|
|
if (CascadingState.PersonaId == loadedPersonaId)
|
||
|
|
return;
|
||
|
|
loadedPersonaId = CascadingState.PersonaId;
|
||
|
|
|
||
|
|
if (CascadingState.IsOffline)
|
||
|
|
{
|
||
|
|
WhenOnline(LoadData);
|
||
|
|
StartOnlinePolling();
|
||
|
|
IsLoading = false;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
await LoadData();
|
||
|
|
IsLoading = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task LoadData()
|
||
|
|
{
|
||
|
|
mutes = [];
|
||
|
|
blocks = [];
|
||
|
|
mutesMaxId = null;
|
||
|
|
blocksMaxId = null;
|
||
|
|
await Task.WhenAll(MoreMutes(), MoreBlocks());
|
||
|
|
domains = await AccountService.DomainBlocks();
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task MoreMutes()
|
||
|
|
{
|
||
|
|
var result = await AccountService.Mutes(mutesMaxId);
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var page = (AccountPage)result.Data;
|
||
|
|
mutes.AddRange(page.Accounts.Where(a => mutes.All(m => m.Id != a.Id)));
|
||
|
|
mutesMaxId = page.MaxId;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task MoreBlocks()
|
||
|
|
{
|
||
|
|
var result = await AccountService.Blocks(blocksMaxId);
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var page = (AccountPage)result.Data;
|
||
|
|
blocks.AddRange(page.Accounts.Where(a => blocks.All(b => b.Id != a.Id)));
|
||
|
|
blocksMaxId = page.MaxId;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task Undo(Account account, AccountAction action)
|
||
|
|
{
|
||
|
|
busy.Add(account.Id);
|
||
|
|
var result = await AccountService.Act(account.Id, action);
|
||
|
|
busy.Remove(account.Id);
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (action is AccountAction.Unmute)
|
||
|
|
mutes.RemoveAll(m => m.Id == account.Id);
|
||
|
|
else
|
||
|
|
blocks.RemoveAll(b => b.Id == account.Id);
|
||
|
|
}
|
||
|
|
|
||
|
|
// what a person types as a server (a URL, a handle's host, any case) as the bare host PrivaPub keys it by
|
||
|
|
static string Domain(string typed)
|
||
|
|
{
|
||
|
|
var text = typed?.Trim().TrimStart('@') ?? "";
|
||
|
|
if (Uri.TryCreate(text.Contains("://") ? text : "https://" + text, UriKind.Absolute, out var uri))
|
||
|
|
text = uri.Host;
|
||
|
|
return text.ToLowerInvariant();
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task BlockDomain()
|
||
|
|
{
|
||
|
|
var domain = Domain(draft.Domain);
|
||
|
|
if (domain.Length == 0 || domains.Contains(domain))
|
||
|
|
{
|
||
|
|
draft = new();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
busy.Add(domain);
|
||
|
|
var result = await AccountService.BlockDomain(domain);
|
||
|
|
busy.Remove(domain);
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
draft = new();
|
||
|
|
domains = [.. domains.Append(domain).Order()];
|
||
|
|
Toast.AddResponseSuccess(Localizer["{0} is blocked.", domain]);
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task UnblockDomain(string domain)
|
||
|
|
{
|
||
|
|
busy.Add(domain);
|
||
|
|
var result = await AccountService.UnblockDomain(domain);
|
||
|
|
busy.Remove(domain);
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
domains.Remove(domain);
|
||
|
|
}
|
||
|
|
}
|