149 lines
6.0 KiB
Plaintext
149 lines
6.0 KiB
Plaintext
@using Microsoft.AspNetCore.Components.Rendering
|
|||
|
|
@inherits LocalizableComponentBase
|
||
|
|
|
||
|
|
@* The persona's notification policy (/api/v2/notifications/policy): what notifications from people it does not follow,
|
||
|
|
people who do not follow it, new accounts, private mentions it did not ask for and accounts this server limits become.
|
||
|
|
Filtered ones wait in a request per account (/api/v1/notifications/requests), let in (with that account from then
|
||
|
|
on) or dismissed here. *@
|
||
|
|
@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-4" data-notification-policy>
|
||
|
|
<EditForm Model="policy" OnValidSubmit="Save" class="flex flex-col gap-2">
|
||
|
|
<label class="flex flex-wrap items-center justify-between gap-2">
|
||
|
|
<span class="text-sm">@Localizer["People you don't follow"]</span>
|
||
|
|
<DSelect TValue="string" Size="DSize.Sm" class="w-40" @bind-Value="policy.ForNotFollowing" data-policy="for_not_following">@Actions</DSelect>
|
||
|
|
</label>
|
||
|
|
<label class="flex flex-wrap items-center justify-between gap-2">
|
||
|
|
<span class="text-sm">@Localizer["People who don't follow you"]</span>
|
||
|
|
<DSelect TValue="string" Size="DSize.Sm" class="w-40" @bind-Value="policy.ForNotFollowers" data-policy="for_not_followers">@Actions</DSelect>
|
||
|
|
</label>
|
||
|
|
<label class="flex flex-wrap items-center justify-between gap-2">
|
||
|
|
<span class="text-sm">@Localizer["New accounts"]</span>
|
||
|
|
<DSelect TValue="string" Size="DSize.Sm" class="w-40" @bind-Value="policy.ForNewAccounts" data-policy="for_new_accounts">@Actions</DSelect>
|
||
|
|
</label>
|
||
|
|
<label class="flex flex-wrap items-center justify-between gap-2">
|
||
|
|
<span class="text-sm">@Localizer["Private mentions you did not ask for"]</span>
|
||
|
|
<DSelect TValue="string" Size="DSize.Sm" class="w-40" @bind-Value="policy.ForPrivateMentions" data-policy="for_private_mentions">@Actions</DSelect>
|
||
|
|
</label>
|
||
|
|
<label class="flex flex-wrap items-center justify-between gap-2">
|
||
|
|
<span class="text-sm">@Localizer["Accounts this server limits"]</span>
|
||
|
|
<DSelect TValue="string" Size="DSize.Sm" class="w-40" @bind-Value="policy.ForLimitedAccounts" data-policy="for_limited_accounts">@Actions</DSelect>
|
||
|
|
</label>
|
||
|
|
<p class="text-xs opacity-70">@Localizer["Filtered notifications wait below, one request per account; dropped ones are never kept."]</p>
|
||
|
|
<DButton Type="submit" Size="DSize.Sm" Color="DColor.Primary" class="self-start" Disabled="@(IsDefaultDisabled || isSaving)" data-policy-save>
|
||
|
|
@Localizer["Save"]
|
||
|
|
</DButton>
|
||
|
|
</EditForm>
|
||
|
|
|
||
|
|
<section class="flex flex-col gap-2" data-notification-requests>
|
||
|
|
<h3 class="font-semibold text-sm">@Localizer["Filtered notifications"]</h3>
|
||
|
|
@if (requests.Count == 0)
|
||
|
|
{
|
||
|
|
<p class="text-sm opacity-70">@Localizer["No filtered notifications."]</p>
|
||
|
|
}
|
||
|
|
@foreach (var request in requests)
|
||
|
|
{
|
||
|
|
<div class="flex flex-col gap-2 p-3 rounded-xl neo-raised neo-2xs min-w-0" data-request="@request.Account.Acct">
|
||
|
|
<div class="flex flex-wrap items-center gap-3 min-w-0">
|
||
|
|
<a class="flex items-center gap-3 min-w-0 flex-1" href="@($"account/{Uri.EscapeDataString(request.Account.Id)}")">
|
||
|
|
@* alt="": the name is next to it. *@
|
||
|
|
<img alt="" class="h-9 w-9 flex-none object-cover rounded-full" src="@(request.Account.Avatar ?? "imgs/icon-192.png")" />
|
||
|
|
<span class="flex flex-col min-w-0">
|
||
|
|
<b class="truncate">@CustomEmojis.Text(request.Account.Name, request.Account.Emojis)</b>
|
||
|
|
<span class="text-xs opacity-70 truncate">@("@" + request.Account.Acct)</span>
|
||
|
|
</span>
|
||
|
|
</a>
|
||
|
|
<DBadge Size="DSize.Sm">@Localizer["{0} notifications", request.NotificationsCount]</DBadge>
|
||
|
|
<DButton Size="DSize.Sm" Color="DColor.Primary" Disabled="@(IsDefaultDisabled || busy.Contains(request.Id))"
|
||
|
|
OnClick="@(() => Answer(request, true))" data-request-accept>@Localizer["Let in"]</DButton>
|
||
|
|
<DButton Size="DSize.Sm" Disabled="@(IsDefaultDisabled || busy.Contains(request.Id))"
|
||
|
|
OnClick="@(() => Answer(request, false))" data-request-dismiss>@Localizer["Dismiss"]</DButton>
|
||
|
|
</div>
|
||
|
|
@if (request.LastStatus?.Content is { Length: > 0 } content)
|
||
|
|
{
|
||
|
|
<div class="post-content text-sm opacity-80 break-words line-clamp-3">@((MarkupString)CustomEmojis.Html(content, request.LastStatus.Emojis))</div>
|
||
|
|
}
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
|
||
|
|
@code {
|
||
|
|
[Inject] TimelineService TimelineService { get; set; }
|
||
|
|
|
||
|
|
NotificationPolicy policy = new();
|
||
|
|
|
||
|
|
// what a notification can become, the same for every rule
|
||
|
|
void Actions(RenderTreeBuilder __builder)
|
||
|
|
{
|
||
|
|
<option value="accept">@Localizer["Accept"]</option>
|
||
|
|
<option value="filter">@Localizer["Filter"]</option>
|
||
|
|
<option value="drop">@Localizer["Drop"]</option>
|
||
|
|
}
|
||
|
|
List<NotificationRequest> requests = [];
|
||
|
|
// the requests whose answer is on its way
|
||
|
|
HashSet<string> busy = [];
|
||
|
|
bool isSaving;
|
||
|
|
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()
|
||
|
|
{
|
||
|
|
policy = await TimelineService.NotificationPolicy() ?? new();
|
||
|
|
requests = await TimelineService.NotificationRequests();
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task Save()
|
||
|
|
{
|
||
|
|
isSaving = true;
|
||
|
|
var result = await TimelineService.SetNotificationPolicy(policy);
|
||
|
|
isSaving = false;
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
policy = (NotificationPolicy)result.Data ?? policy;
|
||
|
|
Toast.AddResponseSuccess(Localizer["Notification policy saved."]);
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task Answer(NotificationRequest request, bool accept)
|
||
|
|
{
|
||
|
|
busy.Add(request.Id);
|
||
|
|
var result = await TimelineService.AnswerNotificationRequest(request.Id, accept);
|
||
|
|
busy.Remove(request.Id);
|
||
|
|
if (result.IsInvalid)
|
||
|
|
{
|
||
|
|
Toast.AddResponseError(result);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
requests.Remove(request);
|
||
|
|
}
|
||
|
|
}
|