From a91bff333a6a3ae8b624a3570b99fcc4c09f19bc Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 18 Jan 2022 18:12:48 -0500 Subject: [PATCH] Catalog navi fully working --- .../catalog/CatalogMessageHandler.tsx | 28 +-- src/components/catalog/CatalogView.tsx | 194 +++++++++++------- src/components/catalog/common/CatalogNode.ts | 39 +++- .../catalog/common/CatalogUtilities.ts | 10 + .../catalog/common/FurnitureOffer.ts | 5 + src/components/catalog/common/ICatalogNode.ts | 7 +- src/components/catalog/common/IProduct.ts | 2 + .../catalog/common/IPurchasableOffer.ts | 1 + src/components/catalog/common/Offer.ts | 5 + src/components/catalog/common/Product.ts | 52 ++++- .../catalog/context/CatalogContext.tsx | 12 +- .../catalog/reducers/CatalogReducer.tsx | 52 +---- .../navigation/CatalogNavigationItemView.tsx | 32 +-- .../navigation/CatalogNavigationSetView.tsx | 56 +---- .../navigation/CatalogNavigationView.tsx | 15 +- .../catalog/views/page/CatalogPageView.tsx | 9 +- .../views/page/layout/GetCatalogLayout.tsx | 3 + .../default/CatalogLayoutDefaultView.tsx | 5 +- .../CatalogLayoutRoomBundleView.tsx | 27 +++ .../CatalogLayoutSingleBundleView.tsx | 21 +- .../spaces-new/CatalogLayoutSpacesView.tsx | 1 - .../vip-buy/CatalogLayoutVipBuyView.tsx | 11 +- .../page/offers/CatalogGridOfferView.tsx | 26 +++ .../page/offers/CatalogPageOfferView.tsx | 13 +- .../page/offers/CatalogPageOffersView.tsx | 7 +- .../widgets/CatalogAddOnBadgeWidgetView.tsx | 27 +++ .../widgets/CatalogBundleGridWidgetView.tsx | 46 +++++ .../widgets/CatalogItemGridWidgetView.tsx | 24 +++ .../widgets/CatalogPriceDisplayWidgetView.tsx | 35 ++++ .../widgets/CatalogPurchaseWidgetView.tsx | 22 ++ .../widgets/CatalogSimplePriceWidgetView.tsx | 26 +++ .../views/search/CatalogSearchView.tsx | 13 +- src/events/catalog/CatalogPageReadyEvent.ts | 11 + .../CatalogSetExtraPurchaseParameterEvent.ts | 20 ++ src/events/catalog/index.ts | 2 + src/hooks/UseMountEffect.tsx | 7 +- 36 files changed, 578 insertions(+), 288 deletions(-) create mode 100644 src/components/catalog/views/page/layout/room-bundle/CatalogLayoutRoomBundleView.tsx create mode 100644 src/components/catalog/views/page/offers/CatalogGridOfferView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx create mode 100644 src/events/catalog/CatalogPageReadyEvent.ts create mode 100644 src/events/catalog/CatalogSetExtraPurchaseParameterEvent.ts diff --git a/src/components/catalog/CatalogMessageHandler.tsx b/src/components/catalog/CatalogMessageHandler.tsx index 1b710ac6..87efa40b 100644 --- a/src/components/catalog/CatalogMessageHandler.tsx +++ b/src/components/catalog/CatalogMessageHandler.tsx @@ -2,7 +2,7 @@ import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent'; import { FC, useCallback } from 'react'; import { GetFurnitureData, GetProductDataForLocalization, LocalizeText } from '../../api'; -import { CatalogNameResultEvent, CatalogPurchaseFailureEvent, CatalogSelectProductEvent } from '../../events'; +import { CatalogNameResultEvent, CatalogPurchaseFailureEvent, CatalogSelectProductEvent, CatalogSetExtraPurchaseParameterEvent } from '../../events'; import { CatalogGiftReceiverNotFoundEvent } from '../../events/catalog/CatalogGiftReceiverNotFoundEvent'; import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent'; import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent'; @@ -20,13 +20,14 @@ import { IPurchasableOffer } from './common/IPurchasableOffer'; import { Offer } from './common/Offer'; import { PageLocalization } from './common/PageLocalization'; import { Product } from './common/Product'; +import { ProductTypeEnum } from './common/ProductTypeEnum'; import { SubscriptionInfo } from './common/SubscriptionInfo'; import { useCatalogContext } from './context/CatalogContext'; import { CatalogActions } from './reducers/CatalogReducer'; export const CatalogMessageHandler: FC<{}> = props => { - const { setIsBusy, pageId, currentType, setCurrentNode, setCurrentOffers, currentPage, setCurrentOffer, setPurchasableOffer, setFrontPageItems, showCatalogPage, catalogState, dispatchCatalogState } = useCatalogContext(); + const { setIsBusy, pageId, currentType, setRootNode, setCurrentOffers, currentPage, setCurrentOffer, setPurchasableOffer, setFrontPageItems, resetState, showCatalogPage, catalogState, dispatchCatalogState } = useCatalogContext(); const onCatalogPagesListEvent = useCallback((event: CatalogPagesListEvent) => { @@ -52,10 +53,10 @@ export const CatalogMessageHandler: FC<{}> = props => BatchUpdates(() => { - setCurrentNode(getCatalogNode(parser.root, 0, null)); + setRootNode(getCatalogNode(parser.root, 0, null)); setCurrentOffers(offers); }); - }, [ setCurrentNode, setCurrentOffers ]); + }, [ setRootNode, setCurrentOffers ]); const onCatalogPageMessageEvent = useCallback((event: CatalogPageMessageEvent) => { @@ -155,12 +156,14 @@ export const CatalogMessageHandler: FC<{}> = props => dispatchUiEvent(new CatalogSelectProductEvent(offer)); - BatchUpdates(() => + if(offer.product && (offer.product.productType === ProductTypeEnum.WALL)) { - setCurrentOffer(offer); - setPurchasableOffer(offer); - }); - }, [ currentType, currentPage, setCurrentOffer, setPurchasableOffer ]); + dispatchUiEvent(new CatalogSetExtraPurchaseParameterEvent(offer.product.extraParam)); + } + + // (this._isObjectMoverRequested) && (this._purchasableOffer) + // setPurchasableOffer(offer); + }, [ currentType, currentPage ]); const onSellablePetPalettesMessageEvent = useCallback((event: SellablePetPalettesMessageEvent) => { @@ -229,11 +232,8 @@ export const CatalogMessageHandler: FC<{}> = props => const onCatalogPublishedMessageEvent = useCallback((event: CatalogPublishedMessageEvent) => { - dispatchCatalogState({ - type: CatalogActions.RESET_STATE, - payload: {} - }); - }, [ dispatchCatalogState ]); + resetState(); + }, [ resetState ]); const onGiftWrappingConfigurationEvent = useCallback((event: GiftWrappingConfigurationEvent) => { diff --git a/src/components/catalog/CatalogView.tsx b/src/components/catalog/CatalogView.tsx index fe4a8c36..5449b53f 100644 --- a/src/components/catalog/CatalogView.tsx +++ b/src/components/catalog/CatalogView.tsx @@ -36,7 +36,7 @@ export const CatalogView: FC<{}> = props => const [ pageId, setPageId ] = useState(-1); const [ previousPageId, setPreviousPageId ] = useState(-1); const [ currentType, setCurrentType ] = useState(CatalogType.NORMAL); - const [ currentNode, setCurrentNode ] = useState(null); + const [ rootNode, setRootNode ] = useState(null); const [ currentOffers, setCurrentOffers ] = useState>(null); const [ currentPage, setCurrentPage ] = useState(null); const [ currentOffer, setCurrentOffer ] = useState(null); @@ -51,6 +51,46 @@ export const CatalogView: FC<{}> = props => const [ roomPreviewer, setRoomPreviewer ] = useState(null); const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog); + const resetState = useCallback(() => + { + BatchUpdates(() => + { + setPageId(-1); + setPreviousPageId(-1); + setRootNode(null); + setCurrentOffers(null); + setCurrentPage(null); + setCurrentOffer(null); + setPurchasableOffer(null); + setCurrentTab(null); + setActiveNodes([]); + setLastActiveNodes(null); + setFrontPageItems([]); + setIsInitialized(false); + setIsVisible(true); + }); + }, []); + + const getNodesByOfferId = useCallback((offerId: number) => + { + if(!currentOffers || !currentOffers.size) return null; + + if(true) + { + const nodes: ICatalogNode[] = []; + const offers = currentOffers.get(offerId); + + if(offers && offers.length) + { + for(const offer of offers) (offer.isVisible && nodes.push(offer)); + } + + if(nodes.length) return nodes; + } + + return currentOffers.get(offerId); + }, [ currentOffers ]); + const loadCatalogPage = useCallback((pageId: number, offerId: number, forceRefresh: boolean = false) => { if(pageId < 0) return; @@ -95,6 +135,41 @@ export const CatalogView: FC<{}> = props => }); }, [ currentPage, forceRefresh, selectOffer ]); + const selectCatalogNode = useCallback((targetNode: ICatalogNode) => + { + setActiveNodes(prevValue => + { + const isActive = (prevValue.indexOf(targetNode) >= 0); + const isOpen = targetNode.isOpen; + const newNodes: ICatalogNode[] = []; + + for(const n of prevValue) + { + n.deactivate(); + + if(n.depth < targetNode.depth) + { + newNodes.push(n); + } + else + { + n.close(); + } + } + + targetNode.activate(); + + if(isActive && isOpen) targetNode.close(); + else targetNode.open(); + + if(newNodes.indexOf(targetNode) < 0) newNodes.push(targetNode); + + return newNodes; + }); + + if(targetNode.pageId > -1) loadCatalogPage(targetNode.pageId, -1, true); + }, [ setActiveNodes, loadCatalogPage ]); + const onCatalogEvent = useCallback((event: CatalogEvent) => { let save = false; @@ -186,7 +261,37 @@ export const CatalogView: FC<{}> = props => useEffect(() => { - if(!currentNode) return; + if(!isVisible) return; + + if(!isInitialized) SendMessageHook(new GetCatalogIndexComposer(currentType)); + }, [ isVisible, isInitialized, currentType ]); + + useEffect(() => + { + if(!rootNode) return; + + BatchUpdates(() => + { + setIsInitialized(true); + + if(rootNode.isBranch) + { + for(const child of rootNode.children) + { + if(child && child.isVisible) + { + setCurrentTab(child); + + return; + } + } + } + }); + }, [ rootNode ]); + + useEffect(() => + { + if(!rootNode) return; switch(requestedPage.requestType) { @@ -200,60 +305,28 @@ export const CatalogView: FC<{}> = props => requestedPage.resetRequest(); return; } - }, [ currentNode, requestedPage ]); + }, [ rootNode, requestedPage ]); useEffect(() => { - if(!isVisible) return; + if(!currentTab) 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)) + if(currentTab.children.length) { - loadCatalogPage(currentTab.pageId, -1); + for(const child of currentTab.children) + { + if(!child.isVisible) continue; + + selectCatalogNode(child); + + return; + } } else { - if(currentTab.children.length) - { - for(const child of currentTab.children) - { - if(child.isVisible) - { - activeNodes.push(child); - - loadCatalogPage(child.pageId, -1); - - break; - } - } - } + loadCatalogPage(currentTab.pageId, -1, true); } - - // 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 ]); + }, [ currentTab, selectCatalogNode, loadCatalogPage ]); useEffect(() => { @@ -262,29 +335,6 @@ export const CatalogView: FC<{}> = props => 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) @@ -310,13 +360,13 @@ export const CatalogView: FC<{}> = props => }); return ( - + { isVisible && { setIsVisible(false); } } /> - + diff --git a/src/components/catalog/common/CatalogNode.ts b/src/components/catalog/common/CatalogNode.ts index a976c8ab..893aa323 100644 --- a/src/components/catalog/common/CatalogNode.ts +++ b/src/components/catalog/common/CatalogNode.ts @@ -12,6 +12,8 @@ export class CatalogNode implements ICatalogNode private _offerIds: number[]; private _parent: ICatalogNode; private _isVisible: boolean; + private _isActive: boolean; + private _isOpen: boolean; constructor(node: NodeData, depth: number, parent: ICatalogNode) { @@ -24,11 +26,35 @@ export class CatalogNode implements ICatalogNode this._children = []; this._offerIds = node.offerIds; this._isVisible = node.visible; + this._isActive = false; + this._isOpen = false; } - public get isOpen(): boolean + public activate(): void { - return false; + this._isActive = true; + } + + public deactivate(): void + { + this._isActive = false; + } + + public open(): void + { + this._isOpen = true; + } + + public close(): void + { + this._isOpen = false; + } + + public addChild(child: ICatalogNode):void + { + if(!child) return; + + this._children.push(child); } public get depth(): number @@ -86,10 +112,13 @@ export class CatalogNode implements ICatalogNode return this._isVisible; } - public addChild(child: ICatalogNode):void + public get isActive(): boolean { - if(!child) return; + return this._isActive; + } - this._children.push(child); + public get isOpen(): boolean + { + return this._isOpen; } } diff --git a/src/components/catalog/common/CatalogUtilities.ts b/src/components/catalog/common/CatalogUtilities.ts index f8a17aef..6f9f7dc6 100644 --- a/src/components/catalog/common/CatalogUtilities.ts +++ b/src/components/catalog/common/CatalogUtilities.ts @@ -13,6 +13,16 @@ export interface ICatalogSearchResult furniture: IFurnitureData[]; } +export const GetPixelEffectIcon = (id: number) => +{ + return ''; +} + +export const GetSubscriptionProductIcon = (id: number) => +{ + return ''; +} + export function GetOfferName(offer: CatalogPageMessageOfferData): string { const productData = GetProductDataForLocalization(offer.localizationId); diff --git a/src/components/catalog/common/FurnitureOffer.ts b/src/components/catalog/common/FurnitureOffer.ts index 6a4816d7..85d3216a 100644 --- a/src/components/catalog/common/FurnitureOffer.ts +++ b/src/components/catalog/common/FurnitureOffer.ts @@ -58,6 +58,11 @@ export class FurnitureOffer implements IPurchasableOffer return this._product; } + public get products(): IProduct[] + { + return [ this._product ]; + } + public get localizationId(): string { return 'roomItem.name.' + this._furniData.id; diff --git a/src/components/catalog/common/ICatalogNode.ts b/src/components/catalog/common/ICatalogNode.ts index d3962647..c69f5a65 100644 --- a/src/components/catalog/common/ICatalogNode.ts +++ b/src/components/catalog/common/ICatalogNode.ts @@ -1,7 +1,10 @@ export interface ICatalogNode { + activate(): void; + deactivate(): void; + open(): void; + close(): void; addChild(node: ICatalogNode): void; - readonly isOpen: boolean; readonly depth: number; readonly isBranch: boolean; readonly isLeaf: boolean; @@ -13,4 +16,6 @@ export interface ICatalogNode readonly offerIds: number[]; readonly parent: ICatalogNode; readonly isVisible: boolean; + readonly isActive: boolean; + readonly isOpen: boolean; } diff --git a/src/components/catalog/common/IProduct.ts b/src/components/catalog/common/IProduct.ts index d52da3a2..ec00a428 100644 --- a/src/components/catalog/common/IProduct.ts +++ b/src/components/catalog/common/IProduct.ts @@ -1,7 +1,9 @@ import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer'; +import { IPurchasableOffer } from './IPurchasableOffer'; export interface IProduct { + getIconUrl(offer?: IPurchasableOffer): string; readonly productType: string; readonly productClassId: number; readonly extraParam: string; diff --git a/src/components/catalog/common/IPurchasableOffer.ts b/src/components/catalog/common/IPurchasableOffer.ts index 7ef453e6..d3198bfa 100644 --- a/src/components/catalog/common/IPurchasableOffer.ts +++ b/src/components/catalog/common/IPurchasableOffer.ts @@ -19,4 +19,5 @@ export interface IPurchasableOffer readonly badgeCode: string; readonly localizationName: string; readonly localizationDescription: string; + readonly products: IProduct[]; } diff --git a/src/components/catalog/common/Offer.ts b/src/components/catalog/common/Offer.ts index ed050357..16e30a0c 100644 --- a/src/components/catalog/common/Offer.ts +++ b/src/components/catalog/common/Offer.ts @@ -160,6 +160,11 @@ export class Offer implements IPurchasableOffer return LocalizeText(this._localizationId); } + public get products(): IProduct[] + { + return this._products; + } + private setPricingModelForProducts(): void { const products = Product.stripAddonProducts(this._products); diff --git a/src/components/catalog/common/Product.ts b/src/components/catalog/common/Product.ts index fa18127e..df0eb9cd 100644 --- a/src/components/catalog/common/Product.ts +++ b/src/components/catalog/common/Product.ts @@ -1,5 +1,8 @@ -import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer'; +import { IFurnitureData, IObjectData, IProductData } from '@nitrots/nitro-renderer'; +import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../../../api'; +import { GetPixelEffectIcon, GetSubscriptionProductIcon } from './CatalogUtilities'; import { IProduct } from './IProduct'; +import { IPurchasableOffer } from './IPurchasableOffer'; import { ProductTypeEnum } from './ProductTypeEnum'; export class Product implements IProduct @@ -36,6 +39,53 @@ export class Product implements IProduct return products.filter(product => ((product.productType !== ProductTypeEnum.BADGE) && (product.productType !== ProductTypeEnum.EFFECT) && (product.productClassId !== Product.EFFECT_CLASSID_NINJA_DISAPPEAR))); } + public getIconUrl(offer: IPurchasableOffer = null, stuffData: IObjectData = null): string + { + switch(this._productType) + { + case ProductTypeEnum.FLOOR: + return GetRoomEngine().getFurnitureFloorIconUrl(this.productClassId); + case ProductTypeEnum.WALL: { + if(offer && this._furnitureData) + { + let iconName = ''; + + switch(this._furnitureData.className) + { + case 'floor': + iconName = [ 'th', this._furnitureData.className, offer.product.extraParam ].join('_'); + break; + case 'wallpaper': + iconName = [ 'th', 'wall', offer.product.extraParam ].join('_'); + break; + case 'landscape': + iconName = [ 'th', this._furnitureData.className, (offer.product.extraParam || '').replace('.', '_'), '001' ].join('_'); + break; + } + + if(iconName !== '') + { + const assetUrl = GetConfiguration('catalog.asset.url'); + + return `${ assetUrl }/${ iconName }.png`; + } + } + + return GetRoomEngine().getFurnitureWallIconUrl(this.productClassId, this._extraParam); + } + case ProductTypeEnum.EFFECT: + return GetPixelEffectIcon(this.productClassId); + case ProductTypeEnum.HABBO_CLUB: + return GetSubscriptionProductIcon(this.productClassId); + case ProductTypeEnum.BADGE: + return GetSessionDataManager().getBadgeUrl(this._extraParam); + case ProductTypeEnum.ROBOT: + return null; + } + + return null; + } + public get productType(): string { return this._productType; diff --git a/src/components/catalog/context/CatalogContext.tsx b/src/components/catalog/context/CatalogContext.tsx index 452787ff..bdabcae8 100644 --- a/src/components/catalog/context/CatalogContext.tsx +++ b/src/components/catalog/context/CatalogContext.tsx @@ -14,8 +14,8 @@ export interface ICatalogContext pageId: number; currentType: string; setCurrentType: Dispatch>; - currentNode: ICatalogNode; - setCurrentNode: Dispatch>; + rootNode: ICatalogNode; + setRootNode: Dispatch>; currentOffers: Map; setCurrentOffers: Dispatch>>; currentPage: ICatalogPage; @@ -28,8 +28,10 @@ export interface ICatalogContext setActiveNodes: Dispatch>; frontPageItems: FrontPageItem[]; setFrontPageItems: Dispatch>; + resetState: () => void; loadCatalogPage: (pageId: number, offerId: number, forceRefresh?: boolean) => void; showCatalogPage: (pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) => void; + selectCatalogNode: (targetNode: ICatalogNode) => void; catalogState: ICatalogState; dispatchCatalogState: Dispatch; @@ -42,8 +44,8 @@ const CatalogContext = createContext({ pageId: null, currentType: null, setCurrentType: null, - currentNode: null, - setCurrentNode: null, + rootNode: null, + setRootNode: null, currentOffers: null, setCurrentOffers: null, currentPage: null, @@ -56,8 +58,10 @@ const CatalogContext = createContext({ setActiveNodes: null, frontPageItems: null, setFrontPageItems: null, + resetState: null, loadCatalogPage: null, showCatalogPage: null, + selectCatalogNode: null, catalogState: null, dispatchCatalogState: null }); diff --git a/src/components/catalog/reducers/CatalogReducer.tsx b/src/components/catalog/reducers/CatalogReducer.tsx index a1cfa713..d7380e85 100644 --- a/src/components/catalog/reducers/CatalogReducer.tsx +++ b/src/components/catalog/reducers/CatalogReducer.tsx @@ -1,17 +1,12 @@ -import { CatalogPageMessageOfferData, CatalogPageMessageParser, ClubGiftInfoParser, ClubOfferData, GiftWrappingConfigurationParser, INodeData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer'; +import { ClubGiftInfoParser, ClubOfferData, GiftWrappingConfigurationParser, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer'; import { HabboGroupEntryData } from '@nitrots/nitro-renderer/src/nitro/communication/messages/parser/user/HabboGroupEntryData'; import { Reducer } from 'react'; import { CatalogPetPalette } from '../common/CatalogPetPalette'; -import { ICatalogSearchResult } from '../common/CatalogUtilities'; import { GiftWrappingConfiguration } from '../common/GiftWrappingConfiguration'; import { SubscriptionInfo } from '../common/SubscriptionInfo'; export interface ICatalogState { - currentTab: INodeData; - pageParser: CatalogPageMessageParser; - activeOffer: CatalogPageMessageOfferData; - searchResult: ICatalogSearchResult; groups: HabboGroupEntryData[]; petPalettes: CatalogPetPalette[]; clubOffers: ClubOfferData[]; @@ -25,10 +20,6 @@ export interface ICatalogAction { type: string; payload: { - currentTab?: INodeData; - pageParser?: CatalogPageMessageParser; - activeOffer?: CatalogPageMessageOfferData; - searchResult?: ICatalogSearchResult; groups?: HabboGroupEntryData[]; petPalette?: CatalogPetPalette; clubOffers?: ClubOfferData[]; @@ -42,9 +33,6 @@ export interface ICatalogAction export class CatalogActions { public static RESET_STATE: string = 'CA_RESET_STATE'; - 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'; public static SET_CLUB_OFFERS: string = 'CA_SET_CLUB_OFFERS'; public static SET_GROUPS: string = 'CA_SET_GROUPS'; public static SET_PET_PALETTE: string = 'CA_SET_PET_PALETTE'; @@ -56,10 +44,6 @@ export class CatalogActions } export const initialCatalog: ICatalogState = { - currentTab: null, - pageParser: null, - activeOffer: null, - searchResult: null, groups: [], petPalettes: [], clubOffers: null, @@ -73,40 +57,6 @@ export const CatalogReducer: Reducer = (state, ac { switch(action.type) { - case CatalogActions.SET_CATALOG_CURRENT_TAB: { - const currentTab = (action.payload.currentTab || state.currentTab || null); - const searchResult = null; - - return { ...state, currentTab, searchResult }; - } - case CatalogActions.SET_CATALOG_PAGE_PARSER: { - let pageParser = (Object.create(action.payload.pageParser) as CatalogPageMessageParser); - let activeOffer = null; - - if(pageParser.layoutCode === 'single_bundle') - { - activeOffer = ((pageParser.offers && pageParser.offers[0]) || null); - } - - const searchResult = state.searchResult; - - if(searchResult) - { - searchResult.furniture = null; - } - - return { ...state, pageParser, activeOffer, searchResult }; - } - case CatalogActions.SET_CATALOG_ACTIVE_OFFER: { - const activeOffer = (action.payload.activeOffer || state.activeOffer || null); - - return { ...state, activeOffer }; - } - case CatalogActions.SET_SEARCH_RESULT: { - const searchResult = (action.payload.searchResult || null); - - return { ...state, searchResult }; - } case CatalogActions.SET_GROUPS: { const groups = (action.payload.groups || null); diff --git a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx index b4607d12..cb83f720 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx @@ -1,8 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { FC, useEffect, useState } from 'react'; +import { FC } from 'react'; import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem'; import { Text } from '../../../../common/Text'; -import { BatchUpdates } from '../../../../hooks'; import { ICatalogNode } from '../../common/ICatalogNode'; import { useCatalogContext } from '../../context/CatalogContext'; import { CatalogIconView } from '../catalog-icon/CatalogIconView'; @@ -11,41 +10,22 @@ import { CatalogNavigationSetView } from './CatalogNavigationSetView'; export interface CatalogNavigationItemViewProps { node: ICatalogNode; - isActive: boolean; - selectNode: (node: ICatalogNode) => void; } export const CatalogNavigationItemView: FC = props => { - const { node = null, isActive = false, selectNode = null } = props; - const [ isExpanded, setIsExpanded ] = useState(false); - const { loadCatalogPage = null } = useCatalogContext(); - - const select = () => - { - BatchUpdates(() => - { - if(!isActive) selectNode(node); - else setIsExpanded(prevValue => !prevValue); - - loadCatalogPage(node.pageId, -1, true); - }); - } - - useEffect(() => - { - setIsExpanded(isActive); - }, [ isActive ]); + const { node = null } = props; + const { selectCatalogNode = null } = useCatalogContext(); return ( <> - + selectCatalogNode(node) }> { node.localization } { node.isBranch && - } + } - { isExpanded && node.isBranch && + { node.isOpen && node.isBranch && } ); diff --git a/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx b/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx index b994ceca..56122fdd 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx @@ -1,7 +1,5 @@ -import { FC, useEffect, useState } from 'react'; -import { UseMountEffect } from '../../../../hooks'; +import { FC } from 'react'; import { ICatalogNode } from '../../common/ICatalogNode'; -import { useCatalogContext } from '../../context/CatalogContext'; import { CatalogNavigationItemView } from './CatalogNavigationItemView'; export interface CatalogNavigationSetViewProps @@ -12,60 +10,14 @@ export interface CatalogNavigationSetViewProps export const CatalogNavigationSetView: FC = props => { const { node = null } = props; - const [ activeNode, setActiveNode ] = useState(null); - const { activeNodes = null, setActiveNodes = null } = useCatalogContext(); - - const selectNode = (node: ICatalogNode) => - { - setActiveNode(node); - } - - useEffect(() => - { - if(!node || !activeNode) return; - - setActiveNodes(prevValue => - { - 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; - }); - } - }, [ 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 ( <> - { node && (node.children.length > 0) && node.children.map((node, index) => + { node && (node.children.length > 0) && node.children.map((n, index) => { - if(!node.isVisible) return null; + if(!n.isVisible) return null; - return -1) } selectNode={ selectNode } /> + return }) } ); diff --git a/src/components/catalog/views/navigation/CatalogNavigationView.tsx b/src/components/catalog/views/navigation/CatalogNavigationView.tsx index ed25d936..acc70046 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationView.tsx @@ -1,9 +1,7 @@ -import { FC, useCallback, useState } from 'react'; +import { FC } 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'; @@ -15,17 +13,6 @@ export interface CatalogNavigationViewProps export const CatalogNavigationView: FC = props => { const { node = null } = props; - const [ filteredNodes, setFilteredNodes ] = useState([]); - const { currentNode = null, activeNodes = null } = useCatalogContext(); - - const filterNodes = useCallback((value: string, furniLines: string[]) => - { - const nodes: ICatalogNode[] = []; - - FilterCatalogNode(value, furniLines, currentNode, nodes); - - setFilteredNodes(nodes.filter(node => (node.isVisible))); - }, [ currentNode ]); return ( <> diff --git a/src/components/catalog/views/page/CatalogPageView.tsx b/src/components/catalog/views/page/CatalogPageView.tsx index 24aff036..dd2ee0ee 100644 --- a/src/components/catalog/views/page/CatalogPageView.tsx +++ b/src/components/catalog/views/page/CatalogPageView.tsx @@ -1,8 +1,8 @@ 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 { CatalogPageReadyEvent, SetRoomPreviewerStuffDataEvent } from '../../../../events'; +import { dispatchUiEvent, useUiEvent } from '../../../../hooks'; import { FurniCategory } from '../../common/FurniCategory'; import { ICatalogPage } from '../../common/ICatalogPage'; import { IPurchasableOffer } from '../../common/IPurchasableOffer'; @@ -111,6 +111,11 @@ export const CatalogPageView: FC = props => updatePreviewerForOffer(currentOffer); }, [ currentOffer, updatePreviewerForOffer ]); + useEffect(() => + { + dispatchUiEvent(new CatalogPageReadyEvent()); + }, [ page ]); + if(!page) return null; return GetCatalogLayout(page, roomPreviewer); diff --git a/src/components/catalog/views/page/layout/GetCatalogLayout.tsx b/src/components/catalog/views/page/layout/GetCatalogLayout.tsx index df020a30..663d59e3 100644 --- a/src/components/catalog/views/page/layout/GetCatalogLayout.tsx +++ b/src/components/catalog/views/page/layout/GetCatalogLayout.tsx @@ -13,6 +13,7 @@ import { CatalogLayoutMarketplacePublicItemsView } from './marketplace/CatalogLa import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView'; import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View'; import { CatalogLayoutPets3View } from './pets3/CatalogLayoutPets3View'; +import { CatalogLayoutRoomBundleView } from './room-bundle/CatalogLayoutRoomBundleView'; import { CatalogLayoutSingleBundleView } from './single-bundle/CatalogLayoutSingleBundleView'; import { CatalogLayoutSpacesView } from './spaces-new/CatalogLayoutSpacesView'; import { CatalogLayoutTrophiesView } from './trophies/CatalogLayoutTrophiesView'; @@ -53,6 +54,8 @@ export const GetCatalogLayout = (page: ICatalogPage, roomPreviewer: RoomPreviewe return ; case 'single_bundle': return ; + case 'room_bundle': + return ; case 'spaces_new': return ; case 'trophies': diff --git a/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx b/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx index 5837e892..56a5875e 100644 --- a/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx +++ b/src/components/catalog/views/page/layout/default/CatalogLayoutDefaultView.tsx @@ -4,7 +4,7 @@ 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 { CatalogItemGridWidgetView } from '../../widgets/CatalogItemGridWidgetView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutDefaultView: FC = props => @@ -15,7 +15,8 @@ export const CatalogLayoutDefaultView: FC = props => return ( - + + {/* */} { !currentOffer && diff --git a/src/components/catalog/views/page/layout/room-bundle/CatalogLayoutRoomBundleView.tsx b/src/components/catalog/views/page/layout/room-bundle/CatalogLayoutRoomBundleView.tsx new file mode 100644 index 00000000..baf42ec7 --- /dev/null +++ b/src/components/catalog/views/page/layout/room-bundle/CatalogLayoutRoomBundleView.tsx @@ -0,0 +1,27 @@ +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 { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView'; +import { CatalogLayoutProps } from '../CatalogLayout.types'; + +export const CatalogLayoutRoomBundleView: FC = props => +{ + const { page = null, roomPreviewer = null } = props; + const { currentOffer = null } = useCatalogContext(); + + return ( + + + + {/* { currentOffer && currentOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => )} */} + + + + + { currentOffer && } + + + ); +} 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 fd43e2b8..8b658dd8 100644 --- a/src/components/catalog/views/page/layout/single-bundle/CatalogLayoutSingleBundleView.tsx +++ b/src/components/catalog/views/page/layout/single-bundle/CatalogLayoutSingleBundleView.tsx @@ -1,26 +1,25 @@ 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 { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView'; +import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView'; +import { CatalogBundleGridWidgetView } from '../../widgets/CatalogBundleGridWidgetView'; +import { CatalogPurchaseWidgetView } from '../../widgets/CatalogPurchaseWidgetView'; +import { CatalogSimplePriceWidgetView } from '../../widgets/CatalogSimplePriceWidgetView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; export const CatalogLayoutSingleBundleView: FC = props => { const { page = null, roomPreviewer = null } = props; - const { currentOffer = null } = useCatalogContext(); return ( - - - {/* { currentOffer && currentOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => )} */} - + + + + - - - { 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 05cd2216..8d4618b8 100644 --- a/src/components/catalog/views/page/layout/spaces-new/CatalogLayoutSpacesView.tsx +++ b/src/components/catalog/views/page/layout/spaces-new/CatalogLayoutSpacesView.tsx @@ -17,7 +17,6 @@ export const CatalogLayoutSpacesView: FC = props => const [ groups, setGroups ] = useState([]); const [ activeGroupIndex, setActiveGroupIndex ] = useState(-1); const { currentOffer = null, catalogState } = useCatalogContext(); - const { activeOffer = null } = catalogState; const groupNames = [ 'floors', 'walls', 'views' ]; diff --git a/src/components/catalog/views/page/layout/vip-buy/CatalogLayoutVipBuyView.tsx b/src/components/catalog/views/page/layout/vip-buy/CatalogLayoutVipBuyView.tsx index 8d9f0f5d..c86379da 100644 --- a/src/components/catalog/views/page/layout/vip-buy/CatalogLayoutVipBuyView.tsx +++ b/src/components/catalog/views/page/layout/vip-buy/CatalogLayoutVipBuyView.tsx @@ -15,7 +15,6 @@ import { GetCurrencyAmount } from '../../../../../../views/purse/common/Currency import { GLOBAL_PURSE } from '../../../../../../views/purse/PurseView'; import { CurrencyIcon } from '../../../../../../views/shared/currency-icon/CurrencyIcon'; import { CatalogPurchaseState } from '../../../../common/CatalogPurchaseState'; -import { GetCatalogPageImage } from '../../../../common/CatalogUtilities'; import { useCatalogContext } from '../../../../context/CatalogContext'; import { CatalogLayoutProps } from '../CatalogLayout.types'; @@ -23,8 +22,8 @@ export const CatalogLayoutVipBuyView: FC = props => { const [ pendingOffer, setPendingOffer ] = useState(null); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); - const { catalogState = null } = useCatalogContext(); - const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState; + const { currentPage = null, catalogState = null } = useCatalogContext(); + const { clubOffers = null, subscriptionInfo = null } = catalogState; const onCatalogEvent = useCallback((event: CatalogEvent) => { @@ -102,8 +101,8 @@ export const CatalogLayoutVipBuyView: FC = props => if(!pendingOffer) return; setPurchaseState(CatalogPurchaseState.PURCHASE); - SendMessageHook(new PurchaseFromCatalogComposer(pageParser.pageId, pendingOffer.offerId, null, 1)); - }, [ pendingOffer, pageParser ]); + SendMessageHook(new PurchaseFromCatalogComposer(currentPage.pageId, pendingOffer.offerId, null, 1)); + }, [ pendingOffer, currentPage ]); const setOffer = useCallback((offer: ClubOfferData) => { @@ -176,7 +175,7 @@ export const CatalogLayoutVipBuyView: FC = props => - { GetCatalogPageImage(pageParser, 1) && } + { currentPage.localization.getImage(1) && } { pendingOffer && diff --git a/src/components/catalog/views/page/offers/CatalogGridOfferView.tsx b/src/components/catalog/views/page/offers/CatalogGridOfferView.tsx new file mode 100644 index 00000000..248e5024 --- /dev/null +++ b/src/components/catalog/views/page/offers/CatalogGridOfferView.tsx @@ -0,0 +1,26 @@ +import { FC, useMemo } from 'react'; +import { LayoutGridItem, LayoutGridItemProps } from '../../../../../common/layout/LayoutGridItem'; +import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; +import { Offer } from '../../../common/Offer'; + +interface CatalogGridOfferViewProps extends LayoutGridItemProps +{ + offer: IPurchasableOffer; +} + +export const CatalogGridOfferView: FC = props => +{ + const { offer = null, ...rest } = props; + + const iconUrl = useMemo(() => + { + if(offer.pricingModel === Offer.PRICING_MODEL_BUNDLE) + { + return null; + } + + return offer.product.getIconUrl(offer); + }, [ offer ]); + + return ; +} diff --git a/src/components/catalog/views/page/offers/CatalogPageOfferView.tsx b/src/components/catalog/views/page/offers/CatalogPageOfferView.tsx index 15a3ad26..64dbfeb5 100644 --- a/src/components/catalog/views/page/offers/CatalogPageOfferView.tsx +++ b/src/components/catalog/views/page/offers/CatalogPageOfferView.tsx @@ -1,5 +1,5 @@ import { GetProductOfferComposer, MouseEventType } from '@nitrots/nitro-renderer'; -import { Dispatch, FC, MouseEvent, SetStateAction, useCallback, useState } from 'react'; +import { FC, MouseEvent, useCallback, useState } from 'react'; import { SendMessageHook } from '../../../../../hooks'; import { FurnitureOffer } from '../../../common/FurnitureOffer'; import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; @@ -10,12 +10,11 @@ export interface CatalogPageOfferViewProps { isActive: boolean; offer: IPurchasableOffer; - setActiveOffer: Dispatch>; } export const CatalogPageOfferView: FC = props => { - const { isActive = false, offer = null, setActiveOffer = null } = props; + const { isActive = false, offer = null } = props; const [ isMouseDown, setMouseDown ] = useState(false); const { setCurrentOffer = null } = useCatalogContext(); @@ -26,16 +25,12 @@ export const CatalogPageOfferView: FC = props => case MouseEventType.MOUSE_CLICK: if(isActive) return; - setActiveOffer(offer); + setCurrentOffer(offer); if(offer instanceof FurnitureOffer) { SendMessageHook(new GetProductOfferComposer(offer.offerId)); } - else - { - setCurrentOffer(offer); - } return; case MouseEventType.MOUSE_DOWN: setMouseDown(true); @@ -47,7 +42,7 @@ export const CatalogPageOfferView: FC = props => if(!isMouseDown || !isActive) return; return; } - }, [ isActive, offer, isMouseDown, setActiveOffer, setCurrentOffer ]); + }, [ isActive, offer, isMouseDown, setCurrentOffer ]); const product = offer.product; diff --git a/src/components/catalog/views/page/offers/CatalogPageOffersView.tsx b/src/components/catalog/views/page/offers/CatalogPageOffersView.tsx index f1eabfd2..52f8514e 100644 --- a/src/components/catalog/views/page/offers/CatalogPageOffersView.tsx +++ b/src/components/catalog/views/page/offers/CatalogPageOffersView.tsx @@ -1,6 +1,7 @@ -import { FC, useState } from 'react'; +import { FC } from 'react'; import { Grid, GridProps } from '../../../../../common/Grid'; import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; +import { useCatalogContext } from '../../../context/CatalogContext'; import { CatalogPageOfferView } from './CatalogPageOfferView'; export interface CatalogPageOffersViewProps extends GridProps @@ -11,11 +12,11 @@ export interface CatalogPageOffersViewProps extends GridProps export const CatalogPageOffersView: FC = props => { const { offers = [], children = null, ...rest } = props; - const [ activeOffer, setActiveOffer ] = useState(null); + const { currentOffer = null } = useCatalogContext(); return ( - { offers && (offers.length > 0) && offers.map((offer, index) => ) } + { offers && (offers.length > 0) && offers.map((offer, index) => ) } { children } ); diff --git a/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx new file mode 100644 index 00000000..0e38349d --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx @@ -0,0 +1,27 @@ +import { FC, useCallback, useState } from 'react'; +import { BaseProps } from '../../../../../common/Base'; +import { CatalogSelectProductEvent } from '../../../../../events'; +import { useUiEvent } from '../../../../../hooks'; +import { BadgeImageView } from '../../../../../views/shared/badge-image/BadgeImageView'; + +interface CatalogAddOnBadgeWidgetViewProps extends BaseProps +{ + +} + +export const CatalogAddOnBadgeWidgetView: FC = props => +{ + const { ...rest } = props; + const [ badgeCode, setBadgeCode ] = useState(null); + + const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) => + { + if(event.offer.badgeCode) setBadgeCode(event.offer.badgeCode); + }, []); + + useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); + + if(!badgeCode || !badgeCode.length) return null; + + return ; +} diff --git a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx new file mode 100644 index 00000000..a9935c89 --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx @@ -0,0 +1,46 @@ +import { FC, useCallback, useState } from 'react'; +import { Grid, GridProps } from '../../../../../common/Grid'; +import { LayoutGridItem } from '../../../../../common/layout/LayoutGridItem'; +import { CatalogPageReadyEvent, CatalogSelectProductEvent } from '../../../../../events'; +import { dispatchUiEvent, useUiEvent } from '../../../../../hooks'; +import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; +import { useCatalogContext } from '../../../context/CatalogContext'; + +interface CatalogBundleGridWidgetViewProps extends GridProps +{ + +} + +export const CatalogBundleGridWidgetView: FC = props => +{ + const { children = null, ...rest } = props; + const [ offer, setOffer ] = useState(null); + const { currentPage = null } = useCatalogContext(); + + const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) => + { + setOffer(event.offer); + }, []); + + useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); + + const onCatalogPageReadyEvent = useCallback((event: CatalogPageReadyEvent) => + { + if(!currentPage || (currentPage.offers.length !== 1)) return; + + const offer = currentPage.offers[0]; + + dispatchUiEvent(new CatalogSelectProductEvent(offer)); + }, [ currentPage ]); + + useUiEvent(CatalogPageReadyEvent.PAGE_READY, onCatalogPageReadyEvent); + + if(!offer) return null; + + return ( + + { offer.products && (offer.products.length > 0) && offer.products.map((product, index) => ) } + { children } + + ); +} diff --git a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx new file mode 100644 index 00000000..e5f02782 --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx @@ -0,0 +1,24 @@ +import { FC } from 'react'; +import { Grid, GridProps } from '../../../../../common/Grid'; +import { useCatalogContext } from '../../../context/CatalogContext'; +import { CatalogGridOfferView } from '../offers/CatalogGridOfferView'; + +interface CatalogItemGridWidgetViewProps extends GridProps +{ + +} + +export const CatalogItemGridWidgetView: FC = props => +{ + const { children = null, ...rest } = props; + const { currentOffer = null, currentPage = null } = useCatalogContext(); + + if(!currentPage) return null; + + return ( + + { currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => ) } + { children } + + ); +} diff --git a/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx new file mode 100644 index 00000000..775c83c2 --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx @@ -0,0 +1,35 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { FC } from 'react'; +import { Flex } from '../../../../../common/Flex'; +import { Text } from '../../../../../common/Text'; +import { CurrencyIcon } from '../../../../../views/shared/currency-icon/CurrencyIcon'; +import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; + +interface CatalogPriceDisplayWidgetViewProps +{ + offer: IPurchasableOffer; +} + +export const CatalogPriceDisplayWidgetView: FC = props => +{ + const { offer = null } = props; + + if(!offer) return null; + + return ( + + { (offer.priceInCredits > 0) && + + { offer.priceInCredits } + + } + { ( offer.priceInCredits > 0) && (offer.priceInActivityPoints > 0) && + } + { (offer.priceInActivityPoints > 0) && + + { offer.priceInActivityPoints } + + } + + ); +} diff --git a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx new file mode 100644 index 00000000..53456a6d --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx @@ -0,0 +1,22 @@ +import { FC, useCallback } from 'react'; +import { CatalogSelectProductEvent } from '../../../../../events'; +import { useUiEvent } from '../../../../../hooks'; + +interface CatalogPurchaseWidgetViewProps +{ + +} + +export const CatalogPurchaseWidgetView: FC = props => +{ + const {} = props; + + const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) => + { + + }, []); + + useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); + + return null; +} diff --git a/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx new file mode 100644 index 00000000..e6f5a493 --- /dev/null +++ b/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx @@ -0,0 +1,26 @@ +import { FC, useCallback, useState } from 'react'; +import { BaseProps } from '../../../../../common/Base'; +import { CatalogSelectProductEvent } from '../../../../../events'; +import { useUiEvent } from '../../../../../hooks'; +import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; +import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; + +interface CatalogSimplePriceWidgetViewProps extends BaseProps +{ + +} + +export const CatalogSimplePriceWidgetView: FC = props => +{ + const { ...rest } = props; + const [ offer, setOffer ] = useState(null); + + const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) => + { + setOffer(event.offer); + }, []); + + useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); + + return ; +} diff --git a/src/components/catalog/views/search/CatalogSearchView.tsx b/src/components/catalog/views/search/CatalogSearchView.tsx index 53b31012..187ad7f9 100644 --- a/src/components/catalog/views/search/CatalogSearchView.tsx +++ b/src/components/catalog/views/search/CatalogSearchView.tsx @@ -17,18 +17,7 @@ import { useCatalogContext } from '../../context/CatalogContext'; export const CatalogSearchView: FC<{}> = props => { const [ searchValue, setSearchValue ] = useState(''); - const { currentType = null, setActiveNodes = null, currentOffers = null, setCurrentPage = null, catalogState = null, dispatchCatalogState = null } = useCatalogContext(); - const { searchResult = null } = catalogState; - - useEffect(() => - { - setSearchValue(prevValue => - { - if(!searchResult && prevValue && prevValue.length) return ''; - - return prevValue; - }); - }, [ searchResult ]); + const { currentType = null, setActiveNodes = null, currentOffers = null, setCurrentPage = null } = useCatalogContext(); const processSearch = useCallback((search: string) => { diff --git a/src/events/catalog/CatalogPageReadyEvent.ts b/src/events/catalog/CatalogPageReadyEvent.ts new file mode 100644 index 00000000..1a94b2c7 --- /dev/null +++ b/src/events/catalog/CatalogPageReadyEvent.ts @@ -0,0 +1,11 @@ +import { NitroEvent } from '@nitrots/nitro-renderer'; + +export class CatalogPageReadyEvent extends NitroEvent +{ + public static PAGE_READY: string = 'CPRE_PAGE_READY'; + + constructor() + { + super(CatalogPageReadyEvent.PAGE_READY); + } +} diff --git a/src/events/catalog/CatalogSetExtraPurchaseParameterEvent.ts b/src/events/catalog/CatalogSetExtraPurchaseParameterEvent.ts new file mode 100644 index 00000000..667c05ad --- /dev/null +++ b/src/events/catalog/CatalogSetExtraPurchaseParameterEvent.ts @@ -0,0 +1,20 @@ +import { NitroEvent } from '@nitrots/nitro-renderer'; + +export class CatalogSetExtraPurchaseParameterEvent extends NitroEvent +{ + public static SET_EXTRA_PARAM: string = 'CSEPPE_SET_EXTRA_PARAM'; + + private _parameter: string; + + constructor(parameter: string) + { + super(CatalogSetExtraPurchaseParameterEvent.SET_EXTRA_PARAM); + + this._parameter = parameter; + } + + public get parameter(): string + { + return this._parameter; + } +} diff --git a/src/events/catalog/index.ts b/src/events/catalog/index.ts index 612a4717..abf62a4b 100644 --- a/src/events/catalog/index.ts +++ b/src/events/catalog/index.ts @@ -1,8 +1,10 @@ export * from './CatalogEvent'; export * from './CatalogNameResultEvent'; export * from './CatalogPageOpenedEvent'; +export * from './CatalogPageReadyEvent'; export * from './CatalogPurchasedEvent'; export * from './CatalogPurchaseFailureEvent'; export * from './CatalogPurchaseSoldOutEvent'; export * from './CatalogSelectProductEvent'; +export * from './CatalogSetExtraPurchaseParameterEvent'; export * from './SetRoomPreviewerStuffDataEvent'; diff --git a/src/hooks/UseMountEffect.tsx b/src/hooks/UseMountEffect.tsx index cb6f7bf6..7b33ea72 100644 --- a/src/hooks/UseMountEffect.tsx +++ b/src/hooks/UseMountEffect.tsx @@ -1,3 +1,6 @@ -import { useEffect } from 'react'; +import { EffectCallback, useEffect } from 'react'; -export const UseMountEffect = (fun: Function) => useEffect(() => fun(), []); + +const useEffectOnce = (effect: EffectCallback) => useEffect(effect, []); + +export const UseMountEffect = (fn: Function) => useEffectOnce(() => fn());