Files
decePubClient/Models/FeedPost.cs
T
thepraandClaude Opus 5.5 620cbef219
Build / Build (push) Successful in 26s
Deploy / decepub.thepra.dev (push) Successful in 1m21s
CDN-fronted servers grouped per CDN, and servers through time
A post whose server is behind a CDN has no place of its own: the globe
groups such posts per CDN in labels on a ring above the Arctic, and
hovering one draws its arc to the CDN's group and a ring where the
server was before the CDN. The card's CDN chip names the CDN, its
domain, how PrivaPub found it (published ranges, response headers or
network), the place before the CDN and the CDN's servers week by week
(/api/privapub/v1/cdns/:domain); the author's tooltip lists the server's
changes of place and CDN week by week (/instances/:host/history). Both
are read on the first hover and kept for the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-04 11:44:51 +02:00

114 lines
4.5 KiB
C#

using decePubClient.Models.Mastodon;
namespace decePubClient.Models;
/// <summary>
/// One item of a feed, whatever its source: a status (a boost shows its original, BoostedBy names the booster), a
/// notification, a direct conversation, or a located post from /clientapi/post/nearby (Located, with no Mastodon
/// actions: PrivaPub keeps those posts off the Mastodon API). Location is filled by InstanceService once the author's
/// server is known.
/// </summary>
public class FeedPost
{
/// <summary>Unique within a feed: the status, notification or conversation id.</summary>
public string Key { get; init; }
/// <summary>The id the feed pages by (max_id/min_id).</summary>
public string CursorId { get; init; }
/// <summary>The post shown; null for a notification without one (a follow).</summary>
public Status Status { get; set; }
public Account BoostedBy { get; init; }
public Notification Notification { get; init; }
public Conversation Conversation { get; init; }
/// <summary>A located post: within its range of the reader, never on the Mastodon API.</summary>
public bool Located { get; init; }
public double? DistanceKm { get; init; }
public double? RangeKm { get; init; }
public FeedLocation Location { get; set; }
/// <summary>The account the item is about: the post's author, or whoever caused the notification.</summary>
public Account Author => Status?.Account ?? Notification?.Account;
public string Host => Author?.Host;
public bool IsOwnedBy(string personaId) => personaId is not null && Status?.Account?.Id == personaId;
public static FeedPost Of(Status status) => status.Reblog is { } original
? new() { Key = status.Id, CursorId = status.Id, Status = original, BoostedBy = status.Account }
: new() { Key = status.Id, CursorId = status.Id, Status = status };
public static FeedPost Of(Notification notification) => new()
{
Key = notification.Id,
CursorId = notification.Id,
Status = notification.Status?.Reblog ?? notification.Status,
Notification = notification
};
public static FeedPost Of(Conversation conversation) => new()
{
Key = conversation.Id,
CursorId = conversation.LastStatus?.Id,
Status = conversation.LastStatus,
Conversation = conversation
};
}
/// <summary>Where a feed item is drawn on the globe, and how sure that is.</summary>
public enum LocationSource
{
/// <summary>The post's own place (Pixelfed): exact.</summary>
Place,
/// <summary>An event's venue: exact.</summary>
Event,
/// <summary>The author's server, to 0.1°.</summary>
Server,
/// <summary>The author's server, known only by country: a point in that country.</summary>
Country,
/// <summary>A located post, within its range of the reader: drawn at the reader.</summary>
NearYou,
/// <summary>The author's server is behind a CDN, whose address says nothing of its place: drawn with the CDN's group.</summary>
Cdn
}
/// <summary>
/// Where a feed item is drawn. For a CDN-fronted server (Source Cdn) Latitude/Longitude mean nothing: the globe groups it
/// under CdnKey, and Before is where the server was before the CDN, when PrivaPub saw it.
/// </summary>
public sealed record FeedLocation(double Latitude, double Longitude, LocationSource Source, string Label, double PrecisionKm, string CdnKey = default,
InstancePlace Before = default)
{
public bool HasPoint => Source is not LocationSource.Cdn;
}
/// <summary>What the client keeps about a server (from /api/privapub/v1/instances), cached for a day.</summary>
public class ServerInfo
{
public string Host { get; set; }
public string Software { get; set; }
public string Version { get; set; }
public string NodeName { get; set; }
public bool? OpenRegistrations { get; set; }
public long? Users { get; set; }
public long? ActiveMonth { get; set; }
public long? LocalPosts { get; set; }
public InstanceLocation Geo { get; set; }
public int ConsecutiveFailures { get; set; }
public DateTime? LastSuccessAt { get; set; }
public DateTime? LastFailureAt { get; set; }
public DateTime? DescribedAt { get; set; }
public string DescriptionError { get; set; }
public DateTime FetchedAt { get; set; }
/// <summary>True when PrivaPub did not know the server: nothing to show, asked again later.</summary>
public bool Unknown { get; set; }
}
/// <summary>A page of a feed and the cursors around it.</summary>
public class FeedPage
{
public List<FeedPost> Posts { get; set; } = [];
/// <summary>Older items start below this id; null at the end of the feed.</summary>
public string MaxId { get; set; }
/// <summary>Newer items start above this id.</summary>
public string MinId { get; set; }
}