merge modal location

This commit is contained in:
Ian Gulliver
2026-10-17 08:51:59 -07:00
parent 38643e65f5
commit 3e537c827d
5 changed files with 35 additions and 16 deletions
-8
View File
@@ -386,14 +386,6 @@ private struct MergeButton: View {
.buttonStyle(WebButtonStyle())
.disabled(busy || held || intoParent || !unmerged)
.help(help)
.onGeometryChange(for: CGRect.self) { g in
g.frame(in: .named("window"))
} action: { frame in
overlays.mergeFrame = frame
}
.onDisappear {
overlays.mergeFrame = nil
}
}
}
+26 -4
View File
@@ -25,6 +25,7 @@ struct OverlayModal {
let content: AnyView
let buttons: [ModalButton]
let anchor: CGRect?
let inPane: Bool
let canSubmit: @MainActor () -> Bool
let done: (String) -> Void
}
@@ -49,7 +50,7 @@ final class Overlays {
var leftTab = LeftTab.lead
var leadFocus = 0
@ObservationIgnored var pickerFrame: CGRect = .zero
@ObservationIgnored var mergeFrame: CGRect?
var paneFrame: CGRect = .zero
@ObservationIgnored private var monitor: Any?
var isOpen: Bool {
@@ -64,10 +65,10 @@ final class Overlays {
alerts.append(OverlayAlert(content: AnyView(content)))
}
func showModal(_ content: some View, _ buttons: [ModalButton], anchor: CGRect? = nil, canSubmit: @escaping @MainActor () -> Bool = { true }) async -> String {
func showModal(_ content: some View, _ buttons: [ModalButton], anchor: CGRect? = nil, inPane: Bool = false, canSubmit: @escaping @MainActor () -> Bool = { true }) async -> String {
modal?.done("cancel")
return await withCheckedContinuation { cont in
modal = OverlayModal(content: AnyView(content), buttons: buttons, anchor: anchor, canSubmit: canSubmit) { value in
modal = OverlayModal(content: AnyView(content), buttons: buttons, anchor: anchor, inPane: inPane, canSubmit: canSubmit) { value in
cont.resume(returning: value)
}
}
@@ -248,7 +249,7 @@ struct OverlayHost: View {
.onTapGesture {
overlays.answerModal("cancel")
}
ModalPlacement(modal: m) { value in
ModalPlacement(modal: m, pane: overlays.paneFrame) { value in
overlays.answerModal(value)
}
}
@@ -316,8 +317,25 @@ private struct AnchoredBox: Layout {
}
}
private struct PaneCenteredBox: Layout {
let pane: CGRect
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
proposal.replacingUnspecifiedDimensions()
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
let width = min(520, pane.width - 48)
let size = subviews[0].sizeThatFits(ProposedViewSize(width: width, height: nil))
let x = pane.midX - size.width / 2
let y = min(max(overlayMargin, pane.midY - size.height / 2), bounds.height - overlayMargin - size.height)
subviews[0].place(at: CGPoint(x: bounds.minX + x, y: bounds.minY + y), proposal: ProposedViewSize(width: width, height: size.height))
}
}
private struct ModalPlacement: View {
let modal: OverlayModal
let pane: CGRect
let answer: (String) -> Void
var body: some View {
@@ -327,6 +345,10 @@ private struct ModalPlacement: View {
AnchoredBox(anchor: anchor) {
box
}
} else if modal.inPane {
PaneCenteredBox(pane: pane) {
box
}
} else {
box
.padding(24)
+2 -3
View File
@@ -137,9 +137,8 @@ func confirmMerge(_ taskId: String, _ store: Store, _ overlays: Overlays) async
overlays.showAlert(errorText(error))
}
}
let anchor = overlays.mergeFrame
if !model.running {
if await overlays.showModal(content, [ModalButton(label: "Cancel", value: "cancel"), ModalButton(label: "Merge", value: "ok", primary: true)], anchor: anchor, canSubmit: { model.valid }) == "ok" {
if await overlays.showModal(content, [ModalButton(label: "Cancel", value: "cancel"), ModalButton(label: "Merge", value: "ok", primary: true)], inPane: true, canSubmit: { model.valid }) == "ok" {
await merge(false)
}
return
@@ -148,7 +147,7 @@ func confirmMerge(_ taskId: String, _ store: Store, _ overlays: Overlays) async
ModalButton(label: "Cancel", value: "cancel"),
ModalButton(label: "Merge now", value: "now"),
ModalButton(label: "After review", value: "after", primary: true, help: "Merge once the running review finishes with no findings"),
], anchor: anchor, canSubmit: { model.valid })
], inPane: true, canSubmit: { model.valid })
if choice == "now" || choice == "after" {
await merge(choice == "after")
}
+6
View File
@@ -180,6 +180,7 @@ private struct LeftPane: View {
private struct TaskPane: View {
@Environment(Store.self) private var store
@Environment(Overlays.self) private var overlays
@ViewState private var seen: Set<String> = []
var body: some View {
@@ -198,6 +199,11 @@ private struct TaskPane: View {
} action: { frame in
store.paneFocus.setFrame(frame)
}
.onGeometryChange(for: CGRect.self) { g in
g.frame(in: .named("window"))
} action: { frame in
overlays.paneFrame = frame
}
.onAppear {
seen = Set(store.inbox.map(\.id))
}