Files
2026-10-06 19:34:32 +03:00

68 lines
2.2 KiB
Swift

import Foundation
// What the iPhone shows for one service Mochi (GitHub, Stripe, Vercel, Resend,
// Cal.com, n8n, Notion): its state dot, why it has that color, and the latest
// items the Mac fetched. The Mac builds it from the data its pollers already
// keep (ServicePublisher) and writes it to the private iCloud zone, encrypted;
// the iPhone only reads it. No API key ever leaves the Mac.
enum ServiceTone: String, Codable, Sendable {
case ok, warning, error, info, idle
}
struct ServiceItem: Codable, Equatable, Sendable {
var title: String
var detail: String = ""
var tone: ServiceTone = .idle
var date: Date? = nil
var url: String? = nil
}
struct ServiceSection: Codable, Equatable, Sendable {
var title: String
var items: [ServiceItem]
}
struct ServiceSnapshot: Codable, Equatable, Sendable {
var pillId: String
var tone: ServiceTone
/// One line under the name ("Balance 1 240,00 EUR", "2 pull requests").
var headline: String
/// Why the dot has its color ("CI failing on coucou · main").
var reason: String
var sections: [ServiceSection]
var updatedAt: Date
static let recordType = "Service"
static func recordName(for pillId: String) -> String { "service-\(pillId)" }
static func pillId(fromRecordName name: String) -> String? {
name.hasPrefix("service-") ? String(name.dropFirst("service-".count)) : nil
}
/// Equal apart from the date: nothing to write again.
func sameContent(as other: ServiceSnapshot) -> Bool {
var a = self, b = other
a.updatedAt = .distantPast
b.updatedAt = .distantPast
return a == b
}
}
extension PillCatalog {
/// The service Mochi the iPhone shows, in this order.
static let phoneServices = [
"integration_github", "integration_stripe", "integration_vercel", "integration_resend",
"integration_calcom", "integration_n8n", "integration_notion",
]
/// Agent sessions (VS Code, Cursor, Codex…), as opposed to services and chat providers.
static func isSession(_ id: String) -> Bool {
switch definition(for: id)?.category {
case .service, .ai: false
default: true
}
}
}