Commit b1fc78b637

b1fc78b637b27ae2642cd722014ca3bbf3a917f3

parent: f4f3e8781b

Unsigned

cmc <hello@cleberg.net> · 2026-09-07 05:35 UTC

Edit opens on the format the body was stored in (!61)

An org body's Edit sheet opened on Markdown. `ComposeSheet` stored the format as `Binding<String>?`, which is not a dynamic property, so the sheet body never re-ran after the binding took the stored format. Now a real `@Binding` plus a `showsFormat` flag; the release call site is unchanged.

Reproduced and verified in the simulator with the real `IssueView` against a stubbed `issue show` returning `body_format: org`. The live smoke edit flow gains an org round-trip on the fixture issue.

Layout: unified · split

gitbay/Views/Shared/ComposeSheet.swift +23 −5
@@ -9,11 +9,29 @@ struct ComposeSheet: View {
99 let errorMessage: String?
1010 @Binding var title: String
1111 @Binding var bodyText: String
12 /// nil hides the picker entirely — the release editor has no parity
13 /// row and passes nothing, leaving that call site untouched.
14 var format: Binding<String>? = nil
12 /// Held as a real `@Binding`, not `Binding<String>?`: an optional
13 /// binding is not a dynamic property, so the sheet body never re-ran
14 /// when the value changed and the picker kept whatever it first saw.
15 @Binding var format: String
16 let showsFormat: Bool
1517 let onSubmit: () -> Void
1618
19 /// A nil `format` hides the picker entirely — the release editor has
20 /// no parity row and passes nothing, leaving that call site untouched.
21 init(heading: String, submitLabel: String, working: Bool, errorMessage: String?,
22 title: Binding<String>, bodyText: Binding<String>,
23 format: Binding<String>? = nil, onSubmit: @escaping () -> Void) {
24 self.heading = heading
25 self.submitLabel = submitLabel
26 self.working = working
27 self.errorMessage = errorMessage
28 _title = title
29 _bodyText = bodyText
30 _format = format ?? .constant("md")
31 showsFormat = format != nil
32 self.onSubmit = onSubmit
33 }
34
1735 @Environment(\.dismiss) private var dismiss
1836
1937 var body: some View {
@@ -33,8 +51,8 @@ struct ComposeSheet: View {
3351 .font(.gbSans(.body))
3452 .autocorrectionDisabled()
3553 .accessibilityIdentifier("compose-body")
36 if let format {
37 Picker("Format", selection: format) {
54 if showsFormat {
55 Picker("Format", selection: $format) {
3856 Text("Markdown").tag("md")
3957 Text("Org").tag("org")
4058 }
gitbayUITests/LiveSmokeUITests.swift +18
@@ -188,11 +188,29 @@ final class LiveSmokeUITests: XCTestCase {
188188 XCTAssertTrue(title.waitForExistence(timeout: 5), "edit sheet did not open")
189189 XCTAssertTrue((title.value as? String)?.contains("ui-smoke fixture") == true,
190190 "edit sheet did not prefill")
191 // Save the body as org, reopen, and the picker must start on Org:
192 // the sheet has to read the stored format, not its own default.
193 let format = app.segmentedControls.matching(identifier: "compose-format").firstMatch
194 XCTAssertTrue(format.waitForExistence(timeout: 5), "format picker missing")
195 XCTAssertTrue(format.buttons["Markdown"].isSelected, "fixture did not open on Markdown")
196 format.buttons["Org"].tap()
191197 app.descendants(matching: .any).matching(identifier: "compose-submit")
192198 .firstMatch.tap()
193199 // Sheet dismissal proves the save round-tripped without error.
194200 XCTAssertTrue(waitForDisappearance(title, timeout: 15), "edit save did not dismiss")
195201
202 app.descendants(matching: .any).matching(identifier: "issue-actions-menu")
203 .firstMatch.tap()
204 XCTAssertTrue(edit.waitForExistence(timeout: 5))
205 edit.tap()
206 XCTAssertTrue(format.waitForExistence(timeout: 5), "edit sheet did not reopen")
207 XCTAssertTrue(format.buttons["Org"].isSelected, "edit sheet did not open on the stored format")
208 // Restore the fixture to markdown.
209 format.buttons["Markdown"].tap()
210 app.descendants(matching: .any).matching(identifier: "compose-submit")
211 .firstMatch.tap()
212 XCTAssertTrue(waitForDisappearance(title, timeout: 15), "edit save did not dismiss")
213
196214 back() // to issues list
197215 back() // to repo screen
198216