fix(datagrid): save the whole filter set when switching away from a table tab - #2827
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Switching away from a table tab rewrote that table's saved filters with only the rows the current query used.
docs/features/filtering.mdxsays filters survive a relaunch "unchecked rows included".Root cause
No single place owned the saved shape.
FilterCoordinatorhad two copied writers that savedfilters.filter(\.isValid).handleTabChangeskipped both and calledFilterSettingsStorage.saveLastFiltersdirectly withappliedFilters, which is the query's reading of the rows:.alldrops unchecked rows and.soloreturns one row forced on. The tab switch has savedappliedFilterssince #968 (v0.38.0). #1561 (v0.49.0) made unchecked rows part of the saved set by moving the two coordinator writers to the working set, and missed this third writer, so the loss has shipped since v0.49.0.Fix
One owner and one writer for the saved shape.
TabFilterState.persistedStatesits next toappliedFiltersinTableFilter.swift. With a commit it returns the valid working set, each row keeping its enabled flag, plusfilterLogicMode. With nothing committed it returns no rows.FilterCoordinator.saveLastFilters(of: QueryTab)replacessaveLastFilters(for: String). It guards the tab's table name and writespersistedState. It takes the tab instead of readingselectedTab, because during a switch the selection has already moved to the incoming tab.saveLastFiltersForActiveTabledelegates to it, andcommitFilterspassestabs[tabIndex].handleTabChangecallsfilterCoordinator.saveLastFilters(of: tabManager.tabs[oldIndex]).MainContentCoordinator+FilterStateforwarder, bothNavigation.swiftcallers and the discard path move to the new signature in the same commit.Why nothing committed saves nothing: the Clear button runs
clearAppliedFilters()and thenclearFiltersAndReload(), which deletes the table's saved filters on purpose, and restore commits whatever was saved as.all. Saving the rows while nothing is committed would write the cleared filters back and apply them on the next open. The investigation behind this PR assumed Clear kept the saved rows; the code says otherwise, and review round 1 caught it. With nothing committed, the tab switch now behaves as it did onmain.Knock-on for the other writers, which already went through the working set: in-place navigation, reusing the active tab and discard also save nothing while nothing is committed. Before, a Clear followed by opening another table in the same tab wrote the cleared rows back the same way. The other side of that: rows typed into a table that had nothing committed are no longer saved when you open another table in the same tab or discard. Before, they were saved and then applied on the next open, which is the unapplied-rows problem listed at the end.
Unchanged: two tabs on the same table still overwrite each other's saved filters, which
docs/features/tabs.mdxalready describes.Tests
MainContentCoordinatorTabSwitchTests, each on auserstable tab followed byhandleTabChangeto a query tab:tabSwitchSavesUncheckedFilterRows: rows [A on, B off] with commit.all. Saves both, B still off. Main saved only A.tabSwitchSavesSoloedFilterWorkingSet: rows [A, B off] with commit.solo(B). Saves [A, B], B still off. Main saved only B, forced on.tabSwitchAfterClearKeepsSavedFiltersCleared: Apply, then the Clear button's two steps (clearAppliedFiltersand the saved-filter delete its reload runs), then switch. Nothing is saved and the panel keeps both rows. This one passes onmaintoo; it guards the regression review round 1 found in the first version of this change.The first two set the commit on the tab directly rather than through Apply, so the switch is the only write to storage. That also means a writer reading
selectedTab(the query tab by then) would save nothing and fail them. Each test clears the saved filters for its own connection id.TabFilterStateTests:persistedStateKeepsWorkingSetForEveryCommit: keeps unchecked rows and drops invalid ones for.alland for.soloon either row.persistedStateIsEmptyWithNothingCommitted.persistedStateCarriesLogicMode.No UI automation: the change is which rows a storage write receives, and the coordinator tests drive the same
handleTabChangepath the tab strip does.Verification
All through
.claude/skills/fix-issue/scripts/verify.shon the committed tree.generate: PASSbuild TablePro: PASSlinton the 8 changed Swift files: PASS, 0 violationstest MainContentCoordinatorTabSwitchTests TabFilterStateTests FilterMoveTests FilterSettingsStorageTests TableScopedSettingsRegistryTests FilterRestoreTests: PASS, 99 executed, 99 passed, 0 failed. All six new cases ran by name.The spec also named a
TabSwitchTestssuite; no suite by that name exists, andMainContentCoordinatorTabSwitchTestsis the only one matching.Reviewed with the
code-reviewskill (Codex was unavailable), two rounds. Round 1 found two issues: the Clear regression, fixed in the second commit, and the unapplied-rows problem listed below. Round 2 found no bugs. It checked every caller ofsaveLastFilters(of:)and every state that leavescommitnil (Clear, Remove All Filters, removing a soloed row, the always-hide panel setting, restored background tabs, find-bar search). In each of them the old tab-switch save ofappliedFilterswas already empty.Found while investigating #2820
This came out of the collateral sweep during the #2820 investigation (holding the session driver while a database switch reconnects). It is unrelated to that fix and ships on its own.
Found in review, not fixed here
resolvedRestoredStaterestores every saved row under.all, so it filters the grid on the next open.mainalready did this through in-place navigation, reusing the active tab and discard, which save the working set by the documented contract. Fixing it means saving which rows were applied and restoring that instead of.all. That changes the stored format and the restore rules, so it belongs in its own change.https://claude.ai/code/session_01SNC28GzXZvLwbgAB8G583A