a54853d3d1
The previous rule kept migration posts visible via their :MIGRATION: badge. Dropping that exception — a body-less post of any kind is now hidden everywhere (timeline, profile, mergeOwnFeed). Consequence: migration announcements no longer surface in the timeline; the user accepted this trade-off for a simpler, single-rule filter.
96 lines
3.3 KiB
Swift
96 lines
3.3 KiB
Swift
import Foundation
|
|
|
|
public struct OrgSocialPost: Sendable, Equatable {
|
|
/// RFC 3339 timestamp, unique identifier for the post.
|
|
public var timestamp: String
|
|
/// Parsed date from `timestamp`.
|
|
public var date: Date
|
|
/// Body text of the post (Org Mode syntax preserved, comment and property lines stripped).
|
|
public var text: String
|
|
/// ISO 639-1 language code.
|
|
public var lang: String?
|
|
/// Space-separated tags parsed into individual strings.
|
|
public var tags: [String]
|
|
/// Client that created the post.
|
|
public let client: String?
|
|
/// URL#timestamp of the post being replied to.
|
|
public let replyTo: String?
|
|
/// URL#timestamp of the post being boosted/reshared.
|
|
public let include: String?
|
|
/// Poll close date.
|
|
public let pollEnd: Date?
|
|
/// Selected option when this post is a poll vote.
|
|
public let pollOption: String?
|
|
/// "Group Name relay-url" when posted to a group.
|
|
public let group: String?
|
|
/// Mood/reaction emoji.
|
|
public var mood: String?
|
|
/// "old-url new-url" for account migration posts.
|
|
public let migration: String?
|
|
/// `"public"` or `"mention"`. `nil` defaults to public.
|
|
public var visibility: String?
|
|
|
|
// Populated during timeline assembly, not by the parser.
|
|
public var authorNick: String?
|
|
public var authorURL: URL?
|
|
public var authorAvatar: URL?
|
|
public var feedURL: URL?
|
|
|
|
/// Stable unique ID combining feed URL and timestamp — safe for ForEach in merged timelines.
|
|
public var stableID: String {
|
|
"\(feedURL?.absoluteString ?? "unknown")#\(timestamp)"
|
|
}
|
|
|
|
/// Whether this post has nothing to render as a standalone timeline row —
|
|
/// i.e. its body is empty. Covers every protocol artefact (pure reaction,
|
|
/// pure boost, poll vote, migration announcement) plus the accidental
|
|
/// empty post. Quote-boosts, text posts and polls all carry a body and
|
|
/// are NOT flagged.
|
|
///
|
|
/// Used by timeline and profile views to hide these rows so the original
|
|
/// content they reference is shown only in its authoritative location.
|
|
public var isPureInteraction: Bool {
|
|
text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
|
}
|
|
|
|
public init(
|
|
timestamp: String,
|
|
date: Date,
|
|
text: String,
|
|
lang: String? = nil,
|
|
tags: [String] = [],
|
|
client: String? = nil,
|
|
replyTo: String? = nil,
|
|
include: String? = nil,
|
|
pollEnd: Date? = nil,
|
|
pollOption: String? = nil,
|
|
group: String? = nil,
|
|
mood: String? = nil,
|
|
migration: String? = nil,
|
|
visibility: String? = nil,
|
|
authorNick: String? = nil,
|
|
authorURL: URL? = nil,
|
|
authorAvatar: URL? = nil,
|
|
feedURL: URL? = nil
|
|
) {
|
|
self.timestamp = timestamp
|
|
self.date = date
|
|
self.text = text
|
|
self.lang = lang
|
|
self.tags = tags
|
|
self.client = client
|
|
self.replyTo = replyTo
|
|
self.include = include
|
|
self.pollEnd = pollEnd
|
|
self.pollOption = pollOption
|
|
self.group = group
|
|
self.mood = mood
|
|
self.migration = migration
|
|
self.visibility = visibility
|
|
self.authorNick = authorNick
|
|
self.authorURL = authorURL
|
|
self.authorAvatar = authorAvatar
|
|
self.feedURL = feedURL
|
|
}
|
|
}
|