Update catalog widgets

This commit is contained in:
Bill 2022-01-20 00:22:53 -05:00
parent c5f8c3c276
commit 4c1130ba0b
9 changed files with 207 additions and 14 deletions

View File

@ -1,6 +1,7 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { BaseProps } from '../../../../../common/Base'; import { BaseProps } from '../../../../../common/Base';
import { CatalogSelectProductEvent } from '../../../../../events'; import { CatalogSelectProductEvent } from '../../../../../events';
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { useUiEvent } from '../../../../../hooks'; import { useUiEvent } from '../../../../../hooks';
import { BadgeImageView } from '../../../../../views/shared/badge-image/BadgeImageView'; import { BadgeImageView } from '../../../../../views/shared/badge-image/BadgeImageView';
@ -19,7 +20,7 @@ export const CatalogAddOnBadgeWidgetView: FC<CatalogAddOnBadgeWidgetViewProps> =
if(event.offer.badgeCode) setBadgeCode(event.offer.badgeCode); if(event.offer.badgeCode) setBadgeCode(event.offer.badgeCode);
}, []); }, []);
useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); useUiEvent(CatalogWidgetEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
if(!badgeCode || !badgeCode.length) return null; if(!badgeCode || !badgeCode.length) return null;

View File

@ -1,7 +1,8 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { Grid, GridProps } from '../../../../../common/Grid'; import { Grid, GridProps } from '../../../../../common/Grid';
import { LayoutGridItem } from '../../../../../common/layout/LayoutGridItem'; import { LayoutGridItem } from '../../../../../common/layout/LayoutGridItem';
import { CatalogPageReadyEvent, CatalogSelectProductEvent } from '../../../../../events'; import { CatalogPageReadyEvent, CatalogSelectProductEvent } from '../../../../../events';
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { dispatchUiEvent, useUiEvent } from '../../../../../hooks'; import { dispatchUiEvent, useUiEvent } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { useCatalogContext } from '../../../context/CatalogContext'; import { useCatalogContext } from '../../../context/CatalogContext';
@ -22,7 +23,7 @@ export const CatalogBundleGridWidgetView: FC<CatalogBundleGridWidgetViewProps> =
setOffer(event.offer); setOffer(event.offer);
}, []); }, []);
useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); useUiEvent(CatalogWidgetEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
const onCatalogPageReadyEvent = useCallback((event: CatalogPageReadyEvent) => const onCatalogPageReadyEvent = useCallback((event: CatalogPageReadyEvent) =>
{ {
@ -35,11 +36,16 @@ export const CatalogBundleGridWidgetView: FC<CatalogBundleGridWidgetViewProps> =
useUiEvent(CatalogPageReadyEvent.PAGE_READY, onCatalogPageReadyEvent); useUiEvent(CatalogPageReadyEvent.PAGE_READY, onCatalogPageReadyEvent);
useEffect(() =>
{
return () => setOffer(null);
}, [ currentPage ]);
if(!offer) return null; if(!offer) return null;
return ( return (
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }> <Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
{ offer.products && (offer.products.length > 0) && offer.products.map((product, index) => <LayoutGridItem key={ index } itemImage={ product.getIconUrl() } />) } { offer.products && (offer.products.length > 0) && offer.products.map((product, index) => <LayoutGridItem key={ index } itemImage={ product.getIconUrl() } itemCount={ product.productCount } />) }
{ children } { children }
</Grid> </Grid>
); );

View File

@ -0,0 +1,20 @@
import { FC, useCallback } from 'react';
import { CatalogPageReadyEvent, CatalogSelectProductEvent } from '../../../../../events';
import { dispatchUiEvent, useUiEvent } from '../../../../../hooks';
import { useCatalogContext } from '../../../context/CatalogContext';
export const CatalogFirstProductSelectorWidgetView: FC<{}> = props =>
{
const { currentPage = null } = useCatalogContext();
const onCatalogPageReadyEvent = useCallback((event: CatalogPageReadyEvent) =>
{
if(!currentPage || !currentPage.offers.length) return;
dispatchUiEvent(new CatalogSelectProductEvent(currentPage.offers[0]));
}, [ currentPage ]);
useUiEvent(CatalogPageReadyEvent.PAGE_READY, onCatalogPageReadyEvent);
return null;
}

View File

@ -1,5 +1,9 @@
import { FC } from 'react'; import { FC } from 'react';
import { Grid, GridProps } from '../../../../../common/Grid'; import { Grid, GridProps } from '../../../../../common/Grid';
import { CatalogSelectProductEvent, CatalogSetExtraPurchaseParameterEvent } from '../../../../../events';
import { dispatchUiEvent } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { ProductTypeEnum } from '../../../common/ProductTypeEnum';
import { useCatalogContext } from '../../../context/CatalogContext'; import { useCatalogContext } from '../../../context/CatalogContext';
import { CatalogGridOfferView } from '../offers/CatalogGridOfferView'; import { CatalogGridOfferView } from '../offers/CatalogGridOfferView';
@ -11,13 +15,28 @@ interface CatalogItemGridWidgetViewProps extends GridProps
export const CatalogItemGridWidgetView: FC<CatalogItemGridWidgetViewProps> = props => export const CatalogItemGridWidgetView: FC<CatalogItemGridWidgetViewProps> = props =>
{ {
const { children = null, ...rest } = props; const { children = null, ...rest } = props;
const { currentOffer = null, currentPage = null } = useCatalogContext(); const { currentOffer = null, setCurrentOffer = null, currentPage = null } = useCatalogContext();
if(!currentPage) return null; if(!currentPage) return null;
const selectOffer = (offer: IPurchasableOffer) =>
{
setCurrentOffer(offer);
dispatchUiEvent(new CatalogSelectProductEvent(offer));
if(offer.product && (offer.product.productType === ProductTypeEnum.WALL))
{
dispatchUiEvent(new CatalogSetExtraPurchaseParameterEvent(offer.product.extraParam));
}
}
return ( return (
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }> <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 } />) } { currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) =>
{
return <CatalogGridOfferView key={ index } itemActive={ (currentOffer === offer) } offer={ offer } onClick={ event => selectOffer(offer) } />;
}) }
{ children } { children }
</Grid> </Grid>
); );

