From 85c562d14e6f84c80af93f0ce83af43639075bf0 Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 15 Jan 2022 23:56:31 -0500 Subject: [PATCH] Start catalog changes --- src/api/nitro/session/GetFurnitureData.ts | 2 +- .../catalog/CatalogMessageHandler.tsx | 121 +++++-- src/components/catalog/CatalogView.scss | 22 ++ src/components/catalog/CatalogView.tsx | 310 ++++++++++++------ src/components/catalog/common/CatalogNode.ts | 95 ++++++ src/components/catalog/common/CatalogPage.ts | 59 ++++ src/components/catalog/common/CatalogType.ts | 5 + .../catalog/common/CatalogUtilities.ts | 19 +- .../catalog/common/FilterCatalogNode.ts | 33 ++ .../catalog/common/FurnitureOffer.ts | 105 ++++++ .../catalog/common/GetProuductIconUrl.ts | 2 +- src/components/catalog/common/ICatalogNode.ts | 16 + src/components/catalog/common/ICatalogPage.ts | 12 + .../catalog/common/IPageLocalization.ts | 5 + src/components/catalog/common/IProduct.ts | 14 + .../catalog/common/IPurchasableOffer.ts | 22 ++ src/components/catalog/common/Offer.ts | 231 +++++++++++++ .../catalog/common/PageLocalization.ts | 36 ++ src/components/catalog/common/Product.ts | 93 ++++++ .../catalog/common/RequestedPage.ts | 59 ++++ .../catalog/context/CatalogContext.tsx | 62 +++- .../catalog/context/CatalogContext.types.ts | 13 - .../catalog/reducers/CatalogReducer.tsx | 19 +- .../catalog/views/CatalogViews.scss | 1 - .../CatalogRoomPreviewerView.tsx | 13 +- .../navigation/CatalogNavigationItemView.tsx | 91 ++--- .../navigation/CatalogNavigationSetView.tsx | 71 ++-- .../navigation/CatalogNavigationView.scss | 21 -- .../navigation/CatalogNavigationView.tsx | 35 +- .../page-details/CatalogPageDetailsView.tsx | 15 +- .../catalog/views/page/CatalogPageView.tsx | 52 ++- .../views/page/layout/CatalogLayout.types.ts | 5 +- .../views/page/layout/GetCatalogLayout.tsx | 46 +-- .../CatalogLayoutBadgeDisplayView.tsx | 20 +- .../CatalogLayoutColorGroupingView.tsx | 137 ++++---- .../default/CatalogLayoutDefaultView.tsx | 15 +- .../CatalogLayoutFrontpage4View.tsx | 23 +- .../CatalogLayoutGuildCustomFurniView.tsx | 28 +- .../CatalogLayoutGuildForumView.tsx | 31 +- .../CatalogLayoutGuildFrontpageView.tsx | 11 +- .../CatalogLayoutInfoLoyaltyView.tsx | 5 +- .../pets/CatalogLayoutPetPurchaseView.tsx | 21 +- .../page/layout/pets/CatalogLayoutPetView.tsx | 35 +- .../layout/pets3/CatalogLayoutPets3View.tsx | 13 +- .../CatalogLayoutSingleBundleView.tsx | 12 +- .../spaces-new/CatalogLayoutSpacesView.tsx | 54 ++- .../trophies/CatalogLayoutTrophiesView.tsx | 14 +- .../CatalogPageOfferPreviewView.tsx} | 32 +- .../page/offers/CatalogPageOfferView.tsx | 35 +- .../page/offers/CatalogPageOffersView.tsx | 12 +- .../views/page/product/CatalogProductView.tsx | 11 +- .../purchase/CatalogPurchaseButtonView.tsx | 15 +- .../CatalogPurchaseGiftButtonView.tsx | 4 +- .../page/purchase/CatalogPurchaseView.tsx | 20 +- .../CatalogLayoutSearchResultView.tsx | 49 --- .../CatalogSearchResultOfferView.tsx | 38 --- .../CatalogSearchResultOffersView.tsx | 37 --- .../views/search/CatalogSearchView.tsx | 105 +++--- .../catalog/views/tabs/CatalogTabsView.tsx | 35 ++ src/events/catalog/CatalogPageOpenedEvent.ts | 27 ++ .../catalog/CatalogSelectProductEvent.ts | 21 ++ .../catalog/SetRoomPreviewerStuffDataEvent.ts | 9 +- src/events/catalog/index.ts | 2 + 63 files changed, 1750 insertions(+), 826 deletions(-) create mode 100644 src/components/catalog/common/CatalogNode.ts create mode 100644 src/components/catalog/common/CatalogPage.ts create mode 100644 src/components/catalog/common/CatalogType.ts create mode 100644 src/components/catalog/common/FilterCatalogNode.ts create mode 100644 src/components/catalog/common/FurnitureOffer.ts create mode 100644 src/components/catalog/common/ICatalogNode.ts create mode 100644 src/components/catalog/common/ICatalogPage.ts create mode 100644 src/components/catalog/common/IPageLocalization.ts create mode 100644 src/components/catalog/common/IProduct.ts create mode 100644 src/components/catalog/common/IPurchasableOffer.ts create mode 100644 src/components/catalog/common/Offer.ts create mode 100644 src/components/catalog/common/PageLocalization.ts create mode 100644 src/components/catalog/common/Product.ts create mode 100644 src/components/catalog/common/RequestedPage.ts delete mode 100644 src/components/catalog/context/CatalogContext.types.ts delete mode 100644 src/components/catalog/views/navigation/CatalogNavigationView.scss rename src/components/catalog/views/page/{product-preview/CatalogProductPreviewView.tsx => offers/CatalogPageOfferPreviewView.tsx} (53%) delete mode 100644 src/components/catalog/views/page/search-result/CatalogLayoutSearchResultView.tsx delete mode 100644 src/components/catalog/views/page/search-result/CatalogSearchResultOfferView.tsx delete mode 100644 src/components/catalog/views/page/search-result/CatalogSearchResultOffersView.tsx create mode 100644 src/components/catalog/views/tabs/CatalogTabsView.tsx create mode 100644 src/events/catalog/CatalogPageOpenedEvent.ts create mode 100644 src/events/catalog/CatalogSelectProductEvent.ts diff --git a/src/api/nitro/session/GetFurnitureData.ts b/src/api/nitro/session/GetFurnitureData.ts index 6958e5c7..4272dbd3 100644 --- a/src/api/nitro/session/GetFurnitureData.ts +++ b/src/api/nitro/session/GetFurnitureData.ts @@ -6,7 +6,7 @@ export function GetFurnitureData(furniClassId: number, productType: string): IFu { let furniData: IFurnitureData = null; - switch(productType.toUpperCase()) + switch(productType.toLowerCase()) { case ProductTypeEnum.FLOOR: furniData = GetSessionDataManager().getFloorItemData(furniClassId); diff --git a/src/components/catalog/CatalogMessageHandler.tsx b/src/components/catalog/CatalogMessageHandler.tsx index 5a0b024c..1b710ac6 100644 --- a/src/components/catalog/CatalogMessageHandler.tsx +++ b/src/components/catalog/CatalogMessageHandler.tsx @@ -1,47 +1,101 @@ -import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, MarketplaceConfigurationEvent, MarketplaceMakeOfferResult, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer'; +import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, MarketplaceConfigurationEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer'; import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent'; import { FC, useCallback } from 'react'; -import { LocalizeText } from '../../api'; -import { CatalogNameResultEvent, CatalogPurchaseFailureEvent } from '../../events'; +import { GetFurnitureData, GetProductDataForLocalization, LocalizeText } from '../../api'; +import { CatalogNameResultEvent, CatalogPurchaseFailureEvent, CatalogSelectProductEvent } from '../../events'; import { CatalogGiftReceiverNotFoundEvent } from '../../events/catalog/CatalogGiftReceiverNotFoundEvent'; import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent'; import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent'; +import { BatchUpdates } from '../../hooks'; import { dispatchUiEvent } from '../../hooks/events/ui/ui-event'; import { CreateMessageHook } from '../../hooks/messages/message-event'; import { NotificationAlertType } from '../../views/notification-center/common/NotificationAlertType'; import { NotificationUtilities } from '../../views/notification-center/common/NotificationUtilities'; +import { CatalogNode } from './common/CatalogNode'; import { CatalogPetPalette } from './common/CatalogPetPalette'; +import { CatalogType } from './common/CatalogType'; +import { ICatalogNode } from './common/ICatalogNode'; +import { IProduct } from './common/IProduct'; +import { IPurchasableOffer } from './common/IPurchasableOffer'; +import { Offer } from './common/Offer'; +import { PageLocalization } from './common/PageLocalization'; +import { Product } from './common/Product'; import { SubscriptionInfo } from './common/SubscriptionInfo'; import { useCatalogContext } from './context/CatalogContext'; import { CatalogActions } from './reducers/CatalogReducer'; export const CatalogMessageHandler: FC<{}> = props => { - const { catalogState = null, dispatchCatalogState = null } = useCatalogContext(); + const { setIsBusy, pageId, currentType, setCurrentNode, setCurrentOffers, currentPage, setCurrentOffer, setPurchasableOffer, setFrontPageItems, showCatalogPage, catalogState, dispatchCatalogState } = useCatalogContext(); const onCatalogPagesListEvent = useCallback((event: CatalogPagesListEvent) => { const parser = event.getParser(); + const offers: Map = new Map(); - dispatchCatalogState({ - type: CatalogActions.SET_CATALOG_ROOT, - payload: { - root: parser.root + const getCatalogNode = (node: NodeData, depth: number, parent: ICatalogNode) => + { + const catalogNode = (new CatalogNode(node, depth, parent) as ICatalogNode); + + for(const offerId of catalogNode.offerIds) + { + if(offers.has(offerId)) offers.get(offerId).push(catalogNode); + else offers.set(offerId, [ catalogNode ]); } + + depth++; + + for(const child of node.children) catalogNode.addChild(getCatalogNode(child, depth, catalogNode)); + + return catalogNode; + } + + BatchUpdates(() => + { + setCurrentNode(getCatalogNode(parser.root, 0, null)); + setCurrentOffers(offers); }); - }, [ dispatchCatalogState ]); + }, [ setCurrentNode, setCurrentOffers ]); const onCatalogPageMessageEvent = useCallback((event: CatalogPageMessageEvent) => { const parser = event.getParser(); - dispatchCatalogState({ - type: CatalogActions.SET_CATALOG_PAGE_PARSER, - payload: { - pageParser: parser + if(parser.catalogType !== currentType) return; + + const purchasableOffers: IPurchasableOffer[] = []; + + for(const offer of parser.offers) + { + const products: IProduct[] = []; + const productData = GetProductDataForLocalization(offer.localizationId); + + for(const product of offer.products) + { + const furnitureData = GetFurnitureData(product.furniClassId, product.productType); + + products.push(new Product(product.productType, product.furniClassId, product.extraParam, product.productCount, productData, furnitureData, product.uniqueLimitedItem, product.uniqueLimitedSeriesSize, product.uniqueLimitedItemsLeft)); + } + + if(!products.length) continue; + + const purchasableOffer = new Offer(offer.offerId, offer.localizationId, offer.rent, offer.priceCredits, offer.priceActivityPoints, offer.priceActivityPointsType, offer.giftable, offer.clubLevel, products, offer.bundlePurchaseAllowed); + + if((currentType === CatalogType.NORMAL) || ((purchasableOffer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (purchasableOffer.pricingModel !== Offer.PRICING_MODEL_MULTI))) purchasableOffers.push(purchasableOffer); + } + + BatchUpdates(() => + { + if(parser.frontPageItems && parser.frontPageItems.length) setFrontPageItems(parser.frontPageItems); + + setIsBusy(false); + + if(pageId === parser.pageId) + { + showCatalogPage(parser.pageId, parser.layoutCode, new PageLocalization(parser.localization.images.concat(), parser.localization.texts.concat()), purchasableOffers, parser.offerId, parser.acceptSeasonCurrencyAsCredits); } }); - }, [ dispatchCatalogState ]); + }, [ currentType, pageId, setFrontPageItems, setIsBusy, showCatalogPage ]); const onPurchaseOKMessageEvent = useCallback((event: PurchaseOKMessageEvent) => { @@ -72,14 +126,41 @@ export const CatalogMessageHandler: FC<{}> = props => const onProductOfferEvent = useCallback((event: ProductOfferEvent) => { const parser = event.getParser(); + const offerData = parser.offer; - dispatchCatalogState({ - type: CatalogActions.SET_CATALOG_ACTIVE_OFFER, - payload: { - activeOffer: parser.offer - } + if(!offerData || !offerData.products.length) return; + + const offerProductData = offerData.products[0]; + + if(offerProductData.uniqueLimitedItem) + { + // update unique + } + + const products: IProduct[] = []; + const productData = GetProductDataForLocalization(offerData.localizationId); + + for(const product of offerData.products) + { + const furnitureData = GetFurnitureData(product.furniClassId, product.productType); + + products.push(new Product(product.productType, product.furniClassId, product.extraParam, product.productCount, productData, furnitureData, product.uniqueLimitedItem, product.uniqueLimitedSeriesSize, product.uniqueLimitedItemsLeft)); + } + + const offer = new Offer(offerData.offerId, offerData.localizationId, offerData.rent, offerData.priceCredits, offerData.priceActivityPoints, offerData.priceActivityPointsType, offerData.giftable, offerData.clubLevel, products, offerData.bundlePurchaseAllowed); + + if(!((currentType === CatalogType.NORMAL) || ((offer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (offer.pricingModel !== Offer.PRICING_MODEL_MULTI)))) return; + + offer.page = currentPage; + + dispatchUiEvent(new CatalogSelectProductEvent(offer)); + + BatchUpdates(() => + { + setCurrentOffer(offer); + setPurchasableOffer(offer); }); - }, [ dispatchCatalogState ]); + }, [ currentType, currentPage, setCurrentOffer, setPurchasableOffer ]); const onSellablePetPalettesMessageEvent = useCallback((event: SellablePetPalettesMessageEvent) => { diff --git a/src/components/catalog/CatalogView.scss b/src/components/catalog/CatalogView.scss index ec71c0ad..c5273544 100644 --- a/src/components/catalog/CatalogView.scss +++ b/src/components/catalog/CatalogView.scss @@ -7,4 +7,26 @@ } } +.nitro-catalog-navigation-grid-container { + border-radius: 0.25rem; + border-color: #B6BEC5 !important; + background-color: #CDD3D9; + border: 2px solid; + + .layout-grid-item { + font-size: $font-size-sm; + height: 23px !important; + border-color: unset !important; + background-color: #CDD3D9; + border: 0 !important; + padding: 1px 3px; + + .svg-inline--fa { + color: $black; + font-size: 10px; + padding: 1px; + } + } +} + @import './views/CatalogViews'; diff --git a/src/components/catalog/CatalogView.tsx b/src/components/catalog/CatalogView.tsx index 6cb4ceeb..fe4a8c36 100644 --- a/src/components/catalog/CatalogView.tsx +++ b/src/components/catalog/CatalogView.tsx @@ -1,40 +1,100 @@ -import { GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetGiftWrappingConfigurationComposer, GetMarketplaceConfigurationMessageComposer, ILinkEventTracker, INodeData, RoomPreviewer } from '@nitrots/nitro-renderer'; +import { FrontPageItem, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetGiftWrappingConfigurationComposer, GetMarketplaceConfigurationMessageComposer, ILinkEventTracker, RoomPreviewer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { AddEventLinkTracker, GetRoomEngine, LocalizeText, RemoveLinkEventTracker } from '../../api'; import { CREDITS, PlaySound } from '../../api/utils/PlaySound'; import { Column } from '../../common/Column'; import { Grid } from '../../common/Grid'; import { CatalogEvent } from '../../events'; -import { UseMountEffect } from '../../hooks'; +import { BatchUpdates, UseMountEffect } from '../../hooks'; import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { SendMessageHook } from '../../hooks/messages/message-event'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsView, NitroCardView } from '../../layout'; import { CatalogMessageHandler } from './CatalogMessageHandler'; -import { CatalogMode } from './common/CatalogMode'; -import { BuildCatalogPageTree } from './common/CatalogUtilities'; +import { CatalogPage } from './common/CatalogPage'; +import { CatalogType } from './common/CatalogType'; +import { ICatalogNode } from './common/ICatalogNode'; +import { ICatalogPage } from './common/ICatalogPage'; +import { IPageLocalization } from './common/IPageLocalization'; +import { IPurchasableOffer } from './common/IPurchasableOffer'; +import { RequestedPage } from './common/RequestedPage'; import { CatalogContextProvider } from './context/CatalogContext'; import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer'; import { CatalogGiftView } from './views/gift/CatalogGiftView'; -import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView'; +import { CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { CatalogPageView } from './views/page/CatalogPageView'; import { MarketplacePostOfferView } from './views/page/layout/marketplace/MarketplacePostOfferView'; +import { CatalogTabsViews } from './views/tabs/CatalogTabsView'; + +const DUMMY_PAGE_ID_FOR_OFFER_SEARCH: number = -12345678; export const CatalogView: FC<{}> = props => { const [ isVisible, setIsVisible ] = useState(false); - const [ roomPreviewer, setRoomPreviewer ] = useState(null); - const [ pendingPageLookup, setPendingPageLookup ] = useState<{ value: string, isOffer: boolean }>(null); - const [ pendingTree, setPendingTree ] = useState(null); - const [ pendingOpenTree, setPendingOpenTree ] = useState(null); - const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog); - const [ currentTab, setCurrentTab ] = useState(null); - const { root = null, pageParser = null, activeOffer = null, searchResult = null } = catalogState; + const [ isInitialized, setIsInitialized ] = useState(false); + const [ isBusy, setIsBusy ] = useState(false); + const [ forceRefresh, setForceRefresh ] = useState(false); + const [ pageId, setPageId ] = useState(-1); + const [ previousPageId, setPreviousPageId ] = useState(-1); + const [ currentType, setCurrentType ] = useState(CatalogType.NORMAL); + const [ currentNode, setCurrentNode ] = useState(null); + const [ currentOffers, setCurrentOffers ] = useState>(null); + const [ currentPage, setCurrentPage ] = useState(null); + const [ currentOffer, setCurrentOffer ] = useState(null); + const [ purchasableOffer, setPurchasableOffer ] = useState(null); + const [ currentTab, setCurrentTab ] = useState(null); + const [ activeNodes, setActiveNodes ] = useState([]); + const [ lastActiveNodes, setLastActiveNodes ] = useState(null); + const [ frontPageItems, setFrontPageItems ] = useState([]); - const saveActivePages = useCallback(() => + + const [ requestedPage, setRequestedPage ] = useState(new RequestedPage()); + const [ roomPreviewer, setRoomPreviewer ] = useState(null); + const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog); + + const loadCatalogPage = useCallback((pageId: number, offerId: number, forceRefresh: boolean = false) => { - setPendingOpenTree(ACTIVE_PAGES.slice()); + if(pageId < 0) return; + + BatchUpdates(() => + { + setIsBusy(true); + setPageId(pageId); + + if(forceRefresh) setForceRefresh(true); + }); + + SendMessageHook(new GetCatalogPageComposer(pageId, offerId, currentType)); + }, [ currentType ]); + + const selectOffer = useCallback((offerId: number) => + { + }, []); + const showCatalogPage = useCallback((pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) => + { + if(currentPage) + { + if(!forceRefresh && (currentPage.pageId === pageId)) + { + if(offerId > -1) selectOffer(offerId); + + return; + } + } + + const catalogPage = (new CatalogPage(pageId, layoutCode, localization, offers, acceptSeasonCurrencyAsCredits) as ICatalogPage); + + BatchUpdates(() => + { + setCurrentPage(catalogPage); + setPreviousPageId(prevValue => ((pageId > DUMMY_PAGE_ID_FOR_OFFER_SEARCH) ? pageId : prevValue)); + setForceRefresh(false); + + selectOffer(offerId); + }); + }, [ currentPage, forceRefresh, selectOffer ]); + const onCatalogEvent = useCallback((event: CatalogEvent) => { let save = false; @@ -56,9 +116,7 @@ export const CatalogView: FC<{}> = props => PlaySound(CREDITS); return; } - - if(save) saveActivePages(); - }, [ saveActivePages ]); + }, []); useUiEvent(CatalogEvent.SHOW_CATALOG, onCatalogEvent); useUiEvent(CatalogEvent.HIDE_CATALOG, onCatalogEvent); @@ -82,13 +140,13 @@ export const CatalogView: FC<{}> = props => switch(parts[2]) { case 'offerId': - setPendingPageLookup({ value: parts[3], isOffer: true }); + //setPendingPageLookup({ value: parts[3], isOffer: true }); return; } } - setPendingPageLookup({ value: parts[2], isOffer: false }); + //setPendingPageLookup({ value: parts[2], isOffer: false }); } else { @@ -111,71 +169,6 @@ export const CatalogView: FC<{}> = props => return () => RemoveLinkEventTracker(linkTracker); }, [ linkReceived ]); - useEffect(() => - { - const loadCatalog = (((pendingPageLookup !== null) && !catalogState.root) || (isVisible && !catalogState.root)); - - if(loadCatalog) - { - SendMessageHook(new GetCatalogIndexComposer(CatalogMode.MODE_NORMAL)); - - return; - } - - if(catalogState.root) - { - if(!isVisible && (pendingPageLookup !== null)) - { - setIsVisible(true); - - return; - } - - if(pendingPageLookup !== null || pendingOpenTree) - { - let tree: INodeData[] = []; - - if(pendingPageLookup !== null) - { - tree = BuildCatalogPageTree(catalogState.root, pendingPageLookup.value, pendingPageLookup.isOffer); - } - else - { - tree = pendingOpenTree.slice(); - } - - setCurrentTab(tree.shift()); - setPendingOpenTree(null); - setPendingPageLookup(null); - setPendingTree(tree); - } - else - { - setCurrentTab(prevValue => - { - if(catalogState.root.children.length) - { - if(prevValue) - { - if(catalogState.root.children.indexOf(prevValue) >= 0) return prevValue; - } - - return ((catalogState.root.children.length && catalogState.root.children[0]) || null); - } - - return null; - }); - } - } - }, [ isVisible, pendingPageLookup, pendingOpenTree, catalogState.root, setCurrentTab ]); - - useEffect(() => - { - if(!currentTab) return; - - SendMessageHook(new GetCatalogPageComposer(currentTab.pageId, -1, CatalogMode.MODE_NORMAL)); - }, [ currentTab ]); - useEffect(() => { setRoomPreviewer(new RoomPreviewer(GetRoomEngine(), ++RoomPreviewer.PREVIEW_COUNTER)); @@ -191,6 +184,124 @@ export const CatalogView: FC<{}> = props => } }, []); + useEffect(() => + { + if(!currentNode) return; + + switch(requestedPage.requestType) + { + // case RequestedPage.REQUEST_TYPE_NONE: + // loadFrontPage(); + // return; + case RequestedPage.REQUEST_TYPE_ID: + requestedPage.resetRequest(); + return; + case RequestedPage.REQUEST_TYPE_NAME: + requestedPage.resetRequest(); + return; + } + }, [ currentNode, requestedPage ]); + + useEffect(() => + { + if(!isVisible) return; + + if(!isInitialized) SendMessageHook(new GetCatalogIndexComposer(currentType)); + }, [ isVisible, isInitialized, currentType ]); + + useEffect(() => + { + if(!currentNode || !currentTab) return; + + const activeNodes: ICatalogNode[] = []; + + if(currentTab.isVisible && !currentTab.children.length && (currentTab !== currentNode)) + { + loadCatalogPage(currentTab.pageId, -1); + } + else + { + if(currentTab.children.length) + { + for(const child of currentTab.children) + { + if(child.isVisible) + { + activeNodes.push(child); + + loadCatalogPage(child.pageId, -1); + + break; + } + } + } + } + + // if(currentTab.children.length) + // { + // for(const child of currentTab.children) + // { + // if(child.isVisible) + // { + // activeNodes.push(child); + + // loadCatalogPage(child.pageId, -1); + + // break; + // } + // } + // } + + setActiveNodes(activeNodes); + }, [ currentNode, currentTab, loadCatalogPage ]); + + useEffect(() => + { + if(!currentPage) return; + + setCurrentOffer(null); + }, [ currentPage ]); + + useEffect(() => + { + if(!currentNode) return; + + BatchUpdates(() => + { + setIsInitialized(true); + + if(currentNode.isBranch) + { + for(const child of currentNode.children) + { + if(child && child.isVisible) + { + setCurrentTab(child); + + return; + } + } + } + }); + }, [ currentNode ]); + + useEffect(() => + { + if(!isVisible && !lastActiveNodes && activeNodes && activeNodes.length) + { + setLastActiveNodes(activeNodes.concat()); + } + + else if(isVisible && lastActiveNodes) + { + BatchUpdates(() => + { + setActiveNodes(lastActiveNodes.concat()); + setLastActiveNodes(null); + }); + } + }, [ isVisible, lastActiveNodes, activeNodes ]); + UseMountEffect(() => { SendMessageHook(new GetMarketplaceConfigurationMessageComposer()); @@ -198,35 +309,22 @@ export const CatalogView: FC<{}> = props => SendMessageHook(new GetClubGiftInfo()); }); - const currentNavigationPage = ((searchResult && searchResult.page) || currentTab); - const navigationHidden = !!(pageParser && pageParser.frontPageItems.length); - return ( - + { isVisible && - { saveActivePages(); setIsVisible(false); } } /> + { setIsVisible(false); } } /> - { root && root.children.length && root.children.map((page, index) => - { - if(!page.visible) return null; - - return ( - setCurrentTab(page) }> - { page.localization } - - ); - }) } + - { currentNavigationPage && !navigationHidden && - - - } - - + + + + + diff --git a/src/components/catalog/common/CatalogNode.ts b/src/components/catalog/common/CatalogNode.ts new file mode 100644 index 00000000..a976c8ab --- /dev/null +++ b/src/components/catalog/common/CatalogNode.ts @@ -0,0 +1,95 @@ +import { NodeData } from '@nitrots/nitro-renderer'; +import { ICatalogNode } from './ICatalogNode'; + +export class CatalogNode implements ICatalogNode +{ + private _depth: number = 0; + private _localization: string = ''; + private _pageId: number = -1; + private _pageName: string = ''; + private _iconId: number = 0; + private _children: ICatalogNode[]; + private _offerIds: number[]; + private _parent: ICatalogNode; + private _isVisible: boolean; + + constructor(node: NodeData, depth: number, parent: ICatalogNode) + { + this._depth = depth; + this._parent = parent; + this._localization = node.localization; + this._pageId = node.pageId; + this._pageName = node.pageName; + this._iconId = node.icon; + this._children = []; + this._offerIds = node.offerIds; + this._isVisible = node.visible; + } + + public get isOpen(): boolean + { + return false; + } + + public get depth(): number + { + return this._depth; + } + + public get isBranch(): boolean + { + return (this._children.length > 0); + } + + public get isLeaf(): boolean + { + return (this._children.length === 0); + } + + public get localization(): string + { + return this._localization; + } + + public get pageId(): number + { + return this._pageId; + } + + public get pageName(): string + { + return this._pageName; + } + + public get iconId(): number + { + return this._iconId; + } + + public get children(): ICatalogNode[] + { + return this._children; + } + + public get offerIds(): number[] + { + return this._offerIds; + } + + public get parent(): ICatalogNode + { + return this._parent; + } + + public get isVisible(): boolean + { + return this._isVisible; + } + + public addChild(child: ICatalogNode):void + { + if(!child) return; + + this._children.push(child); + } +} diff --git a/src/components/catalog/common/CatalogPage.ts b/src/components/catalog/common/CatalogPage.ts new file mode 100644 index 00000000..1e806094 --- /dev/null +++ b/src/components/catalog/common/CatalogPage.ts @@ -0,0 +1,59 @@ +import { ICatalogPage } from './ICatalogPage'; +import { IPageLocalization } from './IPageLocalization'; +import { IPurchasableOffer } from './IPurchasableOffer'; + +export class CatalogPage implements ICatalogPage +{ + public static MODE_NORMAL: number = 0; + + private _pageId: number; + private _layoutCode: string; + private _localization: IPageLocalization; + private _offers: IPurchasableOffer[]; + private _acceptSeasonCurrencyAsCredits: boolean; + private _mode: number; + + constructor(pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], acceptSeasonCurrencyAsCredits: boolean, mode: number = -1) + { + this._pageId = pageId; + this._layoutCode = layoutCode; + this._localization = localization; + this._offers = offers; + this._acceptSeasonCurrencyAsCredits = acceptSeasonCurrencyAsCredits; + + for(const offer of offers) (offer.page = this); + + if(mode === -1) this._mode = CatalogPage.MODE_NORMAL; + else this._mode = mode; + } + + public get pageId(): number + { + return this._pageId; + } + + public get layoutCode(): string + { + return this._layoutCode; + } + + public get localization(): IPageLocalization + { + return this._localization; + } + + public get offers(): IPurchasableOffer[] + { + return this._offers; + } + + public get acceptSeasonCurrencyAsCredits(): boolean + { + return this._acceptSeasonCurrencyAsCredits; + } + + public get mode(): number + { + return this._mode; + } +} diff --git a/src/components/catalog/common/CatalogType.ts b/src/components/catalog/common/CatalogType.ts new file mode 100644 index 00000000..670ad6f8 --- /dev/null +++ b/src/components/catalog/common/CatalogType.ts @@ -0,0 +1,5 @@ +export class CatalogType +{ + public static NORMAL: string = 'NORMAL'; + public static BUILDER: string = 'BUILDERS_CLUB'; +} diff --git a/src/components/catalog/common/CatalogUtilities.ts b/src/components/catalog/common/CatalogUtilities.ts index b9f455e4..f8a17aef 100644 --- a/src/components/catalog/common/CatalogUtilities.ts +++ b/src/components/catalog/common/CatalogUtilities.ts @@ -1,5 +1,6 @@ import { CatalogPageMessageOfferData, CatalogPageMessageParser, IFurnitureData, INodeData, SellablePetPaletteData } from '@nitrots/nitro-renderer'; import { GetConfiguration, GetProductDataForLocalization, GetRoomEngine } from '../../../api'; +import { ICatalogNode } from './ICatalogNode'; export interface ICatalogOffers { @@ -21,25 +22,25 @@ export function GetOfferName(offer: CatalogPageMessageOfferData): string return offer.localizationId; } -export function GetOfferNodes(offers: ICatalogOffers, offerId: number): INodeData[] +export const GetOfferNodes = (offerNodes: Map, offerId: number) => { - const pages = offers[offerId.toString()]; - const allowedPages: INodeData[] = []; + const nodes = offerNodes.get(offerId); + const allowedNodes: ICatalogNode[] = []; - if(pages && pages.length) + if(nodes && nodes.length) { - for(const page of pages) + for(const node of nodes) { - if(!page.visible) continue; + if(!node.isVisible) continue; - allowedPages.push(page); + allowedNodes.push(node); } } - return allowedPages; + return allowedNodes; } -export function SetOffersToNodes(offers: ICatalogOffers, pageData: INodeData): void +export const SetOffersToNodes = (offers: ICatalogOffers, pageData: INodeData) => { if(pageData.offerIds && pageData.offerIds.length) { diff --git a/src/components/catalog/common/FilterCatalogNode.ts b/src/components/catalog/common/FilterCatalogNode.ts new file mode 100644 index 00000000..d0f17538 --- /dev/null +++ b/src/components/catalog/common/FilterCatalogNode.ts @@ -0,0 +1,33 @@ +import { ICatalogNode } from './ICatalogNode'; + +export const FilterCatalogNode = (search: string, furniLines: string[], node: ICatalogNode, nodes: ICatalogNode[]) => +{ + if(node.isVisible && (node.pageId > 0)) + { + let nodeAdded = false; + + const hayStack = [ node.pageName, node.localization ].join(' ').toLowerCase().replace(/ /gi, ''); + + if(hayStack.indexOf(search) > -1) + { + nodes.push(node); + + nodeAdded = true; + } + + if(!nodeAdded) + { + for(const furniLine of furniLines) + { + if(hayStack.indexOf(furniLine) >= 0) + { + nodes.push(node); + + break; + } + } + } + } + + for(const child of node.children) FilterCatalogNode(search, furniLines, child, nodes); +} diff --git a/src/components/catalog/common/FurnitureOffer.ts b/src/components/catalog/common/FurnitureOffer.ts new file mode 100644 index 00000000..6a4816d7 --- /dev/null +++ b/src/components/catalog/common/FurnitureOffer.ts @@ -0,0 +1,105 @@ +import { IFurnitureData } from '@nitrots/nitro-renderer'; +import { GetProductDataForLocalization } from '../../../api'; +import { ICatalogPage } from './ICatalogPage'; +import { IProduct } from './IProduct'; +import { IPurchasableOffer } from './IPurchasableOffer'; +import { Offer } from './Offer'; +import { Product } from './Product'; + +export class FurnitureOffer implements IPurchasableOffer +{ + private _furniData:IFurnitureData; + private _page: ICatalogPage; + private _product: IProduct; + + constructor(furniData: IFurnitureData) + { + this._furniData = furniData; + this._product = (new Product(this._furniData.type, this._furniData.id, this._furniData.customParams, 1, GetProductDataForLocalization(this._furniData.className), this._furniData) as IProduct); + } + + public get offerId(): number + { + return (this.isRentOffer) ? this._furniData.rentOfferId : this._furniData.purchaseOfferId; + } + + public get priceInActivityPoints(): number + { + return 0; + } + + public get activityPointType(): number + { + return 0; + } + + public get priceInCredits(): number + { + return 0; + } + + public get page(): ICatalogPage + { + return this._page; + } + + public set page(page: ICatalogPage) + { + this._page = page; + } + + public get priceType(): string + { + return ''; + } + + public get product(): IProduct + { + return this._product; + } + + public get localizationId(): string + { + return 'roomItem.name.' + this._furniData.id; + } + + public get bundlePurchaseAllowed(): boolean + { + return false; + } + + public get isRentOffer(): boolean + { + return (this._furniData.rentOfferId > -1); + } + + public get giftable(): boolean + { + return false; + } + + public get pricingModel(): string + { + return Offer.PRICING_MODEL_FURNITURE; + } + + public get clubLevel(): number + { + return 0; + } + + public get badgeCode(): string + { + return ''; + } + + public get localizationName(): string + { + return this._furniData.name; + } + + public get localizationDescription(): string + { + return this._furniData.description; + } +} diff --git a/src/components/catalog/common/GetProuductIconUrl.ts b/src/components/catalog/common/GetProuductIconUrl.ts index c775dd37..f4ce9267 100644 --- a/src/components/catalog/common/GetProuductIconUrl.ts +++ b/src/components/catalog/common/GetProuductIconUrl.ts @@ -27,7 +27,7 @@ export const GetProductIconUrl = (furniClassId: number, productType: string, cus iconName = [ 'th', 'wall', customParams ].join('_'); break; case 'landscape': - iconName = [ 'th', furniData.className, customParams.replace('.', '_'), '001' ].join('_'); + iconName = [ 'th', furniData.className, (customParams || '').replace('.', '_'), '001' ].join('_'); break; } diff --git a/src/components/catalog/common/ICatalogNode.ts b/src/components/catalog/common/ICatalogNode.ts new file mode 100644 index 00000000..d3962647 --- /dev/null +++ b/src/components/catalog/common/ICatalogNode.ts @@ -0,0 +1,16 @@ +export interface ICatalogNode +{ + addChild(node: ICatalogNode): void; + readonly isOpen: boolean; + readonly depth: number; + readonly isBranch: boolean; + readonly isLeaf: boolean; + readonly localization: string; + readonly pageId: number; + readonly pageName: string; + readonly iconId: number; + readonly children: ICatalogNode[]; + readonly offerIds: number[]; + readonly parent: ICatalogNode; + readonly isVisible: boolean; +} diff --git a/src/components/catalog/common/ICatalogPage.ts b/src/components/catalog/common/ICatalogPage.ts new file mode 100644 index 00000000..ed11ba0d --- /dev/null +++ b/src/components/catalog/common/ICatalogPage.ts @@ -0,0 +1,12 @@ +import { IPageLocalization } from './IPageLocalization'; +import { IPurchasableOffer } from './IPurchasableOffer'; + +export interface ICatalogPage +{ + readonly pageId: number; + readonly layoutCode: string; + readonly localization: IPageLocalization; + readonly offers: IPurchasableOffer[]; + readonly acceptSeasonCurrencyAsCredits: boolean; + readonly mode: number; +} diff --git a/src/components/catalog/common/IPageLocalization.ts b/src/components/catalog/common/IPageLocalization.ts new file mode 100644 index 00000000..ad652e1a --- /dev/null +++ b/src/components/catalog/common/IPageLocalization.ts @@ -0,0 +1,5 @@ +export interface IPageLocalization +{ + getText(index: number): string + getImage(index: number): string +} diff --git a/src/components/catalog/common/IProduct.ts b/src/components/catalog/common/IProduct.ts new file mode 100644 index 00000000..d52da3a2 --- /dev/null +++ b/src/components/catalog/common/IProduct.ts @@ -0,0 +1,14 @@ +import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer'; + +export interface IProduct +{ + readonly productType: string; + readonly productClassId: number; + readonly extraParam: string; + readonly productCount: number; + readonly productData: IProductData; + readonly furnitureData: IFurnitureData; + readonly isUniqueLimitedItem: boolean; + readonly uniqueLimitedItemSeriesSize: number; + uniqueLimitedItemsLeft: number; +} diff --git a/src/components/catalog/common/IPurchasableOffer.ts b/src/components/catalog/common/IPurchasableOffer.ts new file mode 100644 index 00000000..7ef453e6 --- /dev/null +++ b/src/components/catalog/common/IPurchasableOffer.ts @@ -0,0 +1,22 @@ +import { ICatalogPage } from './ICatalogPage'; +import { IProduct } from './IProduct'; + +export interface IPurchasableOffer +{ + readonly clubLevel: number; + page: ICatalogPage; + readonly offerId: number; + readonly localizationId: string; + readonly priceInCredits: number; + readonly priceInActivityPoints: number; + readonly activityPointType: number; + readonly giftable: boolean; + readonly product: IProduct; + readonly pricingModel: string; + readonly priceType: string; + readonly bundlePurchaseAllowed: boolean; + readonly isRentOffer: boolean; + readonly badgeCode: string; + readonly localizationName: string; + readonly localizationDescription: string; +} diff --git a/src/components/catalog/common/Offer.ts b/src/components/catalog/common/Offer.ts new file mode 100644 index 00000000..ed050357 --- /dev/null +++ b/src/components/catalog/common/Offer.ts @@ -0,0 +1,231 @@ +import { GetFurnitureData, GetProductDataForLocalization, LocalizeText } from '../../../api'; +import { ICatalogPage } from './ICatalogPage'; +import { IProduct } from './IProduct'; +import { IPurchasableOffer } from './IPurchasableOffer'; +import { Product } from './Product'; +import { ProductTypeEnum } from './ProductTypeEnum'; + +export class Offer implements IPurchasableOffer +{ + public static PRICING_MODEL_UNKNOWN: string = 'pricing_model_unknown'; + public static PRICING_MODEL_SINGLE: string = 'pricing_model_single'; + public static PRICING_MODEL_MULTI: string = 'pricing_model_multi'; + public static PRICING_MODEL_BUNDLE: string = 'pricing_model_bundle'; + public static PRICING_MODEL_FURNITURE: string = 'pricing_model_furniture'; + public static PRICE_TYPE_NONE: string = 'price_type_none'; + public static PRICE_TYPE_CREDITS: string = 'price_type_credits'; + public static PRICE_TYPE_ACTIVITYPOINTS: string = 'price_type_activitypoints'; + public static PRICE_TYPE_CREDITS_ACTIVITYPOINTS: string = 'price_type_credits_and_activitypoints'; + + private _pricingModel: string; + private _priceType: string; + private _offerId: number; + private _localizationId: string; + private _priceInCredits: number; + private _priceInActivityPoints: number; + private _activityPointType: number; + private _giftable: boolean; + private _isRentOffer: boolean; + private _page: ICatalogPage; + private _clubLevel: number = 0; + private _products: IProduct[]; + private _badgeCode: string; + private _bundlePurchaseAllowed: boolean = false; + + constructor(offerId: number, localizationId: string, isRentOffer: boolean, priceInCredits: number, priceInActivityPoints: number, activityPointType: number, giftable: boolean, clubLevel: number, products: IProduct[], bundlePurchaseAllowed: boolean) + { + this._offerId = offerId; + this._localizationId = localizationId; + this._isRentOffer = isRentOffer; + this._priceInCredits = priceInCredits; + this._priceInActivityPoints = priceInActivityPoints; + this._activityPointType = activityPointType; + this._giftable = giftable; + this._clubLevel = clubLevel; + this._products = products; + this._bundlePurchaseAllowed = bundlePurchaseAllowed; + + this.setPricingModelForProducts(); + this.setPricingType(); + + for(const product of products) + { + if(product.productType === ProductTypeEnum.BADGE) + { + this._badgeCode = product.extraParam; + + break; + } + } + } + + public get clubLevel(): number + { + return this._clubLevel; + } + + public get page(): ICatalogPage + { + return this._page; + } + + public set page(k: ICatalogPage) + { + this._page = k; + } + + public get offerId(): number + { + return this._offerId; + } + + public get localizationId(): string + { + return this._localizationId; + } + + public get priceInCredits(): number + { + return this._priceInCredits; + } + + public get priceInActivityPoints(): number + { + return this._priceInActivityPoints; + } + + public get activityPointType(): number + { + return this._activityPointType; + } + + public get giftable(): boolean + { + return this._giftable; + } + + public get product(): IProduct + { + if(!this._products || !this._products.length) return null; + + if(this._products.length === 1) return this._products[0]; + + const products = Product.stripAddonProducts(this._products); + + if(products.length) return products[0]; + + return null; + } + + public get pricingModel(): string + { + return this._pricingModel; + } + + public get priceType(): string + { + return this._priceType; + } + + public get bundlePurchaseAllowed(): boolean + { + return this._bundlePurchaseAllowed; + } + + public get isRentOffer(): boolean + { + return this._isRentOffer; + } + + public get badgeCode(): string + { + return this._badgeCode; + } + + public get localizationName(): string + { + const productData = GetProductDataForLocalization(this._localizationId); + + if(productData) return productData.name; + + return LocalizeText(this._localizationId); + } + + public get localizationDescription(): string + { + const productData = GetProductDataForLocalization(this._localizationId); + + if(productData) return productData.description; + + return LocalizeText(this._localizationId); + } + + private setPricingModelForProducts(): void + { + const products = Product.stripAddonProducts(this._products); + + if(products.length === 1) + { + if(products[0].productCount === 1) + { + this._pricingModel = Offer.PRICING_MODEL_SINGLE; + } + else + { + this._pricingModel = Offer.PRICING_MODEL_MULTI; + } + } + + else if(products.length > 1) + { + this._pricingModel = Offer.PRICING_MODEL_BUNDLE; + } + + else + { + this._pricingModel = Offer.PRICING_MODEL_UNKNOWN; + } + } + + private setPricingType(): void + { + if((this._priceInCredits > 0) && (this._priceInActivityPoints > 0)) + { + this._priceType = Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS; + } + + else if(this._priceInCredits > 0) + { + this._priceType = Offer.PRICE_TYPE_CREDITS; + } + + else if(this._priceInActivityPoints > 0) + { + this._priceType = Offer.PRICE_TYPE_ACTIVITYPOINTS; + } + + else + { + this._priceType = Offer.PRICE_TYPE_NONE; + } + } + + public clone(): IPurchasableOffer + { + const products: IProduct[] = []; + const productData = GetProductDataForLocalization(this.localizationId); + + for(const product of this._products) + { + const furnitureData = GetFurnitureData(product.productClassId, product.productType); + + products.push(new Product(product.productType, product.productClassId, product.extraParam, product.productCount, productData, furnitureData)); + } + + const offer = new Offer(this.offerId, this.localizationId, this.isRentOffer, this.priceInCredits, this.priceInActivityPoints, this.activityPointType, this.giftable, this.clubLevel, products, this.bundlePurchaseAllowed); + + offer.page = this.page; + + return offer; + } +} diff --git a/src/components/catalog/common/PageLocalization.ts b/src/components/catalog/common/PageLocalization.ts new file mode 100644 index 00000000..6a51830d --- /dev/null +++ b/src/components/catalog/common/PageLocalization.ts @@ -0,0 +1,36 @@ +import { GetConfiguration } from '../../../api'; +import { IPageLocalization } from './IPageLocalization'; + +export class PageLocalization implements IPageLocalization +{ + private _images: string[]; + private _texts: string[] + + constructor(images: string[], texts: string[]) + { + this._images = images; + this._texts = texts; + } + + public getText(index: number): string + { + let message = (this._texts[index] || ''); + + if(message && message.length) message = message.replace(/\r\n|\r|\n/g, '
'); + + return message; + } + + public getImage(index: number): string + { + const imageName = (this._images[index] || ''); + + if(!imageName || !imageName.length) return null; + + let assetUrl = GetConfiguration('catalog.asset.image.url'); + + assetUrl = assetUrl.replace('%name%', imageName); + + return assetUrl; + } +} diff --git a/src/components/catalog/common/Product.ts b/src/components/catalog/common/Product.ts new file mode 100644 index 00000000..fa18127e --- /dev/null +++ b/src/components/catalog/common/Product.ts @@ -0,0 +1,93 @@ +import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer'; +import { IProduct } from './IProduct'; +import { ProductTypeEnum } from './ProductTypeEnum'; + +export class Product implements IProduct +{ + public static EFFECT_CLASSID_NINJA_DISAPPEAR: number = 108; + + private _productType: string; + private _productClassId: number; + private _extraParam: string; + private _productCount: number; + private _productData: IProductData; + private _furnitureData: IFurnitureData; + private _isUniqueLimitedItem: boolean; + private _uniqueLimitedItemSeriesSize: number; + private _uniqueLimitedItemsLeft: number; + + constructor(productType: string, productClassId: number, extraParam: string, productCount: number, productData: IProductData, furnitureData: IFurnitureData, isUniqueLimitedItem: boolean = false, uniqueLimitedItemSeriesSize: number = 0, uniqueLimitedItemsLeft: number = 0) + { + this._productType = productType; + this._productClassId = productClassId; + this._extraParam = extraParam; + this._productCount = productCount; + this._productData = productData; + this._furnitureData = furnitureData; + this._isUniqueLimitedItem = isUniqueLimitedItem; + this._uniqueLimitedItemSeriesSize = uniqueLimitedItemSeriesSize; + this._uniqueLimitedItemsLeft = uniqueLimitedItemsLeft; + } + + public static stripAddonProducts(products: IProduct[]): IProduct[] + { + if(products.length === 1) return products; + + return products.filter(product => ((product.productType !== ProductTypeEnum.BADGE) && (product.productType !== ProductTypeEnum.EFFECT) && (product.productClassId !== Product.EFFECT_CLASSID_NINJA_DISAPPEAR))); + } + + public get productType(): string + { + return this._productType; + } + + public get productClassId(): number + { + return this._productClassId; + } + + public get extraParam(): string + { + return this._extraParam; + } + + public set extraParam(extraParam: string) + { + this._extraParam = extraParam; + } + + public get productCount(): number + { + return this._productCount; + } + + public get productData(): IProductData + { + return this._productData; + } + + public get furnitureData(): IFurnitureData + { + return this._furnitureData; + } + + public get isUniqueLimitedItem(): boolean + { + return this._isUniqueLimitedItem; + } + + public get uniqueLimitedItemSeriesSize(): number + { + return this._uniqueLimitedItemSeriesSize; + } + + public get uniqueLimitedItemsLeft(): number + { + return this._uniqueLimitedItemsLeft; + } + + public set uniqueLimitedItemsLeft(uniqueLimitedItemsLeft: number) + { + this._uniqueLimitedItemsLeft = uniqueLimitedItemsLeft; + } +} diff --git a/src/components/catalog/common/RequestedPage.ts b/src/components/catalog/common/RequestedPage.ts new file mode 100644 index 00000000..4925776a --- /dev/null +++ b/src/components/catalog/common/RequestedPage.ts @@ -0,0 +1,59 @@ +export class RequestedPage +{ + public static REQUEST_TYPE_NONE: number = 0; + public static REQUEST_TYPE_ID: number = 1; + public static REQUEST_TYPE_NAME: number = 2; + + private _requestType: number; + private _requestId: number; + private _requestedOfferId: number; + private _requestName: string; + + constructor() + { + this._requestType = RequestedPage.REQUEST_TYPE_NONE; + } + + public setRequestById(id: number):void + { + this._requestType = RequestedPage.REQUEST_TYPE_ID; + this._requestId = id; + } + + public setRequestByName(name: string):void + { + this._requestType = RequestedPage.REQUEST_TYPE_NAME; + this._requestName = name; + } + + public resetRequest():void + { + this._requestType = RequestedPage.REQUEST_TYPE_NONE; + this._requestedOfferId = -1; + } + + public get requestType(): number + { + return this._requestType; + } + + public get requestId(): number + { + return this._requestId; + } + + public get requestedOfferId(): number + { + return this._requestedOfferId; + } + + public set requestedOfferId(offerId: number) + { + this._requestedOfferId = offerId; + } + + public get requestName(): string + { + return this._requestName; + } +} diff --git a/src/components/catalog/context/CatalogContext.tsx b/src/components/catalog/context/CatalogContext.tsx index 178d5bd3..452787ff 100644 --- a/src/components/catalog/context/CatalogContext.tsx +++ b/src/components/catalog/context/CatalogContext.tsx @@ -1,12 +1,68 @@ -import { createContext, FC, useContext } from 'react'; -import { CatalogContextProps, ICatalogContext } from './CatalogContext.types'; +import { FrontPageItem } from '@nitrots/nitro-renderer'; +import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react'; +import { ICatalogNode } from '../common/ICatalogNode'; +import { ICatalogPage } from '../common/ICatalogPage'; +import { IPageLocalization } from '../common/IPageLocalization'; +import { IPurchasableOffer } from '../common/IPurchasableOffer'; +import { ICatalogAction, ICatalogState } from '../reducers/CatalogReducer'; + +export interface ICatalogContext +{ + isVisible: boolean; + isBusy: boolean; + setIsBusy: Dispatch>; + pageId: number; + currentType: string; + setCurrentType: Dispatch>; + currentNode: ICatalogNode; + setCurrentNode: Dispatch>; + currentOffers: Map; + setCurrentOffers: Dispatch>>; + currentPage: ICatalogPage; + setCurrentPage: Dispatch>; + currentOffer: IPurchasableOffer; + setCurrentOffer: Dispatch>; + purchasableOffer: IPurchasableOffer; + setPurchasableOffer: Dispatch>; + activeNodes: ICatalogNode[]; + setActiveNodes: Dispatch>; + frontPageItems: FrontPageItem[]; + setFrontPageItems: Dispatch>; + loadCatalogPage: (pageId: number, offerId: number, forceRefresh?: boolean) => void; + showCatalogPage: (pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) => void; + + catalogState: ICatalogState; + dispatchCatalogState: Dispatch; +} const CatalogContext = createContext({ + isVisible: null, + isBusy: null, + setIsBusy: null, + pageId: null, + currentType: null, + setCurrentType: null, + currentNode: null, + setCurrentNode: null, + currentOffers: null, + setCurrentOffers: null, + currentPage: null, + setCurrentPage: null, + currentOffer: null, + setCurrentOffer: null, + purchasableOffer: null, + setPurchasableOffer: null, + activeNodes: null, + setActiveNodes: null, + frontPageItems: null, + setFrontPageItems: null, + loadCatalogPage: null, + showCatalogPage: null, catalogState: null, dispatchCatalogState: null }); -export const CatalogContextProvider: FC = props => +export const CatalogContextProvider: FC> = props => { return { props.children } } diff --git a/src/components/catalog/context/CatalogContext.types.ts b/src/components/catalog/context/CatalogContext.types.ts deleted file mode 100644 index 1045fd8c..00000000 --- a/src/components/catalog/context/CatalogContext.types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Dispatch, ProviderProps } from 'react'; -import { ICatalogAction, ICatalogState } from '../reducers/CatalogReducer'; - -export interface ICatalogContext -{ - catalogState: ICatalogState; - dispatchCatalogState: Dispatch; -} - -export interface CatalogContextProps extends ProviderProps -{ - -} diff --git a/src/components/catalog/reducers/CatalogReducer.tsx b/src/components/catalog/reducers/CatalogReducer.tsx index 9747147d..a1cfa713 100644 --- a/src/components/catalog/reducers/CatalogReducer.tsx +++ b/src/components/catalog/reducers/CatalogReducer.tsx @@ -2,14 +2,12 @@ import { CatalogPageMessageOfferData, CatalogPageMessageParser, ClubGiftInfoPars import { HabboGroupEntryData } from '@nitrots/nitro-renderer/src/nitro/communication/messages/parser/user/HabboGroupEntryData'; import { Reducer } from 'react'; import { CatalogPetPalette } from '../common/CatalogPetPalette'; -import { ICatalogOffers, ICatalogSearchResult, SetOffersToNodes } from '../common/CatalogUtilities'; +import { ICatalogSearchResult } from '../common/CatalogUtilities'; import { GiftWrappingConfiguration } from '../common/GiftWrappingConfiguration'; import { SubscriptionInfo } from '../common/SubscriptionInfo'; export interface ICatalogState { - root: INodeData; - offerRoot: ICatalogOffers; currentTab: INodeData; pageParser: CatalogPageMessageParser; activeOffer: CatalogPageMessageOfferData; @@ -27,8 +25,6 @@ export interface ICatalogAction { type: string; payload: { - root?: INodeData; - offerRoot?: ICatalogOffers; currentTab?: INodeData; pageParser?: CatalogPageMessageParser; activeOffer?: CatalogPageMessageOfferData; @@ -46,7 +42,6 @@ export interface ICatalogAction export class CatalogActions { public static RESET_STATE: string = 'CA_RESET_STATE'; - public static SET_CATALOG_ROOT: string = 'CA_SET_CATALOG_ROOT'; public static SET_CATALOG_CURRENT_TAB: string = 'CA_SET_CATALOG_CURRENT_TAB'; public static SET_CATALOG_PAGE_PARSER: string = 'CA_SET_CATALOG_PAGE'; public static SET_CATALOG_ACTIVE_OFFER: string = 'CA_SET_ACTIVE_OFFER'; @@ -61,8 +56,6 @@ export class CatalogActions } export const initialCatalog: ICatalogState = { - root: null, - offerRoot: null, currentTab: null, pageParser: null, activeOffer: null, @@ -80,16 +73,6 @@ export const CatalogReducer: Reducer = (state, ac { switch(action.type) { - case CatalogActions.SET_CATALOG_ROOT: { - const root = (action.payload.root || state.root || null); - const currentTab = ((root && (root.children.length > 0) && root.children[0]) || null); - - const offerRoot: ICatalogOffers = {}; - - SetOffersToNodes(offerRoot, root); - - return { ...state, root, offerRoot, currentTab }; - } case CatalogActions.SET_CATALOG_CURRENT_TAB: { const currentTab = (action.payload.currentTab || state.currentTab || null); const searchResult = null; diff --git a/src/components/catalog/views/CatalogViews.scss b/src/components/catalog/views/CatalogViews.scss index d86fa058..3caf0f9a 100644 --- a/src/components/catalog/views/CatalogViews.scss +++ b/src/components/catalog/views/CatalogViews.scss @@ -6,5 +6,4 @@ } @import './gift/CatalogGiftView'; -@import './navigation/CatalogNavigationView'; @import './page/CatalogPageView'; diff --git a/src/components/catalog/views/catalog-room-previewer/CatalogRoomPreviewerView.tsx b/src/components/catalog/views/catalog-room-previewer/CatalogRoomPreviewerView.tsx index dce973b6..b5caf217 100644 --- a/src/components/catalog/views/catalog-room-previewer/CatalogRoomPreviewerView.tsx +++ b/src/components/catalog/views/catalog-room-previewer/CatalogRoomPreviewerView.tsx @@ -1,16 +1,25 @@ import { NitroToolbarAnimateIconEvent, TextureUtils, ToolbarIconEnum } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useRef } from 'react'; +import { FC, useCallback, useRef, useState } from 'react'; import { GetRoomEngine } from '../../../../api'; -import { CatalogEvent } from '../../../../events'; +import { CatalogEvent, CatalogSelectProductEvent } from '../../../../events'; import { useUiEvent } from '../../../../hooks'; import { RoomPreviewerView } from '../../../../views/shared/room-previewer/RoomPreviewerView'; import { RoomPreviewerViewProps } from '../../../../views/shared/room-previewer/RoomPreviewerView.types'; +import { IPurchasableOffer } from '../../common/IPurchasableOffer'; export const CatalogRoomPreviewerView: FC = props => { const { roomPreviewer = null } = props; + const [ offer, setOffer ] = useState(null); const elementRef = useRef(null); + const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) => + { + setOffer(event.offer); + }, []); + + useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent) + const animatePurchase = useCallback(() => { if(!elementRef) return; diff --git a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx index b35e02b3..b4607d12 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx @@ -1,95 +1,52 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { GetCatalogPageComposer, INodeData } from '@nitrots/nitro-renderer'; -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem'; import { Text } from '../../../../common/Text'; -import { SendMessageHook } from '../../../../hooks/messages/message-event'; -import { CatalogMode } from '../../common/CatalogMode'; +import { BatchUpdates } from '../../../../hooks'; +import { ICatalogNode } from '../../common/ICatalogNode'; +import { useCatalogContext } from '../../context/CatalogContext'; import { CatalogIconView } from '../catalog-icon/CatalogIconView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView'; -import { ACTIVE_PAGES } from './CatalogNavigationView'; export interface CatalogNavigationItemViewProps { - page: INodeData; + node: ICatalogNode; isActive: boolean; - pendingTree: INodeData[]; - setPendingTree: Dispatch>; - setActiveChild: Dispatch>; + selectNode: (node: ICatalogNode) => void; } export const CatalogNavigationItemView: FC = props => { - const { page = null, isActive = false, pendingTree = null, setPendingTree = null, setActiveChild = null } = props; + const { node = null, isActive = false, selectNode = null } = props; const [ isExpanded, setIsExpanded ] = useState(false); + const { loadCatalogPage = null } = useCatalogContext(); - const select = useCallback((selectPage: INodeData, expand: boolean = false) => + const select = () => { - if(!selectPage) return; - - setActiveChild(prevValue => - { - if(prevValue === selectPage) - { - if(selectPage.pageId > -1) SendMessageHook(new GetCatalogPageComposer(selectPage.pageId, -1, CatalogMode.MODE_NORMAL)); - } - - return selectPage; - }); - - if(selectPage.children && selectPage.children.length) + BatchUpdates(() => { - setIsExpanded(prevValue => - { - if(expand) return true; - - return !prevValue; - }); - } - }, [ setActiveChild ]); + if(!isActive) selectNode(node); + else setIsExpanded(prevValue => !prevValue); + + loadCatalogPage(node.pageId, -1, true); + }); + } useEffect(() => { - if(!pendingTree || !pendingTree.length) return; - - if(page !== pendingTree[0]) return; - - const newTree = [ ...pendingTree ]; - - newTree.shift(); - - if(newTree.length) setPendingTree(newTree); - else setPendingTree(null); - - select(page, true); - }, [ page, pendingTree, setPendingTree, select ]); - - useEffect(() => - { - if(!isActive || !page) return; - - setIsExpanded(true); - - if(page.pageId > -1) SendMessageHook(new GetCatalogPageComposer(page.pageId, -1, CatalogMode.MODE_NORMAL)); - - const index = (ACTIVE_PAGES.push(page) - 1); - - return () => - { - ACTIVE_PAGES.length = index; - } - }, [ isActive, page ]); + setIsExpanded(isActive); + }, [ isActive ]); return ( <> - select(page) }> - - { page.localization } - { (page.children.length > 0) && + + + { node.localization } + { node.isBranch && } - { isActive && isExpanded && page.children && (page.children.length > 0) && - } + { isExpanded && node.isBranch && + } ); } diff --git a/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx b/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx index ce52208f..b994ceca 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx @@ -1,44 +1,71 @@ -import { INodeData } from '@nitrots/nitro-renderer'; -import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; +import { UseMountEffect } from '../../../../hooks'; +import { ICatalogNode } from '../../common/ICatalogNode'; +import { useCatalogContext } from '../../context/CatalogContext'; import { CatalogNavigationItemView } from './CatalogNavigationItemView'; export interface CatalogNavigationSetViewProps { - page: INodeData; - isFirstSet?: boolean; - pendingTree: INodeData[]; - setPendingTree: Dispatch>; + node: ICatalogNode; } export const CatalogNavigationSetView: FC = props => { - const { page = null, isFirstSet = false, pendingTree = null, setPendingTree = null } = props; - const [ activeChild, setActiveChild ] = useState(null); + const { node = null } = props; + const [ activeNode, setActiveNode ] = useState(null); + const { activeNodes = null, setActiveNodes = null } = useCatalogContext(); + + const selectNode = (node: ICatalogNode) => + { + setActiveNode(node); + } useEffect(() => { - if(!page || (page.pageId === -1) || !isFirstSet || pendingTree) return; + if(!node || !activeNode) return; - if(page.children.length) - { - if(activeChild) + setActiveNodes(prevValue => { - if(page.children.indexOf(activeChild) === -1) setActiveChild(null); - - return; - } - - setActiveChild(page.children[0]); + const newNodes = prevValue.slice(0, (node.depth - 1)); + + newNodes.push(activeNode); + + return newNodes; + }); + + return () => + { + setActiveNodes(prevValue => + { + const newNodes = prevValue.slice(0, (node.depth - 1)); + + return newNodes; + }); } - }, [ page, isFirstSet, activeChild, pendingTree ]); + }, [ node, activeNode, setActiveNodes ]); + + UseMountEffect(() => + { + if(activeNodes && activeNodes.length) + { + const index = activeNodes.indexOf(node); + + if(index > -1) + { + const childNode = activeNodes[index + 1]; + + if(childNode) setActiveNode(childNode); + } + } + }); return ( <> - { page && (page.children.length > 0) && page.children.map((page, index) => + { node && (node.children.length > 0) && node.children.map((node, index) => { - if(!page.visible) return null; + if(!node.isVisible) return null; - return + return -1) } selectNode={ selectNode } /> }) } ); diff --git a/src/components/catalog/views/navigation/CatalogNavigationView.scss b/src/components/catalog/views/navigation/CatalogNavigationView.scss deleted file mode 100644 index 38161d9f..00000000 --- a/src/components/catalog/views/navigation/CatalogNavigationView.scss +++ /dev/null @@ -1,21 +0,0 @@ -.nitro-catalog-navigation-grid-container { - border-radius: 0.25rem; - border-color: #B6BEC5 !important; - background-color: #CDD3D9; - border: 2px solid; - - .layout-grid-item { - font-size: $font-size-sm; - height: 23px !important; - border-color: unset !important; - background-color: #CDD3D9; - border: 0 !important; - padding: 1px 3px; - - .svg-inline--fa { - color: $black; - font-size: 10px; - padding: 1px; - } - } -} diff --git a/src/components/catalog/views/navigation/CatalogNavigationView.tsx b/src/components/catalog/views/navigation/CatalogNavigationView.tsx index aaa4ed9d..ed25d936 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationView.tsx @@ -1,41 +1,42 @@ -import { INodeData } from '@nitrots/nitro-renderer'; -import { Dispatch, FC, SetStateAction, useEffect } from 'react'; +import { FC, useCallback, useState } from 'react'; import { Column } from '../../../../common/Column'; import { Grid } from '../../../../common/Grid'; +import { FilterCatalogNode } from '../../common/FilterCatalogNode'; +import { ICatalogNode } from '../../common/ICatalogNode'; +import { useCatalogContext } from '../../context/CatalogContext'; import { CatalogSearchView } from '../search/CatalogSearchView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView'; export interface CatalogNavigationViewProps { - page: INodeData; - pendingTree: INodeData[]; - setPendingTree: Dispatch>; + node: ICatalogNode; } -export let ACTIVE_PAGES: INodeData[] = []; - export const CatalogNavigationView: FC = props => { - const { page = null, pendingTree = null, setPendingTree = null } = props; + const { node = null } = props; + const [ filteredNodes, setFilteredNodes ] = useState([]); + const { currentNode = null, activeNodes = null } = useCatalogContext(); - useEffect(() => + const filterNodes = useCallback((value: string, furniLines: string[]) => { - if(!page) return; + const nodes: ICatalogNode[] = []; - ACTIVE_PAGES = [ page ]; + FilterCatalogNode(value, furniLines, currentNode, nodes); - return () => - { - ACTIVE_PAGES = []; - } - }, [ page ]); + setFilteredNodes(nodes.filter(node => (node.isVisible))); + }, [ currentNode ]); return ( <> - + {/* { filterNodes && (filteredNodes.length > 0) && filteredNodes.map((node, index) => + { + return -1) } selectNode={ selectNode } />; + })} */} + diff --git a/src/components/catalog/views/page-details/CatalogPageDetailsView.tsx b/src/components/catalog/views/page-details/CatalogPageDetailsView.tsx index f9270e42..579591bc 100644 --- a/src/components/catalog/views/page-details/CatalogPageDetailsView.tsx +++ b/src/components/catalog/views/page-details/CatalogPageDetailsView.tsx @@ -1,25 +1,24 @@ -import { CatalogPageMessageParser } from '@nitrots/nitro-renderer'; import { FC } from 'react'; import { NitroLayoutFlexColumn } from '../../../../layout'; -import { GetCatalogPageImage, GetCatalogPageText } from '../../common/CatalogUtilities'; +import { ICatalogPage } from '../../common/ICatalogPage'; export interface CatalogPageDetailsViewProps { - pageParser: CatalogPageMessageParser; + page: ICatalogPage; } export const CatalogPageDetailsView: FC = props => { - const { pageParser = null } = props; + const { page = null } = props; - if(!pageParser) return null; - - const imageUrl = GetCatalogPageImage(pageParser, 1); + if(!page) return null; + + const imageUrl = page.localization.getImage(1); return ( { imageUrl && } -
+
); } diff --git a/src/components/catalog/views/page/CatalogPageView.tsx b/src/components/catalog/views/page/CatalogPageView.tsx index 9ca30aa4..24aff036 100644 --- a/src/components/catalog/views/page/CatalogPageView.tsx +++ b/src/components/catalog/views/page/CatalogPageView.tsx @@ -1,39 +1,35 @@ -import { CatalogPageMessageOfferData, IObjectData, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../api'; +import { IObjectData, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect } from 'react'; +import { GetAvatarRenderManager, GetSessionDataManager } from '../../../../api'; import { SetRoomPreviewerStuffDataEvent } from '../../../../events'; import { useUiEvent } from '../../../../hooks'; import { FurniCategory } from '../../common/FurniCategory'; +import { ICatalogPage } from '../../common/ICatalogPage'; +import { IPurchasableOffer } from '../../common/IPurchasableOffer'; import { ProductTypeEnum } from '../../common/ProductTypeEnum'; import { useCatalogContext } from '../../context/CatalogContext'; import { GetCatalogLayout } from './layout/GetCatalogLayout'; -import { CatalogLayoutSearchResultView } from './search-result/CatalogLayoutSearchResultView'; export interface CatalogPageViewProps { + page: ICatalogPage; roomPreviewer: RoomPreviewer; } export const CatalogPageView: FC = props => { - const { roomPreviewer = null } = props; - const [ lastOffer, setLastOffer ] = useState(null); - const { catalogState = null } = useCatalogContext(); - const { pageParser = null, activeOffer = null, searchResult = null } = catalogState; + const { page = null, roomPreviewer = null } = props; + const { currentOffer = null } = useCatalogContext(); - const updatePreviewerForOffer = useCallback((offer: CatalogPageMessageOfferData, stuffData: IObjectData = null) => + const updatePreviewerForOffer = useCallback((offer: IPurchasableOffer, stuffData: IObjectData = null) => { if(!offer || !roomPreviewer) return; - const product = offer.products[0]; + const product = offer.product; - if(!product) return; + if(!product && !product.furnitureData && (product.productType !== ProductTypeEnum.ROBOT)) return; - const furniData = GetFurnitureDataForProductOffer(product); - - if(!furniData && (product.productType !== ProductTypeEnum.ROBOT)) return; - - switch(product.productType) + switch(product.productType.toLowerCase()) { case ProductTypeEnum.ROBOT: { roomPreviewer.updateObjectRoom('default', 'default', 'default'); @@ -46,10 +42,10 @@ export const CatalogPageView: FC = props => case ProductTypeEnum.FLOOR: { roomPreviewer.updateObjectRoom('default', 'default', 'default'); - if(furniData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET) + if(product.furnitureData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET) { const setIds: number[] = []; - const sets = furniData.customParams.split(','); + const sets = product.furnitureData.customParams.split(','); for(const set of sets) { @@ -64,12 +60,13 @@ export const CatalogPageView: FC = props => } else { - roomPreviewer.addFurnitureIntoRoom(product.furniClassId, new Vector3d(90), stuffData); + console.log('??') + roomPreviewer.addFurnitureIntoRoom(product.productClassId, new Vector3d(90), stuffData); } return; } case ProductTypeEnum.WALL: { - switch(furniData.className) + switch(product.furnitureData.className) { case 'floor': roomPreviewer.reset(false); @@ -85,7 +82,7 @@ export const CatalogPageView: FC = props => break; default: roomPreviewer.updateObjectRoom('default', 'default', 'default'); - roomPreviewer.addWallItemIntoRoom(product.furniClassId, new Vector3d(90), product.extraParam); + roomPreviewer.addWallItemIntoRoom(product.productClassId, new Vector3d(90), product.extraParam); return; } @@ -109,15 +106,12 @@ export const CatalogPageView: FC = props => useEffect(() => { - if(!activeOffer) return; + if(!currentOffer) return; - updatePreviewerForOffer(activeOffer); - }, [ activeOffer, updatePreviewerForOffer ]); + updatePreviewerForOffer(currentOffer); + }, [ currentOffer, updatePreviewerForOffer ]); - if(searchResult && searchResult.furniture) - { - return ; - } + if(!page) return null; - return ((pageParser && GetCatalogLayout(pageParser, roomPreviewer)) || null); + return GetCatalogLayout(page, roomPreviewer); } diff --git a/src/components/catalog/views/page/layout/CatalogLayout.types.ts b/src/components/catalog/views/page/layout/CatalogLayout.types.ts index 53eccea6..ba9be2bb 100644 --- a/src/components/catalog/views/page/layout/CatalogLayout.types.ts +++ b/src/components/catalog/views/page/layout/CatalogLayout.types.ts @@ -1,7 +1,8 @@ -import { CatalogPageMessageParser, RoomPreviewer } from '@nitrots/nitro-renderer'; +import { RoomPreviewer } from '@nitrots/nitro-renderer'; +import { ICatalogPage } from '../../../common/ICatalogPage'; export interface CatalogLayoutProps { + page: ICatalogPage; roomPreviewer: RoomPreviewer; - pageParser?: CatalogPageMessageParser; } diff --git a/src/components/catalog/views/page/layout/GetCatalogLayout.tsx b/src/components/catalog/views/page/layout/GetCatalogLayout.tsx index 88b31948..df020a30 100644 --- a/src/components/catalog/views/page/layout/GetCatalogLayout.tsx +++ b/src/components/catalog/views/page/layout/GetCatalogLayout.tsx @@ -1,5 +1,7 @@ -import { CatalogPageMessageParser, RoomPreviewer } from '@nitrots/nitro-renderer'; +import { RoomPreviewer } from '@nitrots/nitro-renderer'; +import { ICatalogPage } from '../../../common/ICatalogPage'; import { CatalogLayoutBadgeDisplayView } from './badge-display/CatalogLayoutBadgeDisplayView'; +import { CatalogLayoutProps } from './CatalogLayout.types'; import { CatalogLayoutDefaultView } from './default/CatalogLayoutDefaultView'; import { CatalogLayoutFrontpage4View } from './frontpage4/CatalogLayoutFrontpage4View'; import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLayoutGuildCustomFurniView'; @@ -17,51 +19,53 @@ import { CatalogLayoutTrophiesView } from './trophies/CatalogLayoutTrophiesView' import { CatalogLayoutVipBuyView } from './vip-buy/CatalogLayoutVipBuyView'; import { CatalogLayoutVipGiftsView } from './vip-gifts/CatalogLayoutVipGiftsView'; -export const GetCatalogLayout = (pageParser: CatalogPageMessageParser, roomPreviewer: RoomPreviewer) => +export const GetCatalogLayout = (page: ICatalogPage, roomPreviewer: RoomPreviewer) => { - switch(pageParser.layoutCode) + const layoutProps: CatalogLayoutProps = { page, roomPreviewer }; + + switch(page.layoutCode) { case 'frontpage_featured': return null case 'frontpage4': - return ; + return ; case 'pets': - return ; + return ; case 'pets2': - return ; + return ; case 'pets3': - return ; + return ; case 'vip_buy': - return ; + return ; case 'guild_frontpage': - return ; + return ; case 'guild_forum': - return ; + return ; case 'guild_custom_furni': - return ; + return ; case 'search_results': return null; case 'club_gifts': - return ; + return ; case 'marketplace_own_items': - return ; + return ; case 'marketplace': - return ; + return ; case 'single_bundle': - return ; + return ; case 'spaces_new': - return ; + return ; case 'trophies': - return ; + return ; case 'info_loyalty': - return ; + return ; case 'badge_display': - return ; + return ; //case 'default_3x3_color_grouping': - //return ; + //return ; case 'bots': case 'default_3x3': default: - return ; + return ; } } diff --git a/src/components/catalog/views/page/layout/badge-display/CatalogLayoutBadgeDisplayView.tsx b/src/components/catalog/views/page/layout/badge-display/CatalogLayoutBadgeDisplayView.tsx index 78703c6e..9975501c 100644 --- a/src/components/catalog/views/page/layout/badge-display/CatalogLayoutBadgeDisplayView.tsx +++ b/src/components/catalog/views/page/layout/badge-display/CatalogLayoutBadgeDisplayView.tsx @@ -10,21 +10,19 @@ import { InventoryBadgesRequestEvent } from '../../../../../../events/inventory/ import { dispatchUiEvent, useUiEvent } from '../../../../../../hooks'; import { BadgeImageView } from '../../../../../../views/shared/badge-image/BadgeImageView'; import { useCatalogContext } from '../../../../context/CatalogContext'; +import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView'; import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView'; -import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutBadgeDisplayView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; + const { page = null, roomPreviewer = null } = props; const [ badges, setBadges ] = useState([]); const [ currentBadge, setCurrentBadge ] = useState(null); - const { catalogState = null } = useCatalogContext(); - const { activeOffer = null } = catalogState; + const { currentOffer = null } = useCatalogContext(); const onInventoryBadgesUpdatedEvent = useCallback((event: InventoryBadgesUpdatedEvent) => { - console.log(event); setBadges(event.badges); }, []); @@ -37,9 +35,10 @@ export const CatalogLayoutBadgeDisplayView: FC = props => useEffect(() => { - if(!currentBadge || !activeOffer) return; + if(!currentBadge || !currentOffer) return; const productData = []; + productData.push('0'); productData.push(currentBadge); productData.push(''); @@ -49,13 +48,13 @@ export const CatalogLayoutBadgeDisplayView: FC = props => const stringDataType = new StringDataType(); stringDataType.setValue(productData); - dispatchUiEvent(new SetRoomPreviewerStuffDataEvent(activeOffer, stringDataType)); - }, [ currentBadge, activeOffer, roomPreviewer ]); + dispatchUiEvent(new SetRoomPreviewerStuffDataEvent(currentOffer, stringDataType)); + }, [ currentBadge, currentOffer, roomPreviewer ]); return ( - + { LocalizeText('catalog_selectbadge') } @@ -71,7 +70,8 @@ export const CatalogLayoutBadgeDisplayView: FC = props => - + { !!currentOffer && + } ); diff --git a/src/components/catalog/views/page/layout/color-grouping/CatalogLayoutColorGroupingView.tsx b/src/components/catalog/views/page/layout/color-grouping/CatalogLayoutColorGroupingView.tsx index 45307324..9a4c2431 100644 --- a/src/components/catalog/views/page/layout/color-grouping/CatalogLayoutColorGroupingView.tsx +++ b/src/components/catalog/views/page/layout/color-grouping/CatalogLayoutColorGroupingView.tsx @@ -1,9 +1,4 @@ -import { CatalogPageMessageOfferData, IFurnitureData } from '@nitrots/nitro-renderer'; -import { FC, useMemo, useState } from 'react'; -import { GetSessionDataManager } from '../../../../../../api'; -import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout'; -import { ProductTypeEnum } from '../../../../common/ProductTypeEnum'; -import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView'; +import { FC, useState } from 'react'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export interface CatalogLayoutColorGroupViewProps extends CatalogLayoutProps @@ -13,79 +8,81 @@ export interface CatalogLayoutColorGroupViewProps extends CatalogLayoutProps export const CatalogLayoutColorGroupingView : FC = props => { - const { roomPreviewer = null, pageParser = null } = props; + const { page = null, roomPreviewer = null } = props; const [ colorableItems, setColorableItems ] = useState>(new Map()); - const offers = useMemo(() => - { - const offers: CatalogPageMessageOfferData[] = []; - const addedColorableItems = new Map(); + // const offers = useMemo(() => + // { + // const offers: CatalogPageMessageOfferData[] = []; + // const addedColorableItems = new Map(); - pageParser.offers.forEach(offer => - { - const product = offer.products[0]; - if(!product) return; + // pageParser.offers.forEach(offer => + // { + // const product = offer.products[0]; + // if(!product) return; - let furniData: IFurnitureData = null; + // let furniData: IFurnitureData = null; - if(product.productType === ProductTypeEnum.FLOOR) - { - furniData = GetSessionDataManager().getFloorItemData(product.furniClassId); - } - else if(product.productType === ProductTypeEnum.WALL) - { - furniData = GetSessionDataManager().getWallItemData(product.furniClassId); - } + // if(product.productType === ProductTypeEnum.FLOOR) + // { + // furniData = GetSessionDataManager().getFloorItemData(product.furniClassId); + // } + // else if(product.productType === ProductTypeEnum.WALL) + // { + // furniData = GetSessionDataManager().getWallItemData(product.furniClassId); + // } - if(((!(furniData)) || ((furniData.fullName.indexOf('*') === -1)))) - { - offers.push(offer); - } - else - { - const name = furniData.fullName.split('*')[0]; - const colorIndex = parseInt(furniData.fullName.split('*')[1]); + // if(((!(furniData)) || ((furniData.fullName.indexOf('*') === -1)))) + // { + // offers.push(offer); + // } + // else + // { + // const name = furniData.fullName.split('*')[0]; + // const colorIndex = parseInt(furniData.fullName.split('*')[1]); - if(!colorableItems[name]) - { - colorableItems[name] = []; - } + // if(!colorableItems[name]) + // { + // colorableItems[name] = []; + // } - let selectedColor = 0; - if(furniData.colors) - { - for(let color of furniData.colors) - { - if(color !== 0xFFFFFF) - { - selectedColor = color; - } - } - if(colorableItems[name].indexOf(selectedColor) === -1) - { - colorableItems[name][colorIndex] = selectedColor; - } - } + // let selectedColor = 0; + // if(furniData.colors) + // { + // for(let color of furniData.colors) + // { + // if(color !== 0xFFFFFF) + // { + // selectedColor = color; + // } + // } + // if(colorableItems[name].indexOf(selectedColor) === -1) + // { + // colorableItems[name][colorIndex] = selectedColor; + // } + // } - if(!addedColorableItems.has(name)) - { - offers.push(offer); - addedColorableItems.set(name, true); - } - } - }); - console.log(colorableItems); - return offers; - }, [colorableItems, pageParser.offers]); + // if(!addedColorableItems.has(name)) + // { + // offers.push(offer); + // addedColorableItems.set(name, true); + // } + // } + // }); + // console.log(colorableItems); + // return offers; + // }, [colorableItems, pageParser.offers]); - return ( - - - - - + return null; + + // return ( + // + // + // + // + // - - - ); + // + // + // ); } diff --git a/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx b/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx index 6f3595c3..5837e892 100644 --- a/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx +++ b/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx @@ -2,23 +2,26 @@ import { FC } from 'react'; import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; import { useCatalogContext } from '../../../../context/CatalogContext'; +import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView'; +import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView'; import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView'; -import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutDefaultView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; - const { catalogState = null } = useCatalogContext(); - const { activeOffer = null } = catalogState; + const { page = null, roomPreviewer = null } = props; + const { currentOffer = null } = useCatalogContext(); return ( - + - + { !currentOffer && + } + { !!currentOffer && + } ); diff --git a/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx b/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx index a331a151..60c3f8e6 100644 --- a/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx +++ b/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx @@ -3,14 +3,15 @@ import { FC, useCallback } from 'react'; import { CreateLinkEvent } from '../../../../../../api'; import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; -import { GetCatalogPageText } from '../../../../common/CatalogUtilities'; +import { useCatalogContext } from '../../../../context/CatalogContext'; import { CatalogRedeemVoucherView } from '../../redeem-voucher/CatalogRedeemVoucherView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView'; export const CatalogLayoutFrontpage4View: FC = props => { - const { pageParser = null } = props; + const { page = null } = props; + const { frontPageItems = [] } = useCatalogContext(); const selectItem = useCallback((item: FrontPageItem) => { @@ -28,17 +29,17 @@ export const CatalogLayoutFrontpage4View: FC = props => return ( - { pageParser.frontPageItems[0] && - selectItem(pageParser.frontPageItems[0]) } /> } + { frontPageItems[0] && + selectItem(frontPageItems[0]) } /> } - { pageParser.frontPageItems[1] && - selectItem(pageParser.frontPageItems[1]) } /> } - { pageParser.frontPageItems[2] && - selectItem(pageParser.frontPageItems[2]) } /> } - { pageParser.frontPageItems[3] && - selectItem(pageParser.frontPageItems[3]) } /> } - + { frontPageItems[1] && + selectItem(frontPageItems[1]) } /> } + { frontPageItems[2] && + selectItem(frontPageItems[2]) } /> } + { frontPageItems[3] && + selectItem(frontPageItems[3]) } /> } + ); diff --git a/src/components/catalog/views/page/layout/guild-custom-furni/CatalogLayoutGuildCustomFurniView.tsx b/src/components/catalog/views/page/layout/guild-custom-furni/CatalogLayoutGuildCustomFurniView.tsx index 104d38ff..bd672a6f 100644 --- a/src/components/catalog/views/page/layout/guild-custom-furni/CatalogLayoutGuildCustomFurniView.tsx +++ b/src/components/catalog/views/page/layout/guild-custom-furni/CatalogLayoutGuildCustomFurniView.tsx @@ -7,16 +7,16 @@ import { dispatchUiEvent } from '../../../../../../hooks'; import { SendMessageHook } from '../../../../../../hooks/messages'; import { useCatalogContext } from '../../../../context/CatalogContext'; import { CatalogSelectGroupView } from '../../../select-group/CatalogSelectGroupView'; +import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView'; import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView'; -import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayouGuildCustomFurniView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; + const { page = null, roomPreviewer = null } = props; const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { catalogState = null } = useCatalogContext(); - const { activeOffer = null, groups = null } = catalogState; + const { currentOffer = null, catalogState = null } = useCatalogContext(); + const { groups = null } = catalogState; const selectedGroup = useMemo(() => { @@ -27,14 +27,17 @@ export const CatalogLayouGuildCustomFurniView: FC = props => useEffect(() => { + if(!page) return; + SendMessageHook(new CatalogGroupsComposer()); - }, [ pageParser ]); + }, [ page ]); useEffect(() => { - if(!activeOffer || !groups[selectedGroupIndex]) return; + if(!currentOffer || !groups[selectedGroupIndex]) return; const productData = []; + productData.push('0'); productData.push(groups[selectedGroupIndex].groupId); productData.push(groups[selectedGroupIndex].badgeCode); @@ -44,20 +47,21 @@ export const CatalogLayouGuildCustomFurniView: FC = props => const stringDataType = new StringDataType(); stringDataType.setValue(productData); - dispatchUiEvent(new SetRoomPreviewerStuffDataEvent(activeOffer, stringDataType)); - }, [ groups, selectedGroupIndex, activeOffer ]); + dispatchUiEvent(new SetRoomPreviewerStuffDataEvent(currentOffer, stringDataType)); + }, [ groups, currentOffer, selectedGroupIndex ]); if(!groups) return null; return ( - + - - - + { !!currentOffer && + + + } ); diff --git a/src/components/catalog/views/page/layout/guild-forum/CatalogLayoutGuildForumView.tsx b/src/components/catalog/views/page/layout/guild-forum/CatalogLayoutGuildForumView.tsx index e51a1d4d..180081d3 100644 --- a/src/components/catalog/views/page/layout/guild-forum/CatalogLayoutGuildForumView.tsx +++ b/src/components/catalog/views/page/layout/guild-forum/CatalogLayoutGuildForumView.tsx @@ -4,44 +4,35 @@ import { Base } from '../../../../../../common/Base'; import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; import { SendMessageHook } from '../../../../../../hooks/messages'; -import { GetCatalogPageText } from '../../../../common/CatalogUtilities'; import { useCatalogContext } from '../../../../context/CatalogContext'; -import { CatalogActions } from '../../../../reducers/CatalogReducer'; import { CatalogSelectGroupView } from '../../../select-group/CatalogSelectGroupView'; -import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView'; +import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayouGuildForumView: FC = props => { - const { pageParser = null } = props; + const { page = null } = props; const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { catalogState = null, dispatchCatalogState = null } = useCatalogContext(); - const { activeOffer = null, groups = null } = catalogState; + const { currentOffer = null, setCurrentOffer = null, catalogState = null, dispatchCatalogState = null } = useCatalogContext(); + const { groups = null } = catalogState; useEffect(() => { SendMessageHook(new CatalogGroupsComposer()); - if(pageParser.offers.length > 0) - { - dispatchCatalogState({ - type: CatalogActions.SET_CATALOG_ACTIVE_OFFER, - payload: { - activeOffer: pageParser.offers[0] - } - }); - } - }, [ dispatchCatalogState, pageParser ]); + if(page.offers.length) setCurrentOffer(page.offers[0]); + }, [ page, setCurrentOffer ]); return ( - + - - - + { !!currentOffer && + + + } ); diff --git a/src/components/catalog/views/page/layout/guild-frontpage/CatalogLayoutGuildFrontpageView.tsx b/src/components/catalog/views/page/layout/guild-frontpage/CatalogLayoutGuildFrontpageView.tsx index 9412cd1f..c46b9b55 100644 --- a/src/components/catalog/views/page/layout/guild-frontpage/CatalogLayoutGuildFrontpageView.tsx +++ b/src/components/catalog/views/page/layout/guild-frontpage/CatalogLayoutGuildFrontpageView.tsx @@ -5,22 +5,21 @@ import { Button } from '../../../../../../common/Button'; import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; import { LayoutImage } from '../../../../../../common/layout/LayoutImage'; -import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayouGuildFrontpageView: FC = props => { - const { pageParser = null } = props; + const { page = null } = props; return ( - - - + + + - + diff --git a/src/components/catalog/views/page/layout/info-loyalty/CatalogLayoutInfoLoyaltyView.tsx b/src/components/catalog/views/page/layout/info-loyalty/CatalogLayoutInfoLoyaltyView.tsx index cb73e286..f378465c 100644 --- a/src/components/catalog/views/page/layout/info-loyalty/CatalogLayoutInfoLoyaltyView.tsx +++ b/src/components/catalog/views/page/layout/info-loyalty/CatalogLayoutInfoLoyaltyView.tsx @@ -1,15 +1,14 @@ import { FC } from 'react'; -import { GetCatalogPageText } from '../../../../common/CatalogUtilities'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutInfoLoyaltyView: FC = props => { - const { pageParser = null } = props; + const { page = null } = props; return (
-
+
); diff --git a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx index faaaf96a..b73c7b3c 100644 --- a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx +++ b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx @@ -1,4 +1,4 @@ -import { ApproveNameMessageComposer, CatalogPageMessageOfferData } from '@nitrots/nitro-renderer'; +import { ApproveNameMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useState } from 'react'; import { LocalizeText } from '../../../../../../api'; import { Column } from '../../../../../../common/Column'; @@ -8,13 +8,15 @@ import { CatalogEvent } from '../../../../../../events'; import { useUiEvent } from '../../../../../../hooks/events/ui/ui-event'; import { SendMessageHook } from '../../../../../../hooks/messages/message-event'; import { CurrencyIcon } from '../../../../../../views/shared/currency-icon/CurrencyIcon'; +import { IPurchasableOffer } from '../../../../common/IPurchasableOffer'; +import { Offer } from '../../../../common/Offer'; import { CatalogPurchaseButtonView } from '../../purchase/CatalogPurchaseButtonView'; import { CatalogPurchaseGiftButtonView } from '../../purchase/CatalogPurchaseGiftButtonView'; import { CatalogPetNameApprovalView } from './CatalogPetNameApprovalView'; export interface CatalogLayoutPetPurchaseViewProps { - offer: CatalogPageMessageOfferData; + offer: IPurchasableOffer; pageId: number; extra?: string; } @@ -53,22 +55,23 @@ export const CatalogLayoutPetPurchaseView: FC
{ LocalizeText('catalog.bundlewidget.price') }
- - { (offer.priceCredits > 0) && + + { ((offer.priceType === Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS) || (offer.priceType === Offer.PRICE_TYPE_CREDITS)) && - { offer.priceCredits } + { offer.priceInCredits } } - { (offer.priceActivityPoints > 0) && + { ((offer.priceType === Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS) || (offer.priceType === Offer.PRICE_TYPE_ACTIVITYPOINTS)) && - { offer.priceActivityPoints } - + { offer.priceInActivityPoints } + } - { offer.giftable && } + { offer.giftable && + } ); diff --git a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx index 94676c5f..8ff89668 100644 --- a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx +++ b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { ColorConverter, GetSellablePetPalettesComposer, SellablePetPaletteData } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; -import { GetProductDataForLocalization, LocalizeText } from '../../../../../../api'; +import { LocalizeText } from '../../../../../../api'; import { Base } from '../../../../../../common/Base'; import { Button } from '../../../../../../common/Button'; import { Column } from '../../../../../../common/Column'; @@ -13,7 +13,6 @@ import { SendMessageHook } from '../../../../../../hooks/messages/message-event' import { PetImageView } from '../../../../../../views/shared/pet-image/PetImageView'; import { GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities'; import { useCatalogContext } from '../../../../context/CatalogContext'; -import { CatalogActions } from '../../../../reducers/CatalogReducer'; import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView'; import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; @@ -21,15 +20,15 @@ import { CatalogLayoutPetPurchaseView } from './CatalogLayoutPetPurchaseView'; export const CatalogLayoutPetView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; + const { page = null, roomPreviewer = null } = props; const [ petIndex, setPetIndex ] = useState(-1); const [ sellablePalettes, setSellablePalettes ] = useState([]); const [ selectedPaletteIndex, setSelectedPaletteIndex ] = useState(-1); const [ sellableColors, setSellableColors ] = useState([]); const [ selectedColorIndex, setSelectedColorIndex ] = useState(-1); const [ colorsShowing, setColorsShowing ] = useState(false); - const { catalogState = null, dispatchCatalogState = null } = useCatalogContext(); - const { activeOffer = null, petPalettes = [] } = catalogState; + const { currentOffer = null, setCurrentOffer = null, catalogState = null, dispatchCatalogState = null } = useCatalogContext(); + const { petPalettes = [] } = catalogState; const getColor = useMemo(() => { @@ -69,29 +68,23 @@ export const CatalogLayoutPetView: FC = props => useEffect(() => { - if(!pageParser || !pageParser.offers.length) return; + if(!page || !page.offers.length) return; - const offer = pageParser.offers[0]; - - dispatchCatalogState({ - type: CatalogActions.SET_CATALOG_ACTIVE_OFFER, - payload: { - activeOffer: offer - } - }); + const offer = page.offers[0]; BatchUpdates(() => { + setCurrentOffer(offer); setPetIndex(GetPetIndexFromLocalization(offer.localizationId)); setColorsShowing(false); }); - }, [ pageParser, dispatchCatalogState ]); + }, [ page, setCurrentOffer ]); useEffect(() => { - if(!activeOffer) return; + if(!currentOffer) return; - const productData = GetProductDataForLocalization(activeOffer.localizationId); + const productData = currentOffer.product.productData; if(!productData) return; @@ -124,7 +117,7 @@ export const CatalogLayoutPetView: FC = props => }); SendMessageHook(new GetSellablePetPalettesComposer(productData.type)); - }, [ activeOffer, petPalettes ]); + }, [ currentOffer, petPalettes ]); useEffect(() => { @@ -154,7 +147,7 @@ export const CatalogLayoutPetView: FC = props => roomPreviewer.addPetIntoRoom(petFigureString); }, [ roomPreviewer, petIndex, sellablePalettes, selectedPaletteIndex, getColor ]); - if(!activeOffer) return null; + if(!currentOffer) return null; return ( @@ -173,7 +166,7 @@ export const CatalogLayoutPetView: FC = props => { (petIndex === -1) && - } + } { (petIndex >= 0) && <> @@ -187,7 +180,7 @@ export const CatalogLayoutPetView: FC = props => { petBreedName } - + } diff --git a/src/components/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx b/src/components/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx index fe9111aa..309fee1d 100644 --- a/src/components/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx +++ b/src/components/catalog/views/page/layout/pets3/CatalogLayoutPets3View.tsx @@ -2,26 +2,25 @@ import { FC } from 'react'; import { Base } from '../../../../../../common/Base'; import { Column } from '../../../../../../common/Column'; import { Flex } from '../../../../../../common/Flex'; -import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutPets3View: FC = props => { - const { pageParser = null } = props; + const { page = null } = props; - const imageUrl = GetCatalogPageImage(pageParser, 1); + const imageUrl = page.localization.getImage(1); return ( - { imageUrl && } - + { imageUrl && } + - + - + ); diff --git a/src/components/catalog/views/page/layout/single-bundle/CatalogLayoutSingleBundleView.tsx b/src/components/catalog/views/page/layout/single-bundle/CatalogLayoutSingleBundleView.tsx index 38944c38..fd43e2b8 100644 --- a/src/components/catalog/views/page/layout/single-bundle/CatalogLayoutSingleBundleView.tsx +++ b/src/components/catalog/views/page/layout/single-bundle/CatalogLayoutSingleBundleView.tsx @@ -3,26 +3,24 @@ import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; import { useCatalogContext } from '../../../../context/CatalogContext'; import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView'; -import { CatalogProductView } from '../../product/CatalogProductView'; import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutSingleBundleView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; - const { catalogState = null, dispatchCatalogState = null } = useCatalogContext(); - const { activeOffer = null } = catalogState; + const { page = null, roomPreviewer = null } = props; + const { currentOffer = null } = useCatalogContext(); return ( - { activeOffer && activeOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => )} + {/* { currentOffer && currentOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => )} */} - - { activeOffer && } + + { currentOffer && } ); diff --git a/src/components/catalog/views/page/layout/spaces-new/CatalogLayoutSpacesView.tsx b/src/components/catalog/views/page/layout/spaces-new/CatalogLayoutSpacesView.tsx index 7b8877af..05cd2216 100644 --- a/src/components/catalog/views/page/layout/spaces-new/CatalogLayoutSpacesView.tsx +++ b/src/components/catalog/views/page/layout/spaces-new/CatalogLayoutSpacesView.tsx @@ -1,53 +1,41 @@ -import { CatalogPageMessageOfferData, IFurnitureData } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { GetSessionDataManager, LocalizeText } from '../../../../../../api'; +import { LocalizeText } from '../../../../../../api'; import { Button } from '../../../../../../common/Button'; import { ButtonGroup } from '../../../../../../common/ButtonGroup'; import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; -import { ProductTypeEnum } from '../../../../common/ProductTypeEnum'; +import { BatchUpdates } from '../../../../../../hooks'; +import { IPurchasableOffer } from '../../../../common/IPurchasableOffer'; import { useCatalogContext } from '../../../../context/CatalogContext'; +import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView'; import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView'; -import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutSpacesView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; - const [ groups, setGroups ] = useState([]); + const { page = null, roomPreviewer = null } = props; + const [ groups, setGroups ] = useState([]); const [ activeGroupIndex, setActiveGroupIndex ] = useState(-1); - const { catalogState } = useCatalogContext(); + const { currentOffer = null, catalogState } = useCatalogContext(); const { activeOffer = null } = catalogState; const groupNames = [ 'floors', 'walls', 'views' ]; useEffect(() => { - if(!pageParser) return; + if(!page) return; - const groupedOffers: CatalogPageMessageOfferData[][] = [ [], [], [] ]; + const groupedOffers: IPurchasableOffer[][] = [ [], [], [] ]; - for(const offer of pageParser.offers) + for(const offer of page.offers) { - const product = offer.products[0]; + const product = offer.product if(!product) continue; - let furniData: IFurnitureData = null; + if(!product.furnitureData) continue; - if(product.productType === ProductTypeEnum.FLOOR) - { - furniData = GetSessionDataManager().getFloorItemData(product.furniClassId); - } - - else if(product.productType === ProductTypeEnum.WALL) - { - furniData = GetSessionDataManager().getWallItemData(product.furniClassId); - } - - if(!furniData) continue; - - const className = furniData.className; + const className = product.furnitureData.className; switch(className) { @@ -63,22 +51,24 @@ export const CatalogLayoutSpacesView: FC = props => } } - setGroups(groupedOffers); - setActiveGroupIndex(0); - }, [ pageParser ]); - - const product = ((activeOffer && activeOffer.products[0]) || null); + BatchUpdates(() => + { + setGroups(groupedOffers); + setActiveGroupIndex(0); + }); + }, [ page ]); return ( - { groupNames.map((name, index) => )} + { groupNames.map((name, index) => )} - + { !!currentOffer && + } ); diff --git a/src/components/catalog/views/page/layout/trophies/CatalogLayoutTrophiesView.tsx b/src/components/catalog/views/page/layout/trophies/CatalogLayoutTrophiesView.tsx index f107834b..5f58902f 100644 --- a/src/components/catalog/views/page/layout/trophies/CatalogLayoutTrophiesView.tsx +++ b/src/components/catalog/views/page/layout/trophies/CatalogLayoutTrophiesView.tsx @@ -2,27 +2,25 @@ import { FC, useState } from 'react'; import { Column } from '../../../../../../common/Column'; import { Grid } from '../../../../../../common/Grid'; import { useCatalogContext } from '../../../../context/CatalogContext'; +import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView'; import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView'; -import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutTrophiesView: FC = props => { - const { roomPreviewer = null, pageParser = null } = props; - const { catalogState = null, dispatchCatalogState = null } = useCatalogContext(); - const { activeOffer = null } = catalogState; + const { page = null, roomPreviewer = null } = props; const [ trophyText, setTrophyText ] = useState(''); - - const product = ((activeOffer && activeOffer.products[0]) || null); + const { currentOffer = null } = useCatalogContext(); return ( - +