import Foundation /// A `Sendable` snapshot of a conversation between a buyer and a shop, as one of them sees it. public struct Conversation: Sendable, Equatable { // MARK: Properties /// The conversation's identifier. public let id: UUID /// The account buying. public let buyer: User /// The shop selling. public let shop: Shop /// The listing the conversation is about, or `nil` when it is about the shop at large. public let listing: Listing? /// Which side of the conversation the viewer is on. public let side: Side /// What was said, offered, and stamped, oldest first. public let entries: [Entry] /// The number of entries from the other side the viewer has not seen yet. public let unreadCount: Int /// The number of the sale agreed in the conversation, or `nil` when none was. public let saleNumber: Int? // MARK: Initializers public init( id: UUID, buyer: User, shop: Shop, listing: Listing?, side: Side, entries: [Entry], unreadCount: Int, saleNumber: Int? ) { self.id = id self.buyer = buyer self.shop = shop self.listing = listing self.side = side self.entries = entries self.unreadCount = unreadCount self.saleNumber = saleNumber } } // MARK: - Structs public extension Conversation { /// One thing in a conversation: something said, an offer made, or an event stamped. struct Entry: Sendable, Equatable { // MARK: Properties /// What the entry is. public let kind: Kind /// Who wrote the message, made the offer, or caused the event. public let sender: User /// The message, the offer's note, or the event in a sentence. public let body: String /// The offer made, for an offer entry. public let offer: Offer? /// The number of the sale an event opened, or `nil`. public let saleNumber: Int? /// When the entry was made. public let createdAt: Date // MARK: Initializers public init( kind: Kind, sender: User, body: String, offer: Offer? = nil, saleNumber: Int? = nil, createdAt: Date ) { self.kind = kind self.sender = sender self.body = body self.offer = offer self.saleNumber = saleNumber self.createdAt = createdAt } // MARK: Enumerations /// What a conversation entry is. public enum Kind: String, Sendable { /// Something a party wrote. case text /// An offer, with its optional note. case offer /// Something that happened: an offer answered, a sale agreed, a step of the handshake taken. case event } } } // MARK: - Enumerations public extension Conversation { /// A side of a conversation. enum Side: String, Sendable { case buying case selling } } // MARK: - Extensions public extension Conversation { // MARK: Computed /// The latest entry, or `nil` in a conversation nothing was said in yet. var latest: Entry? { entries.last } /// The offer still waiting for an answer, or `nil` when there is none. var openOffer: Offer? { entries .compactMap(\.offer) .last { $0.status == .open } } }