Commit 91041af60b

91041af60b5a98ef01f5588478302c1ef04e043d

parent: ce71e6d93f

Unsigned

cmc <hello@cleberg.net> · 2026-07-20 19:16 UTC

fix: Generate Now tap target, remove keyboard dismiss button and launch focus

Scheduled Reports: the entire Overview section was wrapped in a single
VStack inside one List row, so SwiftUI collapsed every control into one
tap target and the menu-style Cadence Picker captured taps intended for
the Generate Now button. Unwraps the VStack so each control is its own
row, matching the pattern used in IntegrationsView and elsewhere.

Also extends the .automatedMonitoring gate to the two Pickers and the
Generate Now button. Previously only the Toggle was disabled, leaving a
button that appeared active on Free but silently no-opped against the
guard in ScheduledReportService.

Inspect tab: removes the keyboard toolbar's Dismiss Keyboard button and
the onAppear that focused the single-domain field at launch.

Layout: unified · split

DomainDig.xcodeproj/xcuserdata/cmc.xcuserdatad/xcschemes/xcschememanagement.plist +2 −2
@@ -12,12 +12,12 @@
12 <key>DomainDigShareExtension.xcscheme_^#shared#^_</key> 12 <key>DomainDigShareExtension.xcscheme_^#shared#^_</key>
13 <dict> 13 <dict>
14 <key>orderHint</key> 14 <key>orderHint</key>
15 <integer>2</integer> 15 <integer>1</integer>
16 </dict> 16 </dict>
17 <key>DomainDigWidgetExtension.xcscheme_^#shared#^_</key> 17 <key>DomainDigWidgetExtension.xcscheme_^#shared#^_</key>
18 <dict> 18 <dict>
19 <key>orderHint</key> 19 <key>orderHint</key>
20 <integer>1</integer> 20 <integer>2</integer>
21 </dict> 21 </dict>
22 </dict> 22 </dict>
23 <key>SuppressBuildableAutocreation</key> 23 <key>SuppressBuildableAutocreation</key>
DomainDig/ContentView.swift −10
@@ -186,21 +186,11 @@ struct ContentView: View {
186 } 186 }
187 } 187 }
188 } 188 }
189
190 ToolbarItemGroup(placement: .keyboard) {
191 Spacer()
192 Button("Dismiss Keyboard") {
193 focusedInputField = nil
194 }
195 }
196 } 189 }
197 .navigationDestination(for: WorkflowNavigationTarget.self) { target in 190 .navigationDestination(for: WorkflowNavigationTarget.self) { target in
198 WorkflowDetailView(viewModel: viewModel, workflowID: target.workflowID) 191 WorkflowDetailView(viewModel: viewModel, workflowID: target.workflowID)
199 } 192 }
200 } 193 }
201 .onAppear {
202 focusedInputField = .singleDomain
203 }
204 .task { 194 .task {
205 await viewModel.refreshUsageCredits() 195 await viewModel.refreshUsageCredits()
206 } 196 }
DomainDig/ScheduledReportsView.swift +47 −48
@@ -10,63 +10,62 @@ struct ScheduledReportsView: View {
10 var body: some View { 10 var body: some View {
11 List { 11 List {
12 Section("Overview") { 12 Section("Overview") {
13 VStack(alignment: .leading, spacing: 8) { 13 if !FeatureAccessService.hasAccess(to: .automatedMonitoring) {
14 if !FeatureAccessService.hasAccess(to: .automatedMonitoring) { 14 Text("Scheduled reports require Pro.")
15 Text("Scheduled reports require Pro.") 15 .font(appDensity.font(.caption))
16 .font(appDensity.font(.caption)) 16 .foregroundStyle(.secondary)
17 .foregroundStyle(.secondary) 17 }
18 }
19 18
20 Toggle("Scheduled Reports", isOn: Binding( 19 Toggle("Scheduled Reports", isOn: Binding(
21 get: { settings.isEnabled }, 20 get: { settings.isEnabled },
22 set: { newValue in 21 set: { newValue in
23 settings.isEnabled = newValue 22 settings.isEnabled = newValue
24 saveSettings() 23 saveSettings()
25 } 24 }
26 )) 25 ))
27 .disabled(!FeatureAccessService.hasAccess(to: .automatedMonitoring)) 26 .disabled(!FeatureAccessService.hasAccess(to: .automatedMonitoring))
28 27
29 Picker("Cadence", selection: Binding( 28 Picker("Cadence", selection: Binding(
30 get: { settings.cadence }, 29 get: { settings.cadence },
31 set: { newValue in 30 set: { newValue in
32 settings.cadence = newValue 31 settings.cadence = newValue
33 saveSettings() 32 saveSettings()
34 }
35 )) {
36 ForEach(ScheduledReportCadence.allCases) { cadence in
37 Text(cadence.title).tag(cadence)
38 }
39 } 33 }
34 )) {
35 ForEach(ScheduledReportCadence.allCases) { cadence in
36 Text(cadence.title).tag(cadence)
37 }
38 }
39 .disabled(!FeatureAccessService.hasAccess(to: .automatedMonitoring))
40 40
41 Picker("Format", selection: Binding( 41 Picker("Format", selection: Binding(
42 get: { settings.format }, 42 get: { settings.format },
43 set: { newValue in 43 set: { newValue in
44 settings.format = newValue 44 settings.format = newValue
45 saveSettings() 45 saveSettings()
46 }
47 )) {
48 ForEach([DomainExportFormat.markdown, .pdf, .json]) { format in
49 Text(format.title).tag(format)
50 }
51 } 46 }
47 )) {
48 ForEach([DomainExportFormat.markdown, .pdf, .json]) { format in
49 Text(format.title).tag(format)
50 }
51 }
52 .disabled(!FeatureAccessService.hasAccess(to: .automatedMonitoring))
52 53
53 LabeledContent( 54 LabeledContent(
54 "Last Generated", 55 "Last Generated",
55 value: settings.lastGeneratedAt?.formatted(date: .abbreviated, time: .shortened) ?? "Never" 56 value: settings.lastGeneratedAt?.formatted(date: .abbreviated, time: .shortened) ?? "Never"
56 ) 57 )
57 58
58 if let statusMessage { 59 if let statusMessage {
59 Text(statusMessage) 60 Text(statusMessage)
60 .font(appDensity.font(.caption)) 61 .font(appDensity.font(.caption))
61 .foregroundStyle(.secondary) 62 .foregroundStyle(.secondary)
62 } 63 }
63 64
64 Button(isGenerating ? "Generating…" : "Generate Now") { 65 Button(isGenerating ? "Generating…" : "Generate Now") {
65 Task { await generateNow() } 66 Task { await generateNow() }
66 }
67 .disabled(isGenerating)
68 } 67 }
69 .padding(.vertical, 4) 68 .disabled(isGenerating || !FeatureAccessService.hasAccess(to: .automatedMonitoring))
70 } 69 }
71 .listRowBackground(Color(.systemGray6).opacity(0.5)) 70 .listRowBackground(Color(.systemGray6).opacity(0.5))
72 71