Files
ios-gitea-prs/GiteaPR/Settings/SettingsView.swift
T
Hermes 53b3f69d0f
Build iOS App / Build .ipa and publish source (push) Successful in 22s
Merge icon, delete-branch-after-merge setting, floating tab bar; bump to 2.0
- Merge button: use the proper SF Symbol 'arrow.triangle.merge' instead of
  the download icon 'arrow.down.circle'.
- Add a Settings toggle 'Delete branch after merge'. When on, the merge call
  passes delete_branch_after_merge=true and the head branch is deleted after
  a successful merge (best-effort).
- PR detail bottom tab bar: floating capsule style (regularMaterial + shadow)
  like the latest iOS tab bars, instead of a full-width bar.
- Bump to 2.0.
2026-08-12 22:37:42 +00:00

140 lines
5.1 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 deleteBranchAfterMerge: Bool
@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)
_deleteBranchAfterMerge = State(initialValue: SettingsStore.shared.deleteBranchAfterMerge)
}
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)
Toggle("Delete branch after merge", isOn: $deleteBranchAfterMerge)
}
Section("Diagnostics") {
NavigationLink("View logs") {
LogsView()
}
}
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
SettingsStore.shared.deleteBranchAfterMerge = deleteBranchAfterMerge
dismiss()
}
}