View File

@ -17,7 +17,7 @@ export const CatalogPriceDisplayWidgetView: FC<CatalogPriceDisplayWidgetViewProp
if(!offer) return null; if(!offer) return null;
return ( return (
<Flex gap={ 1 }> <Flex gap={ 1 } className="bg-muted p-1 rounded">
{ (offer.priceInCredits > 0) && { (offer.priceInCredits > 0) &&
<Flex alignItems="center" justifyContent="end" gap={ 1 }> <Flex alignItems="center" justifyContent="end" gap={ 1 }>
<Text>{ offer.priceInCredits }</Text> <Text>{ offer.priceInCredits }</Text>

View File

@ -1,22 +1,123 @@
import { FC, useCallback } from 'react'; import { IObjectData } from '@nitrots/nitro-renderer';
import { CatalogSelectProductEvent } from '../../../../../events'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { LocalizeText } from '../../../../../api';
import { Button } from '../../../../../common/Button';
import { CatalogSelectProductEvent, CatalogSetExtraPurchaseParameterEvent } from '../../../../../events';
import { CatalogInitPurchaseEvent } from '../../../../../events/catalog/CatalogInitPurchaseEvent';
import { CatalogPurchaseOverrideEvent } from '../../../../../events/catalog/CatalogPurchaseOverrideEvent';
import { CatalogSetRoomPreviewerStuffDataEvent } from '../../../../../events/catalog/CatalogSetRoomPreviewerStuffDataEvent';
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { useUiEvent } from '../../../../../hooks'; import { useUiEvent } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { Offer } from '../../../common/Offer';
interface CatalogPurchaseWidgetViewProps interface CatalogPurchaseWidgetViewProps
{ {
noGiftOption?: boolean;
} }
export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = props => export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = props =>
{ {
const {} = props; const { noGiftOption = false } = props;
const [ offer, setOffer ] = useState<IPurchasableOffer>(null);
const [ quantity, setQuantity ] = useState(1);
const [ extraData, setExtraData ] = useState<string>('');
const [ extraParamRequired, setExtraParamRequired ] = useState(false);
const [ giftEnabled, setGiftEnabled ] = useState(false);
const [ purchaseCallback, setPurchaseCallback ] = useState<Function>(null);
const [ previewStuffData, setPreviewStuffData ] = useState<IObjectData>(null);
const [ purchaseWillBeGift, setPurchaseWillBeGift ] = useState(false);
const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) => const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) =>
{
setOffer(event.offer);
}, []);
useUiEvent(CatalogWidgetEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
const onCatalogSetExtraPurchaseParameterEvent = useCallback((event: CatalogSetExtraPurchaseParameterEvent) =>
{
setExtraData(event.parameter);
setGiftEnabled(offer && offer.giftable);
}, [ offer ]);
useUiEvent(CatalogWidgetEvent.SET_EXTRA_PARM, onCatalogSetExtraPurchaseParameterEvent);
const onCatalogPurchaseOverrideEvent = useCallback((event: CatalogPurchaseOverrideEvent) =>
{
setPurchaseCallback(event.callback);
}, []);
useUiEvent(CatalogWidgetEvent.PURCHASE_OVERRIDE, onCatalogPurchaseOverrideEvent);
const onCatalogInitPurchaseEvent = useCallback((event: CatalogInitPurchaseEvent) =>
{
if(!offer) return;
// show purchase confirmation
// offer, page.pageId, extraData, quantity, previewStuffData, null, true, null
}, [ offer ]);
useUiEvent(CatalogWidgetEvent.INIT_PURCHASE, onCatalogInitPurchaseEvent);
const onCatalogSetRoomPreviewerStuffDataEvent = useCallback((event: CatalogSetRoomPreviewerStuffDataEvent) =>
{
setPreviewStuffData(event.stuffData);
}, []);
useUiEvent(CatalogWidgetEvent.SET_PREVIEWER_STUFFDATA, onCatalogSetRoomPreviewerStuffDataEvent);
const onCatalogWidgetEvent = useCallback((event: CatalogWidgetEvent) =>
{
setExtraParamRequired(true);
}, []);
useUiEvent(CatalogWidgetEvent.EXTRA_PARAM_REQUIRED_FOR_BUY, onCatalogWidgetEvent);
const isLimitedSoldOut = useMemo(() =>
{
if(!offer) return false;
if(extraParamRequired && (!extraData || !extraData.length)) return false;
if(offer.pricingModel === Offer.PRICING_MODEL_SINGLE)
{
const product = offer.product;
if(product && product.isUniqueLimitedItem) return !product.uniqueLimitedItemsLeft;
}
return false;
}, [ offer, extraParamRequired, extraData ]);
const purchase = useCallback((isGift: boolean = false) =>
{ {
}, []); }, []);
useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); useEffect(() =>
{
setQuantity(1);
setPurchaseWillBeGift(false);
}, [ offer ]);
return null; if(!offer) return null;
const getPurchaseButton = () =>
{
const priceCredits = (offer.priceInCredits * quantity);
const pricePoints = (offer.priceInActivityPoints * quantity);
}
return (
<>
<Button disabled={ (isLimitedSoldOut || (extraParamRequired && (!extraData || !extraData.length))) }>
{ LocalizeText('catalog.purchase_confirmation.' + (offer.isRentOffer ? 'rent' : 'buy')) }
</Button>
{ (!noGiftOption && !offer.isRentOffer) &&
<Button disabled={ ((quantity > 1) || !offer.giftable || isLimitedSoldOut || (extraParamRequired && (!extraData || !extraData.length))) }>
{ LocalizeText('catalog.purchase_confirmation.gift') }
</Button> }
</>
);
} }

