2026-04-17 20:46:27 +02:00
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 ,
2026-04-19 16:22:10 +02:00
tags : [ " Design " , " Launch " ] ,
lane : . people ,
summary : [
" Tanya tightened the onboarding copy and softened the empty-state tone. " ,
" Phil signed off with a note to keep playful language on iPhone. " ,
" Desktop first-run copy still needs a lighter pass before handoff. "
]
2026-04-17 20:46:27 +02:00
) ,
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 ,
2026-04-19 16:22:10 +02:00
tags : [ " System " ] ,
lane : . feed
2026-04-17 20:46:27 +02:00
) ,
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 ,
2026-04-19 16:22:10 +02:00
tags : [ " External " ] ,
lane : . paper ,
summary : [
" Mina wants a concise product update before Friday. " ,
" She's specifically asking for screenshots of the new mail experience. " ,
" Interested in how we differentiate from commodity inboxes. "
]
2026-04-17 20:46:27 +02:00
) ,
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 ,
2026-04-19 16:22:10 +02:00
tags : [ " Search " ] ,
lane : . people
2026-04-17 20:46:27 +02:00
) ,
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 ,
2026-04-19 16:22:10 +02:00
tags : [ " Draft " ] ,
lane : . people
2026-04-17 20:46:27 +02:00
) ,
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 ,
2026-04-19 16:22:10 +02:00
tags : [ " Product " ] ,
lane : . paper ,
summary : [
" Three roadmap themes: faster triage, identity-rich threads, calmer notifications. " ,
" Raw notes were cleaned up and archived by Nora. " ,
" Owner split still needs to be reconciled with eng capacity. "
]
2026-04-17 20:46:27 +02:00
)
]
}
}