mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-30 08:50:51 +01:00
Catalog navi fully working
This commit is contained in:
parent
85c562d14e
commit
a91bff333a
@ -2,7 +2,7 @@ import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent
|
|||||||
import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent';
|
import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent';
|
||||||
import { FC, useCallback } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
import { GetFurnitureData, GetProductDataForLocalization, LocalizeText } from '../../api';
|
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 { CatalogGiftReceiverNotFoundEvent } from '../../events/catalog/CatalogGiftReceiverNotFoundEvent';
|
||||||
import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent';
|
import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent';
|
||||||
import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent';
|
import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent';
|
||||||
@ -20,13 +20,14 @@ import { IPurchasableOffer } from './common/IPurchasableOffer';
|
|||||||
import { Offer } from './common/Offer';
|
import { Offer } from './common/Offer';
|
||||||
import { PageLocalization } from './common/PageLocalization';
|
import { PageLocalization } from './common/PageLocalization';
|
||||||
import { Product } from './common/Product';
|
import { Product } from './common/Product';
|
||||||
|
import { ProductTypeEnum } from './common/ProductTypeEnum';
|
||||||
import { SubscriptionInfo } from './common/SubscriptionInfo';
|
import { SubscriptionInfo } from './common/SubscriptionInfo';
|
||||||
import { useCatalogContext } from './context/CatalogContext';
|
import { useCatalogContext } from './context/CatalogContext';
|
||||||
import { CatalogActions } from './reducers/CatalogReducer';
|
import { CatalogActions } from './reducers/CatalogReducer';
|
||||||
|
|
||||||
export const CatalogMessageHandler: FC<{}> = props =>
|
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) =>
|
const onCatalogPagesListEvent = useCallback((event: CatalogPagesListEvent) =>
|
||||||
{
|
{
|
||||||
@ -52,10 +53,10 @@ export const CatalogMessageHandler: FC<{}> = props =>
|
|||||||
|
|
||||||
BatchUpdates(() =>
|
BatchUpdates(() =>
|
||||||
{
|
{
|
||||||
setCurrentNode(getCatalogNode(parser.root, 0, null));
|
setRootNode(getCatalogNode(parser.root, 0, null));
|
||||||
setCurrentOffers(offers);
|
setCurrentOffers(offers);
|
||||||
});
|
});
|
||||||
}, [ setCurrentNode, setCurrentOffers ]);
|
}, [ setRootNode, setCurrentOffers ]);
|
||||||
|
|
||||||
const onCatalogPageMessageEvent = useCallback((event: CatalogPageMessageEvent) =>
|
const onCatalogPageMessageEvent = useCallback((event: CatalogPageMessageEvent) =>
|
||||||
{
|
{
|
||||||
@ -155,12 +156,14 @@ export const CatalogMessageHandler: FC<{}> = props =>
|
|||||||
|
|
||||||
dispatchUiEvent(new CatalogSelectProductEvent(offer));
|
dispatchUiEvent(new CatalogSelectProductEvent(offer));
|
||||||
|
|
||||||
BatchUpdates(() =>
|
if(offer.product && (offer.product.productType === ProductTypeEnum.WALL))
|
||||||
{
|
{
|
||||||
setCurrentOffer(offer);
|
dispatchUiEvent(new CatalogSetExtraPurchaseParameterEvent(offer.product.extraParam));
|
||||||
setPurchasableOffer(offer);
|
}
|
||||||
});
|
|
||||||
}, [ currentType, currentPage, setCurrentOffer, setPurchasableOffer ]);
|
// (this._isObjectMoverRequested) && (this._purchasableOffer)
|
||||||
|
// setPurchasableOffer(offer);
|
||||||
|
}, [ currentType, currentPage ]);
|
||||||
|
|
||||||
const onSellablePetPalettesMessageEvent = useCallback((event: SellablePetPalettesMessageEvent) =>
|
const onSellablePetPalettesMessageEvent = useCallback((event: SellablePetPalettesMessageEvent) =>
|
||||||
{
|
{
|
||||||
@ -229,11 +232,8 @@ export const CatalogMessageHandler: FC<{}> = props =>
|
|||||||
|
|
||||||
const onCatalogPublishedMessageEvent = useCallback((event: CatalogPublishedMessageEvent) =>
|
const onCatalogPublishedMessageEvent = useCallback((event: CatalogPublishedMessageEvent) =>
|
||||||
{
|
{
|
||||||
dispatchCatalogState({
|
resetState();
|
||||||
type: CatalogActions.RESET_STATE,
|
}, [ resetState ]);
|
||||||
payload: {}
|
|
||||||
});
|
|
||||||
}, [ dispatchCatalogState ]);
|
|
||||||
|
|
||||||
const onGiftWrappingConfigurationEvent = useCallback((event: GiftWrappingConfigurationEvent) =>
|
const onGiftWrappingConfigurationEvent = useCallback((event: GiftWrappingConfigurationEvent) =>
|
||||||
{
|
{
|
||||||
|
@ -36,7 +36,7 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
const [ pageId, setPageId ] = useState(-1);
|
const [ pageId, setPageId ] = useState(-1);
|
||||||
const [ previousPageId, setPreviousPageId ] = useState(-1);
|
const [ previousPageId, setPreviousPageId ] = useState(-1);
|
||||||
const [ currentType, setCurrentType ] = useState(CatalogType.NORMAL);
|
const [ currentType, setCurrentType ] = useState(CatalogType.NORMAL);
|
||||||
const [ currentNode, setCurrentNode ] = useState<ICatalogNode>(null);
|
const [ rootNode, setRootNode ] = useState<ICatalogNode>(null);
|
||||||
const [ currentOffers, setCurrentOffers ] = useState<Map<number, ICatalogNode[]>>(null);
|
const [ currentOffers, setCurrentOffers ] = useState<Map<number, ICatalogNode[]>>(null);
|
||||||
const [ currentPage, setCurrentPage ] = useState<ICatalogPage>(null);
|
const [ currentPage, setCurrentPage ] = useState<ICatalogPage>(null);
|
||||||
const [ currentOffer, setCurrentOffer ] = useState<IPurchasableOffer>(null);
|
const [ currentOffer, setCurrentOffer ] = useState<IPurchasableOffer>(null);
|
||||||
@ -51,6 +51,46 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
|
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
|
||||||
const [ catalogState, dispatchCatalogState ] = useReducer(CatalogReducer, initialCatalog);
|
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) =>
|
const loadCatalogPage = useCallback((pageId: number, offerId: number, forceRefresh: boolean = false) =>
|
||||||
{
|
{
|
||||||
if(pageId < 0) return;
|
if(pageId < 0) return;
|
||||||
@ -95,6 +135,41 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
});
|
});
|
||||||
}, [ currentPage, forceRefresh, selectOffer ]);
|
}, [ 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) =>
|
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||||
{
|
{
|
||||||
let save = false;
|
let save = false;
|
||||||
@ -186,7 +261,37 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
|
|
||||||
useEffect(() =>
|
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)
|
switch(requestedPage.requestType)
|
||||||
{
|
{
|
||||||
@ -200,60 +305,28 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
requestedPage.resetRequest();
|
requestedPage.resetRequest();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, [ currentNode, requestedPage ]);
|
}, [ rootNode, requestedPage ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!isVisible) return;
|
if(!currentTab) return;
|
||||||
|
|
||||||
if(!isInitialized) SendMessageHook(new GetCatalogIndexComposer(currentType));
|
if(currentTab.children.length)
|
||||||
}, [ isVisible, isInitialized, currentType ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!currentNode || !currentTab) return;
|
|
||||||
|
|
||||||
const activeNodes: ICatalogNode[] = [];
|
|
||||||
|
|
||||||
if(currentTab.isVisible && !currentTab.children.length && (currentTab !== currentNode))
|
|
||||||
{
|
{
|
||||||
loadCatalogPage(currentTab.pageId, -1);
|
for(const child of currentTab.children)
|
||||||
|
{
|
||||||
|
if(!child.isVisible) continue;
|
||||||
|
|
||||||
|
selectCatalogNode(child);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(currentTab.children.length)
|
loadCatalogPage(currentTab.pageId, -1, true);
|
||||||
{
|
|
||||||
for(const child of currentTab.children)
|
|
||||||
{
|
|
||||||
if(child.isVisible)
|
|
||||||
{
|
|
||||||
activeNodes.push(child);
|
|
||||||
|
|
||||||
loadCatalogPage(child.pageId, -1);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}, [ currentTab, selectCatalogNode, loadCatalogPage ]);
|
||||||
// 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(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
@ -262,29 +335,6 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
setCurrentOffer(null);
|
setCurrentOffer(null);
|
||||||
}, [ currentPage ]);
|
}, [ 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(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!isVisible && !lastActiveNodes && activeNodes && activeNodes.length)
|
if(!isVisible && !lastActiveNodes && activeNodes && activeNodes.length)
|
||||||
@ -310,13 +360,13 @@ export const CatalogView: FC<{}> = props =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CatalogContextProvider value={ { isVisible, isBusy, setIsBusy, pageId, currentType, setCurrentType, currentNode, setCurrentNode, currentOffers, setCurrentOffers, currentPage, setCurrentPage, currentOffer, setCurrentOffer, purchasableOffer, setPurchasableOffer, activeNodes, setActiveNodes, frontPageItems, setFrontPageItems, loadCatalogPage, showCatalogPage, catalogState, dispatchCatalogState } }>
|
<CatalogContextProvider value={ { isVisible, isBusy, setIsBusy, pageId, currentType, setCurrentType, rootNode, setRootNode, currentOffers, setCurrentOffers, currentPage, setCurrentPage, currentOffer, setCurrentOffer, purchasableOffer, setPurchasableOffer, activeNodes, setActiveNodes, frontPageItems, setFrontPageItems, resetState, loadCatalogPage, showCatalogPage, selectCatalogNode, catalogState, dispatchCatalogState } }>
|
||||||
<CatalogMessageHandler />
|
<CatalogMessageHandler />
|
||||||
{ isVisible &&
|
{ isVisible &&
|
||||||
<NitroCardView uniqueKey="catalog" className="nitro-catalog">
|
<NitroCardView uniqueKey="catalog" className="nitro-catalog">
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => { setIsVisible(false); } } />
|
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => { setIsVisible(false); } } />
|
||||||
<NitroCardTabsView>
|
<NitroCardTabsView>
|
||||||
<CatalogTabsViews node={ currentNode } currentTab={ currentTab } setCurrentTab={ setCurrentTab } />
|
<CatalogTabsViews node={ rootNode } currentTab={ currentTab } setCurrentTab={ setCurrentTab } />
|
||||||
</NitroCardTabsView>
|
</NitroCardTabsView>
|
||||||
<NitroCardContentView>
|
<NitroCardContentView>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
@ -12,6 +12,8 @@ export class CatalogNode implements ICatalogNode
|
|||||||
private _offerIds: number[];
|
private _offerIds: number[];
|
||||||
private _parent: ICatalogNode;
|
private _parent: ICatalogNode;
|
||||||
private _isVisible: boolean;
|
private _isVisible: boolean;
|
||||||
|
private _isActive: boolean;
|
||||||
|
private _isOpen: boolean;
|
||||||
|
|
||||||
constructor(node: NodeData, depth: number, parent: ICatalogNode)
|
constructor(node: NodeData, depth: number, parent: ICatalogNode)
|
||||||
{
|
{
|
||||||
@ -24,11 +26,35 @@ export class CatalogNode implements ICatalogNode
|
|||||||
this._children = [];
|
this._children = [];
|
||||||
this._offerIds = node.offerIds;
|
this._offerIds = node.offerIds;
|
||||||
this._isVisible = node.visible;
|
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
|
public get depth(): number
|
||||||
@ -86,10 +112,13 @@ export class CatalogNode implements ICatalogNode
|
|||||||
return this._isVisible;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,16 @@ export interface ICatalogSearchResult
|
|||||||
furniture: IFurnitureData[];
|
furniture: IFurnitureData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const GetPixelEffectIcon = (id: number) =>
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GetSubscriptionProductIcon = (id: number) =>
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
export function GetOfferName(offer: CatalogPageMessageOfferData): string
|
export function GetOfferName(offer: CatalogPageMessageOfferData): string
|
||||||
{
|
{
|
||||||
const productData = GetProductDataForLocalization(offer.localizationId);
|
const productData = GetProductDataForLocalization(offer.localizationId);
|
||||||
|
@ -58,6 +58,11 @@ export class FurnitureOffer implements IPurchasableOffer
|
|||||||
return this._product;
|
return this._product;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get products(): IProduct[]
|
||||||
|
{
|
||||||
|
return [ this._product ];
|
||||||
|
}
|
||||||
|
|
||||||
public get localizationId(): string
|
public get localizationId(): string
|
||||||
{
|
{
|
||||||
return 'roomItem.name.' + this._furniData.id;
|
return 'roomItem.name.' + this._furniData.id;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
export interface ICatalogNode
|
export interface ICatalogNode
|
||||||
{
|
{
|
||||||
|
activate(): void;
|
||||||
|
deactivate(): void;
|
||||||
|
open(): void;
|
||||||
|
close(): void;
|
||||||
addChild(node: ICatalogNode): void;
|
addChild(node: ICatalogNode): void;
|
||||||
readonly isOpen: boolean;
|
|
||||||
readonly depth: number;
|
readonly depth: number;
|
||||||
readonly isBranch: boolean;
|
readonly isBranch: boolean;
|
||||||
readonly isLeaf: boolean;
|
readonly isLeaf: boolean;
|
||||||
@ -13,4 +16,6 @@ export interface ICatalogNode
|
|||||||
readonly offerIds: number[];
|
readonly offerIds: number[];
|
||||||
readonly parent: ICatalogNode;
|
readonly parent: ICatalogNode;
|
||||||
readonly isVisible: boolean;
|
readonly isVisible: boolean;
|
||||||
|
readonly isActive: boolean;
|
||||||
|
readonly isOpen: boolean;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer';
|
import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer';
|
||||||
|
import { IPurchasableOffer } from './IPurchasableOffer';
|
||||||
|
|
||||||
export interface IProduct
|
export interface IProduct
|
||||||
{
|
{
|
||||||
|
getIconUrl(offer?: IPurchasableOffer): string;
|
||||||
readonly productType: string;
|
readonly productType: string;
|
||||||
readonly productClassId: number;
|
readonly productClassId: number;
|
||||||
readonly extraParam: string;
|
readonly extraParam: string;
|
||||||
|
@ -19,4 +19,5 @@ export interface IPurchasableOffer
|
|||||||
readonly badgeCode: string;
|
readonly badgeCode: string;
|
||||||
readonly localizationName: string;
|
readonly localizationName: string;
|
||||||
readonly localizationDescription: string;
|
readonly localizationDescription: string;
|
||||||
|
readonly products: IProduct[];
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,11 @@ export class Offer implements IPurchasableOffer
|
|||||||
return LocalizeText(this._localizationId);
|
return LocalizeText(this._localizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get products(): IProduct[]
|
||||||
|
{
|
||||||
|
return this._products;
|
||||||
|
}
|
||||||
|
|
||||||
private setPricingModelForProducts(): void
|
private setPricingModelForProducts(): void
|
||||||
{
|
{
|
||||||
const products = Product.stripAddonProducts(this._products);
|
const products = Product.stripAddonProducts(this._products);
|
||||||
|
@ -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 { IProduct } from './IProduct';
|
||||||
|
import { IPurchasableOffer } from './IPurchasableOffer';
|
||||||
import { ProductTypeEnum } from './ProductTypeEnum';
|
import { ProductTypeEnum } from './ProductTypeEnum';
|
||||||
|
|
||||||
export class Product implements IProduct
|
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)));
|
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<string>('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
|
public get productType(): string
|
||||||
{
|
{
|
||||||
return this._productType;
|
return this._productType;
|
||||||
|
@ -14,8 +14,8 @@ export interface ICatalogContext
|
|||||||
pageId: number;
|
pageId: number;
|
||||||
currentType: string;
|
currentType: string;
|
||||||
setCurrentType: Dispatch<SetStateAction<string>>;
|
setCurrentType: Dispatch<SetStateAction<string>>;
|
||||||
currentNode: ICatalogNode;
|
rootNode: ICatalogNode;
|
||||||
setCurrentNode: Dispatch<SetStateAction<ICatalogNode>>;
|
setRootNode: Dispatch<SetStateAction<ICatalogNode>>;
|
||||||
currentOffers: Map<number, ICatalogNode[]>;
|
currentOffers: Map<number, ICatalogNode[]>;
|
||||||
setCurrentOffers: Dispatch<SetStateAction<Map<number, ICatalogNode[]>>>;
|
setCurrentOffers: Dispatch<SetStateAction<Map<number, ICatalogNode[]>>>;
|
||||||
currentPage: ICatalogPage;
|
currentPage: ICatalogPage;
|
||||||
@ -28,8 +28,10 @@ export interface ICatalogContext
|
|||||||
setActiveNodes: Dispatch<SetStateAction<ICatalogNode[]>>;
|
setActiveNodes: Dispatch<SetStateAction<ICatalogNode[]>>;
|
||||||
frontPageItems: FrontPageItem[];
|
frontPageItems: FrontPageItem[];
|
||||||
setFrontPageItems: Dispatch<SetStateAction<FrontPageItem[]>>;
|
setFrontPageItems: Dispatch<SetStateAction<FrontPageItem[]>>;
|
||||||
|
resetState: () => void;
|
||||||
loadCatalogPage: (pageId: number, offerId: number, forceRefresh?: boolean) => void;
|
loadCatalogPage: (pageId: number, offerId: number, forceRefresh?: boolean) => void;
|
||||||
showCatalogPage: (pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) => void;
|
showCatalogPage: (pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) => void;
|
||||||
|
selectCatalogNode: (targetNode: ICatalogNode) => void;
|
||||||
|
|
||||||
catalogState: ICatalogState;
|
catalogState: ICatalogState;
|
||||||
dispatchCatalogState: Dispatch<ICatalogAction>;
|
dispatchCatalogState: Dispatch<ICatalogAction>;
|
||||||
@ -42,8 +44,8 @@ const CatalogContext = createContext<ICatalogContext>({
|
|||||||
pageId: null,
|
pageId: null,
|
||||||
currentType: null,
|
currentType: null,
|
||||||
setCurrentType: null,
|
setCurrentType: null,
|
||||||
currentNode: null,
|
rootNode: null,
|
||||||
setCurrentNode: null,
|
setRootNode: null,
|
||||||
currentOffers: null,
|
currentOffers: null,
|
||||||
setCurrentOffers: null,
|
setCurrentOffers: null,
|
||||||
currentPage: null,
|
currentPage: null,
|
||||||
@ -56,8 +58,10 @@ const CatalogContext = createContext<ICatalogContext>({
|
|||||||
setActiveNodes: null,
|
setActiveNodes: null,
|
||||||
frontPageItems: null,
|
frontPageItems: null,
|
||||||
setFrontPageItems: null,
|
setFrontPageItems: null,
|
||||||
|
resetState: null,
|
||||||
loadCatalogPage: null,
|
loadCatalogPage: null,
|
||||||
showCatalogPage: null,
|
showCatalogPage: null,
|
||||||
|
selectCatalogNode: null,
|
||||||
catalogState: null,
|
catalogState: null,
|
||||||
dispatchCatalogState: null
|
dispatchCatalogState: null
|
||||||
});
|
});
|
||||||
|
@ -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 { HabboGroupEntryData } from '@nitrots/nitro-renderer/src/nitro/communication/messages/parser/user/HabboGroupEntryData';
|
||||||
import { Reducer } from 'react';
|
import { Reducer } from 'react';
|
||||||
import { CatalogPetPalette } from '../common/CatalogPetPalette';
|
import { CatalogPetPalette } from '../common/CatalogPetPalette';
|
||||||
import { ICatalogSearchResult } from '../common/CatalogUtilities';
|
|
||||||
import { GiftWrappingConfiguration } from '../common/GiftWrappingConfiguration';
|
import { GiftWrappingConfiguration } from '../common/GiftWrappingConfiguration';
|
||||||
import { SubscriptionInfo } from '../common/SubscriptionInfo';
|
import { SubscriptionInfo } from '../common/SubscriptionInfo';
|
||||||
|
|
||||||
export interface ICatalogState
|
export interface ICatalogState
|
||||||
{
|
{
|
||||||
currentTab: INodeData;
|
|
||||||
pageParser: CatalogPageMessageParser;
|
|
||||||
activeOffer: CatalogPageMessageOfferData;
|
|
||||||
searchResult: ICatalogSearchResult;
|
|
||||||
groups: HabboGroupEntryData[];
|
groups: HabboGroupEntryData[];
|
||||||
petPalettes: CatalogPetPalette[];
|
petPalettes: CatalogPetPalette[];
|
||||||
clubOffers: ClubOfferData[];
|
clubOffers: ClubOfferData[];
|
||||||
@ -25,10 +20,6 @@ export interface ICatalogAction
|
|||||||
{
|
{
|
||||||
type: string;
|
type: string;
|
||||||
payload: {
|
payload: {
|
||||||
currentTab?: INodeData;
|
|
||||||
pageParser?: CatalogPageMessageParser;
|
|
||||||
activeOffer?: CatalogPageMessageOfferData;
|
|
||||||
searchResult?: ICatalogSearchResult;
|
|
||||||
groups?: HabboGroupEntryData[];
|
groups?: HabboGroupEntryData[];
|
||||||
petPalette?: CatalogPetPalette;
|
petPalette?: CatalogPetPalette;
|
||||||
clubOffers?: ClubOfferData[];
|
clubOffers?: ClubOfferData[];
|
||||||
@ -42,9 +33,6 @@ export interface ICatalogAction
|
|||||||
export class CatalogActions
|
export class CatalogActions
|
||||||
{
|
{
|
||||||
public static RESET_STATE: string = 'CA_RESET_STATE';
|
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_CLUB_OFFERS: string = 'CA_SET_CLUB_OFFERS';
|
||||||
public static SET_GROUPS: string = 'CA_SET_GROUPS';
|
public static SET_GROUPS: string = 'CA_SET_GROUPS';
|
||||||
public static SET_PET_PALETTE: string = 'CA_SET_PET_PALETTE';
|
public static SET_PET_PALETTE: string = 'CA_SET_PET_PALETTE';
|
||||||
@ -56,10 +44,6 @@ export class CatalogActions
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const initialCatalog: ICatalogState = {
|
export const initialCatalog: ICatalogState = {
|
||||||
currentTab: null,
|
|
||||||
pageParser: null,
|
|
||||||
activeOffer: null,
|
|
||||||
searchResult: null,
|
|
||||||
groups: [],
|
groups: [],
|
||||||
petPalettes: [],
|
petPalettes: [],
|
||||||
clubOffers: null,
|
clubOffers: null,
|
||||||
@ -73,40 +57,6 @@ export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, ac
|
|||||||
{
|
{
|
||||||
switch(action.type)
|
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: {
|
case CatalogActions.SET_GROUPS: {
|
||||||
const groups = (action.payload.groups || null);
|
const groups = (action.payload.groups || null);
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC } from 'react';
|
||||||
import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem';
|
import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem';
|
||||||
import { Text } from '../../../../common/Text';
|
import { Text } from '../../../../common/Text';
|
||||||
import { BatchUpdates } from '../../../../hooks';
|
|
||||||
import { ICatalogNode } from '../../common/ICatalogNode';
|
import { ICatalogNode } from '../../common/ICatalogNode';
|
||||||
import { useCatalogContext } from '../../context/CatalogContext';
|
import { useCatalogContext } from '../../context/CatalogContext';
|
||||||
import { CatalogIconView } from '../catalog-icon/CatalogIconView';
|
import { CatalogIconView } from '../catalog-icon/CatalogIconView';
|
||||||
@ -11,41 +10,22 @@ import { CatalogNavigationSetView } from './CatalogNavigationSetView';
|
|||||||
export interface CatalogNavigationItemViewProps
|
export interface CatalogNavigationItemViewProps
|
||||||
{
|
{
|
||||||
node: ICatalogNode;
|
node: ICatalogNode;
|
||||||
isActive: boolean;
|
|
||||||
selectNode: (node: ICatalogNode) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = props =>
|
export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { node = null, isActive = false, selectNode = null } = props;
|
const { node = null } = props;
|
||||||
const [ isExpanded, setIsExpanded ] = useState(false);
|
const { selectCatalogNode = null } = useCatalogContext();
|
||||||
const { loadCatalogPage = null } = useCatalogContext();
|
|
||||||
|
|
||||||
const select = () =>
|
|
||||||
{
|
|
||||||
BatchUpdates(() =>
|
|
||||||
{
|
|
||||||
if(!isActive) selectNode(node);
|
|
||||||
else setIsExpanded(prevValue => !prevValue);
|
|
||||||
|
|
||||||
loadCatalogPage(node.pageId, -1, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
setIsExpanded(isActive);
|
|
||||||
}, [ isActive ]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LayoutGridItem column={ false } itemActive={ isActive } onClick={ select }>
|
<LayoutGridItem column={ false } itemActive={ node.isActive } onClick={ event => selectCatalogNode(node) }>
|
||||||
<CatalogIconView icon={ node.iconId } />
|
<CatalogIconView icon={ node.iconId } />
|
||||||
<Text grow truncate>{ node.localization }</Text>
|
<Text grow truncate>{ node.localization }</Text>
|
||||||
{ node.isBranch &&
|
{ node.isBranch &&
|
||||||
<FontAwesomeIcon icon={ isExpanded ? 'caret-up' : 'caret-down' } /> }
|
<FontAwesomeIcon icon={ node.isOpen ? 'caret-up' : 'caret-down' } /> }
|
||||||
</LayoutGridItem>
|
</LayoutGridItem>
|
||||||
{ isExpanded && node.isBranch &&
|
{ node.isOpen && node.isBranch &&
|
||||||
<CatalogNavigationSetView node={ node } /> }
|
<CatalogNavigationSetView node={ node } /> }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC } from 'react';
|
||||||
import { UseMountEffect } from '../../../../hooks';
|
|
||||||
import { ICatalogNode } from '../../common/ICatalogNode';
|
import { ICatalogNode } from '../../common/ICatalogNode';
|
||||||
import { useCatalogContext } from '../../context/CatalogContext';
|
|
||||||
import { CatalogNavigationItemView } from './CatalogNavigationItemView';
|
import { CatalogNavigationItemView } from './CatalogNavigationItemView';
|
||||||
|
|
||||||
export interface CatalogNavigationSetViewProps
|
export interface CatalogNavigationSetViewProps
|
||||||
@ -12,60 +10,14 @@ export interface CatalogNavigationSetViewProps
|
|||||||
export const CatalogNavigationSetView: FC<CatalogNavigationSetViewProps> = props =>
|
export const CatalogNavigationSetView: FC<CatalogNavigationSetViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { node = null } = props;
|
const { node = null } = props;
|
||||||
const [ activeNode, setActiveNode ] = useState<ICatalogNode>(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 (
|
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 <CatalogNavigationItemView key={ index } node={ node } isActive={ (activeNodes.indexOf(node) > -1) } selectNode={ selectNode } />
|
return <CatalogNavigationItemView key={ index } node={ n } />
|
||||||
}) }
|
}) }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { FC, useCallback, useState } from 'react';
|
import { FC } from 'react';
|
||||||
import { Column } from '../../../../common/Column';
|
import { Column } from '../../../../common/Column';
|
||||||
import { Grid } from '../../../../common/Grid';
|
import { Grid } from '../../../../common/Grid';
|
||||||
import { FilterCatalogNode } from '../../common/FilterCatalogNode';
|
|
||||||
import { ICatalogNode } from '../../common/ICatalogNode';
|
import { ICatalogNode } from '../../common/ICatalogNode';
|
||||||
import { useCatalogContext } from '../../context/CatalogContext';
|
|
||||||
import { CatalogSearchView } from '../search/CatalogSearchView';
|
import { CatalogSearchView } from '../search/CatalogSearchView';
|
||||||
import { CatalogNavigationSetView } from './CatalogNavigationSetView';
|
import { CatalogNavigationSetView } from './CatalogNavigationSetView';
|
||||||
|
|
||||||
@ -15,17 +13,6 @@ export interface CatalogNavigationViewProps
|
|||||||
export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { node = null } = props;
|
const { node = null } = props;
|
||||||
const [ filteredNodes, setFilteredNodes ] = useState<ICatalogNode[]>([]);
|
|
||||||
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { IObjectData, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer';
|
import { IObjectData, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect } from 'react';
|
import { FC, useCallback, useEffect } from 'react';
|
||||||
import { GetAvatarRenderManager, GetSessionDataManager } from '../../../../api';
|
import { GetAvatarRenderManager, GetSessionDataManager } from '../../../../api';
|
||||||
import { SetRoomPreviewerStuffDataEvent } from '../../../../events';
|
import { CatalogPageReadyEvent, SetRoomPreviewerStuffDataEvent } from '../../../../events';
|
||||||
import { useUiEvent } from '../../../../hooks';
|
import { dispatchUiEvent, useUiEvent } from '../../../../hooks';
|
||||||
import { FurniCategory } from '../../common/FurniCategory';
|
import { FurniCategory } from '../../common/FurniCategory';
|
||||||
import { ICatalogPage } from '../../common/ICatalogPage';
|
import { ICatalogPage } from '../../common/ICatalogPage';
|
||||||
import { IPurchasableOffer } from '../../common/IPurchasableOffer';
|
import { IPurchasableOffer } from '../../common/IPurchasableOffer';
|
||||||
@ -111,6 +111,11 @@ export const CatalogPageView: FC<CatalogPageViewProps> = props =>
|
|||||||
updatePreviewerForOffer(currentOffer);
|
updatePreviewerForOffer(currentOffer);
|
||||||
}, [ currentOffer, updatePreviewerForOffer ]);
|
}, [ currentOffer, updatePreviewerForOffer ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
dispatchUiEvent(new CatalogPageReadyEvent());
|
||||||
|
}, [ page ]);
|
||||||
|
|
||||||
if(!page) return null;
|
if(!page) return null;
|
||||||
|
|
||||||
return GetCatalogLayout(page, roomPreviewer);
|
return GetCatalogLayout(page, roomPreviewer);
|
||||||
|
@ -13,6 +13,7 @@ import { CatalogLayoutMarketplacePublicItemsView } from './marketplace/CatalogLa
|
|||||||
import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView';
|
import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView';
|
||||||
import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View';
|
import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View';
|
||||||
import { CatalogLayoutPets3View } from './pets3/CatalogLayoutPets3View';
|
import { CatalogLayoutPets3View } from './pets3/CatalogLayoutPets3View';
|
||||||
|
import { CatalogLayoutRoomBundleView } from './room-bundle/CatalogLayoutRoomBundleView';
|
||||||
import { CatalogLayoutSingleBundleView } from './single-bundle/CatalogLayoutSingleBundleView';
|
import { CatalogLayoutSingleBundleView } from './single-bundle/CatalogLayoutSingleBundleView';
|
||||||
import { CatalogLayoutSpacesView } from './spaces-new/CatalogLayoutSpacesView';
|
import { CatalogLayoutSpacesView } from './spaces-new/CatalogLayoutSpacesView';
|
||||||
import { CatalogLayoutTrophiesView } from './trophies/CatalogLayoutTrophiesView';
|
import { CatalogLayoutTrophiesView } from './trophies/CatalogLayoutTrophiesView';
|
||||||
@ -53,6 +54,8 @@ export const GetCatalogLayout = (page: ICatalogPage, roomPreviewer: RoomPreviewe
|
|||||||
return <CatalogLayoutMarketplacePublicItemsView { ...layoutProps } />;
|
return <CatalogLayoutMarketplacePublicItemsView { ...layoutProps } />;
|
||||||
case 'single_bundle':
|
case 'single_bundle':
|
||||||
return <CatalogLayoutSingleBundleView { ...layoutProps } />;
|
return <CatalogLayoutSingleBundleView { ...layoutProps } />;
|
||||||
|
case 'room_bundle':
|
||||||
|
return <CatalogLayoutRoomBundleView { ...layoutProps } />;
|
||||||
case 'spaces_new':
|
case 'spaces_new':
|
||||||
return <CatalogLayoutSpacesView { ...layoutProps } />;
|
return <CatalogLayoutSpacesView { ...layoutProps } />;
|
||||||
case 'trophies':
|
case 'trophies':
|
||||||
|
@ -4,7 +4,7 @@ import { Grid } from '../../../../../../common/Grid';
|
|||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
||||||
import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView';
|
import { CatalogProductPreviewView } from '../../offers/CatalogPageOfferPreviewView';
|
||||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
import { CatalogItemGridWidgetView } from '../../widgets/CatalogItemGridWidgetView';
|
||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
||||||
@ -15,7 +15,8 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
|||||||
return (
|
return (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Column size={ 7 } overflow="hidden">
|
<Column size={ 7 } overflow="hidden">
|
||||||
<CatalogPageOffersView offers={ page.offers } />
|
<CatalogItemGridWidgetView />
|
||||||
|
{/* <CatalogPageOffersView offers={ page.offers } /> */}
|
||||||
</Column>
|
</Column>
|
||||||
<Column size={ 5 } overflow="hidden">
|
<Column size={ 5 } overflow="hidden">
|
||||||
{ !currentOffer &&
|
{ !currentOffer &&
|
||||||
|
@ -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<CatalogLayoutProps> = props =>
|
||||||
|
{
|
||||||
|
const { page = null, roomPreviewer = null } = props;
|
||||||
|
const { currentOffer = null } = useCatalogContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<Column size={ 7 } overflow="hidden">
|
||||||
|
<Grid grow overflow="auto">
|
||||||
|
{/* { currentOffer && currentOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => <CatalogProductView key={ index } itemActive={ false } product={ product } />)} */}
|
||||||
|
</Grid>
|
||||||
|
</Column>
|
||||||
|
<Column size={ 5 } overflow="hidden">
|
||||||
|
<CatalogPageDetailsView page={ page } />
|
||||||
|
{ currentOffer && <CatalogPurchaseView offer={ currentOffer } pageId={ page.pageId } /> }
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -1,26 +1,25 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { Column } from '../../../../../../common/Column';
|
import { Column } from '../../../../../../common/Column';
|
||||||
import { Grid } from '../../../../../../common/Grid';
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView';
|
||||||
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
import { CatalogBundleGridWidgetView } from '../../widgets/CatalogBundleGridWidgetView';
|
||||||
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
|
import { CatalogPurchaseWidgetView } from '../../widgets/CatalogPurchaseWidgetView';
|
||||||
|
import { CatalogSimplePriceWidgetView } from '../../widgets/CatalogSimplePriceWidgetView';
|
||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutSingleBundleView: FC<CatalogLayoutProps> = props =>
|
export const CatalogLayoutSingleBundleView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { page = null, roomPreviewer = null } = props;
|
const { page = null, roomPreviewer = null } = props;
|
||||||
const { currentOffer = null } = useCatalogContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Column size={ 7 } overflow="hidden">
|
<Column size={ 6 } overflow="hidden">
|
||||||
<Grid grow overflow="auto">
|
<CatalogAddOnBadgeWidgetView />
|
||||||
{/* { currentOffer && currentOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => <CatalogProductView key={ index } itemActive={ false } product={ product } />)} */}
|
<CatalogSimplePriceWidgetView />
|
||||||
</Grid>
|
<CatalogPurchaseWidgetView />
|
||||||
</Column>
|
</Column>
|
||||||
<Column size={ 5 } overflow="hidden">
|
<Column size={ 6 } overflow="hidden">
|
||||||
<CatalogPageDetailsView page={ page } />
|
<CatalogBundleGridWidgetView />
|
||||||
{ currentOffer && <CatalogPurchaseView offer={ currentOffer } pageId={ page.pageId } /> }
|
|
||||||
</Column>
|
</Column>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -17,7 +17,6 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
|
|||||||
const [ groups, setGroups ] = useState<IPurchasableOffer[][]>([]);
|
const [ groups, setGroups ] = useState<IPurchasableOffer[][]>([]);
|
||||||
const [ activeGroupIndex, setActiveGroupIndex ] = useState(-1);
|
const [ activeGroupIndex, setActiveGroupIndex ] = useState(-1);
|
||||||
const { currentOffer = null, catalogState } = useCatalogContext();
|
const { currentOffer = null, catalogState } = useCatalogContext();
|
||||||
const { activeOffer = null } = catalogState;
|
|
||||||
|
|
||||||
const groupNames = [ 'floors', 'walls', 'views' ];
|
const groupNames = [ 'floors', 'walls', 'views' ];
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import { GetCurrencyAmount } from '../../../../../../views/purse/common/Currency
|
|||||||
import { GLOBAL_PURSE } from '../../../../../../views/purse/PurseView';
|
import { GLOBAL_PURSE } from '../../../../../../views/purse/PurseView';
|
||||||
import { CurrencyIcon } from '../../../../../../views/shared/currency-icon/CurrencyIcon';
|
import { CurrencyIcon } from '../../../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
import { CatalogPurchaseState } from '../../../../common/CatalogPurchaseState';
|
import { CatalogPurchaseState } from '../../../../common/CatalogPurchaseState';
|
||||||
import { GetCatalogPageImage } from '../../../../common/CatalogUtilities';
|
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
@ -23,8 +22,8 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
|||||||
{
|
{
|
||||||
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
|
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
|
||||||
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
||||||
const { catalogState = null } = useCatalogContext();
|
const { currentPage = null, catalogState = null } = useCatalogContext();
|
||||||
const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState;
|
const { clubOffers = null, subscriptionInfo = null } = catalogState;
|
||||||
|
|
||||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||||
{
|
{
|
||||||
@ -102,8 +101,8 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
|||||||
if(!pendingOffer) return;
|
if(!pendingOffer) return;
|
||||||
|
|
||||||
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
setPurchaseState(CatalogPurchaseState.PURCHASE);
|
||||||
SendMessageHook(new PurchaseFromCatalogComposer(pageParser.pageId, pendingOffer.offerId, null, 1));
|
SendMessageHook(new PurchaseFromCatalogComposer(currentPage.pageId, pendingOffer.offerId, null, 1));
|
||||||
}, [ pendingOffer, pageParser ]);
|
}, [ pendingOffer, currentPage ]);
|
||||||
|
|
||||||
const setOffer = useCallback((offer: ClubOfferData) =>
|
const setOffer = useCallback((offer: ClubOfferData) =>
|
||||||
{
|
{
|
||||||
@ -176,7 +175,7 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
|||||||
</Column>
|
</Column>
|
||||||
<Column size={ 5 } overflow="hidden">
|
<Column size={ 5 } overflow="hidden">
|
||||||
<Column fullHeight center overflow="hidden">
|
<Column fullHeight center overflow="hidden">
|
||||||
{ GetCatalogPageImage(pageParser, 1) && <img alt="" src={ GetCatalogPageImage(pageParser, 1) } /> }
|
{ currentPage.localization.getImage(1) && <img alt="" src={ currentPage.localization.getImage(1) } /> }
|
||||||
<Text center overflow="auto" dangerouslySetInnerHTML={{ __html: getSubscriptionDetails }} />
|
<Text center overflow="auto" dangerouslySetInnerHTML={{ __html: getSubscriptionDetails }} />
|
||||||
</Column>
|
</Column>
|
||||||
{ pendingOffer &&
|
{ pendingOffer &&
|
||||||
|
@ -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<CatalogGridOfferViewProps> = 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 <LayoutGridItem itemImage={ iconUrl } itemCount={ (offer.pricingModel === Offer.PRICING_MODEL_MULTI) ? offer.product.productCount : 1 } { ...rest } />;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { GetProductOfferComposer, MouseEventType } from '@nitrots/nitro-renderer';
|
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 { SendMessageHook } from '../../../../../hooks';
|
||||||
import { FurnitureOffer } from '../../../common/FurnitureOffer';
|
import { FurnitureOffer } from '../../../common/FurnitureOffer';
|
||||||
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
|
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
|
||||||
@ -10,12 +10,11 @@ export interface CatalogPageOfferViewProps
|
|||||||
{
|
{
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
offer: IPurchasableOffer;
|
offer: IPurchasableOffer;
|
||||||
setActiveOffer: Dispatch<SetStateAction<IPurchasableOffer>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { isActive = false, offer = null, setActiveOffer = null } = props;
|
const { isActive = false, offer = null } = props;
|
||||||
const [ isMouseDown, setMouseDown ] = useState(false);
|
const [ isMouseDown, setMouseDown ] = useState(false);
|
||||||
const { setCurrentOffer = null } = useCatalogContext();
|
const { setCurrentOffer = null } = useCatalogContext();
|
||||||
|
|
||||||
@ -26,16 +25,12 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
|||||||
case MouseEventType.MOUSE_CLICK:
|
case MouseEventType.MOUSE_CLICK:
|
||||||
if(isActive) return;
|
if(isActive) return;
|
||||||
|
|
||||||
setActiveOffer(offer);
|
setCurrentOffer(offer);
|
||||||
|
|
||||||
if(offer instanceof FurnitureOffer)
|
if(offer instanceof FurnitureOffer)
|
||||||
{
|
{
|
||||||
SendMessageHook(new GetProductOfferComposer(offer.offerId));
|
SendMessageHook(new GetProductOfferComposer(offer.offerId));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
setCurrentOffer(offer);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
case MouseEventType.MOUSE_DOWN:
|
case MouseEventType.MOUSE_DOWN:
|
||||||
setMouseDown(true);
|
setMouseDown(true);
|
||||||
@ -47,7 +42,7 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
|||||||
if(!isMouseDown || !isActive) return;
|
if(!isMouseDown || !isActive) return;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, [ isActive, offer, isMouseDown, setActiveOffer, setCurrentOffer ]);
|
}, [ isActive, offer, isMouseDown, setCurrentOffer ]);
|
||||||
|
|
||||||
const product = offer.product;
|
const product = offer.product;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { FC, useState } from 'react';
|
import { FC } from 'react';
|
||||||
import { Grid, GridProps } from '../../../../../common/Grid';
|
import { Grid, GridProps } from '../../../../../common/Grid';
|
||||||
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
|
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
|
||||||
|
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||||
import { CatalogPageOfferView } from './CatalogPageOfferView';
|
import { CatalogPageOfferView } from './CatalogPageOfferView';
|
||||||
|
|
||||||
export interface CatalogPageOffersViewProps extends GridProps
|
export interface CatalogPageOffersViewProps extends GridProps
|
||||||
@ -11,11 +12,11 @@ export interface CatalogPageOffersViewProps extends GridProps
|
|||||||
export const CatalogPageOffersView: FC<CatalogPageOffersViewProps> = props =>
|
export const CatalogPageOffersView: FC<CatalogPageOffersViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { offers = [], children = null, ...rest } = props;
|
const { offers = [], children = null, ...rest } = props;
|
||||||
const [ activeOffer, setActiveOffer ] = useState<IPurchasableOffer>(null);
|
const { currentOffer = null } = useCatalogContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
|
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
|
||||||
{ offers && (offers.length > 0) && offers.map((offer, index) => <CatalogPageOfferView key={ index } isActive={ (activeOffer === offer) } offer={ offer } setActiveOffer={ setActiveOffer } />) }
|
{ offers && (offers.length > 0) && offers.map((offer, index) => <CatalogPageOfferView key={ index } isActive={ (currentOffer === offer) } offer={ offer } />) }
|
||||||
{ children }
|
{ children }
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -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<HTMLDivElement>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogAddOnBadgeWidgetView: FC<CatalogAddOnBadgeWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { ...rest } = props;
|
||||||
|
const [ badgeCode, setBadgeCode ] = useState<string>(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 <BadgeImageView badgeCode={ badgeCode } { ...rest } />;
|
||||||
|
}
|
@ -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<CatalogBundleGridWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { children = null, ...rest } = props;
|
||||||
|
const [ offer, setOffer ] = useState<IPurchasableOffer>(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 (
|
||||||
|
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
|
||||||
|
{ offer.products && (offer.products.length > 0) && offer.products.map((product, index) => <LayoutGridItem key={ index } itemImage={ product.getIconUrl() } />) }
|
||||||
|
{ children }
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -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<CatalogItemGridWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { children = null, ...rest } = props;
|
||||||
|
const { currentOffer = null, currentPage = null } = useCatalogContext();
|
||||||
|
|
||||||
|
if(!currentPage) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
|
||||||
|
{ currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer === offer) } offer={ offer } />) }
|
||||||
|
{ children }
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -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<CatalogPriceDisplayWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { offer = null } = props;
|
||||||
|
|
||||||
|
if(!offer) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap={ 1 }>
|
||||||
|
{ (offer.priceInCredits > 0) &&
|
||||||
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
|
<Text>{ offer.priceInCredits }</Text>
|
||||||
|
<CurrencyIcon type={ -1 } />
|
||||||
|
</Flex> }
|
||||||
|
{ ( offer.priceInCredits > 0) && (offer.priceInActivityPoints > 0) &&
|
||||||
|
<FontAwesomeIcon icon="plus" /> }
|
||||||
|
{ (offer.priceInActivityPoints > 0) &&
|
||||||
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
|
<Text>{ offer.priceInActivityPoints }</Text>
|
||||||
|
<CurrencyIcon type={ offer.activityPointType } />
|
||||||
|
</Flex> }
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { CatalogSelectProductEvent } from '../../../../../events';
|
||||||
|
import { useUiEvent } from '../../../../../hooks';
|
||||||
|
|
||||||
|
interface CatalogPurchaseWidgetViewProps
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const {} = props;
|
||||||
|
|
||||||
|
const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
@ -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<HTMLDivElement>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogSimplePriceWidgetView: FC<CatalogSimplePriceWidgetViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { ...rest } = props;
|
||||||
|
const [ offer, setOffer ] = useState<IPurchasableOffer>(null);
|
||||||
|
|
||||||
|
const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) =>
|
||||||
|
{
|
||||||
|
setOffer(event.offer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
|
||||||
|
|
||||||
|
return <CatalogPriceDisplayWidgetView offer={ offer } { ...rest } />;
|
||||||
|
}
|
@ -17,18 +17,7 @@ import { useCatalogContext } from '../../context/CatalogContext';
|
|||||||
export const CatalogSearchView: FC<{}> = props =>
|
export const CatalogSearchView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ searchValue, setSearchValue ] = useState('');
|
const [ searchValue, setSearchValue ] = useState('');
|
||||||
const { currentType = null, setActiveNodes = null, currentOffers = null, setCurrentPage = null, catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
const { currentType = null, setActiveNodes = null, currentOffers = null, setCurrentPage = null } = useCatalogContext();
|
||||||
const { searchResult = null } = catalogState;
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
setSearchValue(prevValue =>
|
|
||||||
{
|
|
||||||
if(!searchResult && prevValue && prevValue.length) return '';
|
|
||||||
|
|
||||||
return prevValue;
|
|
||||||
});
|
|
||||||
}, [ searchResult ]);
|
|
||||||
|
|
||||||
const processSearch = useCallback((search: string) =>
|
const processSearch = useCallback((search: string) =>
|
||||||
{
|
{
|
||||||
|
11
src/events/catalog/CatalogPageReadyEvent.ts
Normal file
11
src/events/catalog/CatalogPageReadyEvent.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
20
src/events/catalog/CatalogSetExtraPurchaseParameterEvent.ts
Normal file
20
src/events/catalog/CatalogSetExtraPurchaseParameterEvent.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
export * from './CatalogEvent';
|
export * from './CatalogEvent';
|
||||||
export * from './CatalogNameResultEvent';
|
export * from './CatalogNameResultEvent';
|
||||||
export * from './CatalogPageOpenedEvent';
|
export * from './CatalogPageOpenedEvent';
|
||||||
|
export * from './CatalogPageReadyEvent';
|
||||||
export * from './CatalogPurchasedEvent';
|
export * from './CatalogPurchasedEvent';
|
||||||
export * from './CatalogPurchaseFailureEvent';
|
export * from './CatalogPurchaseFailureEvent';
|
||||||
export * from './CatalogPurchaseSoldOutEvent';
|
export * from './CatalogPurchaseSoldOutEvent';
|
||||||
export * from './CatalogSelectProductEvent';
|
export * from './CatalogSelectProductEvent';
|
||||||
|
export * from './CatalogSetExtraPurchaseParameterEvent';
|
||||||
export * from './SetRoomPreviewerStuffDataEvent';
|
export * from './SetRoomPreviewerStuffDataEvent';
|
||||||
|
@ -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());
|
||||||
|
Loading…
Reference in New Issue
Block a user