diff --git a/src/events/catalog/CatalogEvent.ts b/src/events/catalog/CatalogEvent.ts index 1ef36a23..c65d95ca 100644 --- a/src/events/catalog/CatalogEvent.ts +++ b/src/events/catalog/CatalogEvent.ts @@ -9,4 +9,5 @@ export class CatalogEvent extends NitroEvent public static PURCHASE_FAILED: string = 'CE_PURCHASE_FAILED'; public static SOLD_OUT: string = 'CE_SOLD_OUT'; public static APPROVE_NAME_RESULT: string = 'CE_APPROVE_NAME_RESULT'; + public static PURCHASE_APPROVED: string = 'CE_PURCHASE_APPROVED'; } diff --git a/src/views/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx b/src/views/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx index 5d2c0c51..40df3770 100644 --- a/src/views/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx +++ b/src/views/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx @@ -112,6 +112,31 @@ export const CatalogLayoutPetView: FC = props => return LocalizeText(`pet.breed.${ petIndex }.${ sellablePalettes[selectedPaletteIndex].breedId }`); }, [ 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; return ( @@ -143,7 +168,7 @@ export const CatalogLayoutPetView: FC = props => }
{ petBreedName }
- + ); diff --git a/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.tsx b/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.tsx index bc2cda44..b8cf2f6b 100644 --- a/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.tsx +++ b/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.tsx @@ -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 { CatalogPetNameApprovalViewProps } from './CatalogPetNameApprovalView.types'; export const CatalogPetNameApprovalView: FC = 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 ( - setPetNameValue(event.target.value) } /> +
+ 0) ? 'is-invalid ' : '') } placeholder={ LocalizeText('widgets.petpackage.name.title') } value={ petNameValue } onChange={ event => setPetNameValue(event.target.value) } /> + { (validationResult > 0) && +
{ validationErrorMessage }
} +
); } diff --git a/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.types.ts b/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.types.ts index b946d758..0df0c181 100644 --- a/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.types.ts +++ b/src/views/catalog/views/page/layout/pets/name-approval/CatalogPetNameApprovalView.types.ts @@ -4,4 +4,6 @@ export interface CatalogPetNameApprovalViewProps { petNameValue: string; setPetNameValue: Dispatch>; + nameApproved: boolean; + setNameApproved: Dispatch>; } diff --git a/src/views/catalog/views/page/layout/pets/purchase/CatalogLayoutPetPurchaseView.tsx b/src/views/catalog/views/page/layout/pets/purchase/CatalogLayoutPetPurchaseView.tsx index b455a61c..a6ceab62 100644 --- a/src/views/catalog/views/page/layout/pets/purchase/CatalogLayoutPetPurchaseView.tsx +++ b/src/views/catalog/views/page/layout/pets/purchase/CatalogLayoutPetPurchaseView.tsx @@ -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 { LocalizeText } from '../../../../../../../utils/LocalizeText'; import { CatalogPurchaseButtonView } from '../../../purchase/purchase-button/CatalogPurchaseButtonView'; @@ -9,13 +13,31 @@ export const CatalogLayoutPetPurchaseView: FC { const { offer = null, pageId = -1, extra = '' } = props; 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 ( <>
- +
@@ -36,7 +58,7 @@ export const CatalogLayoutPetPurchaseView: FC
- +
diff --git a/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.tsx b/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.tsx index 74bb10b2..25598d34 100644 --- a/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.tsx +++ b/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.tsx @@ -10,8 +10,9 @@ import { CatalogPurchaseButtonViewProps, CatalogPurchaseState } from './CatalogP export const CatalogPurchaseButtonView: FC = 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 [ pendingApproval, setPendingApproval ] = useState(false); const onCatalogEvent = useCallback((event: CatalogEvent) => { @@ -31,16 +32,43 @@ export const CatalogPurchaseButtonView: FC = pro const purchase = useCallback(() => { - setPurchaseState(CatalogPurchaseState.PURCHASE); - SendMessageHook(new CatalogPurchaseComposer(pageId, offer.offerId, 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(() => { setPurchaseState(CatalogPurchaseState.NONE); }, [ offer, quantity ]); + useEffect(() => + { + if(pendingApproval && isPurchaseAllowed) + { + setPendingApproval(false); + + purchase(); + + return; + } + }, [ purchaseState, pendingApproval, isPurchaseAllowed, purchase ]); + const product = offer.products[0]; if(product && product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft) @@ -61,7 +89,7 @@ export const CatalogPurchaseButtonView: FC = pro switch(purchaseState) { case CatalogPurchaseState.CONFIRM: - return ; + return ; case CatalogPurchaseState.PURCHASE: return ; case CatalogPurchaseState.SOLD_OUT: diff --git a/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.types.ts b/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.types.ts index 05cf20b5..0abc0ef3 100644 --- a/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.types.ts +++ b/src/views/catalog/views/page/purchase/purchase-button/CatalogPurchaseButtonView.types.ts @@ -7,6 +7,8 @@ export interface CatalogPurchaseButtonViewProps pageId: number; extra?: string; quantity?: number; + isPurchaseAllowed?: boolean; + beforePurchase?: () => void; } export class CatalogPurchaseState