Files
ios-gitea-prs/GiteaPR/Settings/SettingsView.swift
T
Hermes 1025634978
Build iOS App / Build .ipa and publish source (push) Failing after 35s
GiteaPR: native iOS app to manage Gitea pull requests
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.
2026-08-12 17:07:58 +00:00

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()
}
}