Add pet name approval

This commit is contained in:
Bill 2021-05-23 11:43:23 -04:00
parent 058f8e6dcc
commit f56164e3c2
7 changed files with 141 additions and 12 deletions

View File

@ -9,4 +9,5 @@ export class CatalogEvent extends NitroEvent
public static PURCHASE_FAILED: string = 'CE_PURCHASE_FAILED'; public static PURCHASE_FAILED: string = 'CE_PURCHASE_FAILED';
public static SOLD_OUT: string = 'CE_SOLD_OUT'; public static SOLD_OUT: string = 'CE_SOLD_OUT';
public static APPROVE_NAME_RESULT: string = 'CE_APPROVE_NAME_RESULT'; public static APPROVE_NAME_RESULT: string = 'CE_APPROVE_NAME_RESULT';
public static PURCHASE_APPROVED: string = 'CE_PURCHASE_APPROVED';
} }

View File

@ -112,6 +112,31 @@ export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
return LocalizeText(`pet.breed.${ petIndex }.${ sellablePalettes[selectedPaletteIndex].breedId }`); return LocalizeText(`pet.breed.${ petIndex }.${ sellablePalettes[selectedPaletteIndex].breedId }`);
}, [ petIndex, sellablePalettes, selectedPaletteIndex ]); }, [ petIndex, sellablePalettes, selectedPaletteIndex ]);
const petPurchaseString = useMemo(() =>
{
if(!sellablePalettes.length || (selectedPaletteIndex === -1)) return '';
const paletteId = sellablePalettes[selectedPaletteIndex].paletteId;
let color = 0xFFFFFF;
if(petIndex <= 7)
{
if(selectedColorIndex === -1) return '';
color = sellableColors[selectedColorIndex][0];
}
let colorString = color.toString(16).toUpperCase();
while(colorString.length < 6)
{
colorString = ('0' + colorString);
}
return `${ paletteId }\n${ colorString }`;
}, [ sellablePalettes, selectedPaletteIndex, petIndex, sellableColors, selectedColorIndex ]);
if(!activeOffer) return null; if(!activeOffer) return null;
return ( return (
@ -143,7 +168,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
</button> } </button> }
</RoomPreviewerView> </RoomPreviewerView>
<div className="fs-6 text-black mt-1 overflow-hidden">{ petBreedName }</div> <div className="fs-6 text-black mt-1 overflow-hidden">{ petBreedName }</div>
<CatalogLayoutPetPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } /> <CatalogLayoutPetPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ petPurchaseString } />
</div> </div>
</div> </div>
); );

View File

@ -1,12 +1,61 @@
import { FC } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CatalogEvent, CatalogNameResultEvent } from '../../../../../../../events';
import { useUiEvent } from '../../../../../../../hooks/events/ui/ui-event';
import { LocalizeText } from '../../../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../../../utils/LocalizeText';
import { CatalogPetNameApprovalViewProps } from './CatalogPetNameApprovalView.types'; import { CatalogPetNameApprovalViewProps } from './CatalogPetNameApprovalView.types';
export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = props => export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = props =>
{ {
const { petNameValue = null, setPetNameValue = null } = props; const { petNameValue = null, setPetNameValue = null, nameApproved = false, setNameApproved = null } = props;
const [ validationResult, setValidationResult ] = useState(-1);
useEffect(() =>
{
setValidationResult(-1);
}, [ petNameValue ]);
const onCatalogNameResultEvent = useCallback((event: CatalogNameResultEvent) =>
{
if(event.result === 0)
{
setNameApproved(true);
return;
}
setValidationResult(event.result);
}, [ setNameApproved ]);
useUiEvent(CatalogEvent.APPROVE_NAME_RESULT, onCatalogNameResultEvent);
const validationErrorMessage = useMemo(() =>
{
let key: string = '';
switch(validationResult)
{
case 1:
key = 'catalog.alert.petname.long';
break;
case 2:
key = 'catalog.alert.petname.short';
break;
case 3:
key = 'catalog.alert.petname.chars';
break;
case 4:
key = 'catalog.alert.petname.bobba';
break;
}
return LocalizeText(key);
}, [ validationResult ]);
return ( return (
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('widgets.petpackage.name.title') } value={ petNameValue } onChange={ event => setPetNameValue(event.target.value) } /> <div className="input-group has-validation">
<input type="text" className={ 'form-control form-control-sm '+ ((validationResult > 0) ? 'is-invalid ' : '') } placeholder={ LocalizeText('widgets.petpackage.name.title') } value={ petNameValue } onChange={ event => setPetNameValue(event.target.value) } />
{ (validationResult > 0) &&
<div className="invalid-feedback">{ validationErrorMessage }</div> }
</div>
); );
} }

View File

@ -4,4 +4,6 @@ export interface CatalogPetNameApprovalViewProps
{ {
petNameValue: string; petNameValue: string;
setPetNameValue: Dispatch<SetStateAction<string>>; setPetNameValue: Dispatch<SetStateAction<string>>;
nameApproved: boolean;
setNameApproved: Dispatch<SetStateAction<boolean>>;
} }

View File

