R4 the client catches up: follow requests, followers, downvotes, dismissing, filtering a post, quote policies, statistics in detail
Build / Build (push) Successful in 1m8s
Build / Build (push) Successful in 1m8s
Follow requests answered on the persona's own profile and from the Notifications tab; its followers and the accounts it follows listed there, a follower removed (asked first). Downvotes on threadiverse posts, after the public-vote notice. A notification dismissed from its line. "Filter this post" from a card's menu, and the Filters page showing the posts a filter holds. Who may quote the persona's own posts, and revoking a quote of one. Statistics: a server in detail with "describe again", the ledger's events, the server's own days. The composer deletes an upload taken out before posting. MediaType.Documents goes. 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
b731d8c8c0
commit
b8d7858acb
25 files changed
+1543
-61
No files matched your search
+44
-6
@@ -41,12 +41,36 @@
|
||||
<Composer GroupId="@(Filter.TimelineType is TimelineType.Groups ? Filter.GroupId : null)" OnPublished="Published" />
|
||||
</PhoneSheet>
|
||||
|
||||
@if (Filter.TimelineType is TimelineType.Notifications && filteredRequests > 0)
|
||||
@if (Filter.TimelineType is TimelineType.Notifications && (filteredRequests > 0 || followRequests > 0))
|
||||
{
|
||||
<a class="btn btn-sm self-center" href="notifications/filtered" data-filtered-requests>
|
||||
<i class="ion-md-notifications-off"></i>
|
||||
<span>@Localizer["{0} filtered, waiting for you", filteredRequests]</span>
|
||||
</a>
|
||||
<div class="flex flex-wrap justify-center gap-2">
|
||||
@if (followRequests > 0)
|
||||
{
|
||||
<DButton Size="DSize.Sm" OnClick="() => isFollowRequestsOpen = true" data-follow-requests-open>
|
||||
<i class="ion-md-person-add" aria-hidden="true"></i>
|
||||
<span>@Localizer["{0} follow requests waiting", followRequests]</span>
|
||||
</DButton>
|
||||
}
|
||||
@if (filteredRequests > 0)
|
||||
{
|
||||
<a class="btn btn-sm" href="notifications/filtered" data-filtered-requests>
|
||||
<i class="ion-md-notifications-off"></i>
|
||||
<span>@Localizer["{0} filtered, waiting for you", filteredRequests]</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (isFollowRequestsOpen)
|
||||
{
|
||||
<DModal Placement="DModalPlacement.ResponsiveBottom" IsOpen="true" IsOpenChanged="open => isFollowRequestsOpen = open" CloseText="@Localizer["Close"]"
|
||||
data-follow-requests-dialog>
|
||||
<Heading>@Localizer["Follow requests"]</Heading>
|
||||
<ChildContent>
|
||||
<div class="mt-3">
|
||||
<FollowRequestsPanel OnAnswered="FollowRequestAnswered" />
|
||||
</div>
|
||||
</ChildContent>
|
||||
</DModal>
|
||||
}
|
||||
|
||||
@if (pending.Count > 0)
|
||||
@@ -93,7 +117,7 @@
|
||||
|
||||
@* only the cards near the view are rendered: a feed of thousands stays as light as one of forty *@
|
||||
<Virtualize Items="posts" Context="post" ItemSize="320" OverscanCount="8">
|
||||
<Content @key="post.Key" Post="post" FilterIn="FilterIn" OnDeleted="Remove" OnReplied="Replied" OnTag="OpenTag" />
|
||||
<Content @key="post.Key" Post="post" FilterIn="FilterIn" OnDeleted="Remove" OnDismissed="Dismissed" OnReplied="Replied" OnTag="OpenTag" />
|
||||
</Virtualize>
|
||||
|
||||
<div id="@SentinelId" class="flex justify-center min-h-8 text-xs opacity-60">
|
||||
@@ -127,6 +151,7 @@
|
||||
[Inject] InstanceService InstanceService { get; set; }
|
||||
[Inject] RequestStats RequestStats { get; set; }
|
||||
[Inject] FeedMemory Memory { get; set; }
|
||||
[Inject] AccountService AccountService { get; set; }
|
||||
|
||||
FeedFilter Filter { get; set; } = new();
|
||||
List<FeedPost> posts = [];
|
||||
@@ -136,6 +161,9 @@
|
||||
int unreadNotifications;
|
||||
// the requests the persona's notification policy filtered, shown on the Notifications tab
|
||||
int filteredRequests;
|
||||
// the accounts waiting for the persona to accept their follow (the first page), shown on the Notifications tab
|
||||
int followRequests;
|
||||
bool isFollowRequestsOpen;
|
||||
int unreadConversations;
|
||||
string maxId;
|
||||
string minId;
|
||||
@@ -431,8 +459,11 @@
|
||||
await TimelineService.ReadNotificationsUpTo(newest, cancellationToken);
|
||||
unreadNotifications = 0;
|
||||
filteredRequests = (await TimelineService.NotificationPolicy(cancellationToken))?.Summary?.PendingRequestsCount ?? 0;
|
||||
followRequests = (await AccountService.FollowRequests(null, cancellationToken)).Data is AccountPage requests ? requests.Accounts.Count : 0;
|
||||
}
|
||||
|
||||
void FollowRequestAnswered(bool accepted) => followRequests = Math.Max(0, followRequests - 1);
|
||||
|
||||
/// <summary>The stream that tells this feed's new posts, or null for a feed PrivaPub does not stream.</summary>
|
||||
static StreamSubscription StreamOf(FeedFilter filter) => filter.TimelineType switch
|
||||
{
|
||||
@@ -568,6 +599,13 @@
|
||||
posts.RemoveAll(p => p.Key == post.Key || p.Status?.Id == post.Status?.Id);
|
||||
}
|
||||
|
||||
// a dismissed notification is that item alone: others about the same post stay
|
||||
void Dismissed(FeedPost post)
|
||||
{
|
||||
posts.RemoveAll(p => p.Key == post.Key);
|
||||
pending.RemoveAll(p => p.Key == post.Key);
|
||||
}
|
||||
|
||||
IEnumerable<NerdRow> FeedStats()
|
||||
{
|
||||
yield return NerdRow.Of(Localizer["items"], posts.Count);
|
||||
|
||||
Reference in new issue
Block a user