Pixelfed places: a remote post's own place, shown and never federated again

A Note's `location` that is a Place with coordinates (Pixelfed sends
them as strings) is kept in the new Post.Place and shown as
Status.privapub.place {name, latitude, longitude, country}; an edit
replaces it. An Event's location stays its own `places`, and nothing
renders a place back out. Closes the INTEROP P2 Pixelfed location gap
and the ROADMAP long-tail item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-04 10:07:06 +02:00
1 parent d9fb5c582f
commit 31d1d21d1e
13 files changed
+214 -14

No files matched your search

+3 -1
View File
@@ -1,4 +1,4 @@
namespace PrivaPub.Api.Mastodon.Entities
namespace PrivaPub.Api.Mastodon.Entities
{
public class Account
{
@@ -73,6 +73,7 @@ namespace PrivaPub.Api.Mastodon.Entities
public PrivaPubVideo Video { get; set; }
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 PrivaPubVotes Votes { get; set; } = new();
}
@@ -151,6 +152,7 @@ namespace PrivaPub.Api.Mastodon.Entities
{
public string Name { get; set; }
public string Address { get; set; }
public string Country { get; set; }//a post's own place only
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public string Url { get; set; }
@@ -1,4 +1,4 @@
using MongoDB.Entities;
using MongoDB.Entities;
using PrivaPub.Api.Mastodon.Entities;
using PrivaPub.Api.Mastodon.Infrastructure;
@@ -457,6 +457,10 @@ namespace PrivaPub.Api.Mastodon.Mappers
Status = post.Event.Status,
Category = post.Event.Category
},
Place = post.Place == default ? default : new PrivaPubPlace
{
Name = post.Place.Name, Latitude = post.Place.Latitude, Longitude = post.Place.Longitude, Country = post.Place.Country
},
Votes = new PrivaPubVotes { Up = post.FavouritesCount, Down = post.DownvotesCount }
};
+2 -1
View File
@@ -1,4 +1,4 @@
using MongoDB.Entities;
using MongoDB.Entities;
using PrivaPub.Domain.Statuses;
using PrivaPub.Federation.Actors;
@@ -33,6 +33,7 @@ namespace PrivaPub.Federation.Inbox
post.Video = note.Video ?? post.Video;
post.Audio = note.Audio ?? post.Audio;
post.Event = note.Event ?? post.Event;
post.Place = note.Place;
if (!IsEdit(note, post))
{
await DB.Default.SaveAsync(post, token);
+2 -1
View File
@@ -1,4 +1,4 @@
using MongoDB.Driver;
using MongoDB.Driver;
using MongoDB.Entities;
using PrivaPub.Infrastructure.Http;
@@ -90,6 +90,7 @@ namespace PrivaPub.Federation.Inbox
Video = note.Video,
Audio = note.Audio,
Event = note.Event,
Place = note.Place,
HasContentWarning = note.Sensitive,
Text = note.ContentHtml,
ContentHtml = note.ContentHtml,
+4 -2
View File
@@ -1,4 +1,4 @@
using PrivaPub.Models.Post;
using PrivaPub.Models.Post;
using System.Globalization;
using System.Text;
@@ -44,6 +44,7 @@ namespace PrivaPub.Federation.Objects
public VideoDetails Video { get; init; }
public AudioDetails Audio { get; init; }
public EventDetails Event { get; init; }
public PostPlace Place { get; init; }
}
public static class NoteParser
@@ -110,7 +111,8 @@ namespace PrivaPub.Federation.Objects
Link = ObjectShapes.Link(note),
Video = ObjectShapes.Video(note),
Audio = ObjectShapes.Audio(note),
Event = ObjectShapes.Event(note)
Event = ObjectShapes.Event(note),
Place = ObjectShapes.NotePlace(note)
};
}
+26 -1
View File
@@ -1,4 +1,4 @@
using PrivaPub.Models.Post;
using PrivaPub.Models.Post;
using System.Globalization;
using System.Net;
@@ -293,6 +293,31 @@ namespace PrivaPub.Federation.Objects
};
}
//a post's own place, as Pixelfed sends it: a Place with coordinates (numbers, or numbers as strings); an Event's
//places are its own (Event)
public static PostPlace NotePlace(JsonObject note)
{
if (Value(note, "type") == "Event")
return default;
var place = Objects(note["location"]).FirstOrDefault(l => Value(l, "type") == "Place");
var latitude = Coordinate(place, "latitude");
var longitude = Coordinate(place, "longitude");
if (latitude is not (>= -90 and <= 90) || longitude is not (>= -180 and <= 180))
return default;
return new PostPlace
{
Name = Text(Value(place, "name"), 200),
Latitude = Math.Round(latitude.Value, 4),
Longitude = Math.Round(longitude.Value, 4),
Country = Text(Value(place, "country"), 100)
};
}
static double? Coordinate(JsonObject node, string property) =>
Double(node, property) ?? (double.TryParse(Value(node, property), NumberStyles.Float, CultureInfo.InvariantCulture, out var number) && double.IsFinite(number)
? number
: (double?)null);
static EventPlace Place(JsonObject location)
{
var type = Value(location, "type");
+3 -1
View File
@@ -1,4 +1,4 @@
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Entities;
namespace PrivaPub.Models.Post
@@ -39,6 +39,8 @@ namespace PrivaPub.Models.Post
[BsonIgnoreIfNull]
public EventDetails Event { get; set; }
[BsonIgnoreIfNull]
public PostPlace Place { get; set; }
[BsonIgnoreIfNull]
public PostPoll Poll { get; set; }
public List<float> Location { get; set; } = new();
public float RangeKm { get; set; } = 5.0f;
+10 -1
View File
@@ -1,4 +1,4 @@
namespace PrivaPub.Models.Post
namespace PrivaPub.Models.Post
{
public class CustomEmoji
{
@@ -104,6 +104,15 @@ namespace PrivaPub.Models.Post
public string Category { get; set; }
}
//a remote post's own place (Pixelfed's location): shown, never federated again
public class PostPlace
{
public string Name { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string Country { get; set; }
}
public class EventPlace
{
public string Name { get; set; }