| @@ -7,6 +7,34 @@ struct AdminReposView: View { |
| 7 | 7 | @State private var model: AdminReposViewModel |
| 8 | 8 | @State private var deleting: AdminRepo? |
| 9 | 9 | @State private var typed = "" |
| 10 | /// Archive and visibility change what everyone sees, so they ask first. |
| 11 | @State private var confirming: Pending? |
| 12 | |
| 13 | nonisolated struct Pending: Hashable, Sendable, Identifiable { |
| 14 | let action: AdminRepoAction |
| 15 | let repo: AdminRepo |
| 16 | var id: String { repo.path + String(describing: action) } |
| 17 | |
| 18 | var title: String { |
| 19 | switch action { |
| 20 | case .archive: "Archive \(repo.path)?" |
| 21 | case .unarchive: "Unarchive \(repo.path)?" |
| 22 | case .makePublic: "Make \(repo.path) public?" |
| 23 | case .makePrivate: "Make \(repo.path) private?" |
| 24 | default: "" |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | var message: String { |
| 29 | switch action { |
| 30 | case .archive: "It becomes read-only: pushes, issues and merge requests are refused. Audited." |
| 31 | case .unarchive: "It takes pushes, issues and merge requests again. Audited." |
| 32 | case .makePublic: "Anyone, signed in or not, can read it. Audited." |
| 33 | case .makePrivate: "Only its owner and those granted access can read it. Audited." |
| 34 | default: "" |
| 35 | } |
| 36 | } |
| 37 | } |
| 10 | 38 | |
| 11 | 39 | init(client: GitbayClient) { |
| 12 | 40 | _model = State(initialValue: AdminReposViewModel(client: client)) |
| @@ -50,6 +78,17 @@ struct AdminReposView: View { |
| 50 | 78 | .task { if model.list.needsLoad { await model.load() } } |
| 51 | 79 | .refreshable { await model.load() } |
| 52 | 80 | .onChange(of: model.visibility) { Task { await model.load() } } |
| 81 | .confirmationDialog(confirming?.title ?? "", |
| 82 | isPresented: Binding(get: { confirming != nil }, set: { if !$0 { confirming = nil } }), |
| 83 | titleVisibility: .visible, presenting: confirming) { pending in |
| 84 | Button(pending.action == .makePublic ? "Make public" : "Confirm", |
| 85 | role: pending.action == .makePublic || pending.action == .archive ? .destructive : nil) { |
| 86 | Task { await model.perform(pending.action, on: pending.repo) } |
| 87 | } |
| 88 | Button("Cancel", role: .cancel) {} |
| 89 | } message: { pending in |
| 90 | Text(pending.message) |
| 91 | } |
| 53 | 92 | .alert("Delete \(deleting?.path ?? "")?", isPresented: Binding(get: { deleting != nil }, set: { if !$0 { deleting = nil } }), |
| 54 | 93 | presenting: deleting) { repo in |
| 55 | 94 | TextField(repo.path, text: $typed) |
| @@ -82,10 +121,10 @@ struct AdminReposView: View { |
| 82 | 121 | Spacer() |
| 83 | 122 | Menu { |
| 84 | 123 | Button(repo.isArchived ? "Unarchive" : "Archive") { |
| 85 | | Task { await model.perform(repo.isArchived ? .unarchive : .archive, on: repo) } |
| 124 | confirming = Pending(action: repo.isArchived ? .unarchive : .archive, repo: repo) |
| 86 | 125 | } |
| 87 | 126 | Button(repo.visibility == "private" ? "Make public" : "Make private") { |
| 88 | | Task { await model.perform(repo.visibility == "private" ? .makePublic : .makePrivate, on: repo) } |
| 127 | confirming = Pending(action: repo.visibility == "private" ? .makePublic : .makePrivate, repo: repo) |
| 89 | 128 | } |
| 90 | 129 | Button("Rebuild symbol index") { |
| 91 | 130 | Task { await model.perform(.reindex, on: repo) } |
| @@ -126,10 +165,14 @@ struct AdminOrgLimitsView: View { |
| 126 | 165 | .multilineTextAlignment(.trailing) |
| 127 | 166 | .autocorrectionDisabled() |
| 128 | 167 | .textInputAutocapitalization(.never) |
| 168 | .submitLabel(.search) |
| 129 | 169 | .onSubmit { Task { await model.load(org: org) } } |
| 130 | 170 | .accessibilityIdentifier("org-limits-name") |
| 131 | 171 | } |
| 132 | 172 | .font(.gbSans(.subheadline)) |
| 173 | Button("Show") { Task { await model.load(org: org) } } |
| 174 | .disabled(org.trimmingCharacters(in: .whitespaces).isEmpty || model.working) |
| 175 | .accessibilityIdentifier("org-limits-show") |
| 133 | 176 | } |
| 134 | 177 | if let error = model.actionError, !editing { |
| 135 | 178 | Section { GBNotice(error, .gbWarn) } |