R4 the client catches up: follow requests, followers, downvotes, dismissing, filtering a post, quote policies, statistics in detail
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:
thepraandClaude Opus 5.5 committed 2026-10-17 04:02:20 +02:00
1 parent b731d8c8c0
commit b8d7858acb
25 files changed
+1543 -61

No files matched your search

+39 -2
View File
@@ -21,12 +21,16 @@ namespace decePubClient.Services
Bookmark,
Unbookmark,
Pin,
Unpin
Unpin,
/// <summary>PrivaPub's own, for the threadiverse: a downvote replaces the persona's favourite (its upvote).</summary>
Downvote,
Undownvote
}
/// <summary>
/// Writing as the current persona: publishing (through the Mastodon API, or /clientapi for located and group posts),
/// favourites, boosts, bookmarks, deleting, threads, and muting or blocking an author. Results are WebResults whose
/// favourites, boosts, bookmarks, downvotes, deleting, threads, who may quote the persona's posts (and revoking a quote
/// of one), and muting or blocking an author. Results are WebResults whose
/// Data is the updated Status (a FeedPost when publishing); nothing throws.
/// </summary>
public class StatusService(IHttpService httpService, MediaService mediaService, ILoggingService logger, CoalescingStringLocalizer localizer)
@@ -228,6 +232,39 @@ namespace decePubClient.Services
return await StatusResult(response, cancellationToken);
}
/// <summary>Who may quote the persona's own public or unlisted post (PUT /statuses/:id/interaction_policy); its
/// followers are told of the change. Data is the updated Status.</summary>
public async Task<WebResult> SetQuotePolicy(string statusId, QuotePolicy policy, CancellationToken cancellationToken = default)
{
using var response = await httpService.Put(APIs.Mastodon.PUT_M_StatusInteractionPolicy(statusId),
new[] { new KeyValuePair<string, string>("quote_approval_policy", QuotePolicyValue(policy)) }, cancellationToken: cancellationToken);
return await StatusResult(response, cancellationToken);
}
/// <summary>The quote policy a status states (quote_approval.automatic): who may quote it without asking.</summary>
public static QuotePolicy QuotePolicyOf(Status status) => status.QuoteApproval?.Automatic switch
{
{ } automatic when automatic.Contains("public") => QuotePolicy.Public,
{ } automatic when automatic.Contains("followers") => QuotePolicy.Followers,
_ => QuotePolicy.Nobody
};
/// <summary>A quote policy as the Mastodon API names it.</summary>
public static string QuotePolicyValue(QuotePolicy policy) => policy switch
{
QuotePolicy.Followers => "followers",
QuotePolicy.Nobody => "nobody",
_ => "public"
};
/// <summary>Takes back the permission to quote the persona's own post from a post quoting it (FEP-044f); Data is the
/// quoting Status as it is now, its quote revoked.</summary>
public async Task<WebResult> RevokeQuote(string quotedId, string quotingId, CancellationToken cancellationToken = default)
{
using var response = await httpService.Post(APIs.Mastodon.POST_M_RevokeQuote(quotedId, quotingId), cancellationToken: cancellationToken);
return await StatusResult(response, cancellationToken);
}
/// <summary>Votes in a poll as the persona (each option by its index); Data is the updated Poll.</summary>
public async Task<WebResult> Vote(string pollId, IReadOnlyCollection<int> choices, CancellationToken cancellationToken = default)
{