gitbay/Builds/BuildListViewModel.swift

v1.2.0
gitbay-ios/gitbay/Builds/BuildListViewModel.swift history · blame · raw

67 lines · 2195 bytes

 1import Foundation
 2import Observation
 3
 4/// `build list <repo>`, narrowed by `filter`.
 5@Observable
 6@MainActor
 7final class BuildListViewModel {
 8
 9    private(set) var state: LoadState<[Build]> = .loading
10    /// A change reloads. The sheet edits a copy and assigns once, so a
11    /// typed branch does not fire a request per keystroke.
12    var filter = BuildFilter() {
13        didSet {
14            guard filter != oldValue else { return }
15            reloadTask?.cancel()
16            reloadTask = Task { await load() }
17        }
18    }
19    private var reloadTask: Task<Void, Never>?
20    /// The jobs a trigger can name. Empty when the repo has no CI
21    /// config — the web hides its trigger form in that case.
22    private(set) var jobs: [CIJob] = []
23    private(set) var actionError: String?
24    private(set) var working = false
25
26    private let client: GitbayClient
27    let repoPath: String
28
29    init(client: GitbayClient, repoPath: String) {
30        self.client = client
31        self.repoPath = repoPath
32    }
33
34    func load() async {
35        do {
36            let builds = try await client.readList(
37                ["build", "list", repoPath] + filter.flags(), of: Build.self)
38            state = builds.isEmpty
39                ? .empty(filter.emptyMessage)
40                : .loaded(builds.sorted { $0.number > $1.number })
41        } catch {
42            state = .from(error)
43        }
44    }
45
46    /// `build jobs <owner/name>` — what the picker offers. A repo
47    /// without a CI config answers "not found"; that means no jobs, not
48    /// a failure worth showing.
49    func loadJobs() async {
50        jobs = (try? await client.readList(["build", "jobs", repoPath], of: CIJob.self)) ?? []
51    }
52
53    /// `build trigger <owner/name> <job>` — queue a job now.
54    func trigger(job: String) async {
55        working = true
56        actionError = nil
57        defer { working = false }
58        do {
59            try await client.run(["build", "trigger", repoPath, job])
60            await load()
61        } catch let error as GitbayError {
62            actionError = error.userFacingMessage
63        } catch {
64            actionError = GitbayError.transport(error).userFacingMessage
65        }
66    }
67}