WIP: local handoff implementation

Local work on the social.io handoff before merging the claude
worktree branch. Includes the full per-spec Sources/Core/Design
module (8 files), watchOS target under WatchApp/, Live Activity +
widget extension, entitlements, scheme, and asset catalog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-19 16:26:38 +02:00
parent 15af566353
commit 2fe6b8a6df
32 changed files with 3861 additions and 926 deletions
+53 -3
View File
@@ -3,9 +3,12 @@ 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 }
@@ -13,9 +16,12 @@ enum Mailbox: String, CaseIterable, Identifiable, Codable {
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"
}
}
@@ -23,9 +29,12 @@ enum Mailbox: String, CaseIterable, Identifiable, Codable {
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"
}
}
}
@@ -50,6 +59,7 @@ struct MailMessage: Identifiable, Hashable, Codable {
let sentAt: Date
let body: String
let isDraft: Bool
let attachments: [MailAttachment]
init(
id: UUID = UUID(),
@@ -58,7 +68,8 @@ struct MailMessage: Identifiable, Hashable, Codable {
recipients: [MailPerson],
sentAt: Date,
body: String,
isDraft: Bool = false
isDraft: Bool = false,
attachments: [MailAttachment] = []
) {
self.id = id
self.routeID = routeID
@@ -67,6 +78,19 @@ struct MailMessage: Identifiable, Hashable, Codable {
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
}
}
@@ -80,6 +104,9 @@ struct MailThread: Identifiable, Hashable, Codable {
var isUnread: Bool
var isStarred: Bool
var tags: [String]
var lane: Lane
var summary: [String]?
var isScreeningCandidate: Bool
init(
id: UUID = UUID(),
@@ -90,7 +117,10 @@ struct MailThread: Identifiable, Hashable, Codable {
messages: [MailMessage],
isUnread: Bool,
isStarred: Bool,
tags: [String] = []
tags: [String] = [],
lane: Lane = .people,
summary: [String]? = nil,
isScreeningCandidate: Bool = false
) {
self.id = id
self.routeID = routeID
@@ -101,6 +131,9 @@ struct MailThread: Identifiable, Hashable, Codable {
self.isUnread = isUnread
self.isStarred = isStarred
self.tags = tags
self.lane = lane
self.summary = summary
self.isScreeningCandidate = isScreeningCandidate
}
var latestMessage: MailMessage? {
@@ -114,10 +147,27 @@ struct MailThread: Identifiable, Hashable, Codable {
var lastUpdated: Date {
latestMessage?.sentAt ?? .distantPast
}
var messageCount: Int {
messages.count
}
var hasAttachments: Bool {
messages.contains { !$0.attachments.isEmpty }
}
}
struct ComposeDraft: Equatable {
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
}
}