Files
brov-macbook/NotchBuddy/Sources/CoucouKit/ServiceDetail.swift
T
2026-10-06 19:34:32 +03:00

82 lines
2.7 KiB
Swift

import Foundation
// A service seen up close, for the iPhone: figures, lists and what can be
// done from there. When a service's screen opens, the iPhone asks the Mac for
// it (a `ServiceAction` record of kind "refresh"); the Mac reads the service's
// API with the key in its Keychain and writes a `ServiceDetail` record,
// encrypted. An action (redeploy, re-run, merge…) is the same kind of record:
// the Mac runs it only if the iPhone asked for one it offered on that item,
// then writes the detail again with the result. No key ever leaves the Mac.
struct ServiceActionDef: Codable, Equatable, Sendable, Hashable {
/// What the Mac does, e.g. "vercel.redeploy", "github.merge".
var kind: String
var title: String
var symbol: String
/// The thing it acts on (deployment id, "owner/repo#12"…).
var target: String
var destructive: Bool = false
/// Asked before doing it, when it can't be undone.
var confirm: String? = nil
}
struct DetailStat: Codable, Equatable, Sendable {
var label: String
var value: String
var tone: ServiceTone = .idle
}
struct DetailItem: Codable, Equatable, Sendable {
var title: String
var subtitle: String = ""
var tone: ServiceTone = .idle
var date: Date? = nil
var url: String? = nil
/// A short tag on the right ("Production", "Draft", "Active"…).
var badge: String? = nil
var actions: [ServiceActionDef] = []
}
struct DetailSection: Codable, Equatable, Sendable {
var title: String
var items: [DetailItem]
var footer: String? = nil
}
struct ServiceActionResult: Codable, Equatable, Sendable {
var title: String
var ok: Bool
var message: String
var date: Date
}
struct ServiceDetail: Codable, Equatable, Sendable {
var pillId: String
var stats: [DetailStat] = []
var sections: [DetailSection] = []
var fetchedAt: Date
/// Why the API couldn't be read (no key, 401…).
var error: String? = nil
var lastAction: ServiceActionResult? = nil
static let recordType = "ServiceDetail"
static let requestType = "ServiceAction"
static let refreshKind = "refresh"
static func recordName(for pillId: String) -> String { "detail-\(pillId)" }
static func pillId(fromRecordName name: String) -> String? {
name.hasPrefix("detail-") ? String(name.dropFirst("detail-".count)) : nil
}
/// The action offered on one of the items, if the Mac offered it.
func offered(kind: String, target: String) -> ServiceActionDef? {
for section in sections {
for item in section.items {
if let action = item.actions.first(where: { $0.kind == kind && $0.target == target }) { return action }
}
}
return nil
}
}