import Foundation protocol MailServicing { func loadThreads() async throws -> [MailThread] func send(draft: ComposeDraft) async throws -> MailThread } struct MockMailService: MailServicing { private let me = MailPerson(name: "Phil Kunz", email: "phil@social.io") func loadThreads() async throws -> [MailThread] { try await Task.sleep(for: .milliseconds(150)) return seededThreads.sorted { $0.lastUpdated > $1.lastUpdated } } func send(draft: ComposeDraft) async throws -> MailThread { try await Task.sleep(for: .milliseconds(120)) let threadRouteID = "sent-\(UUID().uuidString.lowercased())" let messageRouteID = "\(threadRouteID)-message" let recipientNames = draft.to .split(separator: ",") .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } let recipients = recipientNames.map { raw in MailPerson( name: raw.components(separatedBy: "@").first?.capitalized ?? raw, email: raw ) } let message = MailMessage( routeID: messageRouteID, sender: me, recipients: recipients, sentAt: .now, body: draft.body ) return MailThread( routeID: threadRouteID, mailbox: .sent, subject: draft.subject.isEmpty ? "(No Subject)" : draft.subject, participants: recipients + [me], messages: [message], isUnread: false, isStarred: false, tags: ["Sent"] ) } private var seededThreads: [MailThread] { let alex = MailPerson(name: "Alex Rivera", email: "alex@social.io") let nora = MailPerson(name: "Nora Chen", email: "nora@social.io") let tanya = MailPerson(name: "Tanya Hall", email: "tanya@design.social.io") let ops = MailPerson(name: "Ops Bot", email: "ops@social.io") let investor = MailPerson(name: "Mina Park", email: "mina@northshore.vc") return [ MailThread( routeID: "launch-copy", mailbox: .inbox, subject: "Launch copy for the onboarding flow", participants: [tanya, me], messages: [ MailMessage( routeID: "launch-copy-1", sender: tanya, recipients: [me], sentAt: .now.addingTimeInterval(-3600 * 2), body: "I tightened the onboarding copy and added a warmer empty-state tone. If you're good with it, I can hand the strings to the app team today." ), MailMessage( routeID: "launch-copy-2", sender: me, recipients: [tanya], sentAt: .now.addingTimeInterval(-3600), body: "Looks strong. Let's keep the playful language on iPhone and simplify the desktop first-run copy a bit." ) ], isUnread: true, isStarred: true, tags: ["Design", "Launch"] ), MailThread( routeID: "daily-sync-status", mailbox: .inbox, subject: "Daily inbox sync status", participants: [ops, me], messages: [ MailMessage( routeID: "daily-sync-status-1", sender: ops, recipients: [me], sentAt: .now.addingTimeInterval(-3600 * 4), body: "Mock sync complete. 1,284 messages mirrored from staging. Attachment fetch remains disabled in the sandbox profile." ) ], isUnread: false, isStarred: false, tags: ["System"] ), MailThread( routeID: "investor-update", mailbox: .inbox, subject: "Investor update before next Friday", participants: [investor, me], messages: [ MailMessage( routeID: "investor-update-1", sender: investor, recipients: [me], sentAt: .now.addingTimeInterval(-3600 * 26), body: "Could you send a concise product update and a few screenshots of the new mail experience before Friday? Interested in how you are differentiating from commodity inboxes." ) ], isUnread: true, isStarred: false, tags: ["External"] ), MailThread( routeID: "search-ranking-polish", mailbox: .sent, subject: "Re: Search ranking polish", participants: [alex, me], messages: [ MailMessage( routeID: "search-ranking-polish-1", sender: alex, recipients: [me], sentAt: .now.addingTimeInterval(-3600 * 30), body: "The current search sort is useful, but I still feel too much recency over intent." ), MailMessage( routeID: "search-ranking-polish-2", sender: me, recipients: [alex], sentAt: .now.addingTimeInterval(-3600 * 28), body: "Agreed. I want us to bias toward active collaborators and threads with lightweight action language like review, approve, or ship." ) ], isUnread: false, isStarred: false, tags: ["Search"] ), MailThread( routeID: "welcome-to-socialio", mailbox: .drafts, subject: "Welcome to social.io mail", participants: [me, nora], messages: [ MailMessage( routeID: "welcome-to-socialio-1", sender: me, recipients: [nora], sentAt: .now.addingTimeInterval(-3600 * 6), body: "Thanks for joining the early design partner group. Here is a quick overview of our calm, collaborative take on email...", isDraft: true ) ], isUnread: false, isStarred: false, tags: ["Draft"] ), MailThread( routeID: "roadmap-notes", mailbox: .archive, subject: "Roadmap notes from product sync", participants: [nora, alex, me], messages: [ MailMessage( routeID: "roadmap-notes-1", sender: nora, recipients: [alex, me], sentAt: .now.addingTimeInterval(-3600 * 72), body: "Captured the big roadmap themes: faster triage, identity-rich threads, and calmer notifications. I archived the raw notes after cleanup." ) ], isUnread: false, isStarred: true, tags: ["Product"] ) ] } }