Skip to content

Commit 89b79ec

Browse files
authored
refactor(events): scope sqlFavoritesDidUpdate by connection id (#1160)
1 parent 83e51c9 commit 89b79ec

5 files changed

Lines changed: 25 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
- Internal: thread `KeychainHelper` through `ConnectionStorage`, `SSHProfileStorage`, `AIKeyStorage`, and `LicenseStorage` via init (default `.shared`). The 24 raw `KeychainHelper.shared` reads inside those classes are gone, matching the existing injected-dependency pattern (`syncTracker`, `appSettings`).
3737
- Internal: extend `AppServices` with `queryHistoryManager`, `dateFormattingService`, `copilotService`, and `mcpServerManager`. `AppSettingsManager` now takes its eight cross-singleton dependencies (`AppSettingsStorage`, `ThemeEngine`, `SyncChangeTracker`, `AppEvents`, `DateFormattingService`, `QueryHistoryManager`, `MCPServerManager`, `CopilotService`) via init with `.shared` defaults. The 32 raw `.shared` reads inside `AppSettingsManager` are gone, including the four `Task { ... }` capture closures that previously dialed `CopilotService.shared` / `MCPServerManager.shared` mid-`didSet`. The 49 caller-side `AppSettingsManager.shared` reads are unchanged in this PR; they will be threaded as their owners adopt `services` in subsequent waves.
3838
- Internal: `AppEvents.connectionUpdated` payload changes from `Void` to `UUID?`. Single-connection senders (`ConnectionFormCoordinator`, sample-DB launcher, per-connection iCloud-sync toggle) now pass the affected id; bulk senders (sync pull, multi-import, multi-select sync toggle) pass `nil`. The current `WelcomeViewModel` subscriber refreshes on every event regardless, but per-connection subscribers added in the future can filter by id without re-shaping the bus.
39+
- Internal: `AppEvents.sqlFavoritesDidUpdate` payload changes from `Void` to `UUID?` and the per-connection subscribers (`ConnectionDataCache`, `SQLEditorView`) now filter on it. Previously, editing a favorite for connection A in one window forced `ConnectionDataCache` for every other open connection to refetch its favorite list and `SQLEditorView` for every other window to refresh its keyword map. The senders pass `favorite.connectionId` / `folder.connectionId` for adds and updates, and `nil` for deletes (where the affected connection isn't easily recoverable post-delete). `nil` payloads still trigger a refresh on every subscriber, preserving correctness for cross-connection favorites and bulk deletes.
3940
- Internal: extend `AppServices` with `tagStorage`, `sshProfileStorage`, `licenseManager`, `conflictResolver`, and `syncMetadataStorage`. `SyncCoordinator` now takes `services: AppServices` in init (default `.live`); 34 raw `.shared` reads of those types inside `SyncCoordinator` are routed through `services.*`.
4041
- Internal: Redis sidebar key tree uses SwiftUI `OutlineGroup` instead of recursive `DisclosureGroup` + `ForEach` wrapped in `AnyView`. Expansion state is now managed natively per branch identifier; the explicit `expandedPrefixes` set is gone.
4142
- Result-grid cells render via direct `draw(_:)` on a layer-backed `NSView` instead of an `NSTableCellView` wrapping an `NSTextField` plus an `NSButton` accessory. Per cell during scroll there is no Auto Layout solving, no `NSTextField` re-layout, and no `NSButton` tracking-area work. Editing for plain-text columns now opens the overlay editor (the same surface previously used for multi-line cells) rather than an inline text field.

TablePro/Core/Events/AppEvents.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ final class AppEvents {
4848

4949
let queryHistoryDidUpdate = PassthroughSubject<Void, Never>()
5050

51-
let sqlFavoritesDidUpdate = PassthroughSubject<Void, Never>()
51+
/// SQL favorites or favorite folders changed.
52+
/// Payload is the affected connection's id, or `nil` for cross-connection
53+
/// favorites (`favorite.connectionId == nil`) and bulk operations
54+
/// (multi-favorite delete) where the sender doesn't track a single id.
55+
/// Per-connection subscribers should refresh on `payload == nil || payload == self.connectionId`.
56+
let sqlFavoritesDidUpdate = PassthroughSubject<UUID?, Never>()
5257

5358
let linkedFoldersDidUpdate = PassthroughSubject<Void, Never>()
5459

TablePro/Core/Storage/SQLFavoriteManager.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,31 @@ internal final class SQLFavoriteManager: @unchecked Sendable {
2323
func addFavorite(_ favorite: SQLFavorite) async -> Bool {
2424
let result = await storage.addFavorite(favorite)
2525
if result {
26-
postUpdateNotification()
26+
postUpdateNotification(connectionId: favorite.connectionId)
2727
}
2828
return result
2929
}
3030

3131
func updateFavorite(_ favorite: SQLFavorite) async -> Bool {
3232
let result = await storage.updateFavorite(favorite)
3333
if result {
34-
postUpdateNotification()
34+
postUpdateNotification(connectionId: favorite.connectionId)
3535
}
3636
return result
3737
}
3838

3939
func deleteFavorite(id: UUID) async -> Bool {
4040
let result = await storage.deleteFavorite(id: id)
4141
if result {
42-
postUpdateNotification()
42+
postUpdateNotification(connectionId: nil)
4343
}
4444
return result
4545
}
4646

4747
func deleteFavorites(ids: [UUID]) async {
4848
let result = await storage.deleteFavorites(ids: ids)
4949
if result {
50-
postUpdateNotification()
50+
postUpdateNotification(connectionId: nil)
5151
}
5252
}
5353

@@ -68,23 +68,23 @@ internal final class SQLFavoriteManager: @unchecked Sendable {
6868
func addFolder(_ folder: SQLFavoriteFolder) async -> Bool {
6969
let result = await storage.addFolder(folder)
7070
if result {
71-
postUpdateNotification()
71+
postUpdateNotification(connectionId: folder.connectionId)
7272
}
7373
return result
7474
}
7575

7676
func updateFolder(_ folder: SQLFavoriteFolder) async -> Bool {
7777
let result = await storage.updateFolder(folder)
7878
if result {
79-
postUpdateNotification()
79+
postUpdateNotification(connectionId: folder.connectionId)
8080
}
8181
return result
8282
}
8383

8484
func deleteFolder(id: UUID) async -> Bool {
8585
let result = await storage.deleteFolder(id: id)
8686
if result {
87-
postUpdateNotification()
87+
postUpdateNotification(connectionId: nil)
8888
}
8989
return result
9090
}
@@ -150,9 +150,9 @@ internal final class SQLFavoriteManager: @unchecked Sendable {
150150

151151
// MARK: - Notifications
152152

153-
private func postUpdateNotification() {
153+
private func postUpdateNotification(connectionId: UUID?) {
154154
Task { @MainActor in
155-
AppEvents.shared.sqlFavoritesDidUpdate.send(())
155+
AppEvents.shared.sqlFavoritesDidUpdate.send(connectionId)
156156
}
157157
}
158158
}

TablePro/ViewModels/ConnectionDataCache.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ internal final class ConnectionDataCache {
3939

4040
AppEvents.shared.sqlFavoritesDidUpdate
4141
.receive(on: RunLoop.main)
42-
.sink { [weak self] _ in self?.scheduleRefresh() }
42+
.sink { [weak self] payload in
43+
guard let self else { return }
44+
guard payload == nil || payload == self.connectionId else { return }
45+
self.scheduleRefresh()
46+
}
4347
.store(in: &cancellables)
4448

4549
AppEvents.shared.linkedSQLFoldersDidUpdate

TablePro/Views/Editor/SQLEditorView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,10 @@ struct SQLEditorView: View {
162162
}
163163
AppEvents.shared.sqlFavoritesDidUpdate
164164
.receive(on: RunLoop.main)
165-
.sink { _ in refresh() }
165+
.sink { payload in
166+
guard payload == nil || payload == connectionId else { return }
167+
refresh()
168+
}
166169
.store(in: &favoritesCancellables)
167170
AppEvents.shared.linkedSQLFoldersDidUpdate
168171
.receive(on: RunLoop.main)

0 commit comments

Comments
 (0)