mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
catalog: three-way singleton-filter split + first 3 consumer migrations
Completes the useCatalog decomposition. After the previous commit
extracted the pure helpers, this one splits the singleton-via-useBetween
store into three slice-specific entry points and migrates a handful of
consumers as proof.
`src/hooks/catalog/useCatalog.ts`
- Internal `useCatalogState` → renamed to `useCatalogStore` and is no
longer exported. The full return shape is unchanged so callers that
still go through the shim see the exact same object.
- Three new exports built on top of the same `useBetween` instance:
- `useCatalogData()` — server-driven read-only slice (rootNode,
offersToNodes, currentPage, currentOffer, frontPageItems,
searchResult, roomPreviewer, isBusy, catalog localization
version, Builders Club counters + timers).
- `useCatalogUiState()` — UI ephemeral state + writers
(isVisible, pageId, previousPageId, currentType, activeNodes,
navigationHidden, purchaseOptions, catalogPlaceMultipleObjects,
plus every `set*` writer including the ones that mutate the
data slice on user-driven selection).
- `useCatalogActions()` — imperative operations only
(openCatalogByType, toggleCatalogByType, activateNode,
openPageBy{Id,Name,OfferId}, requestOfferToMover,
selectCatalogOffer, getNodeBy{Id,Name},
getBuilderFurniPlaceableStatus).
- `useCatalog` is kept as a deprecated shim that returns the full
historical surface, so the 48 existing consumers compile and run
unchanged.
Pilot consumer migrations (3 of 48):
- `CatalogBuildersClubStatusView` — Data (furni counters, seconds
timers) + UiState (currentType).
- `CatalogBreadcrumbView` — UiState (activeNodes) + Actions
(activateNode).
- `CatalogNavigationItemView` — UiState (currentType) + Actions
(activateNode).
Tests: `tests/useCatalog.filters.test.tsx` (5 cases).
`useBetween` is mocked via `vi.hoisted` so the four hooks share one
deterministic fake store — rendering the real `useCatalogStore`
would mount ~30 useState calls + open a fresh RoomPreviewer +
subscribe to a dozen renderer events, which is more than these
contract tests need.
- `useCatalogData` exposes exactly its read-only keys.
- `useCatalogUiState` exposes exactly its UI keys + setters.
- `useCatalogActions` exposes exactly its imperative ops (and
explicitly NOT data fields — proves no leak across slices).
- Singleton identity: callbacks read through the shim are `===` to
the ones read through the slices.
- Shim surface: the historical key set is still present so
un-migrated consumers don't silently break.
Suite: 163/163 (was 158/158). `yarn typecheck` green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+40
-4
@@ -480,9 +480,39 @@ Status after this round of work:
|
||||
| CatalogPagesList / CatalogPage | **deferred** — core state slice (rootNode / offersToNodes / currentPage), needs its own split-out store |
|
||||
| BuildersClubFurniCount / SubscriptionStatus | **deferred** — read by the internal `getBuilderFurniPlaceableStatus` logic, moves with the data/actions split |
|
||||
|
||||
Pure-helper extraction landed before the singleton split:
|
||||
`src/hooks/catalog/useCatalog.helpers.ts` hosts the dependency-free
|
||||
pieces previously inlined in the hook —
|
||||
**Helper extraction + filter split both landed.** The 1100-line hook
|
||||
now has its dependency-free logic in
|
||||
`src/hooks/catalog/useCatalog.helpers.ts` and exposes three public
|
||||
filters built on top of the same `useBetween` singleton:
|
||||
|
||||
- `useCatalogData()` — server-driven read-only slice (`rootNode`,
|
||||
`offersToNodes`, `currentPage`, `currentOffer`, `frontPageItems`,
|
||||
`searchResult`, `roomPreviewer`, `isBusy`,
|
||||
`catalogLocalizationVersion`, Builders Club counters + timers).
|
||||
- `useCatalogUiState()` — UI ephemeral state + writers
|
||||
(`isVisible`, `pageId`, `previousPageId`, `currentType`,
|
||||
`activeNodes`, `navigationHidden`, `purchaseOptions`,
|
||||
`catalogPlaceMultipleObjects`, plus all the `set*` writers,
|
||||
including the ones that mutate the data slice on page / offer /
|
||||
search-result selection).
|
||||
- `useCatalogActions()` — imperative operations
|
||||
(`openCatalogByType`, `toggleCatalogByType`, `activateNode`,
|
||||
`openPageBy{Id,Name,OfferId}`, `requestOfferToMover`,
|
||||
`selectCatalogOffer`, `getNodeBy{Id,Name}`,
|
||||
`getBuilderFurniPlaceableStatus`).
|
||||
|
||||
The internal store is named `useCatalogStore` and is **not exported**;
|
||||
the four public entry points (`useCatalogData` / `useCatalogUiState`
|
||||
/ `useCatalogActions` / `useCatalog`) all funnel into the same
|
||||
`useBetween` instance, so listeners + state register once. The
|
||||
deprecated `useCatalog` shim continues to expose the full historical
|
||||
return shape so the 48 existing consumers compile unchanged; they
|
||||
should be incrementally migrated to the specific filters as PRs
|
||||
touch them. Three pilot migrations already landed in
|
||||
`CatalogBuildersClubStatusView`, `CatalogBreadcrumbView`, and
|
||||
`CatalogNavigationItemView`.
|
||||
|
||||
Pure helpers in `useCatalog.helpers.ts`:
|
||||
|
||||
- `normalizeCatalogType(type?)` — coerce the optional catalog type
|
||||
back to `NORMAL` / `BUILDER`.
|
||||
@@ -512,7 +542,7 @@ empty-map / partial-bucket branches of the offer lookup).
|
||||
- Vitest 3 + jsdom + `@testing-library/react` + `@testing-library/jest-dom`
|
||||
configured. Separate `vitest.config.mts` so the runner doesn't drag in
|
||||
the renderer SDK aliases from `vite.config.mjs`.
|
||||
- **158 cases passing** across 11 test files. Pure-module suites:
|
||||
- **163 cases passing** across 12 test files. Pure-module suites:
|
||||
- `WiredCreatorTools.helpers.test.ts` (18) — formatters + snapshot
|
||||
factory.
|
||||
- `navigatorRoomCreatorStore.test.ts` (4) — Zustand store invariants
|
||||
@@ -539,6 +569,12 @@ empty-map / partial-bucket branches of the offer lookup).
|
||||
the partial-visible fallback), `buildCatalogNodeTree` (tree
|
||||
depth + offerId index), and the full decision tree of
|
||||
`resolveBuilderFurniPlaceableStatus`.
|
||||
- `useCatalog.filters.test.tsx` (5) — contract tests for the
|
||||
three-way singleton-filter split. Stubs `use-between` so the
|
||||
filters share one fake store, asserts each filter exposes
|
||||
exactly the keys it owns (no leak across slices), and pins
|
||||
down `===` identity of callbacks between the shim and each
|
||||
slice so the migration of the 48 consumers stays safe.
|
||||
|
||||
Component-/hook-level suites (on the new renderer-SDK mock):
|
||||
- `WidgetErrorBoundary.test.tsx` (4) — happy path + caught render
|
||||
|
||||
Reference in New Issue
Block a user