Commit 63507dba22
Verified · cmc
Layout: unified · split
gitbay/Issues/IssueModels.swift +2
| @@ -46,10 +46,12 @@ nonisolated struct IssueDetail: Decodable, Sendable, Hashable { | ||
| 46 | 46 | nonisolated struct Comment: Decodable, Sendable, Hashable, Identifiable { |
| 47 | 47 | let author: String |
| 48 | 48 | let body: String |
| 49 | let bodyFormat: String? | |
| 49 | 50 | let createdAt: Date |
| 50 | 51 | |
| 51 | 52 | enum CodingKeys: String, CodingKey { |
| 52 | 53 | case author, body |
| 54 | case bodyFormat = "body_format" | |
| 53 | 55 | case createdAt = "created_at" |
| 54 | 56 | } |
| 55 | 57 | |
gitbay/MRs/MRModels.swift +2
| @@ -156,10 +156,12 @@ nonisolated struct MRDetail: Decodable, Sendable, Hashable { | ||
| 156 | 156 | nonisolated struct MRComment: Decodable, Sendable, Hashable, Identifiable { |
| 157 | 157 | let author: String |
| 158 | 158 | let body: String |
| 159 | let bodyFormat: String? | |
| 159 | 160 | let createdAt: Date |
| 160 | 161 | |
| 161 | 162 | enum CodingKeys: String, CodingKey { |
| 162 | 163 | case author, body |
| 164 | case bodyFormat = "body_format" | |
| 163 | 165 | case createdAt = "created_at" |
| 164 | 166 | } |
| 165 | 167 | |
gitbay/Views/Issues/IssueView.swift +2 −2
| @@ -31,7 +31,7 @@ struct IssueView: View { | ||
| 31 | 31 | |
| 32 | 32 | if let body = issue.body, !body.isEmpty { |
| 33 | 33 | Section { |
| 34 | MarkdownView(markdown: body) | |
| 34 | FormattedBodyView(text: body, format: issue.bodyFormat) | |
| 35 | 35 | .padding(.vertical, 4) |
| 36 | 36 | } |
| 37 | 37 | } |
| @@ -194,7 +194,7 @@ struct IssueView: View { | ||
| 194 | 194 | .font(.gbSans(.caption)) |
| 195 | 195 | .foregroundStyle(.tertiary) |
| 196 | 196 | } |
| 197 | MarkdownView(markdown: comment.body) | |
| 197 | FormattedBodyView(text: comment.body, format: comment.bodyFormat) | |
| 198 | 198 | .font(.gbSans(.subheadline)) |
| 199 | 199 | } |
| 200 | 200 | .padding(.vertical, 2) |
gitbay/Views/MRs/MRView.swift +2 −2
| @@ -34,7 +34,7 @@ struct MRView: View { | ||
| 34 | 34 | |
| 35 | 35 | if let body = mr.body, !body.isEmpty { |
| 36 | 36 | Section { |
| 37 | MarkdownView(markdown: body) | |
| 37 | FormattedBodyView(text: body, format: mr.bodyFormat) | |
| 38 | 38 | .padding(.vertical, 4) |
| 39 | 39 | } |
| 40 | 40 | } |
| @@ -397,7 +397,7 @@ struct MRView: View { | ||
| 397 | 397 | .font(.gbSans(.caption)) |
| 398 | 398 | .foregroundStyle(.tertiary) |
| 399 | 399 | } |
| 400 | MarkdownView(markdown: comment.body) | |
| 400 | FormattedBodyView(text: comment.body, format: comment.bodyFormat) | |
| 401 | 401 | .font(.gbSans(.subheadline)) |
| 402 | 402 | } |
| 403 | 403 | .padding(.vertical, 2) |
gitbay/Views/Repos/FormattedBodyView.swift added +28
| @@ -0,0 +1,28 @@ | ||
| 1 | import OrgSwiftUI | |
| 2 | import SwiftUI | |
| 3 | ||
| 4 | /// Renders an issue/MR body or comment in whichever format it was stored as. | |
| 5 | /// Mirrors `ReadmeView`'s dispatch, keyed on the stored `body_format` rather | |
| 6 | /// than a file name. A missing format renders as markdown, matching every | |
| 7 | /// body written before org support existed. | |
| 8 | struct FormattedBodyView: View { | |
| 9 | let text: String | |
| 10 | var format: String? | |
| 11 | ||
| 12 | @Environment(\.colorScheme) private var colorScheme | |
| 13 | ||
| 14 | var isOrg: Bool { format == "org" } | |
| 15 | ||
| 16 | var body: some View { | |
| 17 | if isOrg { | |
| 18 | // Qualified: `OrgView` here is org-mode, not the organization view. | |
| 19 | OrgSwiftUI.OrgView( | |
| 20 | text, | |
| 21 | styler: OrgSyntaxStyler(colorScheme: colorScheme), | |
| 22 | keywords: OrgKeywordStyle(todo: .gbOK, done: .gbDone) | |
| 23 | ) | |
| 24 | } else { | |
| 25 | MarkdownView(markdown: text) | |
| 26 | } | |
| 27 | } | |
| 28 | } | |
gitbayTests/BodyFormatTests.swift +43 −3
| @@ -83,6 +83,45 @@ struct BodyFormatDecodingTests { | ||
| 83 | 83 | #expect(mr.checksCombined == "success") |
| 84 | 84 | #expect(mr.unresolvedThreads == 2) |
| 85 | 85 | } |
| 86 | ||
| 87 | /// body_format is omitempty on a comment too; an ordinary one carries no key. | |
| 88 | @Test func anIssueCommentWithoutAStoredFormatDecodesNil() throws { | |
| 89 | let issue = try decodeIssue(""" | |
| 90 | {"number":7,"title":"t","state":"open","author":"cmc",\ | |
| 91 | "created_at":"2026-09-01T00:00:00Z","comments":[{"author":"rae",\ | |
| 92 | "body":"hi","created_at":"2026-09-01T00:00:00Z"}]} | |
| 93 | """) | |
| 94 | #expect(issue.comments?.first?.bodyFormat == nil) | |
| 95 | } | |
| 96 | ||
| 97 | @Test func anIssueCommentStoredAsOrgSaysSo() throws { | |
| 98 | let issue = try decodeIssue(""" | |
| 99 | {"number":7,"title":"t","state":"open","author":"cmc",\ | |
| 100 | "created_at":"2026-09-01T00:00:00Z","comments":[{"author":"rae",\ | |
| 101 | "body":"hi","body_format":"org","created_at":"2026-09-01T00:00:00Z"}]} | |
| 102 | """) | |
| 103 | #expect(issue.comments?.first?.bodyFormat == "org") | |
| 104 | } | |
| 105 | ||
| 106 | @Test func aMergeRequestCommentWithoutAStoredFormatDecodesNil() throws { | |
| 107 | let mr = try decodeMR(""" | |
| 108 | {"number":7,"title":"t","state":"open","author":"cmc","source":"f",\ | |
| 109 | "target_ref":"main","head_sha":"a","created_at":"2026-09-01T00:00:00Z",\ | |
| 110 | "comments":[{"author":"rae","body":"hi",\ | |
| 111 | "created_at":"2026-09-01T00:00:00Z"}]} | |
| 112 | """) | |
| 113 | #expect(mr.comments?.first?.bodyFormat == nil) | |
| 114 | } | |
| 115 | ||
| 116 | @Test func aMergeRequestCommentStoredAsOrgSaysSo() throws { | |
| 117 | let mr = try decodeMR(""" | |
| 118 | {"number":7,"title":"t","state":"open","author":"cmc","source":"f",\ | |
| 119 | "target_ref":"main","head_sha":"a","created_at":"2026-09-01T00:00:00Z",\ | |
| 120 | "comments":[{"author":"rae","body":"hi","body_format":"org",\ | |
| 121 | "created_at":"2026-09-01T00:00:00Z"}]} | |
| 122 | """) | |
| 123 | #expect(mr.comments?.first?.bodyFormat == "org") | |
| 124 | } | |
| 86 | 125 | } |
| 87 | 126 | |
| 88 | 127 | @MainActor |
| @@ -141,9 +180,10 @@ struct BodyFormatWriteTests { | ||
| 141 | 180 | stub.enqueue(.init(status: 200, json: issueJSON)) |
| 142 | 181 | await model.edit(title: "t2", body: "b2", format: "md") |
| 143 | 182 | |
| 144 | let argv = try argvOf(try #require(stub.seen.first { $0.method == "POST" })) | |
| 145 | #expect(argv.contains("--format")) | |
| 146 | #expect(argv.contains("md")) | |
| 183 | let write = try #require(stub.seen.first { $0.method == "POST" }) | |
| 184 | #expect(try argvOf(write) | |
| 185 | == ["issue", "edit", "krz/gitbay", "7", "--title", "t2", "--file", "-", "--format", "md"]) | |
| 186 | #expect(try stdinOf(write) == "b2") | |
| 147 | 187 | } |
| 148 | 188 | } |
| 149 | 189 | |