Commit Graph

4 Commits

Author SHA1 Message Date
simoleo89 59d6c4cab3 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>
2026-05-13 21:50:56 +02:00
simoleo89 39eb2c6b84 Phase C (targeted): clear 4 set-state-in-effect violations on safe candidates
Fix only the cases that are unambiguous anti-patterns; leave the
event-driven setState patterns (useNitroEvent / useMessageEvent
subscriptions, async fetches with cleanup) alone since they're
legitimate in this architecture.

- src/components/catalog/views/catalog-header/CatalogHeaderView.tsx:
  displayImageUrl was pure-derived from imageUrl. Drop the useState +
  useEffect entirely; compute in render.
- src/components/navigator/views/NavigatorRoomCreatorView.tsx:
  the maxVisitors list (10..100 step 10) and roomModels/selectedModel
  came from static config; convert to module-level MAX_VISITORS_LIST
  constant + useState lazy initializers. Removes 2 init effects.
  setCategory(categories[0].id) is left as-is because categories
  arrives async from a hook.
- src/components/login/LoginView.tsx:
  Replace useEffect(() => setLocalError(null), [step]) with the
  React-recommended "track previous prop" render-time reset:
  if(prevStep !== step) { setPrevStep(step); setLocalError(null); }
  Same observable behavior, no extra render.
- src/components/room/widgets/choosers/ChooserWidgetView.tsx:
  Wrap the selectItem callback prop call in useEffectEvent so a
  parent re-render that changes selectItem identity doesn't
  re-fire the visualizer side-effects.

Net: 4 fewer set-state-in-effect violations; behavior preserved.
The remaining ~328 violations across the codebase are predominantly
legitimate event-bus / async-fetch patterns and need per-case
review with running app, not a sweep.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:52 +00:00
Lorenzune 954e477e47 feat: add builders club catalog ui flow 2026-04-07 14:40:51 +02:00
DuckieTM 7feb10ab15 🆙 Init V3 2026-01-31 09:10:52 +01:00