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,79 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Read-only unified diff viewer for a single changed file.
|
||||
///
|
||||
/// Parses a Gitea unified diff patch into line hunks so added/deleted lines can
|
||||
/// be colour-coded. Gitea only returns `patch` for small diffs; large files
|
||||
/// fall back to a raw text view.
|
||||
struct DiffView: View {
|
||||
let file: ChangedFile
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let patch = file.patch, !patch.isEmpty {
|
||||
ScrollView([.horizontal, .vertical]) {
|
||||
LazyVStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(parse(patch)) { line in
|
||||
diffLine(line)
|
||||
}
|
||||
}
|
||||
.font(.caption.monospaced())
|
||||
}
|
||||
} else {
|
||||
ContentUnavailableView(
|
||||
"Diff too large to preview",
|
||||
systemImage: "doc.text",
|
||||
description: Text("Gitea didn't return a patch for this file."))
|
||||
}
|
||||
}
|
||||
.navigationTitle(file.filename)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
private func diffLine(_ line: DiffLine) -> some View {
|
||||
Text(line.text)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 1)
|
||||
.background(background(for: line.type))
|
||||
.foregroundColor(foreground(for: line.type))
|
||||
}
|
||||
|
||||
private func background(for type: DiffLineType) -> Color {
|
||||
switch type {
|
||||
case .addition: return Color.green.opacity(0.15)
|
||||
case .deletion: return Color.red.opacity(0.15)
|
||||
case .hunk, .meta: return Color(.secondarySystemBackground)
|
||||
case .context: return .clear
|
||||
}
|
||||
}
|
||||
private func foreground(for type: DiffLineType) -> Color {
|
||||
switch type {
|
||||
case .addition: return .green
|
||||
case .deletion: return .red
|
||||
case .hunk: return .secondary
|
||||
case .meta, .context: return .primary
|
||||
}
|
||||
}
|
||||
|
||||
private func parse(_ patch: String) -> [DiffLine] {
|
||||
patch.components(separatedBy: "\n").map { line in
|
||||
if line.hasPrefix("@@") { return DiffLine(text: line, type: .hunk) }
|
||||
if line.hasPrefix("diff ") || line.hasPrefix("index ")
|
||||
|| line.hasPrefix("--- ") || line.hasPrefix("+++ ") {
|
||||
return DiffLine(text: line, type: .meta)
|
||||
}
|
||||
if line.hasPrefix("+") { return DiffLine(text: line, type: .addition) }
|
||||
if line.hasPrefix("-") { return DiffLine(text: line, type: .deletion) }
|
||||
return DiffLine(text: line, type: .context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum DiffLineType { case context, addition, deletion, hunk, meta }
|
||||
|
||||
private struct DiffLine: Identifiable {
|
||||
let text: String
|
||||
let type: DiffLineType
|
||||
var id: UUID { UUID() }
|
||||
}
|
||||
Reference in new issue
Block a user