import Foundation enum Mailbox: String, CaseIterable, Identifiable, Codable { case inbox case starred case snoozed case screener case sent case drafts case archive case trash var id: String { rawValue } var title: String { switch self { case .inbox: "Inbox" case .starred: "Starred" case .snoozed: "Snoozed" case .screener: "The Screener" case .sent: "Sent" case .drafts: "Drafts" case .archive: "Archive" case .trash: "Trash" } } var systemImage: String { switch self { case .inbox: "tray.full" case .starred: "star" case .snoozed: "clock.badge" case .screener: "line.3.horizontal.decrease.circle" case .sent: "paperplane" case .drafts: "doc.text" case .archive: "archivebox" case .trash: "trash" } } } struct MailPerson: Identifiable, Hashable, Codable { let id: UUID let name: String let email: String init(id: UUID = UUID(), name: String, email: String) { self.id = id self.name = name self.email = email } } struct MailMessage: Identifiable, Hashable, Codable { let id: UUID let routeID: String let sender: MailPerson let recipients: [MailPerson] let sentAt: Date let body: String let isDraft: Bool let attachments: [MailAttachment] init( id: UUID = UUID(), routeID: String = UUID().uuidString.lowercased(), sender: MailPerson, recipients: [MailPerson], sentAt: Date, body: String, isDraft: Bool = false, attachments: [MailAttachment] = [] ) { self.id = id self.routeID = routeID self.sender = sender self.recipients = recipients self.sentAt = sentAt self.body = body self.isDraft = isDraft self.attachments = attachments } } struct MailAttachment: Identifiable, Hashable, Codable { let id: UUID let name: String let size: String init(id: UUID = UUID(), name: String, size: String) { self.id = id self.name = name self.size = size } } struct MailThread: Identifiable, Hashable, Codable { let id: UUID let routeID: String var mailbox: Mailbox var subject: String var participants: [MailPerson] var messages: [MailMessage] var isUnread: Bool var isStarred: Bool var tags: [String] var lane: Lane var summary: [String]? var isScreeningCandidate: Bool init( id: UUID = UUID(), routeID: String = UUID().uuidString.lowercased(), mailbox: Mailbox, subject: String, participants: [MailPerson], messages: [MailMessage], isUnread: Bool, isStarred: Bool, tags: [String] = [], lane: Lane = .people, summary: [String]? = nil, isScreeningCandidate: Bool = false ) { self.id = id self.routeID = routeID self.mailbox = mailbox self.subject = subject self.participants = participants self.messages = messages.sorted { $0.sentAt < $1.sentAt } self.isUnread = isUnread self.isStarred = isStarred self.tags = tags self.lane = lane self.summary = summary self.isScreeningCandidate = isScreeningCandidate } var latestMessage: MailMessage? { messages.max(by: { $0.sentAt < $1.sentAt }) } var previewText: String { latestMessage?.body.replacingOccurrences(of: "\n", with: " ") ?? "" } var lastUpdated: Date { latestMessage?.sentAt ?? .distantPast } var messageCount: Int { messages.count } var hasAttachments: Bool { messages.contains { !$0.attachments.isEmpty } } } struct ComposeDraft: Equatable, Codable { var to = "" var cc = "" var from = "" var subject = "" var body = "" var isEmpty: Bool { to.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && cc.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && subject.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && body.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } }