Commit 3ee1c43ddb

3ee1c43ddb8a317d1525e9c55dd123736f643826

parent: 0a5413dcd9

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-07 00:28 UTC

Cancel, delete and organisation actions in their screens

Layout: unified · split

gitbay/Views/Builds/BuildDetailView.swift +36
@@ -4,6 +4,7 @@ import SwiftUI
44struct BuildDetailView: View {
55
66 @State private var model: BuildDetailViewModel
7 @State private var confirmingCancel = false
78
89 init(client: GitbayClient, repo: String, number: Int64) {
910 _model = State(initialValue: BuildDetailViewModel(
@@ -16,6 +17,11 @@ struct BuildDetailView: View {
1617 Color.clear
1718 if let detail = model.state.value {
1819 VStack(spacing: 0) {
20 if let error = model.actionError {
21 GBNotice(error, .gbWarn)
22 .padding(.horizontal, 16)
23 .padding(.vertical, 8)
24 }
1925 header(detail.build)
2026 Divider()
2127 log(detail.log)
@@ -25,8 +31,38 @@ struct BuildDetailView: View {
2531 .overlay { LoadStateOverlay(state: model.state) }
2632 .navigationTitle("Build #\(model.number)")
2733 .navigationBarTitleDisplayMode(.inline)
34 .toolbar {
35 ToolbarItem(placement: .topBarTrailing) {
36 if model.isCancellable {
37 if model.working {
38 ProgressView()
39 } else {
40 Button("Cancel", role: .destructive) {
41 confirmingCancel = true
42 }
43 .accessibilityIdentifier("build-cancel-button")
44 }
45 }
46 }
47 }
2848 .task { await model.load() }
2949 .refreshable { await model.load() }
50 .confirmationDialog(
51 "Cancel this build?",
52 isPresented: $confirmingCancel,
53 titleVisibility: .visible
54 ) {
55 Button("Cancel Build", role: .destructive) {
56 Task { await model.cancel() }
57 }
58 Button("Keep Running", role: .cancel) {}
59 } message: {
60 Text(
61 "A running step ends within a couple of seconds. If this is a " +
62 "duplicate of a commit that already passed, that earlier result " +
63 "is restored on the commit."
64 )
65 }
3066 }
3167
3268 private func header(_ build: Build) -> some View {
gitbay/Views/Discovery/ProfileView.swift +33
@@ -4,7 +4,10 @@ import SwiftUI
44/// have been doing, and what they own that you can see.
55struct ProfileView: View {
66
7 @Environment(SessionStore.self) private var session
78 @State private var model: ProfileViewModel
9 @State private var creatingOrg = false
10 @State private var newOrgName = ""
811
912 /// Someone else's profile is titled with their name. Your own is
1013 /// reached from a tab and titled to match it.
@@ -18,6 +21,11 @@ struct ProfileView: View {
1821 var body: some View {
1922 List {
2023 if let profile = model.state.value {
24 if let error = model.actionError {
25 Section {
26 GBNotice(error, .gbWarn)
27 }
28 }
2129 header(profile)
2230 peopleSection(profile)
2331 aboutSection(profile)
@@ -35,8 +43,33 @@ struct ProfileView: View {
3543 .overlay { LoadStateOverlay(state: model.state) }
3644 .navigationTitle(title ?? model.name)
3745 .navigationBarTitleDisplayMode(.inline)
46 .toolbar {
47 if let profile = model.state.value,
48 !profile.isOrg, session.current?.username == profile.name {
49 ToolbarItem(placement: .topBarTrailing) {
50 Button {
51 newOrgName = ""
52 creatingOrg = true
53 } label: {
54 Image(systemName: "plus.circle")
55 }
56 .disabled(model.working)
57 .accessibilityIdentifier("org-create-button")
58 }
59 }
60 }
3861 .task { await model.load() }
3962 .refreshable { await model.load() }
63 .alert("New Organization", isPresented: $creatingOrg) {
64 TextField("Name", text: $newOrgName)
65 .autocorrectionDisabled()
66 .textInputAutocapitalization(.never)
67 Button("Create") {
68 Task { await model.createOrg(newOrgName) }
69 }
70 .disabled(newOrgName.trimmingCharacters(in: .whitespaces).isEmpty)
71 Button("Cancel", role: .cancel) {}
72 }
4073 }
4174
4275 // MARK: - Sections
gitbay/Views/Orgs/OrgView.swift +26
@@ -10,6 +10,8 @@ struct OrgView: View {
1010 @State private var newTeam = ""
1111 @State private var removingMember: OrgMember?
1212 @State private var deletingTeam: String?
13 @State private var renamingOrg = false
14 @State private var newOrgName = ""
1315
1416 init(client: GitbayClient, org: String) {
1517 _model = State(initialValue: OrgViewModel(client: client, orgName: org))
@@ -30,8 +32,32 @@ struct OrgView: View {
3032 .overlay { LoadStateOverlay(state: model.state) }
3133 .navigationTitle(model.orgName)
3234 .navigationBarTitleDisplayMode(.inline)
35 .toolbar {
36 ToolbarItem(placement: .topBarTrailing) {
37 Button {
38 newOrgName = model.orgName
39 renamingOrg = true
40 } label: {
41 Image(systemName: "pencil")
42 }
43 .disabled(model.working)
44 .accessibilityIdentifier("org-rename-button")
45 }
46 }
3347 .task { await model.load() }
3448 .refreshable { await model.load() }
49 .alert("Rename \(model.orgName)", isPresented: $renamingOrg) {
50 TextField("New name", text: $newOrgName)
51 .autocorrectionDisabled()
52 .textInputAutocapitalization(.never)
53 Button("Rename") {
54 Task { await model.rename(to: newOrgName) }
55 }
56 .disabled(newOrgName.trimmingCharacters(in: .whitespaces).isEmpty)
57 Button("Cancel", role: .cancel) {}
58 } message: {
59 Text("Clone URLs for every repository in this organization change to the new name.")
60 }
3561 .confirmationDialog(
3662 "Remove \(removingMember?.user ?? "") from \(model.orgName)?",
3763 isPresented: Binding(
gitbay/Views/Releases/ReleaseView.swift +27
@@ -6,6 +6,8 @@ struct ReleaseView: View {
66 @State private var editing = false
77 @State private var draftTitle = ""
88 @State private var draftNotes = ""
9 @State private var confirmingDelete = false
10 @Environment(\.dismiss) private var dismiss
911
1012 init(client: GitbayClient, repo: String, tag: String) {
1113 _model = State(initialValue: ReleaseDetailViewModel(
@@ -89,6 +91,31 @@ struct ReleaseView: View {
8991 .accessibilityIdentifier("release-edit-button")
9092 }
9193 }
94 ToolbarItem(placement: .topBarTrailing) {
95 if model.state.value != nil {
96 Button("Delete", role: .destructive) {
97 confirmingDelete = true
98 }
99 .disabled(model.working)
100 .accessibilityIdentifier("release-delete-button")
101 }
102 }
103 }
104 .confirmationDialog(
105 "Delete \(model.tag)?",
106 isPresented: $confirmingDelete,
107 titleVisibility: .visible
108 ) {
109 Button("Delete", role: .destructive) {
110 Task {
111 if await model.delete() {
112 dismiss()
113 }
114 }
115 }
116 Button("Cancel", role: .cancel) {}
117 } message: {
118 Text("This deletes the release's assets along with it.")
92119 }
93120 .sheet(isPresented: $editing) {
94121 ComposeSheet(