a native ios client for gitbay

client ios swift

https://gitbay.org

gitbay/Views/Builds/BuildListView.swift

91 lines · 2981 bytes

 1import SwiftUI
 2
 3struct BuildListView: View {
 4
 5    @State private var model: BuildListViewModel
 6
 7    init(client: GitbayClient, repo: String) {
 8        _model = State(initialValue: BuildListViewModel(client: client, repoPath: repo))
 9    }
10
11    var body: some View {
12        List {
13            if let error = model.actionError {
14                Section {
15                    GBNotice(error, .gbWarn)
16                }
17            }
18            ForEach(model.state.value ?? []) { build in
19                NavigationLink(value: BuildRoute.detail(repo: model.repoPath, number: build.number)) {
20                    BuildRow(build: build)
21                }
22            }
23        }
24        .overlay { LoadStateOverlay(state: model.state) }
25        .navigationTitle("Builds")
26        .navigationBarTitleDisplayMode(.inline)
27        .toolbar {
28            ToolbarItem(placement: .topBarTrailing) {
29                // A menu of jobs, like the web's picker  not a name to
30                // type. Nothing to trigger without a job file.
31                Menu {
32                    ForEach(model.jobs) { job in
33                        Button("\(job.name)\(job.trigger)") {
34                            Task { await model.trigger(job: job.name) }
35                        }
36                    }
37                } label: {
38                    Image(systemName: "play.circle")
39                }
40                .disabled(model.working || model.jobs.isEmpty)
41                .accessibilityIdentifier("build-trigger-button")
42            }
43        }
44        .task {
45            await model.load()
46            await model.loadJobs()
47        }
48        .refreshable {
49            await model.load()
50            await model.loadJobs()
51        }
52    }
53}
54
55private struct BuildRow: View {
56    let build: Build
57
58    var body: some View {
59        HStack(spacing: 10) {
60            Image(systemName: build.statusIcon)
61                .foregroundStyle(build.statusColor)
62            VStack(alignment: .leading, spacing: 2) {
63                Text("#\(build.number) \(build.job)")
64                    .font(.gbSans(.subheadline).weight(.medium))
65                HStack(spacing: 6) {
66                    Text(build.ref)
67                    Text(build.shortSHA)
68                        .font(.gbMono(.caption))
69                }
70                .font(.gbSans(.caption))
71                .foregroundStyle(.secondary)
72            }
73            Spacer()
74            VStack(alignment: .trailing, spacing: 2) {
75                Text(build.status)
76                    .font(.gbSans(.caption).weight(.medium))
77                    .foregroundStyle(build.statusColor)
78                Text(build.createdAt, format: .relative(presentation: .named))
79                    .font(.gbSans(.caption))
80                    .foregroundStyle(.secondary)
81            }
82        }
83        .padding(.vertical, 2)
84    }
85
86}
87
88nonisolated enum BuildRoute: Hashable {
89    case list(repo: String)
90    case detail(repo: String, number: Int64)
91}