Fix diff text not rendering: LazyVStack in dual-axis ScrollView collapses row width; bump to 1.6
Build iOS App / Build .ipa and publish source (push) Successful in 21s
Build iOS App / Build .ipa and publish source (push) Successful in 21s
The diff data was fine, but the render collapsed: a LazyVStack inside a dual-axis ScrollView([.horizontal, .vertical]) gets an unbounded proposal and rows collapse to near-zero width — the green addition background rendered but the monospaced text was laid out at ~0 width and disappeared. Use a plain VStack inside nested vertical→horizontal ScrollViews (the stable pattern for diff text) and give each line fixedSize(horizontal:) so the text isn't clipped. Bump to 1.6.
This commit is contained in:
1 parent
db694543c1
commit
a3b8eefdb2
2 files changed
+24
-16
No files matched your search
@@ -187,7 +187,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.5;
|
MARKETING_VERSION = 1.6;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
||||||
@@ -221,7 +221,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.5;
|
MARKETING_VERSION = 1.6;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import SwiftUI
|
|||||||
/// `/pulls/{n}/files` (the field is absent), so the diff is fetched from the
|
/// `/pulls/{n}/files` (the field is absent), so the diff is fetched from the
|
||||||
/// PR's `.diff` endpoint and the hunks belonging to this file are extracted.
|
/// PR's `.diff` endpoint and the hunks belonging to this file are extracted.
|
||||||
/// Added/deleted lines are colour-coded.
|
/// Added/deleted lines are colour-coded.
|
||||||
|
///
|
||||||
|
/// Rendering: a plain VStack inside nested vertical→horizontal ScrollViews.
|
||||||
|
/// A LazyVStack inside a dual-axis `ScrollView([.horizontal, .vertical])` is a
|
||||||
|
/// known-bad combination (rows collapse to ~zero width, so the background tint
|
||||||
|
/// renders but the text disappears); VStack + nested scroll is the stable
|
||||||
|
/// pattern for monospaced diff text.
|
||||||
struct DiffView: View {
|
struct DiffView: View {
|
||||||
let repo: Repository
|
let repo: Repository
|
||||||
let prNumber: Int
|
let prNumber: Int
|
||||||
@@ -24,13 +30,14 @@ struct DiffView: View {
|
|||||||
systemImage: "doc.text",
|
systemImage: "doc.text",
|
||||||
description: Text(errorMessage))
|
description: Text(errorMessage))
|
||||||
} else {
|
} else {
|
||||||
ScrollView([.horizontal, .vertical]) {
|
ScrollView(.vertical) {
|
||||||
LazyVStack(alignment: .leading, spacing: 0) {
|
ScrollView(.horizontal) {
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
ForEach(lines) { line in
|
ForEach(lines) { line in
|
||||||
diffLine(line)
|
diffLine(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.font(.caption.monospaced())
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,28 +69,30 @@ struct DiffView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the unified-diff block for `filename` from a multi-file diff.
|
/// Extract the unified-diff block for `filename` from a multi-file diff.
|
||||||
/// Falls back to the whole diff if the header can't be matched.
|
|
||||||
private func extractSection(named filename: String, from diff: String) -> String {
|
private func extractSection(named filename: String, from diff: String) -> String {
|
||||||
let marker = "+++ b/\(filename)"
|
let marker = "+++ b/\(filename)"
|
||||||
guard let idx = diff.range(of: marker)?.upperBound else {
|
guard let idx = diff.range(of: marker)?.upperBound else {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
let tail = String(diff[idx...])
|
let tail = String(diff[idx...])
|
||||||
// Find the next "diff --git " after this file's header to delimit it.
|
if let next = tail.range(of: "\ndiff --git ") {
|
||||||
let rest = tail.dropFirst(marker.count)
|
|
||||||
if let next = rest.range(of: "\ndiff --git ") {
|
|
||||||
return String(tail[..<next.lowerBound])
|
return String(tail[..<next.lowerBound])
|
||||||
}
|
}
|
||||||
return tail
|
return tail
|
||||||
}
|
}
|
||||||
|
|
||||||
private func diffLine(_ line: DiffLine) -> some View {
|
private func diffLine(_ line: DiffLine) -> some View {
|
||||||
|
HStack(spacing: 0) {
|
||||||
Text(line.text)
|
Text(line.text)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.font(.caption.monospaced())
|
||||||
|
.foregroundColor(foreground(for: line.type))
|
||||||
|
.fixedSize(horizontal: true, vertical: false)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
.padding(.horizontal, 8)
|
.padding(.horizontal, 8)
|
||||||
.padding(.vertical, 1)
|
.padding(.vertical, 1)
|
||||||
.background(background(for: line.type))
|
.background(background(for: line.type))
|
||||||
.foregroundColor(foreground(for: line.type))
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func background(for type: DiffLineType) -> Color {
|
private func background(for type: DiffLineType) -> Color {
|
||||||
@@ -120,9 +129,8 @@ struct DiffView: View {
|
|||||||
private enum DiffLineType { case context, addition, deletion, hunk, meta }
|
private enum DiffLineType { case context, addition, deletion, hunk, meta }
|
||||||
|
|
||||||
private struct DiffLine: Identifiable {
|
private struct DiffLine: Identifiable {
|
||||||
// Stable id: a fresh UUID() on EVERY access (computed property) makes
|
// Stable id: generate once, not on every access (a fresh UUID each read
|
||||||
// SwiftUI's ForEach re-identify on every render, churning the main thread
|
// churns SwiftUI's ForEach on the main thread and can crash).
|
||||||
// and eventually crashing. Store it once.
|
|
||||||
let id: UUID = UUID()
|
let id: UUID = UUID()
|
||||||
let text: String
|
let text: String
|
||||||
let type: DiffLineType
|
let type: DiffLineType
|
||||||
|
|||||||
Reference in new issue
Block a user