krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.1.6: Hutch/App/HutchIntents.swift · raw

  1import AppIntents
  2import Foundation
  3
  4// MARK: - Open Hutch Intent
  5
  6enum HutchDestination: String, AppEnum {
  7    case home
  8    case work
  9    case builds
 10    case repositories
 11    case trackers
 12    case systemStatus
 13    case lookup
 14
 15    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Hutch Section")
 16
 17    static var caseDisplayRepresentations: [HutchDestination: DisplayRepresentation] = [
 18        .home: "Home",
 19        .work: "Work",
 20        .builds: "Builds",
 21        .repositories: "Repositories",
 22        .trackers: "Trackers",
 23        .systemStatus: "System Status",
 24        .lookup: "Look Up"
 25    ]
 26}
 27
 28struct OpenHutchIntent: AppIntent {
 29    static var title: LocalizedStringResource = "Open Hutch"
 30    static var description = IntentDescription("Opens Hutch to a specific section.")
 31    static var openAppWhenRun = true
 32
 33    @Parameter(title: "Section", default: .home)
 34    var destination: HutchDestination
 35
 36    @MainActor
 37    func perform() async throws -> some IntentResult {
 38        HutchIntentNavigator.shared.pendingDestination = destination
 39        return .result()
 40    }
 41}
 42
 43// MARK: - Check System Status Intent
 44
 45struct CheckSystemStatusIntent: AppIntent {
 46    static var title: LocalizedStringResource = "Check SourceHut Status"
 47    static var description = IntentDescription("Returns the current SourceHut system status.")
 48
 49    @MainActor
 50    func perform() async throws -> some IntentResult & ReturnsValue<String> {
 51        guard let snapshot = SystemStatusWidgetSnapshotStore.load() else {
 52            return .result(value: "System status is unavailable. Open Hutch to refresh.")
 53        }
 54
 55        if snapshot.hasDisruption {
 56            let disrupted = snapshot.services
 57                .filter { $0.requiresAttention }
 58                .map { "\($0.name): \($0.status)" }
 59                .joined(separator: ", ")
 60            return .result(value: "SourceHut disruption detected: \(disrupted)")
 61        }
 62
 63        return .result(value: "All SourceHut services operational.")
 64    }
 65}
 66
 67// MARK: - Check Builds Intent
 68
 69struct CheckBuildsIntent: AppIntent {
 70    static var title: LocalizedStringResource = "Check Hutch Builds"
 71    static var description = IntentDescription("Returns a summary of your recent build status.")
 72
 73    @MainActor
 74    func perform() async throws -> some IntentResult & ReturnsValue<String> {
 75        guard let snapshot = NeedsAttentionSnapshotStore.load() else {
 76            return .result(value: "Build status unavailable. Open Hutch to refresh.")
 77        }
 78
 79        var parts: [String] = []
 80
 81        if let failed = snapshot.failedBuilds {
 82            if failed > 0 {
 83                parts.append("\(failed) failed build\(failed == 1 ? "" : "s")")
 84            } else {
 85                parts.append("No failed builds")
 86            }
 87        }
 88
 89        if let unread = snapshot.unreadInboxThreads, unread > 0 {
 90            parts.append("\(unread) unread thread\(unread == 1 ? "" : "s")")
 91        }
 92
 93        if let assigned = snapshot.assignedOpenTickets, assigned > 0 {
 94            parts.append("\(assigned) assigned ticket\(assigned == 1 ? "" : "s")")
 95        }
 96
 97        if parts.isEmpty {
 98            return .result(value: "No recent data. Open Hutch to refresh.")
 99        }
100
101        return .result(value: parts.joined(separator: ". ") + ".")
102    }
103}
104
105// MARK: - Shortcuts Provider
106
107struct HutchShortcuts: AppShortcutsProvider {
108    static var appShortcuts: [AppShortcut] {
109        AppShortcut(
110            intent: OpenHutchIntent(),
111            phrases: [
112                "Open \(.applicationName)",
113                "Open \(.applicationName) \(\.$destination)",
114                "Show my \(.applicationName) \(\.$destination)",
115                "Go to \(\.$destination) in \(.applicationName)"
116            ],
117            shortTitle: "Open Hutch",
118            systemImageName: "house"
119        )
120
121        AppShortcut(
122            intent: CheckSystemStatusIntent(),
123            phrases: [
124                "Check \(.applicationName) status",
125                "Is SourceHut up in \(.applicationName)",
126                "SourceHut status in \(.applicationName)"
127            ],
128            shortTitle: "Check SourceHut Status",
129            systemImageName: "server.rack"
130        )
131
132        AppShortcut(
133            intent: CheckBuildsIntent(),
134            phrases: [
135                "Check my \(.applicationName) builds",
136                "How are my builds in \(.applicationName)",
137                "Build status in \(.applicationName)"
138            ],
139            shortTitle: "Check Builds",
140            systemImageName: "hammer"
141        )
142    }
143}
144
145// MARK: - Intent Navigator
146
147@MainActor
148@Observable
149final class HutchIntentNavigator {
150    static let shared = HutchIntentNavigator()
151    var pendingDestination: HutchDestination?
152
153    private init() {
154        /* Singleton; external code uses `shared`. */
155    }
156}