Personas have walls their followers write on
Owner decision of 2026-10-06 (G-0009, FEP-400e). A persona's actor names its wall (…/graffiti, sm:wall) and, in
Smithereen's privacySettings, that its followers may write on it. A public post that is not a reply, sent with the wall
as its target by an account following the persona, is hosted: the persona is notified, it reaches the persona's and its
followers' homes, and the followers' servers and the author's are told with Add{Note}. The persona deletes it with
DELETE /api/v1/statuses/:id, which sends Remove{Note}; Smithereen deletes the post then. The wall lists the persona's
public posts that start a thread and what was written on it.
Elsewhere: an account's Add{Note} on its own wall shows the post to its followers here as on that wall (privapub.wall on
the status), and its Remove takes it away. An Add or Remove naming a collection of the account's own server PrivaPub
does not know has its document read again first, at most hourly: accounts kept before walls were read had none.
Checked live: Smithereen 40 checks (G-0009 closed); GoToSocial and Mastodon unchanged (121).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
e0682fa868
commit
c47b6e5533
26 files changed
+582
-36
No files matched your search
@@ -34,10 +34,13 @@ namespace PrivaPub.Api.Mastodon.Controllers
|
||||
readonly Domain.Media.IMediaService _media;
|
||||
|
||||
readonly IParticipations _participations;
|
||||
readonly Federation.Actors.IWalls _walls;
|
||||
|
||||
public StatusesController(IStatusService statuses, MastodonMapper mapper, DbEntities dbEntities, IMemoryCache cache, IQuoteService quotes,
|
||||
IOutboxPublisher outbox, PrivaPub.Infrastructure.Jobs.IJobQueue jobs, Domain.Media.IMediaService media, IParticipations participations = default)
|
||||
IOutboxPublisher outbox, PrivaPub.Infrastructure.Jobs.IJobQueue jobs, Domain.Media.IMediaService media, IParticipations participations = default,
|
||||
Federation.Actors.IWalls walls = default)
|
||||
{
|
||||
_walls = walls;
|
||||
_participations = participations;
|
||||
_jobs = jobs;
|
||||
_media = media;
|
||||
@@ -161,7 +164,7 @@ namespace PrivaPub.Api.Mastodon.Controllers
|
||||
{
|
||||
var before = await _dbEntities.Posts.Match(p => p.ID == id && p.GroupUserId == MyId && !p.IsFederatedCopy && !p.DeletedAt.HasValue).ExecuteFirstAsync(token);
|
||||
if (before == default)
|
||||
return NotFoundError();
|
||||
return await OffWall(id, token);
|
||||
var status = await _mapper.Status(before, MyId, token);
|
||||
var outcome = await _statuses.Remove(Me, id, token);
|
||||
if (!outcome.Ok)
|
||||
@@ -174,6 +177,20 @@ namespace PrivaPub.Api.Mastodon.Controllers
|
||||
return Json(status);
|
||||
}
|
||||
|
||||
// someone's post on the persona's wall (FEP-400e), taken off it: as on Smithereen, the owner of a wall deletes what is on it
|
||||
async Task<IActionResult> OffWall(string id, CancellationToken token)
|
||||
{
|
||||
var walled = _walls == default
|
||||
? default
|
||||
: await _dbEntities.Posts.Match(p => p.ID == id && p.WallOwnerAccountId == MyId && p.IsFederatedCopy && !p.DeletedAt.HasValue).ExecuteFirstAsync(token);
|
||||
if (walled == default)
|
||||
return NotFoundError();
|
||||
var status = await _mapper.Status(walled, MyId, token);
|
||||
if (!await _walls.Remove(Me, walled, token))
|
||||
return NotFoundError();
|
||||
return Json(status);
|
||||
}
|
||||
|
||||
[HttpGet("/api/v1/statuses/{id}/context"), Scope("read:statuses", requiresUser: false), Microsoft.AspNetCore.Authorization.AllowAnonymous]
|
||||
public async Task<IActionResult> Context(string id, CancellationToken token)
|
||||
{
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
public PrivaPubAudio Audio { get; set; }
|
||||
public PrivaPubEvent Event { get; set; }
|
||||
public PrivaPubPlace Place { get; set; }//a remote post's own place (Pixelfed), shown only
|
||||
public Account Wall { get; set; }//whose wall it was written on (FEP-400e), when not its author's
|
||||
public PrivaPubVotes Votes { get; set; } = new();
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace PrivaPub.Api.Mastodon.Mappers
|
||||
var mentionAccounts = mentionUris.Count == 0
|
||||
? new Dictionary<string, ForeignAvatar>()
|
||||
: (await _dbEntities.ForeignAvatars.Match(f => mentionUris.Contains(f.ActorURI)).ExecuteAsync(token)).ToDictionary(f => f.ActorURI);
|
||||
var accounts = await Accounts(all.Select(AuthorOf), token);
|
||||
var accounts = await Accounts(all.Select(AuthorOf).Concat(all.Select(p => p.WallOwnerAccountId).Where(w => w != default)), token);
|
||||
|
||||
var ids = all.Select(p => p.ID).ToList();
|
||||
var favourited = viewerId == default
|
||||
@@ -277,7 +277,7 @@ namespace PrivaPub.Api.Mastodon.Mappers
|
||||
Card = Card(post),
|
||||
Poll = Poll(post, viewerId, ownVotes.GetValueOrDefault(post.ID)),
|
||||
Emojis = Emojis(post.Emojis),
|
||||
Privapub = Extension(post),
|
||||
Privapub = Extension(post, accounts),
|
||||
EmojiReactions = reactions.GetValueOrDefault(post.ID) ?? new(),
|
||||
Pleroma = new PleromaStatus { EmojiReactions = reactions.GetValueOrDefault(post.ID) ?? new() },
|
||||
Mentions = post.Mentions.Where(m => !m.Silent).Select(m => Mention(m, mentionAccounts)).Where(m => m != default).ToList(),
|
||||
@@ -480,8 +480,9 @@ namespace PrivaPub.Api.Mastodon.Mappers
|
||||
List<CustomEmojiEntity> Emojis(IEnumerable<CustomEmoji> emojis) =>
|
||||
emojis?.Select(e => new CustomEmojiEntity { Shortcode = e.Shortcode, Url = Proxied(e.URL), StaticUrl = Proxied(e.URL) }).ToList() ?? new();
|
||||
|
||||
PrivaPubStatus Extension(PostEntity post) => new()
|
||||
PrivaPubStatus Extension(PostEntity post, Dictionary<string, Account> accounts) => new()
|
||||
{
|
||||
Wall = post.WallOwnerAccountId == default || post.WallOwnerAccountId == AuthorOf(post) ? default : accounts.GetValueOrDefault(post.WallOwnerAccountId),
|
||||
ObjectType = post.ObjectType,
|
||||
Locked = post.LockedAt.HasValue,
|
||||
Approval = post.Approval is ApprovalState.Pending or ApprovalState.Rejected ? post.Approval.ToString().ToLowerInvariant() : default,
|
||||
|
||||
Reference in new issue
Block a user