Conversation
A star toggle on each connection row in the sidebar, persisted to the storage facade's new favorite_connections collection. Favorited connections render together in their own group above the rest of the list, which keeps exactly the order and behaviour it always had - this only ever pulls entries out of it, never reorders or filters what remains. Deliberately a separate id set rather than a field on DatabaseConnection: a managed:true connection is always taken fresh from the server on every load (mergeManagedConnections in use-connection-manager.ts), so a field on the connection itself would be silently discarded on reload for exactly the connections a user is most likely to want to favorite. A separate id list favorites correctly regardless of who owns the connection, and a duplicated connection (new id) does not inherit the original's favorite status for free. useFavoriteConnections is built on useSyncExternalStore rather than an effect that reads storage and calls setState - favorite_connections is exactly that: state that lives outside React, mutated by the storage facade - so a favorite pulled down from the server, or toggled from another mounted instance of the hook, is reflected without a synchronous setState-in-effect render cascade (caught by react-hooks/set-state-in-effect on the first draft). The new collection is wired into every place that treats STORAGE_COLLECTIONS as the full set: the server-sync pull path and the collection-data switch in use-storage-sync.ts, alongside the existing dismissed_seeds entry. Testing: bun run format, lint, typecheck, knip, chart:check, channels:showcase:check, readme:check, security:check, and build all pass. bun run test:coverage + coverage:check: 100.00% (47133/47133 lines) on the merged report; every file this PR touches is individually at 100%, including the SSR-only getServerSnapshot branch in use-favorite-connections.ts (exercised via ReactDOMServer.renderToString, matching the existing pattern in use-line-numbers-preference.test.ts). bun run test itself still fails locally - 195 pre-existing failures, all either Helm chart tests (no `helm` binary in this environment) or DB-provider integration tests that need a live database connection; verified identical on a clean, unmodified checkout via git stash before starting this change. None are in a file this PR touches.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ctions # Conflicts: # src/components/Studio.tsx # src/components/sidebar/Sidebar.tsx
cevheri
left a comment
There was a problem hiding this comment.
Nice work, @Asgabani. I ran it in the app: starring, the Favorites group, persistence across a reload, unstarring, and the star not selecting the row all behave. Favoriting a managed seed connection survives a reload too, so the reasoning you wrote in types.ts for a separate id list rather than a field on DatabaseConnection is right, and it is wired at all five sites dismissed_seeds occupies. Three things before I merge.
-
storage.deleteConnectiondoes not prunefavorite_connections. I deleted a favorited connection and its id was still in the list afterwards, and it keeps getting pushed to server storage.deleteConnectionis already where a sibling id list is maintained on delete:dismissed_seedsis recorded there. Favorites are the inverse, so they should be removed there, with a test. -
Star every connection and the "Connections" header renders above nothing.
restis empty whileconnectionsis not, and the empty state is correctly suppressed, so the section itself needs to go whenrestis empty. Note the guard can be justconnections.length === 0, sincerestis a subset ofconnections. -
docs/STORAGE.mdis the collection inventory and it still describes ten. The collection list in the schema table, the collection reference table, the localStorage key map, the facade API row, and two places that spell the count. Two code comments count them too:encrypting-provider.tssays "the remaining six hold metadata", which is the written justification for encrypting onlyconnections, andconnection-secrets.tssays "all ten collections".
Two small ones while you are in there. The comment on useFavoriteConnections says a favorite pulled from the server arrives through the subscription, but the pull writes localStorage directly and dispatches no change event; it works because storageReady flips after the pull and that re-render re-reads the snapshot. And the star is the first focusable control a managed row has ever had, so focus-visible:opacity-100 would keep it reachable for a keyboard user.
- deleteConnection now prunes the deleted id out of favorite_connections too, mirroring the existing dismissed_seeds handling. - ConnectionsList hides the "Connections" section entirely when every connection is favorited, instead of rendering its header above nothing. - docs/STORAGE.md's collection inventory (schema table, collection reference table, localStorage key map, facade API row, and both "10 collections" call-outs) now accounts for favorite_connections, along with the two code comments in encrypting-provider.ts and connection-secrets.ts that counted collections. - Corrected the useFavoriteConnections comment describing how a server-pulled favorite reaches the hook: the pull writes localStorage directly with no dispatched event: useSyncExternalStore simply re-reads getSnapshot on the re-render storageReady's own flip already causes. - Added focus-visible:opacity-100 to the star toggle so it stays reachable for keyboard users. - Updated tests/components/studio/source-tab.test.tsx's storage mock, which predates this branch's favorites work and was missing the two methods useFavoriteConnections calls.
|
Addressed all three:
Also fixed the two smaller ones: corrected the Rebased onto latest Ran the full local suite again: format/lint/typecheck/knip clean, all four drift guards pass, |
Closes #694.
A star toggle on each connection row in the sidebar, persisted to a new
favorite_connectionsstorage collection. Favorited connections render together in their own group above the rest of the list, which keeps exactly the order and behaviour it always had — toggling a favorite only ever pulls entries out of the regular list, never reorders or filters what remains.Why a separate id set, not a field on
DatabaseConnection: amanaged:trueconnection is always taken fresh from the server on every load, so a field on the connection object itself would be silently discarded on reload for exactly the connections a user is most likely to want to favorite. A separate id list favorites correctly regardless of who owns the connection, and a duplicated connection (new id) doesn't inherit the original's favorite status for free.Implementation:
useFavoriteConnectionsis built onuseSyncExternalStorerather than an effect that reads storage and callssetState—favorite_connectionsis exactly that: state living outside React, mutated by the storage facade — so a favorite pulled down from the server, or toggled from another mounted instance of the hook, is reflected without a synchronous setState-in-effect render cascade (caught byreact-hooks/set-state-in-effecton my first draft). The new collection is wired into every place that treatsSTORAGE_COLLECTIONSas the full set (server-sync pull path, the collection-data switch), alongside the existingdismissed_seedsentry.Testing
bun run format,lint,typecheck,knip,chart:check,channels:showcase:check,readme:check,security:check, andbuildall pass.bun run test:coverage+coverage:check: 100.00% (47133/47133 lines) on the merged report. Every file this PR touches is individually at 100%, including the SSR-onlygetServerSnapshotbranch (exercised viaReactDOMServer.renderToString, matching the existing pattern inuse-line-numbers-preference.test.ts).bun run testitself still fails locally — 195 pre-existing failures, all either Helm chart tests (nohelmbinary in my environment) or DB-provider integration tests needing a live database. Verified identical on a clean, unmodified checkout viagit stashbefore starting. None are in a file this PR touches.