Build iOS App / Build .ipa and publish source (push) Failing after 35s
Multi-repo PR inbox, review (approve/request changes/comment), merge with gating toggle, read-only diff viewer, and Gitea Actions check status with one-tap re-run. Ships unsigned via the iMac runner to the SideStore source.
130 lines
4.6 KiB
Swift
130 lines
4.6 KiB
Swift
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@Environment(\.dismiss) private var dismiss
|
|
@State private var instanceURL: String
|
|
@State private var token: String
|
|
@State private var mergeGateMode: MergeGateMode
|
|
@State private var testResult: TestResult?
|
|
@State private var testing = false
|
|
@State private var showLogoutConfirm = false
|
|
|
|
enum TestResult: Equatable {
|
|
case success(String)
|
|
case failure(String)
|
|
}
|
|
|
|
init() {
|
|
_instanceURL = State(initialValue: SettingsStore.shared.instanceURL)
|
|
_token = State(initialValue: SettingsStore.shared.token)
|
|
_mergeGateMode = State(initialValue: SettingsStore.shared.mergeGateMode)
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section("Server") {
|
|
TextField("Instance URL", text: $instanceURL)
|
|
.keyboardType(.URL)
|
|
.autocapitalization(.none)
|
|
.autocorrectionDisabled()
|
|
.textInputAutocapitalization(.never)
|
|
SecureField("Personal access token", text: $token)
|
|
Button(testing ? "Testing…" : "Test connection") {
|
|
test()
|
|
}
|
|
.disabled(testing || token.isEmpty)
|
|
if let testResult {
|
|
testResultView(testResult)
|
|
}
|
|
}
|
|
|
|
Section("Merge gating") {
|
|
Picker("Merge behavior", selection: $mergeGateMode) {
|
|
ForEach(MergeGateMode.allCases) { m in
|
|
Text(m.rawValue).tag(m)
|
|
}
|
|
}
|
|
.pickerStyle(.inline)
|
|
Text("Warn mode shows CI status and allows merging a red PR after confirmation. Block mode disables merge while any check is failing.")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Section {
|
|
Button("Log out", role: .destructive) {
|
|
showLogoutConfirm = true
|
|
}
|
|
.disabled(token.isEmpty)
|
|
}
|
|
}
|
|
.navigationTitle("Settings")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Cancel") { dismiss() }
|
|
}
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button("Save") { save() }
|
|
.disabled(instanceURL.trimmingCharacters(in: .whitespaces)
|
|
.isEmpty)
|
|
}
|
|
}
|
|
.confirmationDialog("Log out?",
|
|
isPresented: $showLogoutConfirm,
|
|
titleVisibility: .visible) {
|
|
Button("Log out", role: .destructive) {
|
|
SettingsStore.shared.logout()
|
|
testResult = nil
|
|
dismiss()
|
|
}
|
|
Button("Cancel", role: .cancel) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func testResultView(_ result: TestResult) -> some View {
|
|
switch result {
|
|
case .success(let login):
|
|
Label("Connected as \(login)", systemImage: "checkmark.circle.fill")
|
|
.font(.callout)
|
|
.foregroundColor(.green)
|
|
case .failure(let message):
|
|
Label(message, systemImage: "xmark.circle.fill")
|
|
.font(.callout)
|
|
.foregroundColor(.red)
|
|
}
|
|
}
|
|
|
|
private func test() {
|
|
testing = true
|
|
testResult = nil
|
|
let trimmed = instanceURL.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard let url = URL(string: trimmed) else {
|
|
testResult = .failure("Invalid URL")
|
|
testing = false
|
|
return
|
|
}
|
|
let client = GiteaClient(instanceURL: url, token: token)
|
|
Task {
|
|
do {
|
|
let user = try await client.currentUser()
|
|
AppSession.currentLogin = user.login
|
|
testResult = .success(user.login)
|
|
} catch {
|
|
testResult = .failure(error.localizedDescription)
|
|
}
|
|
testing = false
|
|
}
|
|
}
|
|
|
|
private func save() {
|
|
SettingsStore.shared.instanceURL =
|
|
instanceURL.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
SettingsStore.shared.token = token
|
|
SettingsStore.shared.mergeGateMode = mergeGateMode
|
|
dismiss()
|
|
}
|
|
}
|