@ -1,4 +1,8 @@
import { FC, useState } from 'react'; import { ApproveNameMessageComposer } from 'nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { CatalogEvent } from '../../../../../../../events';
import { useUiEvent } from '../../../../../../../hooks/events/ui/ui-event';
import { SendMessageHook } from '../../../../../../../hooks/messages/message-event';
import { CurrencyIcon } from '../../../../../../../utils/currency-icon/CurrencyIcon'; import { CurrencyIcon } from '../../../../../../../utils/currency-icon/CurrencyIcon';
import { LocalizeText } from '../../../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../../../utils/LocalizeText';
import { CatalogPurchaseButtonView } from '../../../purchase/purchase-button/CatalogPurchaseButtonView'; import { CatalogPurchaseButtonView } from '../../../purchase/purchase-button/CatalogPurchaseButtonView';
@ -9,13 +13,31 @@ export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps>
{ {
const { offer = null, pageId = -1, extra = '' } = props; const { offer = null, pageId = -1, extra = '' } = props;
const [ petNameValue, setPetNameValue ] = useState(''); const [ petNameValue, setPetNameValue ] = useState('');
const [ nameApproved, setNameApproved ] = useState(false);
const extraData = ((extra && extra.length) ? extra : (offer?.products[0]?.extraParam || null)); const onCatalogEvent = useCallback((event: CatalogEvent) =>
{
switch(event.type)
{
case CatalogEvent.PURCHASE_SUCCESS:
setNameApproved(false);
return;
}
}, []);
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
const beforePurchase = useCallback(() =>
{
SendMessageHook(new ApproveNameMessageComposer(petNameValue, 1));
}, [ petNameValue ]);
const extraData = `${ petNameValue }\n${ extra }`;
return ( return (
<> <>
<div className="d-flex flex-grow-1 justify-content-center align-items-center"> <div className="d-flex flex-grow-1 justify-content-center align-items-center">
<CatalogPetNameApprovalView petNameValue={ petNameValue } setPetNameValue={ setPetNameValue } /> <CatalogPetNameApprovalView petNameValue={ petNameValue } setPetNameValue={ setPetNameValue } nameApproved={ nameApproved } setNameApproved={ setNameApproved } />
</div> </div>
<div className="d-flex flex-column flex-grow-1 justify-content-end w-100"> <div className="d-flex flex-column flex-grow-1 justify-content-end w-100">
<div className="d-flex align-items-end"> <div className="d-flex align-items-end">
@ -36,7 +58,7 @@ export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps>
</div> </div>
</div> </div>
<div className="d-flex flex-column mt-1"> <div className="d-flex flex-column mt-1">
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } /> <CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
</div> </div>
</div> </div>
</> </>

View File

@ -10,8 +10,9 @@ import { CatalogPurchaseButtonViewProps, CatalogPurchaseState } from './CatalogP
export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = props => export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = props =>
{ {
const { className = '', offer = null, pageId = -1, extra = null, quantity = 1 } = props; const { className = '', offer = null, pageId = -1, extra = null, quantity = 1, isPurchaseAllowed = true, beforePurchase = null } = props;
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
const [ pendingApproval, setPendingApproval ] = useState(false);
const onCatalogEvent = useCallback((event: CatalogEvent) => const onCatalogEvent = useCallback((event: CatalogEvent) =>
{ {
@ -31,16 +32,43 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
const purchase = useCallback(() => const purchase = useCallback(() =>
{ {
setPurchaseState(CatalogPurchaseState.PURCHASE);
SendMessageHook(new CatalogPurchaseComposer(pageId, offer.offerId, extra, quantity)); SendMessageHook(new CatalogPurchaseComposer(pageId, offer.offerId, extra, quantity));
}, [ pageId, offer, extra, quantity ]); }, [ pageId, offer, extra, quantity ]);
const attemptPurchase = useCallback(() =>
{
setPurchaseState(CatalogPurchaseState.PURCHASE);
if(beforePurchase) beforePurchase();
if(!isPurchaseAllowed)
{
setPendingApproval(true);
setPurchaseState(CatalogPurchaseState.NONE);
return;
}
purchase();
}, [ isPurchaseAllowed, beforePurchase, purchase ]);
useEffect(() => useEffect(() =>
{ {
setPurchaseState(CatalogPurchaseState.NONE); setPurchaseState(CatalogPurchaseState.NONE);
}, [ offer, quantity ]); }, [ offer, quantity ]);
useEffect(() =>
{
if(pendingApproval && isPurchaseAllowed)
{
setPendingApproval(false);
purchase();
return;
}
}, [ purchaseState, pendingApproval, isPurchaseAllowed, purchase ]);
const product = offer.products[0]; const product = offer.products[0];
if(product && product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft) if(product && product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft)
@ -61,7 +89,7 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
switch(purchaseState) switch(purchaseState)
{ {
case CatalogPurchaseState.CONFIRM: case CatalogPurchaseState.CONFIRM:
return <button type="button" className={ 'btn btn-warning ' + className } onClick={ purchase }>{ LocalizeText('catalog.marketplace.confirm_title') }</button>; return <button type="button" className={ 'btn btn-warning ' + className } onClick={ attemptPurchase }>{ LocalizeText('catalog.marketplace.confirm_title') }</button>;
case CatalogPurchaseState.PURCHASE: case CatalogPurchaseState.PURCHASE:
return <button type="button" className={ 'btn btn-primary ' + className } disabled><LoadingSpinnerView /></button>; return <button type="button" className={ 'btn btn-primary ' + className } disabled><LoadingSpinnerView /></button>;
case CatalogPurchaseState.SOLD_OUT: case CatalogPurchaseState.SOLD_OUT:

View File

@ -7,6 +7,8 @@ export interface CatalogPurchaseButtonViewProps
pageId: number; pageId: number;
extra?: string; extra?: string;
quantity?: number; quantity?: number;
isPurchaseAllowed?: boolean;
beforePurchase?: () => void;
} }
export class CatalogPurchaseState export class CatalogPurchaseState