Skip to content

Commit a7083cc

Browse files
committed
feat: pre-apply FK filter when navigating to referenced table
1 parent 87a1b73 commit a7083cc

6 files changed

Lines changed: 45 additions & 4 deletions

File tree

TablePro/Core/Services/Infrastructure/SessionStateFactory.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ enum SessionStateFactory {
7171
if payload.showStructure {
7272
tabMgr.tabs[index].showStructure = true
7373
}
74+
if let initialFilter = payload.initialFilterState {
75+
tabMgr.tabs[index].filterState = initialFilter
76+
filterMgr.restoreFromTabState(initialFilter)
77+
}
7478
}
7579
} else {
7680
tabMgr.addTab(databaseName: payload.databaseName ?? connection.database)

TablePro/Models/Database/TableFilter.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ enum FilterOperator: String, CaseIterable, Identifiable, Codable {
7171
}
7272

7373
/// Represents a single table filter condition
74-
struct TableFilter: Identifiable, Equatable, Codable {
74+
struct TableFilter: Identifiable, Equatable, Hashable, Codable {
7575
let id: UUID
7676
var columnName: String // Column to filter on, or "__RAW__" for raw SQL
7777
var filterOperator: FilterOperator
@@ -151,7 +151,7 @@ struct TableFilter: Identifiable, Equatable, Codable {
151151
}
152152

153153
/// Stores per-tab filter state (preserves filters when switching tabs)
154-
struct TabFilterState: Equatable, Codable {
154+
struct TabFilterState: Equatable, Hashable, Codable {
155155
var filters: [TableFilter]
156156
var appliedFilters: [TableFilter]
157157
var isVisible: Bool

TablePro/Models/Query/EditorTabPayload.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ internal struct EditorTabPayload: Codable, Hashable {
3131
internal let skipAutoExecute: Bool
3232
/// Whether this tab is a preview (temporary) tab
3333
internal let isPreview: Bool
34+
/// Initial filter state (for FK navigation — pre-applies a WHERE filter)
35+
internal let initialFilterState: TabFilterState?
3436

3537
internal init(
3638
id: UUID = UUID(),
@@ -42,7 +44,8 @@ internal struct EditorTabPayload: Codable, Hashable {
4244
isView: Bool = false,
4345
showStructure: Bool = false,
4446
skipAutoExecute: Bool = false,
45-
isPreview: Bool = false
47+
isPreview: Bool = false,
48+
initialFilterState: TabFilterState? = nil
4649
) {
4750
self.id = id
4851
self.connectionId = connectionId
@@ -54,6 +57,7 @@ internal struct EditorTabPayload: Codable, Hashable {
5457
self.showStructure = showStructure
5558
self.skipAutoExecute = skipAutoExecute
5659
self.isPreview = isPreview
60+
self.initialFilterState = initialFilterState
5761
}
5862

5963
internal init(from decoder: Decoder) throws {
@@ -68,6 +72,7 @@ internal struct EditorTabPayload: Codable, Hashable {
6872
showStructure = try container.decodeIfPresent(Bool.self, forKey: .showStructure) ?? false
6973
skipAutoExecute = try container.decodeIfPresent(Bool.self, forKey: .skipAutoExecute) ?? false
7074
isPreview = try container.decodeIfPresent(Bool.self, forKey: .isPreview) ?? false
75+
initialFilterState = try container.decodeIfPresent(TabFilterState.self, forKey: .initialFilterState)
7176
}
7277

7378
/// Whether this payload is a "connection-only" payload — just a connectionId
@@ -89,5 +94,6 @@ internal struct EditorTabPayload: Codable, Hashable {
8994
self.showStructure = tab.showStructure
9095
self.skipAutoExecute = skipAutoExecute
9196
self.isPreview = false
97+
self.initialFilterState = nil
9298
}
9399
}

TablePro/Resources/Localizable.xcstrings

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13360,6 +13360,16 @@
1336013360
}
1336113361
}
1336213362
},
13363+
"Plugin was built with PluginKit version %lld, but version %lld or later is required. Please update the plugin." : {
13364+
"localizations" : {
13365+
"en" : {
13366+
"stringUnit" : {
13367+
"state" : "new",
13368+
"value" : "Plugin was built with PluginKit version %1$lld, but version %2$lld or later is required. Please update the plugin."
13369+
}
13370+
}
13371+
}
13372+
},
1336313373
"Plugins" : {
1336413374
"localizations" : {
1336513375
"vi" : {

TablePro/Views/Main/Extensions/MainContentCoordinator+FKNavigation.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,20 @@ extension MainContentCoordinator {
5252

5353
// If current tab has unsaved changes, open in a new native tab instead of replacing
5454
if changeManager.hasChanges {
55+
let fkFilterState = TabFilterState(
56+
filters: [filter],
57+
appliedFilters: [filter],
58+
isVisible: true,
59+
quickSearchText: "",
60+
filterLogicMode: .and
61+
)
5562
let payload = EditorTabPayload(
5663
connectionId: connection.id,
5764
tabType: .table,
5865
tableName: referencedTable,
5966
databaseName: currentDatabase,
60-
isView: false
67+
isView: false,
68+
initialFilterState: fkFilterState
6169
)
6270
WindowOpener.shared.openNativeTab(payload)
6371
return

TablePro/Views/Main/MainContentView.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,19 @@ struct MainContentView: View {
487487
{
488488
Task { await coordinator.switchDatabase(to: selectedTab.databaseName) }
489489
} else {
490+
if !selectedTab.filterState.appliedFilters.isEmpty,
491+
let tableName = selectedTab.tableName,
492+
let tabIndex = tabManager.selectedTabIndex
493+
{
494+
let filteredQuery = coordinator.queryBuilder.buildFilteredQuery(
495+
tableName: tableName,
496+
filters: selectedTab.filterState.appliedFilters,
497+
columns: selectedTab.resultColumns,
498+
limit: selectedTab.pagination.pageSize,
499+
offset: selectedTab.pagination.currentOffset
500+
)
501+
tabManager.tabs[tabIndex].query = filteredQuery
502+
}
490503
coordinator.executeTableTabQueryDirectly()
491504
}
492505
} else {

0 commit comments

Comments
 (0)