Skip to content

Commit 330a37a

Browse files
authored
Merge pull request #610 from TableProApp/refactor/appstate-to-focused-value
refactor: migrate menu commands from AppState.shared to @focusedvalue
2 parents bfdc0b4 + 238c6ef commit 330a37a

10 files changed

Lines changed: 104 additions & 62 deletions

TablePro/ContentView.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ struct ContentView: View {
3131
@State private var windowTitle: String
3232
@Environment(\.openWindow)
3333
private var openWindow
34-
@Environment(AppState.self) private var appState
3534

3635
private let storage = ConnectionStorage.shared
3736

TablePro/Models/Connection/ConnectionToolbarState.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,15 @@ final class ConnectionToolbarState {
183183
/// Whether there are pending data grid changes (for SQL preview button)
184184
var hasDataPendingChanges: Bool = false
185185

186+
/// Whether the structure view has pending schema changes
187+
var hasStructureChanges: Bool = false
188+
189+
/// Whether the current editor has non-empty query text
190+
var hasQueryText: Bool = false
191+
192+
/// Whether the history panel is visible
193+
var isHistoryPanelVisible: Bool = false
194+
186195
/// Whether the SQL review popover is showing
187196
var showSQLReviewPopover: Bool = false
188197

TablePro/TableProApp.swift

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ final class AppState {
3636

3737
/// Custom Commands struct for pasteboard operations
3838
struct PasteboardCommands: Commands {
39-
var appState: AppState
4039
var settingsManager: AppSettingsManager
4140
@FocusedValue(\.commandActions) var actions: MainContentCommandActions?
4241

@@ -55,8 +54,8 @@ struct PasteboardCommands: Commands {
5554
Button("Copy") {
5655
let action = PasteboardActionRouter.resolveCopyAction(
5756
firstResponder: NSApp.keyWindow?.firstResponder,
58-
hasRowSelection: appState.hasRowSelection,
59-
hasTableSelection: appState.hasTableSelection
57+
hasRowSelection: actions?.hasRowSelection ?? false,
58+
hasTableSelection: actions?.hasTableSelection ?? false
6059
)
6160
switch action {
6261
case .textCopy:
@@ -73,18 +72,18 @@ struct PasteboardCommands: Commands {
7372
actions?.copySelectedRowsWithHeaders()
7473
}
7574
.optionalKeyboardShortcut(shortcut(for: .copyWithHeaders))
76-
.disabled(!appState.hasRowSelection)
75+
.disabled(!(actions?.hasRowSelection ?? false))
7776

7877
Button("Copy as JSON") {
7978
actions?.copySelectedRowsAsJson()
8079
}
8180
.optionalKeyboardShortcut(shortcut(for: .copyAsJson))
82-
.disabled(!appState.hasRowSelection)
81+
.disabled(!(actions?.hasRowSelection ?? false))
8382

8483
Button("Paste") {
8584
let action = PasteboardActionRouter.resolvePasteAction(
8685
firstResponder: NSApp.keyWindow?.firstResponder,
87-
isCurrentTabEditable: appState.isCurrentTabEditable
86+
isCurrentTabEditable: actions?.isCurrentTabEditable ?? false
8887
)
8988
switch action {
9089
case .textPaste:
@@ -99,7 +98,7 @@ struct PasteboardCommands: Commands {
9998
actions?.deleteSelectedRows()
10099
}
101100
.optionalKeyboardShortcut(shortcut(for: .delete))
102-
.disabled(!appState.isCurrentTabEditable && !appState.hasTableSelection)
101+
.disabled(!(actions?.isCurrentTabEditable ?? false) && !(actions?.hasTableSelection ?? false))
103102

104103
Divider()
105104

@@ -122,7 +121,6 @@ struct PasteboardCommands: Commands {
122121
/// All menu commands extracted into a separate Commands struct so that AppState
123122
/// changes only re-evaluate the menu items — NOT the Scene body / WindowGroups.
124123
struct AppMenuCommands: Commands {
125-
var appState: AppState
126124
var settingsManager: AppSettingsManager
127125
var updaterBridge: UpdaterBridge
128126
@FocusedValue(\.commandActions) var actions: MainContentCommandActions?
@@ -169,62 +167,62 @@ struct AppMenuCommands: Commands {
169167
actions?.newTab()
170168
}
171169
.optionalKeyboardShortcut(shortcut(for: .newTab))
172-
.disabled(!appState.isConnected)
170+
.disabled(!(actions?.isConnected ?? false))
173171

174172
Button("New View...") {
175173
actions?.createView()
176174
}
177-
.disabled(!appState.isConnected || appState.isReadOnly)
175+
.disabled(!(actions?.isConnected ?? false) || actions?.isReadOnly ?? false)
178176

179177
Button("Open Database...") {
180178
actions?.openDatabaseSwitcher()
181179
}
182180
.optionalKeyboardShortcut(shortcut(for: .openDatabase))
183-
.disabled(!appState.isConnected || !appState.supportsDatabaseSwitching)
181+
.disabled(!(actions?.isConnected ?? false) || !(actions?.supportsDatabaseSwitching ?? false))
184182

185183
Button(String(localized: "Open File...")) {
186184
actions?.openSQLFile()
187185
}
188186
.optionalKeyboardShortcut(shortcut(for: .openFile))
189-
.disabled(!appState.isConnected)
187+
.disabled(!(actions?.isConnected ?? false))
190188

191189
Button("Switch Connection...") {
192190
NotificationCenter.default.post(name: .openConnectionSwitcher, object: nil)
193191
}
194192
.optionalKeyboardShortcut(shortcut(for: .switchConnection))
195-
.disabled(!appState.isConnected)
193+
.disabled(!(actions?.isConnected ?? false))
196194

197195
Button("Quick Switcher...") {
198196
actions?.openQuickSwitcher()
199197
}
200198
.optionalKeyboardShortcut(shortcut(for: .quickSwitcher))
201-
.disabled(!appState.isConnected)
199+
.disabled(!(actions?.isConnected ?? false))
202200

203201
Divider()
204202

205203
Button("Save Changes") {
206204
actions?.saveChanges()
207205
}
208206
.optionalKeyboardShortcut(shortcut(for: .saveChanges))
209-
.disabled(!appState.isConnected || appState.isReadOnly)
207+
.disabled(!(actions?.isConnected ?? false) || actions?.isReadOnly ?? false)
210208

211209
Button(String(localized: "Save As...")) {
212210
actions?.saveFileAs()
213211
}
214212
.optionalKeyboardShortcut(shortcut(for: .saveAs))
215-
.disabled(!appState.isConnected)
213+
.disabled(!(actions?.isConnected ?? false))
216214

217215
Button {
218216
actions?.previewSQL()
219217
} label: {
220-
if let dbType = appState.currentDatabaseType {
218+
if let dbType = actions?.currentDatabaseType {
221219
Text(String(format: String(localized: "Preview %@"), PluginManager.shared.queryLanguageName(for: dbType)))
222220
} else {
223221
Text("Preview SQL")
224222
}
225223
}
226224
.optionalKeyboardShortcut(shortcut(for: .previewSQL))
227-
.disabled(!appState.isConnected)
225+
.disabled(!(actions?.isConnected ?? false))
228226

229227
Button("Close Tab") {
230228
if let actions {
@@ -241,13 +239,13 @@ struct AppMenuCommands: Commands {
241239
NotificationCenter.default.post(name: .refreshData, object: nil)
242240
}
243241
.optionalKeyboardShortcut(shortcut(for: .refresh))
244-
.disabled(!appState.isConnected)
242+
.disabled(!(actions?.isConnected ?? false))
245243

246244
Button("Explain Query") {
247245
actions?.explainQuery()
248246
}
249247
.optionalKeyboardShortcut(shortcut(for: .explainQuery))
250-
.disabled(!appState.isConnected || !appState.hasQueryText)
248+
.disabled(!(actions?.isConnected ?? false) || !(actions?.hasQueryText ?? false))
251249

252250
Divider()
253251

@@ -265,19 +263,19 @@ struct AppMenuCommands: Commands {
265263
actions?.exportTables()
266264
}
267265
.optionalKeyboardShortcut(shortcut(for: .export))
268-
.disabled(!appState.isConnected)
266+
.disabled(!(actions?.isConnected ?? false))
269267

270268
Button("Export Results...") {
271269
actions?.exportQueryResults()
272270
}
273-
.disabled(!appState.isConnected)
271+
.disabled(!(actions?.isConnected ?? false))
274272

275-
if appState.currentDatabaseType.map({ PluginManager.shared.supportsImport(for: $0) }) ?? true {
273+
if actions.map({ PluginManager.shared.supportsImport(for: $0.currentDatabaseType) }) ?? true {
276274
Button("Import...") {
277275
actions?.importTables()
278276
}
279277
.optionalKeyboardShortcut(shortcut(for: .importData))
280-
.disabled(!appState.isConnected || appState.isReadOnly)
278+
.disabled(!(actions?.isConnected ?? false) || actions?.isReadOnly ?? false)
281279
}
282280
}
283281

@@ -312,7 +310,7 @@ struct AppMenuCommands: Commands {
312310
}
313311

314312
// Edit menu - pasteboard commands with FocusedValue support
315-
PasteboardCommands(appState: appState, settingsManager: settingsManager)
313+
PasteboardCommands(settingsManager: settingsManager)
316314

317315
// Edit menu - row operations (after pasteboard)
318316
CommandGroup(after: .pasteboard) {
@@ -322,13 +320,13 @@ struct AppMenuCommands: Commands {
322320
actions?.addNewRow()
323321
}
324322
.optionalKeyboardShortcut(shortcut(for: .addRow))
325-
.disabled(!appState.isCurrentTabEditable || appState.isReadOnly)
323+
.disabled(!(actions?.isCurrentTabEditable ?? false) || actions?.isReadOnly ?? false)
326324

327325
Button("Duplicate Row") {
328326
actions?.duplicateRow()
329327
}
330328
.optionalKeyboardShortcut(shortcut(for: .duplicateRow))
331-
.disabled(!appState.isCurrentTabEditable || appState.isReadOnly)
329+
.disabled(!(actions?.isCurrentTabEditable ?? false) || actions?.isReadOnly ?? false)
332330

333331
Divider()
334332

@@ -337,7 +335,7 @@ struct AppMenuCommands: Commands {
337335
actions?.truncateTables()
338336
}
339337
.optionalKeyboardShortcut(shortcut(for: .truncateTable))
340-
.disabled(!appState.hasTableSelection || appState.isReadOnly)
338+
.disabled(!(actions?.hasTableSelection ?? false) || actions?.isReadOnly ?? false)
341339
}
342340

343341
// View menu
@@ -346,53 +344,53 @@ struct AppMenuCommands: Commands {
346344
NSApp.sendAction(#selector(NSSplitViewController.toggleSidebar(_:)), to: nil, from: nil)
347345
}
348346
.optionalKeyboardShortcut(shortcut(for: .toggleTableBrowser))
349-
.disabled(!appState.isConnected)
347+
.disabled(!(actions?.isConnected ?? false))
350348

351349
Button("Toggle Inspector") {
352350
actions?.toggleRightSidebar()
353351
}
354352
.optionalKeyboardShortcut(shortcut(for: .toggleInspector))
355-
.disabled(!appState.isConnected)
353+
.disabled(!(actions?.isConnected ?? false))
356354

357355
Divider()
358356

359357
Button("Toggle Filters") {
360358
actions?.toggleFilterPanel()
361359
}
362360
.optionalKeyboardShortcut(shortcut(for: .toggleFilters))
363-
.disabled(!appState.isConnected || !appState.isTableTab)
361+
.disabled(!(actions?.isConnected ?? false) || !(actions?.isTableTab ?? false))
364362

365363
Button("Toggle History") {
366364
actions?.toggleHistoryPanel()
367365
}
368366
.optionalKeyboardShortcut(shortcut(for: .toggleHistory))
369-
.disabled(!appState.isConnected)
367+
.disabled(!(actions?.isConnected ?? false))
370368

371369
Divider()
372370

373371
Button("Toggle Results") {
374372
actions?.toggleResults()
375373
}
376374
.optionalKeyboardShortcut(shortcut(for: .toggleResults))
377-
.disabled(!appState.isConnected)
375+
.disabled(!(actions?.isConnected ?? false))
378376

379377
Button("Previous Result") {
380378
actions?.previousResultTab()
381379
}
382380
.optionalKeyboardShortcut(shortcut(for: .previousResultTab))
383-
.disabled(!appState.isConnected)
381+
.disabled(!(actions?.isConnected ?? false))
384382

385383
Button("Next Result") {
386384
actions?.nextResultTab()
387385
}
388386
.optionalKeyboardShortcut(shortcut(for: .nextResultTab))
389-
.disabled(!appState.isConnected)
387+
.disabled(!(actions?.isConnected ?? false))
390388

391389
Button("Close Result Tab") {
392390
actions?.closeResultTab()
393391
}
394392
.optionalKeyboardShortcut(shortcut(for: .closeResultTab))
395-
.disabled(!appState.isConnected)
393+
.disabled(!(actions?.isConnected ?? false))
396394

397395
Divider()
398396

@@ -418,7 +416,7 @@ struct AppMenuCommands: Commands {
418416
KeyEquivalent(Character(String(number))),
419417
modifiers: .command
420418
)
421-
.disabled(!appState.isConnected)
419+
.disabled(!(actions?.isConnected ?? false))
422420
}
423421

424422
Divider()
@@ -428,28 +426,28 @@ struct AppMenuCommands: Commands {
428426
NSApp.sendAction(#selector(NSWindow.selectPreviousTab(_:)), to: nil, from: nil)
429427
}
430428
.optionalKeyboardShortcut(shortcut(for: .showPreviousTabBrackets))
431-
.disabled(!appState.isConnected)
429+
.disabled(!(actions?.isConnected ?? false))
432430

433431
// Next tab (Cmd+Shift+]) — delegate to native macOS tab switching
434432
Button("Show Next Tab") {
435433
NSApp.sendAction(#selector(NSWindow.selectNextTab(_:)), to: nil, from: nil)
436434
}
437435
.optionalKeyboardShortcut(shortcut(for: .showNextTabBrackets))
438-
.disabled(!appState.isConnected)
436+
.disabled(!(actions?.isConnected ?? false))
439437

440438
// Previous tab (Cmd+Option+Left)
441439
Button("Previous Tab") {
442440
NSApp.sendAction(#selector(NSWindow.selectPreviousTab(_:)), to: nil, from: nil)
443441
}
444442
.optionalKeyboardShortcut(shortcut(for: .previousTabArrows))
445-
.disabled(!appState.isConnected)
443+
.disabled(!(actions?.isConnected ?? false))
446444

447445
// Next tab (Cmd+Option+Right)
448446
Button("Next Tab") {
449447
NSApp.sendAction(#selector(NSWindow.selectNextTab(_:)), to: nil, from: nil)
450448
}
451449
.optionalKeyboardShortcut(shortcut(for: .nextTabArrows))
452-
.disabled(!appState.isConnected)
450+
.disabled(!(actions?.isConnected ?? false))
453451
}
454452

455453
// Help menu
@@ -514,7 +512,6 @@ struct TableProApp: App {
514512
// Each native window-tab gets its own ContentView with independent state.
515513
WindowGroup(id: "main", for: EditorTabPayload.self) { $payload in
516514
ContentView(payload: payload)
517-
.environment(AppState.shared)
518515
.background(OpenWindowHandler())
519516
}
520517
.windowStyle(.automatic)
@@ -529,7 +526,6 @@ struct TableProApp: App {
529526

530527
.commands {
531528
AppMenuCommands(
532-
appState: AppState.shared,
533529
settingsManager: AppSettingsManager.shared,
534530
updaterBridge: updaterBridge
535531
)

TablePro/Views/Editor/QueryEditorView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import TableProPluginKit
1414
struct QueryEditorView: View {
1515
private static let logger = Logger(subsystem: "com.TablePro", category: "QueryEditorView")
1616

17-
@Environment(AppState.self) private var appState
1817

1918
@Binding var queryText: String
2019
@Binding var cursorPositions: [CursorPosition]
@@ -33,7 +32,7 @@ struct QueryEditorView: View {
3332
@State private var vimMode: VimMode = .normal
3433

3534
var body: some View {
36-
let hasQuery = appState.hasQueryText
35+
let hasQuery = !queryText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
3736

3837
VStack(alignment: .leading, spacing: 0) {
3938
// Editor header with toolbar (above editor, higher z-index)

TablePro/Views/Main/Child/MainEditorContentView.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ struct MainEditorContentView: View {
7777

7878
// MARK: - Environment
7979

80-
@Environment(AppState.self) private var appState
8180

8281
/// Returns the cached AnyChangeManager, creating it on first access.
8382
private var currentChangeManager: AnyChangeManager {
@@ -92,7 +91,7 @@ struct MainEditorContentView: View {
9291
// MARK: - Body
9392

9493
var body: some View {
95-
let isHistoryVisible = appState.isHistoryPanelVisible
94+
let isHistoryVisible = coordinator.toolbarState.isHistoryPanelVisible
9695

9796
VStack(spacing: 0) {
9897
// Native macOS window tabs replace the custom tab bar.
@@ -237,10 +236,10 @@ struct MainEditorContentView: View {
237236

238237
private func updateHasQueryText() {
239238
if let tab = tabManager.selectedTab, tab.tabType == .query {
240-
appState.hasQueryText = !tab.query.trimmingCharacters(in: .whitespacesAndNewlines)
239+
coordinator.toolbarState.hasQueryText = !tab.query.trimmingCharacters(in: .whitespacesAndNewlines)
241240
.isEmpty
242241
} else {
243-
appState.hasQueryText = false
242+
coordinator.toolbarState.hasQueryText = false
244243
}
245244
}
246245

0 commit comments

Comments
 (0)