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 { BatchUpdates, UseMountEffect } from '../../hooks'; import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { SendMessageHook } from '../../hooks/messages/message-event'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsView, NitroCardView } from '../../layout'; import { CatalogMessageHandler } from './CatalogMessageHandler'; 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 { 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; const REQUESTED_PAGE = new RequestedPage(); export const CatalogView: FC<{}> = props => { const [ isVisible, setIsVisible ] = useState(false); 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 [ rootNode, setRootNode ] = 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 [ frontPageItems, setFrontPageItems ] = useState([]); 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([]); setFrontPageItems([]); setIsInitialized(false); setIsVisible(true); }); }, []); const getNodeById = useCallback((id: number, node: ICatalogNode = null) => { if(!node) node = rootNode; if(!node) return null; if((node.pageId === id) && (node !== rootNode)) return node; for(const child of node.children) { const n = (getNodeById(id, child) as ICatalogNode); if(n) return n; } return null; }, [ rootNode ]); const getNodesByOfferId = useCallback((offerId: number, flag: boolean = false) => { if(!currentOffers || !currentOffers.size) return null; if(flag) { 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) => { if(pageId < 0) return; BatchUpdates(() => { setIsBusy(true); setPageId(pageId); }); 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 activateNode = 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); }, [ setActiveNodes, loadCatalogPage ]); const onCatalogEvent = useCallback((event: CatalogEvent) => { let save = false; switch(event.type) { case CatalogEvent.SHOW_CATALOG: setIsVisible(true); return; case CatalogEvent.HIDE_CATALOG: save = true; setIsVisible(false); return; case CatalogEvent.TOGGLE_CATALOG: save = true; setIsVisible(value => !value); return; case CatalogEvent.PURCHASE_SUCCESS: PlaySound(CREDITS); return; } }, []); useUiEvent(CatalogEvent.SHOW_CATALOG, onCatalogEvent); useUiEvent(CatalogEvent.HIDE_CATALOG, onCatalogEvent); useUiEvent(CatalogEvent.TOGGLE_CATALOG, onCatalogEvent); useUiEvent(CatalogEvent.CATALOG_RESET, onCatalogEvent); useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent); const linkReceived = useCallback((url: string) => { const parts = url.split('/'); if(parts.length < 2) return; switch(parts[1]) { case 'open': if(parts.length > 2) { if(parts.length === 4) { switch(parts[2]) { case 'offerId': return; } } } else { setIsVisible(true); } return; } }, []); useEffect(() => { const linkTracker: ILinkEventTracker = { linkReceived, eventUrlPrefix: 'catalog/' }; AddEventLinkTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker); }, [ linkReceived ]); useEffect(() => { setRoomPreviewer(new RoomPreviewer(GetRoomEngine(), ++RoomPreviewer.PREVIEW_COUNTER)); return () => { setRoomPreviewer(prevValue => { prevValue.dispose(); return null; }); } }, []); useEffect(() => { if(!isVisible) return; if(!isInitialized) SendMessageHook(new GetCatalogIndexComposer(currentType)); }, [ isVisible, isInitialized, currentType ]); useEffect(() => { if(!rootNode) return; setIsInitialized(true); switch(REQUESTED_PAGE.requestType) { case RequestedPage.REQUEST_TYPE_NONE: BatchUpdates(() => { setIsInitialized(true); if(rootNode.isBranch) { for(const child of rootNode.children) { if(child && child.isVisible) { setCurrentTab(child); return; } } } }); return; case RequestedPage.REQUEST_TYPE_ID: REQUESTED_PAGE.resetRequest(); return; case RequestedPage.REQUEST_TYPE_NAME: REQUESTED_PAGE.resetRequest(); return; } }, [ rootNode ]); useEffect(() => { if(!currentTab) return; if(currentTab.children.length) { for(const child of currentTab.children) { if(!child.isVisible) continue; activateNode(child); return; } } else { loadCatalogPage(currentTab.pageId, -1); } }, [ currentTab, activateNode, loadCatalogPage ]); useEffect(() => { if(!currentPage) return; setCurrentOffer(null); }, [ currentPage ]); UseMountEffect(() => { SendMessageHook(new GetMarketplaceConfigurationMessageComposer()); SendMessageHook(new GetGiftWrappingConfigurationComposer()); SendMessageHook(new GetClubGiftInfo()); }); return ( { isVisible && { setIsVisible(false); } } /> } ); }