GiteaPR: native iOS app to manage Gitea pull requests
Build iOS App / Build .ipa and publish source (push) Failing after 35s
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.
This commit is contained in:
commit
1025634978
27 files changed
+2322
No files matched your search
@@ -0,0 +1,135 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Composes a comment. `mode` picks whether it posts a plain comment or a
|
||||
/// review with a state (Approve / Request changes / Comment).
|
||||
struct CommentSheet: View {
|
||||
@ObservedObject var vm: PRDetailViewModel
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var text = ""
|
||||
var mode: CommentMode = .comment
|
||||
|
||||
enum CommentMode { case comment, approve, requestChanges }
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text(title).font(.headline)
|
||||
TextEditor(text: $text)
|
||||
.frame(minHeight: 150)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke(Color(.separator)))
|
||||
Text("\(text.count) characters")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle(title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Submit") { submit() }
|
||||
.disabled(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var title: String {
|
||||
switch mode {
|
||||
case .approve: return "Approve"
|
||||
case .requestChanges: return "Request changes"
|
||||
case .comment: return "Comment"
|
||||
}
|
||||
}
|
||||
|
||||
private func submit() {
|
||||
let body = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
switch mode {
|
||||
case .approve:
|
||||
Task { await vm.submitReview(state: "approved", comment: body) }
|
||||
case .requestChanges:
|
||||
Task { await vm.submitReview(state: "changes", comment: body) }
|
||||
case .comment:
|
||||
Task { await vm.postComment(body) }
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
struct MergeSheet: View {
|
||||
@ObservedObject var vm: PRDetailViewModel
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var method = "merge"
|
||||
|
||||
private let methods = [("merge", "Create merge commit"),
|
||||
("rebase", "Rebase and merge"),
|
||||
("squash", "Squash and merge")]
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section("Merge method") {
|
||||
Picker("Method", selection: $method) {
|
||||
ForEach(methods, id: \.0) { m in
|
||||
Text(m.1).tag(m.0)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.inline)
|
||||
}
|
||||
Section {
|
||||
HStack {
|
||||
Text("CI")
|
||||
Spacer()
|
||||
switch vm.ciStatus {
|
||||
case .success: Label("Passing", systemImage: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
case .failure: Label("Failing", systemImage: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
case .running: Label("Running", systemImage: "hourglass")
|
||||
.foregroundColor(.orange)
|
||||
case .cancelled, .unknown: Label("Unknown", systemImage: "questionmark.circle")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
if vm.ciStatus == .failure {
|
||||
if SettingsStore.shared.mergeGateMode == .block {
|
||||
Label("Merging is blocked while CI is red (Settings).",
|
||||
systemImage: "lock")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Label("CI is red — you can still merge (Settings allows it).",
|
||||
systemImage: "exclamationmark.triangle")
|
||||
.font(.caption)
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Merge")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Merge") { performMerge() }
|
||||
.disabled(SettingsStore.shared.mergeGateMode == .block
|
||||
&& vm.ciStatus == .failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func performMerge() {
|
||||
Task {
|
||||
let success = await vm.merge(method: method)
|
||||
if success { dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user