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 } }