Add catalog spaces layout

This commit is contained in:
Bill 2021-05-11 00:41:54 -04:00
parent a1605537c8
commit 5e2e393486
14 changed files with 289 additions and 209 deletions

View File

@ -66,22 +66,6 @@ export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, ac
case CatalogActions.SET_CATALOG_PAGE_PARSER: {
const pageParser = action.payload.pageParser;
let activeOffer = null;
if(state.activeOffer)
{
for(const offer of pageParser.offers)
{
if(offer.offerId !== state.activeOffer.offerId) continue;
activeOffer = offer;
break;
}
}
if(!activeOffer) activeOffer = ((pageParser && (pageParser.offers.length > 0) && pageParser.offers[0]) || null);
const searchResult = state.searchResult;
if(searchResult)
@ -89,7 +73,7 @@ export const CatalogReducer: Reducer<ICatalogState, ICatalogAction> = (state, ac
searchResult.furniture = null;
}
return { ...state, pageParser, activeOffer, searchResult };
return { ...state, pageParser, searchResult };
}
case CatalogActions.SET_CATALOG_ACTIVE_OFFER: {
const activeOffer = (action.payload.activeOffer || state.activeOffer || null);

View File

@ -1,5 +1,9 @@
import { FC } from 'react';
import { Vector3d } from 'nitro-renderer';
import { FC, useEffect } from 'react';
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../api';
import { useCatalogContext } from '../../context/CatalogContext';
import { FurniCategory } from '../../enums/FurniCategory';
import { ProductTypeEnum } from '../../enums/ProductTypeEnum';
import { CatalogPageViewProps } from './CatalogPageView.types';
import { GetCatalogLayout } from './layout/GetCatalogLayout';
import { CatalogLayoutSearchResultView } from './search-result/CatalogLayoutSearchResultView';
@ -8,7 +12,91 @@ export const CatalogPageView: FC<CatalogPageViewProps> = props =>
{
const { roomPreviewer = null } = props;
const { catalogState = null } = useCatalogContext();
const { pageParser = null, searchResult = null } = catalogState;
const { pageParser = null, activeOffer = null, searchResult = null } = catalogState;
useEffect(() =>
{
if(!roomPreviewer) return;
roomPreviewer && roomPreviewer.reset(false);
if(activeOffer && activeOffer.products.length)
{
const product = activeOffer.products[0];
if(!product) return;
const furniData = GetFurnitureDataForProductOffer(product);
if(!furniData) return;
switch(product.productType)
{
case ProductTypeEnum.ROBOT: {
roomPreviewer.updateObjectRoom('default', 'default', 'default');
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(product.extraParam, 'm', []);
roomPreviewer.addAvatarIntoRoom(figure, 0);
return;
}
case ProductTypeEnum.FLOOR: {
roomPreviewer.updateObjectRoom('default', 'default', 'default');
if(furniData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET)
{
const setIds: number[] = [];
const sets = furniData.customParams.split(',');
for(const set of sets)
{
const setId = parseInt(set);
if(GetAvatarRenderManager().isValidFigureSetForGender(setId, GetSessionDataManager().gender)) setIds.push(setId);
}
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(GetSessionDataManager().figure, GetSessionDataManager().gender, setIds);
roomPreviewer.addAvatarIntoRoom(figure, 0);
}
else
{
roomPreviewer.addFurnitureIntoRoom(product.furniClassId, new Vector3d(90));
}
return;
}
case ProductTypeEnum.WALL:
switch(furniData.className)
{
case 'floor':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(product.extraParam);
break;
case 'wallpaper':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(null, product.extraParam);
break;
case 'landscape':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(null, null, product.extraParam);
break;
default:
roomPreviewer.updateObjectRoom('default', 'default', 'default');
roomPreviewer.addWallItemIntoRoom(product.furniClassId, new Vector3d(90), product.extraParam);
return;
}
// const windowData = Nitro.instance.sessionDataManager.getWallItemDataByName('ads_twi_windw');
// if(windowData)
// {
// this._roomPreviewer.addWallItemIntoRoom(windowData.id, new Vector3d(90), windowData.customParams)
// }
return;
}
}
}, [ roomPreviewer, activeOffer ]);
if(searchResult && searchResult.furniture)
{

View File

@ -1 +1,2 @@
@import './default/CatalogLayoutDefaultView';
@import './spaces-new/CatalogLayoutSpacesView';

View File

@ -1,9 +1,10 @@
import { ICatalogPageParser, RoomPreviewer } from 'nitro-renderer';
import { CatalogLayoutDefaultView } from './default/CatalogLayoutDefaultView';
import { CatalogLayoutSpacesView } from './spaces-new/CatalogLayoutSpacesView';
export function GetCatalogLayout(pageParser: ICatalogPageParser, roomPreviewer: RoomPreviewer): JSX.Element
{
switch(pageParser.catalogType)
switch(pageParser.layoutCode)
{
case 'frontpage_featured':
return null;
@ -18,7 +19,7 @@ export function GetCatalogLayout(pageParser: ICatalogPageParser, roomPreviewer:
case 'pets3':
return null;
case 'spaces_new':
return null;
return <CatalogLayoutSpacesView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
case 'vip_buy':
return null;
case 'guild_frontpage':
@ -37,6 +38,6 @@ export function GetCatalogLayout(pageParser: ICatalogPageParser, roomPreviewer:
return null;
case 'default_3x3':
default:
return <CatalogLayoutDefaultView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />
return <CatalogLayoutDefaultView roomPreviewer={ roomPreviewer } pageParser={ pageParser } />;
}
}

View File

@ -1,11 +1,7 @@
import { Vector3d } from 'nitro-renderer';
import { FC, useEffect } from 'react';
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../../../api';
import { FC } from 'react';
import { LimitedEditionCompletePlateView } from '../../../../../limited-edition/complete-plate/LimitedEditionCompletePlateView';
import { RoomPreviewerView } from '../../../../../room-previewer/RoomPreviewerView';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { FurniCategory } from '../../../../enums/FurniCategory';
import { ProductTypeEnum } from '../../../../enums/ProductTypeEnum';
import { GetOfferName } from '../../../../utils/CatalogUtilities';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
@ -17,92 +13,6 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutDefaultViewProps> = props
const { catalogState } = useCatalogContext();
const { activeOffer = null } = catalogState;
useEffect(() =>
{
if(!roomPreviewer) return;
if(!activeOffer)
{
roomPreviewer && roomPreviewer.reset(false);
return;
}
const product = activeOffer.products[0];
if(!product) return;
const furniData = GetFurnitureDataForProductOffer(product);
if(!furniData && product.productType !== ProductTypeEnum.ROBOT) return;
switch(product.productType)
{
case ProductTypeEnum.ROBOT: {
roomPreviewer.updateObjectRoom('default', 'default', 'default');
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(product.extraParam, 'm', []);
roomPreviewer.addAvatarIntoRoom(figure, 0);
return;
}
case ProductTypeEnum.FLOOR: {
roomPreviewer.updateObjectRoom('default', 'default', 'default');
if(furniData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET)
{
const setIds: number[] = [];
const sets = furniData.customParams.split(',');
for(const set of sets)
{
const setId = parseInt(set);
if(GetAvatarRenderManager().isValidFigureSetForGender(setId, GetSessionDataManager().gender)) setIds.push(setId);
}
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(GetSessionDataManager().figure, GetSessionDataManager().gender, setIds);
roomPreviewer.addAvatarIntoRoom(figure, 0);
}
else
{
roomPreviewer.addFurnitureIntoRoom(product.furniClassId, new Vector3d(90));
}
return;
}
case ProductTypeEnum.WALL:
switch(furniData.className)
{
case 'floor':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(product.extraParam);
break;
case 'wallpaper':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(null, product.extraParam);
break;
case 'landscape':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(null, null, product.extraParam);
break;
default:
roomPreviewer.updateObjectRoom('default', 'default', 'default');
roomPreviewer.addWallItemIntoRoom(product.furniClassId, new Vector3d(90), product.extraParam);
return;
}
// const windowData = Nitro.instance.sessionDataManager.getWallItemDataByName('ads_twi_windw');
// if(windowData)
// {
// this._roomPreviewer.addWallItemIntoRoom(windowData.id, new Vector3d(90), windowData.customParams)
// }
return;
}
}, [ roomPreviewer, activeOffer ]);
const product = ((activeOffer && activeOffer.products[0]) || null);
return (

View File

@ -0,0 +1,7 @@
.nitro-catalog-layout-spaces {
.catalog-offers-container {
height: 287px;
max-height: 287px;
}
}

View File

@ -0,0 +1,92 @@
import { CatalogPageOfferData, IFurnitureData } from 'nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { GetSessionDataManager } from '../../../../../../api';
import { LocalizeText } from '../../../../../../utils/LocalizeText';
import { RoomPreviewerView } from '../../../../../room-previewer/RoomPreviewerView';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { ProductTypeEnum } from '../../../../enums/ProductTypeEnum';
import { GetOfferName } from '../../../../utils/CatalogUtilities';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogLayoutSpacesViewProps } from './CatalogLayoutSpacesView.types';
export const CatalogLayoutSpacesView: FC<CatalogLayoutSpacesViewProps> = props =>
{
const { roomPreviewer = null, pageParser = null } = props;
const { catalogState } = useCatalogContext();
const { activeOffer = null } = catalogState;
const [ groups, setGroups ] = useState<CatalogPageOfferData[][]>([]);
const [ activeGroupIndex, setActiveGroupIndex ] = useState(-1);
const groupNames = [ 'floors', 'walls', 'views' ];
useEffect(() =>
{
if(!pageParser) return;
const groupedOffers: CatalogPageOfferData[][] = [ [], [], [] ];
for(const offer of pageParser.offers)
{
const product = offer.products[0];
if(!product) continue;
let furniData: IFurnitureData = null;
if(product.productType === ProductTypeEnum.FLOOR)
{
furniData = GetSessionDataManager().getFloorItemData(product.furniClassId);
}
else if(product.productType === ProductTypeEnum.WALL)
{
furniData = GetSessionDataManager().getWallItemData(product.furniClassId);
}
if(!furniData) continue;
const className = furniData.className;
switch(className)
{
case 'floor':
groupedOffers[0].push(offer);
break;
case 'wallpaper':
groupedOffers[1].push(offer);
break;
case 'landscape':
groupedOffers[2].push(offer);
break;
}
}
setGroups(groupedOffers);
setActiveGroupIndex(0);
}, [ pageParser ]);
const product = ((activeOffer && activeOffer.products[0]) || null);
return (
<div className="row h-100 nitro-catalog-layout-spaces">
<div className="col-7">
<div className="d-flex">
<div className="btn-group mx-auto mb-1 w-100">
{ groupNames.map((name, index) =>
{
return <button key={ index } type="button" className={ 'btn btn-primary btn-sm ' + ((activeGroupIndex === index) ? 'active ' : '' )} onClick={ event => setActiveGroupIndex(index) }>{ LocalizeText(`catalog.spaces.tab.${ name }`) }</button>
})}
</div>
</div>
<CatalogPageOffersView offers={ groups[activeGroupIndex] } />
</div>
{ product &&
<div className="position-relative d-flex flex-column col">
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } />
</div> }
</div>
);
}

View File

@ -0,0 +1,6 @@
import { CatalogLayoutProps } from '../CatalogLayout.types';
export interface CatalogLayoutSpacesViewProps extends CatalogLayoutProps
{
}

View File

@ -1,6 +1,7 @@
import { FurnitureType, MouseEventType } from 'nitro-renderer';
import { FC, MouseEvent, useCallback } from 'react';
import { GetRoomEngine, GetSessionDataManager } from '../../../../../api';
import { GetConfiguration } from '../../../../../utils/GetConfiguration';
import { LimitedEditionStyledNumberView } from '../../../../limited-edition/styled-number/LimitedEditionStyledNumberView';
import { useCatalogContext } from '../../../context/CatalogContext';
import { CatalogActions } from '../../../reducers/CatalogReducer';
@ -44,8 +45,36 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
return GetSessionDataManager().getBadgeUrl(product.extraParam);
case FurnitureType.FLOOR:
return GetRoomEngine().getFurnitureFloorIconUrl(product.furniClassId);
case FurnitureType.WALL:
case FurnitureType.WALL: {
const furniData = GetSessionDataManager().getWallItemData(product.furniClassId);
let iconName = '';
if(furniData)
{
switch(furniData.className)
{
case 'floor':
iconName = ['th', furniData.className, product.extraParam].join('_');
break;
case 'wallpaper':
iconName = ['th', 'wall', product.extraParam].join('_');
break;
case 'landscape':
iconName = ['th', furniData.className, product.extraParam.replace('.', '_'), '001'].join('_');
break;
}
if(iconName !== '')
{
const assetUrl = GetConfiguration<string>('catalog.asset.url');
return `${ assetUrl }/${ iconName }.png`;
}
}
return GetRoomEngine().getFurnitureWallIconUrl(product.furniClassId, product.extraParam);
}
}
return '';

View File

@ -1,14 +1,27 @@
import { FC } from 'react';
import { FC, useEffect } from 'react';
import { useCatalogContext } from '../../../context/CatalogContext';
import { CatalogActions } from '../../../reducers/CatalogReducer';
import { CatalogPageOfferView } from '../offer/CatalogPageOfferView';
import { CatalogPageOffersViewProps } from './CatalogPageOffersView.types';
export const CatalogPageOffersView: FC<CatalogPageOffersViewProps> = props =>
{
const { offers = [] } = props;
const { catalogState } = useCatalogContext();
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
const { activeOffer = null } = catalogState;
useEffect(() =>
{
if(!offers || !offers.length) return;
dispatchCatalogState({
type: CatalogActions.SET_CATALOG_ACTIVE_OFFER,
payload: {
activeOffer: offers[0]
}
})
}, [ offers, dispatchCatalogState ]);
return (
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container">
{ offers && (offers.length > 0) && offers.map((offer, index) =>

View File

@ -41,6 +41,8 @@ export const CatalogPurchaseView: FC<CatalogPurchaseViewProps> = props =>
setQuantity(amount);
}
const extra = (offer?.products[0]?.extraParam || null);
return (
<div className="d-flex flex-column flex-grow-1 justify-content-end">
<div className="d-flex align-items-end">
@ -67,7 +69,7 @@ export const CatalogPurchaseView: FC<CatalogPurchaseViewProps> = props =>
</div>
</div>
<div className="d-flex flex-column mt-1">
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } quantity={ quantity } />
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extra } quantity={ quantity } />
{ offer.giftable && <button type="button" className="btn btn-secondary btn-sm w-100 mt-1">{ LocalizeText('catalog.purchase_confirmation.gift') }</button> }
</div>
</div>

View File

@ -1,11 +1,7 @@
import { Vector3d } from 'nitro-renderer';
import { FC, useEffect } from 'react';
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../../api';
import { FC } from 'react';
import { LimitedEditionCompletePlateView } from '../../../../limited-edition/complete-plate/LimitedEditionCompletePlateView';
import { RoomPreviewerView } from '../../../../room-previewer/RoomPreviewerView';
import { useCatalogContext } from '../../../context/CatalogContext';
import { FurniCategory } from '../../../enums/FurniCategory';
import { ProductTypeEnum } from '../../../enums/ProductTypeEnum';
import { GetOfferName } from '../../../utils/CatalogUtilities';
import { CatalogPurchaseView } from '../purchase/CatalogPurchaseView';
import { CatalogLayoutSearchResultViewProps } from './CatalogLayoutSearchResultView.types';
@ -17,92 +13,6 @@ export const CatalogLayoutSearchResultView: FC<CatalogLayoutSearchResultViewProp
const { catalogState } = useCatalogContext();
const { activeOffer = null } = catalogState;
useEffect(() =>
{
if(!roomPreviewer) return;
if(!activeOffer)
{
roomPreviewer && roomPreviewer.reset(false);
return;
}
const product = activeOffer.products[0];
if(!product) return;
const furniData = GetFurnitureDataForProductOffer(product);
if(!furniData && product.productType !== ProductTypeEnum.ROBOT) return;
switch(product.productType)
{
case ProductTypeEnum.ROBOT: {
roomPreviewer.updateObjectRoom('default', 'default', 'default');
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(product.extraParam, 'm', []);
roomPreviewer.addAvatarIntoRoom(figure, 0);
return;
}
case ProductTypeEnum.FLOOR: {
roomPreviewer.updateObjectRoom('default', 'default', 'default');
if(furniData.specialType === FurniCategory.FIGURE_PURCHASABLE_SET)
{
const setIds: number[] = [];
const sets = furniData.customParams.split(',');
for(const set of sets)
{
const setId = parseInt(set);
if(GetAvatarRenderManager().isValidFigureSetForGender(setId, GetSessionDataManager().gender)) setIds.push(setId);
}
const figure = GetAvatarRenderManager().getFigureStringWithFigureIds(GetSessionDataManager().figure, GetSessionDataManager().gender, setIds);
roomPreviewer.addAvatarIntoRoom(figure, 0);
}
else
{
roomPreviewer.addFurnitureIntoRoom(product.furniClassId, new Vector3d(90));
}
return;
}
case ProductTypeEnum.WALL:
switch(furniData.className)
{
case 'floor':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(product.extraParam);
break;
case 'wallpaper':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(null, product.extraParam);
break;
case 'landscape':
roomPreviewer.reset(false);
roomPreviewer.updateObjectRoom(null, null, product.extraParam);
break;
default:
roomPreviewer.updateObjectRoom('default', 'default', 'default');
roomPreviewer.addWallItemIntoRoom(product.furniClassId, new Vector3d(90), product.extraParam);
return;
}
// const windowData = Nitro.instance.sessionDataManager.getWallItemDataByName('ads_twi_windw');
// if(windowData)
// {
// this._roomPreviewer.addWallItemIntoRoom(windowData.id, new Vector3d(90), windowData.customParams)
// }
return;
}
}, [ roomPreviewer, activeOffer ]);
const product = ((activeOffer && activeOffer.products[0]) || null);
return (

View File

@ -2,6 +2,7 @@ import { CatalogSearchComposer, FurnitureType, MouseEventType } from 'nitro-rend
import { FC, MouseEvent, useCallback } from 'react';
import { GetRoomEngine, GetSessionDataManager } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { GetConfiguration } from '../../../../../../utils/GetConfiguration';
import { CatalogSearchResultOfferViewProps } from './CatalogSearchResultOfferView.types';
export const CatalogSearchResultOfferView: FC<CatalogSearchResultOfferViewProps> = props =>
@ -33,6 +34,33 @@ export const CatalogSearchResultOfferView: FC<CatalogSearchResultOfferViewProps>
case FurnitureType.FLOOR:
return GetRoomEngine().getFurnitureFloorIconUrl(offer.id);
case FurnitureType.WALL:
const furniData = GetSessionDataManager().getWallItemData(offer.id);
let iconName = '';
if(furniData)
{
switch(furniData.className)
{
case 'floor':
iconName = ['th', furniData.className, offer.customParams].join('_');
break;
case 'wallpaper':
iconName = ['th', 'wall', offer.customParams].join('_');
break;
case 'landscape':
iconName = ['th', furniData.className, offer.customParams.replace('.', '_'), '001'].join('_');
break;
}
if(iconName !== '')
{
const assetUrl = GetConfiguration<string>('catalog.asset.url');
return `${ assetUrl }/${ iconName }.png`;
}
}
return GetRoomEngine().getFurnitureWallIconUrl(offer.id, offer.customParams);
}

View File

@ -1,4 +1,6 @@
import { FC } from 'react';
import { CatalogSearchComposer } from 'nitro-renderer';
import { FC, useEffect } from 'react';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogSearchResultOfferView } from '../offer/CatalogSearchResultOfferView';
import { CatalogSearchResultOffersViewProps } from './CatalogSearchResultOffersView.types';
@ -9,6 +11,13 @@ export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProp
const { catalogState } = useCatalogContext();
const { activeOffer = null } = catalogState;
useEffect(() =>
{
if(!offers || !offers.length) return;
SendMessageHook(new CatalogSearchComposer(offers[0].purchaseOfferId));
}, [ offers ]);
return (
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container">
{ offers && (offers.length > 0) && offers.map((offer, index) =>