gitbay/Issues/LabelListViewModel.swift
61 lines · 1902 bytes
1import Foundation
2import Observation
3
4/// `label list <repo>`, plus the two commands that change the set:
5/// `label set` creates one or recolours it, `label remove` takes it off
6/// the repository and off every issue that carried it.
7@Observable
8@MainActor
9final class LabelListViewModel {
10
11 private(set) var state: LoadState<[IssueLabel]> = .loading
12 private(set) var actionError: String?
13 private(set) var working = false
14
15 private let client: GitbayClient
16 let repoPath: String
17
18 init(client: GitbayClient, repoPath: String) {
19 self.client = client
20 self.repoPath = repoPath
21 }
22
23 func load() async {
24 state = .loading
25 do {
26 let labels = try await client.readList(
27 ["label", "list", repoPath], of: IssueLabel.self
28 )
29 state = labels.isEmpty ? .empty("No labels yet.") : .loaded(labels)
30 } catch {
31 state = .from(error)
32 }
33 }
34
35 /// Create a label or set its colour. A nil colour omits `--color`
36 /// entirely, which the server reads as "make sure it exists" and
37 /// leaves any colour it already had; an empty string clears it.
38 func setLabel(_ name: String, color: String?) async {
39 var argv = ["label", "set", repoPath, name]
40 if let color { argv.append(contentsOf: ["--color", color]) }
41 await perform(argv)
42 }
43
44 func remove(_ name: String) async {
45 await perform(["label", "remove", repoPath, name])
46 }
47
48 private func perform(_ argv: [String]) async {
49 working = true
50 actionError = nil
51 defer { working = false }
52 do {
53 try await client.run(argv)
54 await load()
55 } catch let error as GitbayError {
56 actionError = error.userFacingMessage
57 } catch {
58 actionError = GitbayError.transport(error).userFacingMessage
59 }
60 }
61}