P6: custom emoji, fuller remote profiles, and polls both ways
- Custom emoji (Emoji tags) on posts, display names, bios and profile fields, at most 64 per object, proxied, in Status.emojis and Account.emojis. - Remote profiles keep their header, profile fields, locked flag, published date, movedTo, indexable, memorial and image descriptions (a locked GoToSocial account no longer shows as open). - Polls: incoming Questions (Mastodon, Misskey, Pleroma, GoToSocial shapes) with counts, voters, end and closed; our own polls from the Mastodon API go out as Questions; votes in are counted once per voter and never become replies; personas vote on other servers' polls with one Note per choice; counts refresh with an Update at most every three minutes; a poll closes on time and tells its voters and its author. GET /api/v1/polls/:id and POST /api/v1/polls/:id/votes. - An Update without a newer `updated` only refreshes poll, video, audio and event details and leaves no revision. Checked live against GoToSocial: each side's poll reaches the other as a poll and each side's vote is counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
1 parent
81de470f64
commit
7be6749a23
33 files changed
+937
-28
No files matched your search
@@ -39,6 +39,7 @@ namespace PrivaPub.Domain.Statuses
|
||||
public double? Latitude { get; init; }
|
||||
public double? Longitude { get; init; }
|
||||
public double? RangeKm { get; init; }
|
||||
public PollDraft Poll { get; init; }
|
||||
}
|
||||
|
||||
public sealed record StatusOutcome(PostEntity Post, int Status = StatusCodes.Status200OK, string Error = default)
|
||||
@@ -69,10 +70,12 @@ namespace PrivaPub.Domain.Statuses
|
||||
readonly IFanout _fanout;
|
||||
readonly IMediaService _media;
|
||||
readonly IGroupDistributor _groups;
|
||||
readonly IPollService _polls;
|
||||
|
||||
public StatusService(DbEntities dbEntities, ILocalActorService localActors, IRemoteActorService remoteActors, IDeliveryService delivery,
|
||||
IContentRenderer content, IOutboxPublisher outbox, IFanout fanout, IMediaService media, IGroupDistributor groups)
|
||||
IContentRenderer content, IOutboxPublisher outbox, IFanout fanout, IMediaService media, IGroupDistributor groups, IPollService polls)
|
||||
{
|
||||
_polls = polls;
|
||||
_media = media;
|
||||
_groups = groups;
|
||||
_dbEntities = dbEntities;
|
||||
@@ -89,8 +92,10 @@ namespace PrivaPub.Domain.Statuses
|
||||
var media = await Media(author, draft.MediaIds, default, token);
|
||||
if (media == default)
|
||||
return StatusOutcome.Fail(StatusCodes.Status422UnprocessableEntity, "Validation failed: Media attachments are invalid");
|
||||
if (string.IsNullOrWhiteSpace(draft.Text) && media.Count == 0)
|
||||
if (string.IsNullOrWhiteSpace(draft.Text) && media.Count == 0 && draft.Poll == default)
|
||||
return StatusOutcome.Fail(StatusCodes.Status422UnprocessableEntity, "Validation failed: Text can't be blank");
|
||||
if (draft.Poll != default && _polls.Invalid(draft.Poll) is { } invalidPoll)
|
||||
return StatusOutcome.Fail(StatusCodes.Status422UnprocessableEntity, invalidPoll);
|
||||
|
||||
LocalActor group = default;
|
||||
if (!string.IsNullOrEmpty(draft.GroupId))
|
||||
@@ -157,7 +162,8 @@ namespace PrivaPub.Domain.Statuses
|
||||
InReplyToURI = parent?.ObjectURI ?? (IsRemoteUri(draft.InReplyTo) ? draft.InReplyTo : default),
|
||||
InReplyToAccountId = parent?.AuthorAccountId ?? parent?.GroupUserId,
|
||||
IsLocalOnly = isLocalOnly,
|
||||
ActorURI = author.Uri
|
||||
ActorURI = author.Uri,
|
||||
Poll = draft.Poll == default ? default : _polls.Create(draft.Poll)
|
||||
};
|
||||
post.ID = (string)post.GenerateNewID();
|
||||
post.ObjectURI = author.PostUri(post.ID);
|
||||
@@ -200,6 +206,7 @@ namespace PrivaPub.Domain.Statuses
|
||||
if (parent != default)
|
||||
await DB.Default.Update<PostEntity>().MatchID(parent.ID).Modify(b => b.Inc(p => p.RepliesCount, 1)).ExecuteAsync(token);
|
||||
await _fanout.Distribute(post, token);
|
||||
await _polls.Scheduled(post, token);
|
||||
if (create != default)
|
||||
await _outbox.Publish(author, post, create, token);
|
||||
if (create != default && group is { IsCircle: false } && visibility is PostVisibility.Public or PostVisibility.Unlisted)
|
||||
|
||||
Reference in new issue
Block a user