View File

@ -1,6 +1,7 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { BaseProps } from '../../../../../common/Base'; import { BaseProps } from '../../../../../common/Base';
import { CatalogSelectProductEvent } from '../../../../../events'; import { CatalogSelectProductEvent } from '../../../../../events';
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { useUiEvent } from '../../../../../hooks'; import { useUiEvent } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView';
@ -20,7 +21,7 @@ export const CatalogSimplePriceWidgetView: FC<CatalogSimplePriceWidgetViewProps>
setOffer(event.offer); setOffer(event.offer);
}, []); }, []);
useUiEvent(CatalogSelectProductEvent.SELECT_PRODUCT, onCatalogSelectProductEvent); useUiEvent(CatalogWidgetEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
return <CatalogPriceDisplayWidgetView offer={ offer } { ...rest } />; return <CatalogPriceDisplayWidgetView offer={ offer } { ...rest } />;
} }

View File

@ -0,0 +1,7 @@
import { FC } from 'react';
import { CatalogFirstProductSelectorWidgetView } from './CatalogFirstProductSelectorWidgetView';
export const CatalogSingleViewWidgetView: FC<{}> = props =>
{
return <CatalogFirstProductSelectorWidgetView />;
}

View File

@ -0,0 +1,38 @@
import { IObjectData } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { CatalogSelectProductEvent } from '../../../../../events';
import { CatalogSetRoomPreviewerStuffDataEvent } from '../../../../../events/catalog/CatalogSetRoomPreviewerStuffDataEvent';
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { BatchUpdates, useUiEvent } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { useCatalogContext } from '../../../context/CatalogContext';
export const CatalogViewProductWidgetView: FC<{}> = props =>
{
const [ selectedProductEvent, setSelectedProductEvent ] = useState<CatalogSelectProductEvent>(null);
const [ offer, setOffer ] = useState<IPurchasableOffer>(null);
const [ stuffData, setStuffData ] = useState<IObjectData>(null);
const { roomPreviewer = null } = useCatalogContext();
const onCatalogSelectProductEvent = useCallback((event: CatalogSelectProductEvent) =>
{
BatchUpdates(() =>
{
setSelectedProductEvent(event);
setOffer(event.offer);
})
}, []);
useUiEvent(CatalogWidgetEvent.SELECT_PRODUCT, onCatalogSelectProductEvent);
const onCatalogSetRoomPreviewerStuffDataEvent = useCallback((event: CatalogSetRoomPreviewerStuffDataEvent) =>
{
setStuffData(event.stuffData);
if(roomPreviewer) roomPreviewer.reset(false);
}, [ roomPreviewer ]);
useUiEvent(CatalogWidgetEvent.SET_PREVIEWER_STUFFDATA, onCatalogSetRoomPreviewerStuffDataEvent);
return null;
}