Catalog done for now

This commit is contained in:
Bill 2022-02-05 23:28:18 -05:00
parent 6f4a41ff33
commit 52810938df
7 changed files with 50 additions and 30 deletions

View File

@ -10,6 +10,14 @@
min-width: 30px; min-width: 30px;
width: 30px; width: 30px;
} }
.quantity-input {
min-height: 17px;
height: 17px;
width: 20px;
padding: 0 4px;
text-align: right;
}
} }
.catalog-icon-image { .catalog-icon-image {

View File

@ -3,6 +3,7 @@ import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames'; import classNames from 'classnames';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetSessionDataManager, LocalizeText } from '../../../../api'; import { GetSessionDataManager, LocalizeText } from '../../../../api';
import { Base } from '../../../../common/Base';
import { Button } from '../../../../common/Button'; import { Button } from '../../../../common/Button';
import { ButtonGroup } from '../../../../common/ButtonGroup'; import { ButtonGroup } from '../../../../common/ButtonGroup';
import { Column } from '../../../../common/Column'; import { Column } from '../../../../common/Column';
@ -16,12 +17,14 @@ import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutGi
import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon'; import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon';
import { FurniImageView } from '../../../../views/shared/furni-image/FurniImageView'; import { FurniImageView } from '../../../../views/shared/furni-image/FurniImageView';
import { useCatalogContext } from '../../CatalogContext'; import { useCatalogContext } from '../../CatalogContext';
import { ProductTypeEnum } from '../../common/ProductTypeEnum';
export const CatalogGiftView: FC<{}> = props => export const CatalogGiftView: FC<{}> = props =>
{ {
const [ isVisible, setIsVisible ] = useState<boolean>(false); const [ isVisible, setIsVisible ] = useState<boolean>(false);
const [ pageId, setPageId ] = useState<number>(0); const [ pageId, setPageId ] = useState<number>(0);
const [ offerId, setOfferId ] = useState<number>(0); const [ offerId, setOfferId ] = useState<number>(0);
const [ extraData, setExtraData ] = useState<string>('');
const [ receiverName, setReceiverName ] = useState<string>(''); const [ receiverName, setReceiverName ] = useState<string>('');
const [ showMyFace, setShowMyFace ] = useState<boolean>(true); const [ showMyFace, setShowMyFace ] = useState<boolean>(true);
const [ message, setMessage ] = useState<string>(''); const [ message, setMessage ] = useState<string>('');
@ -42,6 +45,7 @@ export const CatalogGiftView: FC<{}> = props =>
setIsVisible(false); setIsVisible(false);
setPageId(0); setPageId(0);
setOfferId(0); setOfferId(0);
setExtraData('');
setReceiverName(''); setReceiverName('');
setShowMyFace(true); setShowMyFace(true);
setMessage(''); setMessage('');
@ -68,6 +72,7 @@ export const CatalogGiftView: FC<{}> = props =>
setPageId(castedEvent.pageId); setPageId(castedEvent.pageId);
setOfferId(castedEvent.offerId); setOfferId(castedEvent.offerId);
setExtraData(castedEvent.extraData);
setIsVisible(true); setIsVisible(true);
}); });
return; return;
@ -86,7 +91,7 @@ export const CatalogGiftView: FC<{}> = props =>
return giftConfiguration ? (giftConfiguration.defaultStuffTypes.findIndex(s => (s === giftConfiguration.boxTypes[selectedBoxIndex])) > -1) : true; return giftConfiguration ? (giftConfiguration.defaultStuffTypes.findIndex(s => (s === giftConfiguration.boxTypes[selectedBoxIndex])) > -1) : true;
}, [ giftConfiguration, selectedBoxIndex ]); }, [ giftConfiguration, selectedBoxIndex ]);
const extraData = useMemo(() => const boxExtraData = useMemo(() =>
{ {
if(!giftConfiguration) return ''; if(!giftConfiguration) return '';
@ -176,18 +181,19 @@ export const CatalogGiftView: FC<{}> = props =>
<FormGroup column> <FormGroup column>
<Text>{ LocalizeText('catalog.gift_wrapping.receiver') }</Text> <Text>{ LocalizeText('catalog.gift_wrapping.receiver') }</Text>
<input type="text" className={ 'form-control form-control-sm' + classNames({ ' is-invalid': receiverNotFound }) } value={ receiverName } onChange={ (e) => setReceiverName(e.target.value) } /> <input type="text" className={ 'form-control form-control-sm' + classNames({ ' is-invalid': receiverNotFound }) } value={ receiverName } onChange={ (e) => setReceiverName(e.target.value) } />
{ receiverNotFound && <div className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</div> } { receiverNotFound &&
<Base className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</Base> }
</FormGroup> </FormGroup>
<NitroLayoutGiftCardView figure={ GetSessionDataManager().figure } userName={ GetSessionDataManager().userName } message={ message } editable={ true } onChange={ (value) => setMessage(value) } /> <NitroLayoutGiftCardView figure={ GetSessionDataManager().figure } userName={ GetSessionDataManager().userName } message={ message } editable={ true } onChange={ (value) => setMessage(value) } />
<div className="form-check"> <Base className="form-check">
<input className="form-check-input" type="checkbox" name="showMyFace" checked={ showMyFace } onChange={ (e) => setShowMyFace(value => !value) } /> <input className="form-check-input" type="checkbox" name="showMyFace" checked={ showMyFace } onChange={ (e) => setShowMyFace(value => !value) } />
<label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label> <label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label>
</div> </Base>
<Flex alignItems="center" gap={ 2 }> <Flex alignItems="center" gap={ 2 }>
{ selectedColorId && { selectedColorId &&
<div className="gift-preview"> <Base className="gift-preview">
<FurniImageView spriteId={ selectedColorId } type="s" extras={ extraData } /> <FurniImageView productType={ ProductTypeEnum.FLOOR } productClassId={ selectedColorId } extraData={ boxExtraData } />
</div> } </Base> }
<Column gap={ 1 }> <Column gap={ 1 }>
<Flex gap={ 2 }> <Flex gap={ 2 }>
<ButtonGroup> <ButtonGroup>

View File

@ -25,7 +25,6 @@ export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
if(subscriptionInfo.isVip) return LocalizeText('catalog.club_gift.not_available'); if(subscriptionInfo.isVip) return LocalizeText('catalog.club_gift.not_available');
return LocalizeText('catalog.club_gift.no_club'); return LocalizeText('catalog.club_gift.no_club');
}, [ clubGifts, subscriptionInfo ]); }, [ clubGifts, subscriptionInfo ]);
const selectGift = useCallback((localizationId: string) => const selectGift = useCallback((localizationId: string) =>

View File

@ -3,9 +3,10 @@ import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CreateLinkEvent, GetClubMemberLevel, LocalizeText } from '../../../../../api'; import { CreateLinkEvent, GetClubMemberLevel, LocalizeText } from '../../../../../api';
import { Button } from '../../../../../common/Button'; import { Button } from '../../../../../common/Button';
import { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent } from '../../../../../events'; import { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent } from '../../../../../events';
import { CatalogInitGiftEvent } from '../../../../../events/catalog/CatalogInitGiftEvent';
import { CatalogInitPurchaseEvent } from '../../../../../events/catalog/CatalogInitPurchaseEvent'; import { CatalogInitPurchaseEvent } from '../../../../../events/catalog/CatalogInitPurchaseEvent';
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent'; import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { SendMessageHook, useUiEvent } from '../../../../../hooks'; import { dispatchUiEvent, SendMessageHook, useUiEvent } from '../../../../../hooks';
import { LoadingSpinnerView } from '../../../../../layout'; import { LoadingSpinnerView } from '../../../../../layout';
import { GetCurrencyAmount } from '../../../../../views/purse/common/CurrencyHelper'; import { GetCurrencyAmount } from '../../../../../views/purse/common/CurrencyHelper';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalogContext } from '../../../CatalogContext';
@ -87,6 +88,13 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
return; return;
} }
if(isGift)
{
dispatchUiEvent(new CatalogInitGiftEvent(currentOffer.page.pageId, currentOffer.offerId, extraData));
return;
}
setPurchaseState(CatalogPurchaseState.PURCHASE); setPurchaseState(CatalogPurchaseState.PURCHASE);
if(purchaseCallback) if(purchaseCallback)
@ -108,8 +116,6 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
SendMessageHook(new PurchaseFromCatalogComposer(pageId, currentOffer.offerId, extraData, quantity)); SendMessageHook(new PurchaseFromCatalogComposer(pageId, currentOffer.offerId, extraData, quantity));
}, [ currentOffer, purchaseCallback, extraData, quantity, getNodesByOfferId ]); }, [ currentOffer, purchaseCallback, extraData, quantity, getNodesByOfferId ]);
// dispatchUiEvent(new CatalogInitGiftEvent(pageId, offer.offerId, extra)); setup gift
useEffect(() => useEffect(() =>
{ {
if(!currentOffer) return; if(!currentOffer) return;
@ -171,7 +177,7 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
<> <>
<PurchaseButton /> <PurchaseButton />
{ (!noGiftOption && !currentOffer.isRentOffer) && { (!noGiftOption && !currentOffer.isRentOffer) &&
<Button disabled={ ((quantity > 1) || !currentOffer.giftable || isLimitedSoldOut || (extraParamRequired && (!extraData || !extraData.length))) }> <Button disabled={ ((quantity > 1) || !currentOffer.giftable || isLimitedSoldOut || (extraParamRequired && (!extraData || !extraData.length))) } onClick={ event => purchase(true) }>
{ LocalizeText('catalog.purchase_confirmation.gift') } { LocalizeText('catalog.purchase_confirmation.gift') }
</Button> } </Button> }
</> </>

View File

@ -15,6 +15,8 @@ export const CatalogSpinnerWidgetView: FC<{}> = props =>
const updateQuantity = (value: number) => const updateQuantity = (value: number) =>
{ {
if(isNaN(value)) value = 1;
value = Math.max(value, MIN_VALUE); value = Math.max(value, MIN_VALUE);
value = Math.min(value, MAX_VALUE); value = Math.min(value, MAX_VALUE);

View File

@ -1,20 +1,27 @@
import { IGetImageListener, ImageResult, TextureUtils, Vector3d } from '@nitrots/nitro-renderer'; import { IGetImageListener, ImageResult, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetRoomEngine } from '../../../api'; import { GetRoomEngine } from '../../../api';
import { Base } from '../../../common/Base';
import { ProductTypeEnum } from '../../../components/catalog/common/ProductTypeEnum'; import { ProductTypeEnum } from '../../../components/catalog/common/ProductTypeEnum';
import { FurniImageViewProps } from './FurniImageView.types';
interface FurniImageViewProps
{
productType: string;
productClassId: number;
direction?: number;
extraData?: string;
scale?: number;
}
export const FurniImageView: FC<FurniImageViewProps> = props => export const FurniImageView: FC<FurniImageViewProps> = props =>
{ {
const { type = 's', spriteId = -1, direction = 0, extras = '', scale = 1 } = props; const { productType = 's', productClassId = -1, direction = 0, extraData = '', scale = 1 } = props;
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null); const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null);
const buildImage = useCallback(() => const buildImage = useCallback(() =>
{ {
let imageResult: ImageResult = null; let imageResult: ImageResult = null;
const furniType = type.toLocaleLowerCase();
const listener: IGetImageListener = { const listener: IGetImageListener = {
imageReady: (id, texture, image) => imageReady: (id, texture, image) =>
{ {
@ -28,13 +35,13 @@ export const FurniImageView: FC<FurniImageViewProps> = props =>
imageFailed: null imageFailed: null
}; };
switch(furniType) switch(productType.toLocaleLowerCase())
{ {
case ProductTypeEnum.FLOOR: case ProductTypeEnum.FLOOR:
imageResult = GetRoomEngine().getFurnitureFloorImage(spriteId, new Vector3d(direction), 64, listener, 0, extras); imageResult = GetRoomEngine().getFurnitureFloorImage(productClassId, new Vector3d(direction), 64, listener, 0, extraData);
break; break;
case ProductTypeEnum.WALL: case ProductTypeEnum.WALL:
imageResult = GetRoomEngine().getFurnitureWallImage(spriteId, new Vector3d(direction), 64, listener, 0, extras); imageResult = GetRoomEngine().getFurnitureWallImage(productClassId, new Vector3d(direction), 64, listener, 0, extraData);
break; break;
} }
@ -44,7 +51,7 @@ export const FurniImageView: FC<FurniImageViewProps> = props =>
image.onload = () => setImageElement(image); image.onload = () => setImageElement(image);
} }
}, [ type, spriteId, direction, extras ]); }, [ productType, productClassId, direction, extraData ]);
useEffect(() => useEffect(() =>
{ {
@ -54,6 +61,6 @@ export const FurniImageView: FC<FurniImageViewProps> = props =>
if(!imageElement) return null; if(!imageElement) return null;
const imageUrl = `url('${ imageElement.src }')`; const imageUrl = `url('${ imageElement.src }')`;
return <div className={ 'furni-image scale-' + scale } style={ { backgroundImage: imageUrl, width: imageElement.width, height: imageElement.height } }></div>; return <Base classNames={ [ 'furni-image', `scale-${ scale }` ] } style={ { backgroundImage: imageUrl, width: imageElement.width, height: imageElement.height } } />;
} }

View File

@ -1,8 +0,0 @@
export interface FurniImageViewProps
{
type: string;
spriteId: number;
direction?: number;
extras?: string;
scale?: number;
}