Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Internal: extend `AppServices` with `appSettingsStorage`, `schemaProviderRegistry`, `aiKeyStorage`, `groupStorage`, `favoritesExpansionState`, and `linkedFolderWatcher`. Convert the raw `.shared` reads of those types in `AIChatViewModel`, `FavoritesSidebarViewModel`, `WelcomeViewModel`, and `MainContentCoordinator` to `services.*`.
- 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`).
- 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.
- 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.
- 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.*`.
- 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.
- 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.
Expand Down
8 changes: 6 additions & 2 deletions TablePro/Core/Events/AppEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ final class AppEvents {

let connectionStatusChanged = PassthroughSubject<ConnectionStatusChange, Never>()

let connectionUpdated = PassthroughSubject<Void, Never>()
/// Connection metadata changed (name, color, group, type, etc.).
/// Payload is the affected connection's id, or `nil` for bulk updates
/// (sync pull, multi-import) where the sender doesn't track individual ids.
/// Subscribers scoped to a single connection should filter `payload == id`;
/// list-level subscribers refresh on every event regardless.
let connectionUpdated = PassthroughSubject<UUID?, Never>()

let databaseDidConnect = PassthroughSubject<DatabaseDidConnect, Never>()

Expand Down Expand Up @@ -74,4 +79,3 @@ struct ConnectionStatusChange: Sendable {
struct DatabaseDidConnect: Sendable {
let connectionId: UUID
}

Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ enum ConnectionExportService {
}

if importedCount > 0 {
AppEvents.shared.connectionUpdated.send(())
AppEvents.shared.connectionUpdated.send(nil)
logger.info("Imported \(importedCount) connections")
}

Expand Down
2 changes: 1 addition & 1 deletion TablePro/Core/Sync/SyncCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ final class SyncCoordinator {
}

if actualConnectionChanges || groupsOrTagsChanged {
services.appEvents.connectionUpdated.send(())
services.appEvents.connectionUpdated.send(nil)
}
}

Expand Down
4 changes: 2 additions & 2 deletions TablePro/ViewModels/WelcomeViewModel+Sample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal enum SampleDatabaseLauncher {
installedURL: installedURL,
connectionStorage: connectionStorage
)
AppEvents.shared.connectionUpdated.send(())
AppEvents.shared.connectionUpdated.send(connection.id)
bumpSampleOpenedCounter()
launchSampleConnection(connection, onError: onError)
}
Expand Down Expand Up @@ -174,7 +174,7 @@ internal enum SampleDatabaseLauncher {
if let sampleConnection {
do {
try await DatabaseManager.shared.ensureConnected(sampleConnection)
AppEvents.shared.connectionUpdated.send(())
AppEvents.shared.connectionUpdated.send(sampleConnection.id)
} catch {
logger.warning(
"Reopening sample after reset failed: \(error.localizedDescription, privacy: .public)"
Expand Down
4 changes: 2 additions & 2 deletions TablePro/Views/Connection/WelcomeContextMenus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ extension WelcomeWindowView {
updated.localOnly = !allLocalOnly
ConnectionStorage.shared.updateConnection(updated)
}
AppEvents.shared.connectionUpdated.send(())
AppEvents.shared.connectionUpdated.send(nil)
} label: {
Label(
allLocalOnly
Expand Down Expand Up @@ -173,7 +173,7 @@ extension WelcomeWindowView {
var updated = connection
updated.localOnly.toggle()
ConnectionStorage.shared.updateConnection(updated)
AppEvents.shared.connectionUpdated.send(())
AppEvents.shared.connectionUpdated.send(connection.id)
} label: {
Label(
connection.localOnly
Expand Down
6 changes: 3 additions & 3 deletions TablePro/Views/ConnectionForm/ConnectionFormCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ final class ConnectionFormCoordinator {
let connection = storage.loadConnections().first(where: { $0.id == id }) else { return }
storage.deleteConnection(connection)
dismissAction?()
services.appEvents.connectionUpdated.send(())
services.appEvents.connectionUpdated.send(connection.id)
}

// MARK: - Type change
Expand Down Expand Up @@ -317,7 +317,7 @@ final class ConnectionFormCoordinator {
services.syncTracker.markDirty(.connection, id: connectionToSave.id.uuidString)
}
dismissAction?()
services.appEvents.connectionUpdated.send(())
services.appEvents.connectionUpdated.send(connectionToSave.id)
if connect {
connectToDatabase(connectionToSave)
}
Expand All @@ -335,7 +335,7 @@ final class ConnectionFormCoordinator {
services.syncTracker.markDirty(.connection, id: connectionToSave.id.uuidString)
}
dismissAction?()
services.appEvents.connectionUpdated.send(())
services.appEvents.connectionUpdated.send(connectionToSave.id)
}
}

Expand Down
Loading