import Foundation import Observation /// `build list `. @Observable @MainActor final class BuildListViewModel { private(set) var state: LoadState<[Build]> = .loading /// The jobs a trigger can name. Empty when the repo has no CI /// config — the web hides its trigger form in that case. private(set) var jobs: [CIJob] = [] private(set) var actionError: String? private(set) var working = false private let client: GitbayClient let repoPath: String init(client: GitbayClient, repoPath: String) { self.client = client self.repoPath = repoPath } func load() async { do { let builds = try await client.readList(["build", "list", repoPath], of: Build.self) state = builds.isEmpty ? .empty("No builds. Builds run when a push touches a repo with a .gitbay/ job file.") : .loaded(builds.sorted { $0.number > $1.number }) } catch { state = .from(error) } } /// `build jobs ` — what the picker offers. A repo /// without a CI config answers "not found"; that means no jobs, not /// a failure worth showing. func loadJobs() async { jobs = (try? await client.readList(["build", "jobs", repoPath], of: CIJob.self)) ?? [] } /// `build trigger ` — queue a job now. func trigger(job: String) async { working = true actionError = nil defer { working = false } do { try await client.run(["build", "trigger", repoPath, job]) await load() } catch let error as GitbayError { actionError = error.userFacingMessage } catch { actionError = GitbayError.transport(error).userFacingMessage } } }