From 0f9fa1203b3a581b51c40f2c65c069997170faa4 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 14 May 2026 20:05:44 +0200 Subject: [PATCH] catalog: migrate remaining 36 useCatalog() consumers to the three filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces every direct call to the deprecated useCatalog() shim with the targeted filter(s) (useCatalogData / useCatalogUiState / useCatalogActions). Each consumer now subscribes only to the slice it actually reads, which restores React Compiler memoization and stops catalog-wide re-renders whenever an unrelated key changes. Removes the now-unused useCatalog shim from useCatalog.ts and the shim-specific case in tests/useCatalog.filters.test.tsx. The "all four hooks observe the same singleton" test becomes "all three filters", since there is no shim left to compare against. useCatalogFavorites swaps its internal useCatalog() call for useCatalogUiState() (currentType lives in the UI slice). Updates CLAUDE.md and docs/ARCHITECTURE.md to reflect that all 48 historical consumers are migrated and the shim is gone. Vitest: 162/162 (was 163 — minus the deprecated-shim contract case). --- CLAUDE.md | 10 ++-- docs/ARCHITECTURE.md | 16 +++---- .../catalog/CatalogAdminContext.tsx | 4 +- src/components/catalog/CatalogClassicView.tsx | 6 ++- src/components/catalog/CatalogModernView.tsx | 6 ++- src/components/catalog/CatalogView.tsx | 4 +- .../views/admin/CatalogAdminOfferEditView.tsx | 4 +- .../views/admin/CatalogAdminPageEditView.tsx | 5 +- .../views/favorites/CatalogFavoritesView.tsx | 5 +- .../navigation/CatalogNavigationView.tsx | 4 +- .../page/common/CatalogGridOfferView.tsx | 5 +- .../views/page/common/CatalogSearchView.tsx | 5 +- .../layout/CatalogLayoutBadgeDisplayView.tsx | 4 +- .../CatalogLayoutBuildersClubBuyView.tsx | 4 +- .../layout/CatalogLayoutColorGroupingView.tsx | 5 +- .../page/layout/CatalogLayoutDefaultView.tsx | 4 +- .../CatalogLayoutGuildCustomFurniView.tsx | 4 +- .../layout/CatalogLayoutGuildForumView.tsx | 5 +- .../page/layout/CatalogLayoutRoomAdsView.tsx | 4 +- .../layout/CatalogLayoutSoundMachineView.tsx | 4 +- .../page/layout/CatalogLayoutSpacesView.tsx | 4 +- .../page/layout/CatalogLayoutTrophiesView.tsx | 5 +- .../page/layout/CatalogLayoutVipBuyView.tsx | 4 +- .../CatalogLayoutFrontpage4View.tsx | 4 +- .../page/layout/pets/CatalogLayoutPetView.tsx | 5 +- .../widgets/CatalogAddOnBadgeWidgetView.tsx | 4 +- .../CatalogBadgeSelectorWidgetView.tsx | 5 +- .../widgets/CatalogBundleGridWidgetView.tsx | 4 +- .../CatalogFirstProductSelectorWidgetView.tsx | 5 +- .../widgets/CatalogGuildBadgeWidgetView.tsx | 5 +- .../CatalogGuildSelectorWidgetView.tsx | 5 +- .../widgets/CatalogItemGridWidgetView.tsx | 5 +- .../widgets/CatalogLimitedItemWidgetView.tsx | 4 +- .../widgets/CatalogPriceDisplayWidgetView.tsx | 4 +- .../widgets/CatalogPurchaseWidgetView.tsx | 6 ++- .../widgets/CatalogSimplePriceWidgetView.tsx | 4 +- .../page/widgets/CatalogSpacesWidgetView.tsx | 5 +- .../page/widgets/CatalogSpinnerWidgetView.tsx | 5 +- .../page/widgets/CatalogTotalPriceWidget.tsx | 4 +- .../widgets/CatalogViewProductWidgetView.tsx | 5 +- src/hooks/catalog/useCatalog.ts | 8 ---- src/hooks/catalog/useCatalogFavorites.ts | 4 +- tests/useCatalog.filters.test.tsx | 48 +++++-------------- 43 files changed, 123 insertions(+), 137 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6a3981f..982ba69 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -259,15 +259,14 @@ into `configurePreviewServer` so `yarn preview` keeps working. | `useNitroQuery` + `useNitroEventInvalidator` | `OfferView`, `CatalogLayoutRoomAdsView`, `ModToolsChatlogView`, `CfhChatlogView`, `useGiftConfiguration`, `useUserGroups`, `useClubOffers(windowId)`, `useSellablePetPalette(breed)`, `useMarketplaceConfiguration`, `useClubGifts` (with invalidator) | | Zustand | `NavigatorRoomCreatorView` (`useRoomCreatorStore`) | | God-hook split (state + actions + shim) | `doorbell`, `poll`, `furni-chooser`, `user-chooser`, `friend-request`, `chat-input` | -| God-hook split (`useBetween` singleton + state filter + actions filter + shim) | `wired-tools`, `translation`, `notification`, `friends`, `catalog` (three-way: `useCatalogData` / `useCatalogUiState` / `useCatalogActions`, plus the `useCatalog` shim) | +| God-hook split (`useBetween` singleton + state filter + actions filter + shim) | `wired-tools`, `translation`, `notification`, `friends`, `catalog` (three-way: `useCatalogData` / `useCatalogUiState` / `useCatalogActions` — all 48 consumers migrated, deprecated `useCatalog` shim removed) | | `WidgetErrorBoundary` | `RoomWidgetsView` umbrella | -| Vitest | 163/163 cases — pure helpers + Zustand store + 2 component-/hook-level pilots (WidgetErrorBoundary, useDoorbellState) on top of the renderer-SDK mock at `tests/mocks/renderer-mock.ts`, 34 cases on the catalog pure helpers, 5 contract cases on the catalog filters | +| Vitest | 162/162 cases — pure helpers + Zustand store + 2 component-/hook-level pilots (WidgetErrorBoundary, useDoorbellState) on top of the renderer-SDK mock at `tests/mocks/renderer-mock.ts`, 34 cases on the catalog pure helpers, 4 contract cases on the catalog filters | | Form Actions | Login / Register / Forgot (LoginView.tsx) | | Cherry-picked from `duckietm` PR #126 | `UserAccountSettingsView` (reset password / email / username under user settings), plus the wear-badge popup `canShowWearButton` gating | | Not yet | Notes | |---|---| -| Migrate the 48 `useCatalog()` consumers to the new filters | The split is done: pure helpers in `useCatalog.helpers.ts`, three filters (`useCatalogData` / `useCatalogUiState` / `useCatalogActions`) plus the deprecated `useCatalog` shim. Three pilot consumers already migrated (`CatalogBuildersClubStatusView`, `CatalogBreadcrumbView`, `CatalogNavigationItemView`). The remaining 45 still hit the shim — incremental work, each migration is mechanical: split the destructure into 2-3 filter calls based on which keys are read. | | Split `useChatWidget` / `useAvatarInfoWidget` | Both state-driven via events with no clean imperative actions to extract — skip-motivated. Already touched today for the InfoStand listener move. | | Split `usePetPackageWidget` / `useWordQuizWidget` / `useChatCommandSelector` | Their "actions" mutate internal state or are tightly interdependent — skip-motivated. | | Hoist Wired Creator Tools shared state to a Zustand slice | Would remove ~25 props passed to the 3 tab sub-components. (Wired-tools split done as singleton-filter; Zustand slice is the next step.) | @@ -331,8 +330,9 @@ Fix shapes documented; both are reasonable PRs on their own. `getNodesByOfferIdFromMap`, `getOfferProductKeys`, `normalizeCatalogType`, `resolveBuilderFurniPlaceableStatus`) - Catalog three-way filter split: `useCatalogData` / - `useCatalogUiState` / `useCatalogActions` (with the deprecated - `useCatalog` shim) in `src/hooks/catalog/useCatalog.ts` + `useCatalogUiState` / `useCatalogActions` in + `src/hooks/catalog/useCatalog.ts` (all 48 consumers migrated; + deprecated `useCatalog` shim removed) - Renderer-SDK mock for Vitest: `tests/mocks/renderer-mock.ts` (aliased over `@nitrots/nitro-renderer` via `vitest.config.mts`). Hosts the explicit `NitroLogger` mock, the `mockEventDispatcher` / diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 8eafc58..57fca54 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -502,15 +502,11 @@ filters built on top of the same `useBetween` singleton: `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`. +the three public entry points (`useCatalogData` / `useCatalogUiState` +/ `useCatalogActions`) all funnel into the same `useBetween` +instance, so listeners + state register once. All 48 historical +consumers have been migrated to the targeted filters; the deprecated +`useCatalog` shim has been removed. Pure helpers in `useCatalog.helpers.ts`: @@ -569,7 +565,7 @@ 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 + - `useCatalog.filters.test.tsx` (4) — 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 diff --git a/src/components/catalog/CatalogAdminContext.tsx b/src/components/catalog/CatalogAdminContext.tsx index 2d27597..29e1f84 100644 --- a/src/components/catalog/CatalogAdminContext.tsx +++ b/src/components/catalog/CatalogAdminContext.tsx @@ -1,7 +1,7 @@ import { CatalogAdminCreateOfferComposer, CatalogAdminCreatePageComposer, CatalogAdminDeleteOfferComposer, CatalogAdminDeletePageComposer, CatalogAdminMoveOfferComposer, CatalogAdminMovePageComposer, CatalogAdminPublishComposer, CatalogAdminResultEvent, CatalogAdminSaveOfferComposer, CatalogAdminSavePageComposer } from '@nitrots/nitro-renderer'; import { createContext, FC, ReactNode, useCallback, useContext, useEffect, useRef, useState } from 'react'; import { ICatalogNode, IPurchasableOffer, NotificationAlertType, SendMessageComposer } from '../../api'; -import { useCatalog, useMessageEvent, useNotification } from '../../hooks'; +import { useCatalogUiState, useMessageEvent, useNotification } from '../../hooks'; export interface IPageEditData { @@ -76,7 +76,7 @@ export const useCatalogAdmin = () => useContext(CatalogAdminContext); export const CatalogAdminProvider: FC<{ children: ReactNode }> = ({ children }) => { - const { currentType } = useCatalog(); + const { currentType } = useCatalogUiState(); const [ adminMode, setAdminMode ] = useState(false); const [ editingOffer, setEditingOffer ] = useState(null); const [ editingPageData, setEditingPageData ] = useState(false); diff --git a/src/components/catalog/CatalogClassicView.tsx b/src/components/catalog/CatalogClassicView.tsx index 2a49735..9c3111c 100644 --- a/src/components/catalog/CatalogClassicView.tsx +++ b/src/components/catalog/CatalogClassicView.tsx @@ -3,7 +3,7 @@ import { FC, useEffect } from 'react'; import { FaCog, FaEdit, FaEye, FaEyeSlash, FaPlus, FaTrash } from 'react-icons/fa'; import { CatalogType, GetConfigurationValue, LocalizeText } from '../../api'; import { Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; -import { useCatalog } from '../../hooks'; +import { useCatalogActions, useCatalogData, useCatalogUiState } from '../../hooks'; import { CatalogAdminProvider, useCatalogAdmin } from './CatalogAdminContext'; import { CatalogAdminOfferEditView } from './views/admin/CatalogAdminOfferEditView'; import { CatalogAdminPageEditView } from './views/admin/CatalogAdminPageEditView'; @@ -16,7 +16,9 @@ import { MarketplacePostOfferView } from './views/page/layout/marketplace/Market const CatalogClassicViewInner: FC<{}> = () => { - const { isVisible = false, setIsVisible = null, rootNode = null, currentPage = null, navigationHidden = false, setNavigationHidden = null, activeNodes = [], searchResult = null, setSearchResult = null, openPageByName = null, openPageByOfferId = null, activateNode = null, openCatalogByType = null, toggleCatalogByType = null, currentType = CatalogType.NORMAL } = useCatalog(); + const { rootNode = null, currentPage = null, searchResult = null } = useCatalogData(); + const { isVisible = false, setIsVisible = null, navigationHidden = false, setNavigationHidden = null, activeNodes = [], setSearchResult = null, currentType = CatalogType.NORMAL } = useCatalogUiState(); + const { openPageByName = null, openPageByOfferId = null, activateNode = null, openCatalogByType = null, toggleCatalogByType = null } = useCatalogActions(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; const setAdminMode = catalogAdmin?.setAdminMode ?? (() => diff --git a/src/components/catalog/CatalogModernView.tsx b/src/components/catalog/CatalogModernView.tsx index 5e56267..12b8ac1 100644 --- a/src/components/catalog/CatalogModernView.tsx +++ b/src/components/catalog/CatalogModernView.tsx @@ -3,7 +3,7 @@ import { FC, useEffect, useState } from 'react'; import { FaCog, FaEdit, FaEye, FaEyeSlash, FaHeart, FaPlus, FaStar, FaTrash } from 'react-icons/fa'; import { CatalogType, LocalizeText } from '../../api'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common'; -import { useCatalog, useCatalogFavorites } from '../../hooks'; +import { useCatalogActions, useCatalogData, useCatalogFavorites, useCatalogUiState } from '../../hooks'; import { CatalogAdminProvider, useCatalogAdmin } from './CatalogAdminContext'; import { CatalogAdminOfferEditView } from './views/admin/CatalogAdminOfferEditView'; import { CatalogAdminPageEditView } from './views/admin/CatalogAdminPageEditView'; @@ -18,7 +18,9 @@ import { MarketplacePostOfferView } from './views/page/layout/marketplace/Market const CatalogModernViewInner: FC<{}> = () => { - const { isVisible = false, setIsVisible = null, rootNode = null, currentPage = null, navigationHidden = false, setNavigationHidden = null, activeNodes = [], searchResult = null, setSearchResult = null, openPageByName = null, openPageByOfferId = null, activateNode = null, openCatalogByType = null, toggleCatalogByType = null, currentType = CatalogType.NORMAL } = useCatalog(); + const { rootNode = null, currentPage = null, searchResult = null } = useCatalogData(); + const { isVisible = false, setIsVisible = null, navigationHidden = false, setNavigationHidden = null, activeNodes = [], setSearchResult = null, currentType = CatalogType.NORMAL } = useCatalogUiState(); + const { openPageByName = null, openPageByOfferId = null, activateNode = null, openCatalogByType = null, toggleCatalogByType = null } = useCatalogActions(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; const setAdminMode = catalogAdmin?.setAdminMode ?? (() => diff --git a/src/components/catalog/CatalogView.tsx b/src/components/catalog/CatalogView.tsx index a7ea8f1..2652fd6 100644 --- a/src/components/catalog/CatalogView.tsx +++ b/src/components/catalog/CatalogView.tsx @@ -1,12 +1,12 @@ import { FC } from 'react'; import { GetConfigurationValue } from '../../api'; -import { useCatalog } from '../../hooks'; +import { useCatalogData } from '../../hooks'; import { CatalogClassicView } from './CatalogClassicView'; import { CatalogModernView } from './CatalogModernView'; export const CatalogView: FC<{}> = () => { - const { catalogLocalizationVersion = 0 } = useCatalog(); + const { catalogLocalizationVersion = 0 } = useCatalogData(); const useNewStyle = GetConfigurationValue('catalog.style.new', false); if(useNewStyle) return ( diff --git a/src/components/catalog/views/admin/CatalogAdminOfferEditView.tsx b/src/components/catalog/views/admin/CatalogAdminOfferEditView.tsx index 32fec7f..c61c14b 100644 --- a/src/components/catalog/views/admin/CatalogAdminOfferEditView.tsx +++ b/src/components/catalog/views/admin/CatalogAdminOfferEditView.tsx @@ -2,12 +2,12 @@ import { FC, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import { FaSave, FaSpinner, FaTimes, FaTrash } from 'react-icons/fa'; import { LocalizeText } from '../../../../api'; -import { useCatalog } from '../../../../hooks'; +import { useCatalogData } from '../../../../hooks'; import { IOfferEditData, useCatalogAdmin } from '../../CatalogAdminContext'; export const CatalogAdminOfferEditView: FC<{}> = () => { - const { currentPage = null } = useCatalog(); + const { currentPage = null } = useCatalogData(); const catalogAdmin = useCatalogAdmin(); const editingOffer = catalogAdmin?.editingOffer ?? null; const setEditingOffer = catalogAdmin?.setEditingOffer; diff --git a/src/components/catalog/views/admin/CatalogAdminPageEditView.tsx b/src/components/catalog/views/admin/CatalogAdminPageEditView.tsx index 037af96..457a301 100644 --- a/src/components/catalog/views/admin/CatalogAdminPageEditView.tsx +++ b/src/components/catalog/views/admin/CatalogAdminPageEditView.tsx @@ -1,7 +1,7 @@ import { FC, useEffect, useState } from 'react'; import { FaSave, FaSpinner, FaTimes, FaTrash } from 'react-icons/fa'; import { CatalogType, LocalizeText } from '../../../../api'; -import { useCatalog } from '../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../hooks'; import { IPageEditData, useCatalogAdmin } from '../../CatalogAdminContext'; const LAYOUT_OPTIONS = [ @@ -21,7 +21,8 @@ const MODE_OPTIONS = [ export const CatalogAdminPageEditView: FC<{}> = () => { - const { currentPage = null, activeNodes = [], rootNode = null, currentType = CatalogType.NORMAL } = useCatalog(); + const { currentPage = null, rootNode = null } = useCatalogData(); + const { activeNodes = [], currentType = CatalogType.NORMAL } = useCatalogUiState(); const catalogAdmin = useCatalogAdmin(); const editingPageData = catalogAdmin?.editingPageData ?? false; const editingRootPage = catalogAdmin?.editingRootPage ?? false; diff --git a/src/components/catalog/views/favorites/CatalogFavoritesView.tsx b/src/components/catalog/views/favorites/CatalogFavoritesView.tsx index 6b403d3..0de88c5 100644 --- a/src/components/catalog/views/favorites/CatalogFavoritesView.tsx +++ b/src/components/catalog/views/favorites/CatalogFavoritesView.tsx @@ -1,7 +1,7 @@ import { FC, useMemo } from 'react'; import { FaHeart, FaStar, FaTimes } from 'react-icons/fa'; import { ICatalogNode, LocalizeText } from '../../../../api'; -import { useCatalog, useCatalogFavorites } from '../../../../hooks'; +import { useCatalogActions, useCatalogData, useCatalogFavorites } from '../../../../hooks'; import { CatalogIconView } from '../catalog-icon/CatalogIconView'; interface CatalogFavoritesViewProps @@ -13,7 +13,8 @@ export const CatalogFavoritesView: FC = props => { const { onClose } = props; const { favoriteOffers, favoritePageIds, toggleFavoritePage, toggleFavoriteOffer } = useCatalogFavorites(); - const { offersToNodes, activateNode, openPageByOfferId, rootNode } = useCatalog(); + const { offersToNodes, rootNode } = useCatalogData(); + const { activateNode, openPageByOfferId } = useCatalogActions(); const favoritePages = useMemo(() => { diff --git a/src/components/catalog/views/navigation/CatalogNavigationView.tsx b/src/components/catalog/views/navigation/CatalogNavigationView.tsx index 777c5fd..97907f6 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationView.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; import { ICatalogNode } from '../../../../api'; -import { useCatalog } from '../../../../hooks'; +import { useCatalogData } from '../../../../hooks'; import { CatalogNavigationItemView } from './CatalogNavigationItemView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView'; @@ -12,7 +12,7 @@ export interface CatalogNavigationViewProps export const CatalogNavigationView: FC = props => { const { node = null } = props; - const { searchResult = null } = useCatalog(); + const { searchResult = null } = useCatalogData(); return (
diff --git a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx index a634705..660f1ef 100644 --- a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx +++ b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx @@ -3,7 +3,7 @@ import { FC, MouseEvent, useMemo, useState } from 'react'; import { FaHeart } from 'react-icons/fa'; import { CatalogType, IPurchasableOffer, Offer, ProductTypeEnum } from '../../../../../api'; import { LayoutAvatarImageView, LayoutGridItem, LayoutGridItemProps } from '../../../../../common'; -import { useCatalog, useCatalogFavorites, useInventoryFurni } from '../../../../../hooks'; +import { useCatalogActions, useCatalogFavorites, useCatalogUiState, useInventoryFurni } from '../../../../../hooks'; interface CatalogGridOfferViewProps extends LayoutGridItemProps { @@ -15,7 +15,8 @@ export const CatalogGridOfferView: FC = props => { const { offer = null, selectOffer = null, itemActive = false, ...rest } = props; const [ isMouseDown, setMouseDown ] = useState(false); - const { requestOfferToMover = null, currentType = CatalogType.NORMAL } = useCatalog(); + const { requestOfferToMover = null } = useCatalogActions(); + const { currentType = CatalogType.NORMAL } = useCatalogUiState(); const { isVisible = false } = useInventoryFurni(); const { isFavoriteOffer, toggleFavoriteOffer } = useCatalogFavorites(); const isFav = offer ? isFavoriteOffer(offer.offerId) : false; diff --git a/src/components/catalog/views/page/common/CatalogSearchView.tsx b/src/components/catalog/views/page/common/CatalogSearchView.tsx index bb8583c..5fd9dca 100644 --- a/src/components/catalog/views/page/common/CatalogSearchView.tsx +++ b/src/components/catalog/views/page/common/CatalogSearchView.tsx @@ -2,12 +2,13 @@ import { GetSessionDataManager, IFurnitureData } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; import { FaSearch, FaTimes } from 'react-icons/fa'; import { CatalogPage, CatalogType, FilterCatalogNode, FurnitureOffer, ICatalogNode, ICatalogPage, IPurchasableOffer, LocalizeText, PageLocalization, SearchResult } from '../../../../../api'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; export const CatalogSearchView: FC<{}> = () => { const [ searchValue, setSearchValue ] = useState(''); - const { currentType = null, rootNode = null, searchResult = null, setSearchResult = null, setCurrentPage = null } = useCatalog(); + const { rootNode = null, searchResult = null } = useCatalogData(); + const { currentType = null, setSearchResult = null, setCurrentPage = null } = useCatalogUiState(); const normalizeSearchText = (value: string) => (value || '') .toLocaleLowerCase() diff --git a/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx index ee82e6e..d2ef2af 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx @@ -1,7 +1,7 @@ import { FC } from 'react'; import { LocalizeText, SanitizeHtml } from '../../../../../api'; import { Column, Grid, Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; import { CatalogBadgeSelectorWidgetView } from '../widgets/CatalogBadgeSelectorWidgetView'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; @@ -14,7 +14,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutBadgeDisplayView: FC = props => { const { page = null } = props; - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); return ( <> diff --git a/src/components/catalog/views/page/layout/CatalogLayoutBuildersClubBuyView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutBuildersClubBuyView.tsx index 19e8c2c..9c458fe 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutBuildersClubBuyView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutBuildersClubBuyView.tsx @@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { CatalogPurchaseState, LocalizeText, SanitizeHtml, SendMessageComposer } from '../../../../../api'; import { Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common'; import { CatalogEvent, CatalogPurchaseFailureEvent, CatalogPurchasedEvent } from '../../../../../events'; -import { useCatalog, useClubOffers, usePurse, useUiEvent } from '../../../../../hooks'; +import { useCatalogData, useClubOffers, usePurse, useUiEvent } from '../../../../../hooks'; import { CatalogHeaderView } from '../../catalog-header/CatalogHeaderView'; import { CatalogLayoutProps } from './CatalogLayout.types'; @@ -14,7 +14,7 @@ export const CatalogLayoutBuildersClubBuyView: FC = () => { const [ pendingOffer, setPendingOffer ] = useState(null); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); - const { currentPage = null } = useCatalog(); + const { currentPage = null } = useCatalogData(); const { getCurrencyAmount = null } = usePurse(); const isPurchasingRef = useRef(false); const isAddonLayout = (currentPage?.layoutCode === 'builders_club_addons'); diff --git a/src/components/catalog/views/page/layout/CatalogLayoutColorGroupingView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutColorGroupingView.tsx index 4c1944c..cb4c53a 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutColorGroupingView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutColorGroupingView.tsx @@ -3,7 +3,7 @@ import { FC, useMemo, useState } from 'react'; import { FaFillDrip } from 'react-icons/fa'; import { IPurchasableOffer, SanitizeHtml } from '../../../../../api'; import { AutoGrid, Button, Column, Grid, LayoutGridItem, Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogLimitedItemWidgetView } from '../widgets/CatalogLimitedItemWidgetView'; @@ -22,7 +22,8 @@ export const CatalogLayoutColorGroupingView: FC>(new Map()); - const { currentOffer = null, setCurrentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { setCurrentOffer = null } = useCatalogUiState(); const [ colorsShowing, setColorsShowing ] = useState(false); const sortByColorIndex = (a: IPurchasableOffer, b: IPurchasableOffer) => diff --git a/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx index e9417ab..f01401f 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx @@ -2,7 +2,7 @@ import { FC } from 'react'; import { FaEdit, FaPlus } from 'react-icons/fa'; import { GetConfigurationValue, LocalizeText, ProductTypeEnum, SanitizeHtml } from '../../../../../api'; import { Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; import { useCatalogAdmin } from '../../../CatalogAdminContext'; import { CatalogHeaderView } from '../../catalog-header/CatalogHeaderView'; import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView'; @@ -17,7 +17,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutDefaultView: FC = props => { const { page = null } = props; - const { currentOffer = null, currentPage = null } = useCatalog(); + const { currentOffer = null, currentPage = null } = useCatalogData(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx index cb8b6fa..6d28e1e 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx @@ -1,7 +1,7 @@ import { FC } from 'react'; import { SanitizeHtml } from '../../../../../api'; import { Column, Grid, Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; @@ -13,7 +13,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayouGuildCustomFurniView: FC = props => { const { page = null } = props; - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); return ( diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx index b7d9cf9..58baa3a 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx @@ -1,7 +1,7 @@ import { FC, useState } from 'react'; import { SanitizeHtml } from '../../../../../api'; import { Column, Grid, Text } from '../../../../../common'; -import { useCatalog, useUserGroups } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState, useUserGroups } from '../../../../../hooks'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; @@ -12,7 +12,8 @@ export const CatalogLayouGuildForumView: FC = props => { const { page = null } = props; const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { currentOffer = null, setCurrentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { setCurrentOffer = null } = useCatalogUiState(); const { data: groups = null } = useUserGroups(); return ( diff --git a/src/components/catalog/views/page/layout/CatalogLayoutRoomAdsView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutRoomAdsView.tsx index d9c6c87..8016c40 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutRoomAdsView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutRoomAdsView.tsx @@ -3,7 +3,7 @@ import { FC, useEffect, useState } from 'react'; import { LocalizeText, SendMessageComposer } from '../../../../../api'; import { useNitroQuery } from '../../../../../api/nitro-query'; import { Button, Column, Text } from '../../../../../common'; -import { useCatalog, useNavigator, useRoomPromote } from '../../../../../hooks'; +import { useCatalogUiState, useNavigator, useRoomPromote } from '../../../../../hooks'; import { NitroInput } from '../../../../../layout'; import { CatalogLayoutProps } from './CatalogLayout.types'; @@ -18,7 +18,7 @@ export const CatalogLayoutRoomAdsView: FC = props => const [ extended, setExtended ] = useState(false); const [ categoryId, setCategoryId ] = useState(1); const { categories = null } = useNavigator(); - const { setIsVisible = null } = useCatalog(); + const { setIsVisible = null } = useCatalogUiState(); const { promoteInformation, isExtended, setIsExtended } = useRoomPromote(); const { data: availableRooms = [] } = useNitroQuery({ diff --git a/src/components/catalog/views/page/layout/CatalogLayoutSoundMachineView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutSoundMachineView.tsx index 0aa131e..e6c12f4 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutSoundMachineView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutSoundMachineView.tsx @@ -2,7 +2,7 @@ import { GetOfficialSongIdMessageComposer, GetSoundManager, MusicPriorities, Off import { FC, useEffect, useState } from 'react'; import { GetConfigurationValue, LocalizeText, ProductTypeEnum, SanitizeHtml, SendMessageComposer } from '../../../../../api'; import { Button, Column, Grid, LayoutImage, Text } from '../../../../../common'; -import { useCatalog, useMessageEvent } from '../../../../../hooks'; +import { useCatalogData, useMessageEvent } from '../../../../../hooks'; import { CatalogHeaderView } from '../../catalog-header/CatalogHeaderView'; import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; @@ -18,7 +18,7 @@ export const CatalogLayoutSoundMachineView: FC = props => const { page = null } = props; const [ songId, setSongId ] = useState(-1); const [ officialSongId, setOfficialSongId ] = useState(''); - const { currentOffer = null, currentPage = null } = useCatalog(); + const { currentOffer = null, currentPage = null } = useCatalogData(); const previewSong = (previewSongId: number) => GetSoundManager().musicController?.playSong(previewSongId, MusicPriorities.PRIORITY_PURCHASE_PREVIEW, 15, 0, 0, 0); diff --git a/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx index 9cd18b6..3d5ccfe 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx @@ -1,7 +1,7 @@ import { FC, useEffect } from 'react'; import { SanitizeHtml } from '../../../../../api'; import { Column, Grid, Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; import { CatalogSpacesWidgetView } from '../widgets/CatalogSpacesWidgetView'; import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget'; @@ -11,7 +11,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutSpacesView: FC = props => { const { page = null } = props; - const { currentOffer = null, roomPreviewer = null } = useCatalog(); + const { currentOffer = null, roomPreviewer = null } = useCatalogData(); useEffect(() => { diff --git a/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx index 1e1c174..31299a0 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx @@ -2,7 +2,7 @@ import { FC, useEffect, useState } from 'react'; import { FaEdit, FaPen, FaPlus, FaTrophy } from 'react-icons/fa'; import { LocalizeText, ProductTypeEnum, SanitizeHtml } from '../../../../../api'; import { Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; import { useCatalogAdmin } from '../../../CatalogAdminContext'; import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; @@ -15,7 +15,8 @@ export const CatalogLayoutTrophiesView: FC = props => { const { page = null } = props; const [ trophyText, setTrophyText ] = useState(''); - const { currentOffer = null, setPurchaseOptions = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { setPurchaseOptions = null } = useCatalogUiState(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; diff --git a/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx index b63f4f6..09202de 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx @@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { CatalogPurchaseState, LocalizeText, SanitizeHtml, SendMessageComposer } from '../../../../../api'; import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common'; import { CatalogEvent, CatalogPurchaseFailureEvent, CatalogPurchasedEvent } from '../../../../../events'; -import { useCatalog, useClubOffers, usePurse, useUiEvent } from '../../../../../hooks'; +import { useCatalogData, useClubOffers, usePurse, useUiEvent } from '../../../../../hooks'; import { CatalogLayoutProps } from './CatalogLayout.types'; const VIP_WINDOW_ID = 1; @@ -12,7 +12,7 @@ export const CatalogLayoutVipBuyView: FC = props => { const [ pendingOffer, setPendingOffer ] = useState(null); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); - const { currentPage = null } = useCatalog(); + const { currentPage = null } = useCatalogData(); const { purse = null, getCurrencyAmount = null } = usePurse(); const { data: offers = null } = useClubOffers(VIP_WINDOW_ID); const isPurchasingRef = useRef(false); diff --git a/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx b/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx index a7e74b2..29ead13 100644 --- a/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx +++ b/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx @@ -1,7 +1,7 @@ import { CreateLinkEvent, FrontPageItem } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect } from 'react'; import { Column, Grid } from '../../../../../../common'; -import { useCatalog } from '../../../../../../hooks'; +import { useCatalogData } from '../../../../../../hooks'; import { CatalogRedeemVoucherView } from '../../common/CatalogRedeemVoucherView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView'; @@ -9,7 +9,7 @@ import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView export const CatalogLayoutFrontpage4View: FC = props => { const { page = null, hideNavigation = null } = props; - const { frontPageItems = [] } = useCatalog(); + const { frontPageItems = [] } = useCatalogData(); const selectItem = useCallback((item: FrontPageItem) => { diff --git a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx index 589d6bb..7081428 100644 --- a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx +++ b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx @@ -4,7 +4,7 @@ import { FaCheck, FaEdit, FaFillDrip, FaPaw, FaPlus, FaTimes } from 'react-icons import { DispatchUiEvent, GetPetAvailableColors, GetPetIndexFromLocalization, LocalizeText, SanitizeHtml, SendMessageComposer } from '../../../../../../api'; import { LayoutGridItem, LayoutPetImageView } from '../../../../../../common'; import { CatalogPurchaseFailureEvent } from '../../../../../../events'; -import { useCatalog, useMessageEvent, useSellablePetPalette } from '../../../../../../hooks'; +import { useCatalogData, useCatalogUiState, useMessageEvent, useSellablePetPalette } from '../../../../../../hooks'; import { useCatalogAdmin } from '../../../../CatalogAdminContext'; import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogTotalPriceWidget } from '../../widgets/CatalogTotalPriceWidget'; @@ -23,7 +23,8 @@ export const CatalogLayoutPetView: FC = props => const [ petName, setPetName ] = useState(''); const [ approvalPending, setApprovalPending ] = useState(true); const [ approvalResult, setApprovalResult ] = useState(-1); - const { currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null, roomPreviewer = null } = useCatalog(); + const { currentOffer = null, roomPreviewer = null } = useCatalogData(); + const { setCurrentOffer = null, setPurchaseOptions = null } = useCatalogUiState(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; const breed: string = (currentOffer?.product?.productData?.type as unknown as string) ?? ''; diff --git a/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx index fd3b652..657729b 100644 --- a/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; import { BaseProps, LayoutBadgeImageView } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; interface CatalogAddOnBadgeWidgetViewProps extends BaseProps { @@ -10,7 +10,7 @@ interface CatalogAddOnBadgeWidgetViewProps extends BaseProps export const CatalogAddOnBadgeWidgetView: FC = props => { const { ...rest } = props; - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); if(!currentOffer || !currentOffer.badgeCode || !currentOffer.badgeCode.length) return null; diff --git a/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx index 625b39f..8ef5c4d 100644 --- a/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx @@ -1,7 +1,7 @@ import { StringDataType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; import { AutoGrid, AutoGridProps, LayoutBadgeImageView, LayoutGridItem } from '../../../../../common'; -import { useCatalog, useInventoryBadges } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState, useInventoryBadges } from '../../../../../hooks'; const EXCLUDED_BADGE_CODES: string[] = []; @@ -15,7 +15,8 @@ export const CatalogBadgeSelectorWidgetView: FC(null); - const { currentOffer = null, setPurchaseOptions = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { setPurchaseOptions = null } = useCatalogUiState(); const { badgeCodes = [], activate = null, deactivate = null } = useInventoryBadges(); const previewStuffData = useMemo(() => diff --git a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx index a5cffce..cf77622 100644 --- a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useRef } from 'react'; import { AutoGrid, AutoGridProps, LayoutGridItem } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; interface CatalogBundleGridWidgetViewProps extends AutoGridProps { @@ -10,7 +10,7 @@ interface CatalogBundleGridWidgetViewProps extends AutoGridProps export const CatalogBundleGridWidgetView: FC = props => { const { columnCount = 5, children = null, ...rest } = props; - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); const elementRef = useRef(null); useEffect(() => diff --git a/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx index 855a175..c5e9542 100644 --- a/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx @@ -1,9 +1,10 @@ import { FC, useEffect } from 'react'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; export const CatalogFirstProductSelectorWidgetView: FC<{}> = props => { - const { currentPage = null, setCurrentOffer = null } = useCatalog(); + const { currentPage = null } = useCatalogData(); + const { setCurrentOffer = null } = useCatalogUiState(); useEffect(() => { diff --git a/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx index d56958e..c69c895 100644 --- a/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx @@ -1,7 +1,7 @@ import { StringDataType } from '@nitrots/nitro-renderer'; import { FC, useMemo } from 'react'; import { BaseProps, LayoutBadgeImageView } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; interface CatalogGuildBadgeWidgetViewProps extends BaseProps { @@ -11,7 +11,8 @@ interface CatalogGuildBadgeWidgetViewProps extends BaseProps export const CatalogGuildBadgeWidgetView: FC = props => { const { ...rest } = props; - const { currentOffer = null, purchaseOptions = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { purchaseOptions = null } = useCatalogUiState(); const { previewStuffData = null } = purchaseOptions; const badgeCode = useMemo(() => diff --git a/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx index d98ba4c..3d9e4a4 100644 --- a/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx @@ -2,12 +2,13 @@ import { StringDataType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; import { LocalizeText } from '../../../../../api'; import { Button, Flex } from '../../../../../common'; -import { useCatalog, useUserGroups } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState, useUserGroups } from '../../../../../hooks'; export const CatalogGuildSelectorWidgetView: FC<{}> = props => { const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { currentOffer = null, setPurchaseOptions = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { setPurchaseOptions = null } = useCatalogUiState(); const { data: groups = null } = useUserGroups(); const previewStuffData = useMemo(() => diff --git a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx index 5b627c7..deff50a 100644 --- a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx @@ -1,7 +1,7 @@ import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { IPurchasableOffer } from '../../../../../api'; import { AutoGrid, AutoGridProps } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogActions, useCatalogData } from '../../../../../hooks'; import { useCatalogAdmin } from '../../../CatalogAdminContext'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; @@ -13,7 +13,8 @@ interface CatalogItemGridWidgetViewProps extends AutoGridProps export const CatalogItemGridWidgetView: FC = props => { const { columnCount = 5, children = null, ...rest } = props; - const { currentOffer = null, currentPage = null, selectCatalogOffer = null } = useCatalog(); + const { currentOffer = null, currentPage = null } = useCatalogData(); + const { selectCatalogOffer = null } = useCatalogActions(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; const elementRef = useRef(null); diff --git a/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx index b2c85e8..faf02a2 100644 --- a/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx @@ -1,11 +1,11 @@ import { FC } from 'react'; import { Offer } from '../../../../../api'; import { LayoutLimitedEditionCompletePlateView } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; export const CatalogLimitedItemWidgetView: FC = props => { - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); if(!currentOffer || (currentOffer.pricingModel !== Offer.PRICING_MODEL_SINGLE) || !currentOffer.product.isUniqueLimitedItem) return null; diff --git a/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx index 718eb2e..a83a981 100644 --- a/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx @@ -2,7 +2,7 @@ import { FC } from 'react'; import { FaPlus } from 'react-icons/fa'; import { IPurchasableOffer } from '../../../../../api'; import { LayoutCurrencyIcon, Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogUiState } from '../../../../../hooks'; interface CatalogPriceDisplayWidgetViewProps { @@ -13,7 +13,7 @@ interface CatalogPriceDisplayWidgetViewProps export const CatalogPriceDisplayWidgetView: FC = props => { const { offer = null, separator = false } = props; - const { purchaseOptions = null } = useCatalog(); + const { purchaseOptions = null } = useCatalogUiState(); const { quantity = 1 } = purchaseOptions; if(!offer) return null; diff --git a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx index b329fa7..d5321e0 100644 --- a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx @@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { BuilderFurniPlaceableStatus, CatalogPurchaseState, CatalogType, DispatchUiEvent, GetClubMemberLevel, LocalStorageKeys, LocalizeText, NotificationBubbleType, Offer, ProductTypeEnum, SendMessageComposer } from '../../../../../api'; import { Button, LayoutLoadingSpinnerView, Text } from '../../../../../common'; import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent, CatalogPurchasedEvent } from '../../../../../events'; -import { useCatalog, useLocalStorage, useNotification, usePurse, useUiEvent } from '../../../../../hooks'; +import { useCatalogActions, useCatalogData, useCatalogUiState, useLocalStorage, useNotification, usePurse, useUiEvent } from '../../../../../hooks'; interface CatalogPurchaseWidgetViewProps { @@ -20,7 +20,9 @@ export const CatalogPurchaseWidgetView: FC = pro const [ purchaseWillBeGift, setPurchaseWillBeGift ] = useState(false); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useLocalStorage(LocalStorageKeys.CATALOG_SKIP_PURCHASE_CONFIRMATION, false); - const { currentOffer = null, currentPage = null, currentType = CatalogType.NORMAL, purchaseOptions = null, setPurchaseOptions = null, requestOfferToMover = null, setCatalogPlaceMultipleObjects = null, getBuilderFurniPlaceableStatus = null } = useCatalog(); + const { currentOffer = null, currentPage = null } = useCatalogData(); + const { currentType = CatalogType.NORMAL, purchaseOptions = null, setPurchaseOptions = null, setCatalogPlaceMultipleObjects = null } = useCatalogUiState(); + const { requestOfferToMover = null, getBuilderFurniPlaceableStatus = null } = useCatalogActions(); const { getCurrencyAmount = null } = usePurse(); const { showSingleBubble = null } = useNotification(); diff --git a/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx index bd82411..c34fe4a 100644 --- a/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx @@ -1,10 +1,10 @@ import { FC } from 'react'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; export const CatalogSimplePriceWidgetView: FC<{}> = props => { - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); return (
diff --git a/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx index 722589c..b100cbe 100644 --- a/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx @@ -1,7 +1,7 @@ import { FC, useEffect, useRef, useState } from 'react'; import { IPurchasableOffer, LocalizeText, Offer, ProductTypeEnum } from '../../../../../api'; import { AutoGrid, AutoGridProps, Button } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; interface CatalogSpacesWidgetViewProps extends AutoGridProps @@ -17,7 +17,8 @@ export const CatalogSpacesWidgetView: FC = props = const [ groupedOffers, setGroupedOffers ] = useState(null); const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(-1); const [ selectedOfferForGroup, setSelectedOfferForGroup ] = useState(null); - const { currentPage = null, currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null } = useCatalog(); + const { currentPage = null, currentOffer = null } = useCatalogData(); + const { setCurrentOffer = null, setPurchaseOptions = null } = useCatalogUiState(); const elementRef = useRef(null); const setSelectedOffer = (offer: IPurchasableOffer) => diff --git a/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx index 6b543a1..573465e 100644 --- a/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx @@ -1,14 +1,15 @@ import { FC } from 'react'; import { FaMinus, FaPlus } from 'react-icons/fa'; import { LocalizeText } from '../../../../../api'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; const MIN_VALUE: number = 1; const MAX_VALUE: number = 99; export const CatalogSpinnerWidgetView: FC<{}> = props => { - const { currentOffer = null, purchaseOptions = null, setPurchaseOptions = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); + const { purchaseOptions = null, setPurchaseOptions = null } = useCatalogUiState(); const { quantity = 1 } = purchaseOptions; const updateQuantity = (value: number) => diff --git a/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx b/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx index 365bdf6..2ad6129 100644 --- a/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx +++ b/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; import { Column, ColumnProps } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData } from '../../../../../hooks'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; interface CatalogSimplePriceWidgetViewProps extends ColumnProps @@ -10,7 +10,7 @@ interface CatalogSimplePriceWidgetViewProps extends ColumnProps export const CatalogTotalPriceWidget: FC = props => { const { gap = 1, ...rest } = props; - const { currentOffer = null } = useCatalog(); + const { currentOffer = null } = useCatalogData(); return ( diff --git a/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx index 14b4516..027aa36 100644 --- a/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx @@ -2,11 +2,12 @@ import { GetAvatarRenderManager, GetSessionDataManager, Vector3d } from '@nitrot import { FC, useEffect } from 'react'; import { BuildPurchasableClothingFigure, FurniCategory, Offer, ProductTypeEnum } from '../../../../../api'; import { AutoGrid, Column, LayoutGridItem, LayoutRoomPreviewerView } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalogData, useCatalogUiState } from '../../../../../hooks'; export const CatalogViewProductWidgetView: FC<{}> = props => { - const { currentOffer = null, roomPreviewer = null, purchaseOptions = null } = useCatalog(); + const { currentOffer = null, roomPreviewer = null } = useCatalogData(); + const { purchaseOptions = null } = useCatalogUiState(); const { previewStuffData = null } = purchaseOptions; useEffect(() => diff --git a/src/hooks/catalog/useCatalog.ts b/src/hooks/catalog/useCatalog.ts index 67d3054..0214cf5 100644 --- a/src/hooks/catalog/useCatalog.ts +++ b/src/hooks/catalog/useCatalog.ts @@ -1060,11 +1060,3 @@ export const useCatalogActions = () => }; }; -/** - * Deprecated. Kept so the 48 existing consumers compile unchanged — - * incrementally migrate them to `useCatalogData` / `useCatalogUiState` - * / `useCatalogActions` and remove this shim once the call sites are - * gone. Mirrors the same `useBetween` singleton, so behavior is - * identical. - */ -export const useCatalog = () => useBetween(useCatalogStore); diff --git a/src/hooks/catalog/useCatalogFavorites.ts b/src/hooks/catalog/useCatalogFavorites.ts index 5fc4d9e..2eb1854 100644 --- a/src/hooks/catalog/useCatalogFavorites.ts +++ b/src/hooks/catalog/useCatalogFavorites.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useBetween } from 'use-between'; import { CatalogType } from '../../api'; -import { useCatalog } from './useCatalog'; +import { useCatalogUiState } from './useCatalog'; import { getOffersStorageKey, getPagesStorageKey, IFavoriteOffer, LEGACY_STORAGE_KEY_OFFERS, LEGACY_STORAGE_KEY_PAGES, normalizeCatalogType, parseOffers, parsePages } from './useCatalogFavorites.helpers'; export type { IFavoriteOffer } from './useCatalogFavorites.helpers'; @@ -66,7 +66,7 @@ const writePages = (catalogType: string, ids: number[]) => const useCatalogFavoritesState = () => { - const { currentType = CatalogType.NORMAL } = useCatalog(); + const { currentType = CatalogType.NORMAL } = useCatalogUiState(); const catalogType = normalizeCatalogType(currentType); const [ favoriteOffersByType, setFavoriteOffersByType ] = useState>({ [CatalogType.NORMAL]: [], diff --git a/tests/useCatalog.filters.test.tsx b/tests/useCatalog.filters.test.tsx index a44eb56..4164e1c 100644 --- a/tests/useCatalog.filters.test.tsx +++ b/tests/useCatalog.filters.test.tsx @@ -75,7 +75,7 @@ vi.mock('use-between', () => ({ // Import AFTER the mock is set up. The hooks resolve `useBetween` at // import time via the module graph, so the order matters. -import { useCatalog, useCatalogActions, useCatalogData, useCatalogUiState } from '../src/hooks/catalog/useCatalog'; +import { useCatalogActions, useCatalogData, useCatalogUiState } from '../src/hooks/catalog/useCatalog'; describe('useCatalog filter contract', () => { @@ -160,47 +160,23 @@ describe('useCatalog filter contract', () => expect(result.current.openCatalogByType).toBe(fakeStore.openCatalogByType); }); - it('all four hooks observe the same singleton — refs are ===', () => + it('all three filters observe the same singleton — refs are ===', () => { const { result } = renderHook(() => ({ data: useCatalogData(), ui: useCatalogUiState(), - actions: useCatalogActions(), - full: useCatalog() + actions: useCatalogActions() })); - // The shim and the slices reach the same fakeStore. Any - // accidental copy would break this `===` check. - expect(result.current.full.activateNode).toBe(result.current.actions.activateNode); - expect(result.current.full.openCatalogByType).toBe(result.current.actions.openCatalogByType); - expect(result.current.full.setIsVisible).toBe(result.current.ui.setIsVisible); - expect(result.current.full.setCurrentPage).toBe(result.current.ui.setCurrentPage); - expect(result.current.full.rootNode).toBe(result.current.data.rootNode); - expect(result.current.full.furniCount).toBe(result.current.data.furniCount); - expect(result.current.full.roomPreviewer).toBe(result.current.data.roomPreviewer); - }); - - it('useCatalog (deprecated shim) preserves the full historical surface', () => - { - const { result } = renderHook(() => useCatalog()); - - // Sample one field from each slice, including the setters - // that the 48 existing consumers still destructure straight - // out of `useCatalog()`. If a setter or callback ever stops - // being forwarded, the shim breaks and those consumers - // silently fail. - const required = [ - 'rootNode', 'offersToNodes', 'currentPage', 'currentOffer', 'frontPageItems', - 'isVisible', 'setIsVisible', 'pageId', 'previousPageId', 'currentType', - 'setCurrentPage', 'setCurrentOffer', 'setSearchResult', - 'openCatalogByType', 'toggleCatalogByType', 'activateNode', - 'openPageById', 'openPageByName', 'openPageByOfferId', - 'requestOfferToMover', 'selectCatalogOffer', - 'getNodeById', 'getNodeByName', 'getBuilderFurniPlaceableStatus', - 'furniCount', 'furniLimit', 'secondsLeft', 'updateTime' - ]; - - for(const key of required) expect(result.current).toHaveProperty(key); + // Each slice reaches the same fakeStore via useBetween. Any + // accidental copy would break these `===` checks. + expect(result.current.actions.activateNode).toBe(fakeStore.activateNode); + expect(result.current.actions.openCatalogByType).toBe(fakeStore.openCatalogByType); + expect(result.current.ui.setIsVisible).toBe(fakeStore.setIsVisible); + expect(result.current.ui.setCurrentPage).toBe(fakeStore.setCurrentPage); + expect(result.current.data.rootNode).toBe(fakeStore.rootNode); + expect(result.current.data.furniCount).toBe(fakeStore.furniCount); + expect(result.current.data.roomPreviewer).toBe(fakeStore.roomPreviewer); }); });