mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-01-18 13:26:27 +01:00
Gifts
This commit is contained in:
parent
bdc3211fce
commit
52e9e3208a
BIN
src/assets/images/catalog/gift/gift_tag.png
Normal file
BIN
src/assets/images/catalog/gift/gift_tag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 795 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -10,5 +10,6 @@ export class CatalogEvent extends NitroEvent
|
||||
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';
|
||||
public static INIT_GIFT: string = 'CE_INIT_GIFT';
|
||||
public static CATALOG_RESET: string = 'CE_RESET';
|
||||
}
|
||||
|
32
src/events/catalog/CatalogInitGiftEvent.ts
Normal file
32
src/events/catalog/CatalogInitGiftEvent.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { CatalogEvent } from './CatalogEvent';
|
||||
|
||||
export class CatalogInitGiftEvent extends CatalogEvent
|
||||
{
|
||||
private _pageId: number;
|
||||
private _offerId: number;
|
||||
private _extraData: string;
|
||||
|
||||
constructor(pageId: number, offerId: number, extraData: string)
|
||||
{
|
||||
super(CatalogEvent.INIT_GIFT);
|
||||
|
||||
this._pageId = pageId;
|
||||
this._offerId = offerId;
|
||||
this._extraData = extraData;
|
||||
}
|
||||
|
||||
public get pageId(): number
|
||||
{
|
||||
return this._pageId;
|
||||
}
|
||||
|
||||
public get offerId(): number
|
||||
{
|
||||
return this._offerId;
|
||||
}
|
||||
|
||||
public get extraData(): string
|
||||
{
|
||||
return this._extraData;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import { CatalogMode, CatalogViewProps } from './CatalogView.types';
|
||||
import { BuildCatalogPageTree } from './common/CatalogUtilities';
|
||||
import { CatalogContextProvider } from './context/CatalogContext';
|
||||
import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
|
||||
import { CatalogPageGiftView } from './views/gift/CatalogPageGiftView';
|
||||
import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView';
|
||||
import { CatalogPageView } from './views/page/CatalogPageView';
|
||||
|
||||
@ -213,6 +214,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView> }
|
||||
<CatalogPageGiftView />
|
||||
</CatalogContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -2,3 +2,4 @@
|
||||
@import './navigation/CatalogNavigationView';
|
||||
@import './page/CatalogPageView';
|
||||
@import './search/CatalogSearchView';
|
||||
@import './gift/CatalogPageGiftView';
|
||||
|
56
src/views/catalog/views/gift/CatalogPageGiftView.scss
Normal file
56
src/views/catalog/views/gift/CatalogPageGiftView.scss
Normal file
@ -0,0 +1,56 @@
|
||||
.nitro-catalog-gift {
|
||||
|
||||
.gift-tag {
|
||||
width: 306px;
|
||||
height: 159px;
|
||||
background: url(../../../../assets/images/catalog/gift/gift_tag.png) center no-repeat;
|
||||
}
|
||||
|
||||
.gift-face {
|
||||
width: 65px;
|
||||
|
||||
.gift-incognito {
|
||||
width: 37px;
|
||||
height: 48px;
|
||||
background: url(../../../../assets/images/catalog/gift/incognito.png) center no-repeat;
|
||||
}
|
||||
|
||||
.gift-avatar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 40px;
|
||||
height: 50px;
|
||||
|
||||
.avatar-image {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: -20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gift-message {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
height: 90px;
|
||||
min-height: 90px;
|
||||
max-height: 90px;
|
||||
border: none;
|
||||
resize: none;
|
||||
outline: none;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.gift-preview {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gift-color {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
}
|
227
src/views/catalog/views/gift/CatalogPageGiftView.tsx
Normal file
227
src/views/catalog/views/gift/CatalogPageGiftView.tsx
Normal file
@ -0,0 +1,227 @@
|
||||
import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer';
|
||||
import classNames from 'classnames';
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||
import { CatalogEvent } from '../../../../events';
|
||||
import { CatalogInitGiftEvent } from '../../../../events/catalog/CatalogInitGiftEvent';
|
||||
import { SendMessageHook, useUiEvent } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
|
||||
import { FurniImageView } from '../../../shared/furni-image/FurniImageView';
|
||||
import { useCatalogContext } from '../../context/CatalogContext';
|
||||
|
||||
export const CatalogPageGiftView: FC<{}> = props =>
|
||||
{
|
||||
const { catalogState = null } = useCatalogContext();
|
||||
const { giftConfiguration = null } = catalogState;
|
||||
|
||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||
const [ pageId, setPageId ] = useState<number>(0);
|
||||
const [ offerId, setOfferId ] = useState<number>(0);
|
||||
|
||||
const [ receiverName, setReceiverName ] = useState<string>('');
|
||||
const [ showMyFace, setShowMyFace ] = useState<boolean>(true);
|
||||
const [ message, setMessage ] = useState<string>('');
|
||||
const [ colors, setColors ] = useState<{ id: number, color: string }[]>([]);
|
||||
const [ selectedBoxIndex, setSelectedBoxIndex ] = useState<number>(0);
|
||||
const [ selectedRibbonIndex, setSelectedRibbonIndex ] = useState<number>(0);
|
||||
const [ selectedColorId, setSelectedColorId ] = useState<number>(0);
|
||||
const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0);
|
||||
const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!giftConfiguration) return;
|
||||
|
||||
setMaxBoxIndex(giftConfiguration.boxTypes.length - 1);
|
||||
setMaxRibbonIndex(giftConfiguration.ribbonTypes.length - 1);
|
||||
|
||||
const newColors: { id: number, color: string }[] = [];
|
||||
|
||||
for(const colorId of giftConfiguration.stuffTypes)
|
||||
{
|
||||
const giftData = GetSessionDataManager().getFloorItemData(colorId);
|
||||
|
||||
if(!giftData) continue;
|
||||
|
||||
if(giftData.colors && giftData.colors.length > 0) newColors.push({ id: colorId, color: `#${giftData.colors[0].toString(16)}` });
|
||||
}
|
||||
|
||||
setSelectedColorId(newColors[0].id);
|
||||
setColors(newColors);
|
||||
}, [ giftConfiguration ]);
|
||||
|
||||
const close = useCallback(() =>
|
||||
{
|
||||
setIsVisible(false);
|
||||
setPageId(0);
|
||||
setOfferId(0);
|
||||
setReceiverName('');
|
||||
setShowMyFace(true);
|
||||
setMessage('');
|
||||
setSelectedBoxIndex(0);
|
||||
setSelectedRibbonIndex(0);
|
||||
setSelectedColorId(colors[0].id);
|
||||
}, [ colors ]);
|
||||
|
||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case CatalogEvent.PURCHASE_SUCCESS:
|
||||
close();
|
||||
return;
|
||||
case CatalogEvent.INIT_GIFT:
|
||||
const castedEvent = (event as CatalogInitGiftEvent);
|
||||
close();
|
||||
|
||||
setPageId(castedEvent.pageId);
|
||||
setOfferId(castedEvent.offerId);
|
||||
setIsVisible(true);
|
||||
return;
|
||||
}
|
||||
}, [ close ]);
|
||||
|
||||
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
|
||||
useUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent);
|
||||
|
||||
const isBoxDefault = useMemo(() =>
|
||||
{
|
||||
return giftConfiguration ? giftConfiguration.defaultStuffTypes.findIndex(s => s === giftConfiguration.boxTypes[selectedBoxIndex]) > -1 : true;
|
||||
}, [ giftConfiguration, selectedBoxIndex ]);
|
||||
|
||||
const boxName = useMemo(() =>
|
||||
{
|
||||
return isBoxDefault ? 'catalog.gift_wrapping_new.box.default' : `catalog.gift_wrapping_new.box.${selectedBoxIndex}`;
|
||||
}, [ isBoxDefault, selectedBoxIndex ]);
|
||||
|
||||
const ribbonName = useMemo(() =>
|
||||
{
|
||||
return `catalog.gift_wrapping_new.ribbon.${selectedRibbonIndex}`;
|
||||
}, [ selectedRibbonIndex ]);
|
||||
|
||||
const priceText = useMemo(() =>
|
||||
{
|
||||
return isBoxDefault ? 'catalog.gift_wrapping_new.freeprice' : 'catalog.gift_wrapping_new.price';
|
||||
}, [ isBoxDefault ]);
|
||||
|
||||
const extraData = useMemo(() =>
|
||||
{
|
||||
if(!giftConfiguration) return '';
|
||||
|
||||
return ((giftConfiguration.boxTypes[selectedBoxIndex] * 1000) + giftConfiguration.ribbonTypes[selectedRibbonIndex]).toString();
|
||||
}, [ giftConfiguration, selectedBoxIndex, selectedRibbonIndex ]);
|
||||
|
||||
const isColorable = useMemo(() =>
|
||||
{
|
||||
if(!giftConfiguration) return false;
|
||||
|
||||
const boxType = giftConfiguration.boxTypes[selectedBoxIndex];
|
||||
|
||||
return (boxType === 8 || (boxType >= 3 && boxType <= 6)) ? false : true;
|
||||
}, [ giftConfiguration, selectedBoxIndex ]);
|
||||
|
||||
const handleAction = useCallback((action: string) =>
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case 'prev_box':
|
||||
setSelectedBoxIndex(value =>
|
||||
{
|
||||
return (value === 0 ? maxBoxIndex : value - 1);
|
||||
});
|
||||
return;
|
||||
case 'next_box':
|
||||
setSelectedBoxIndex(value =>
|
||||
{
|
||||
return (value === maxBoxIndex ? 0 : value + 1);
|
||||
});
|
||||
return;
|
||||
case 'prev_ribbon':
|
||||
setSelectedRibbonIndex(value =>
|
||||
{
|
||||
return (value === 0 ? maxRibbonIndex : value - 1);
|
||||
});
|
||||
return;
|
||||
case 'next_ribbon':
|
||||
setSelectedRibbonIndex(value =>
|
||||
{
|
||||
return (value === maxRibbonIndex ? 0 : value + 1);
|
||||
});
|
||||
return;
|
||||
case 'buy':
|
||||
SendMessageHook(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, selectedColorId, selectedBoxIndex, selectedRibbonIndex, !showMyFace));
|
||||
return;
|
||||
}
|
||||
}, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]);
|
||||
|
||||
if(!giftConfiguration || !giftConfiguration.isEnabled || !isVisible) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView uniqueKey="catalog-gift" className="nitro-catalog-gift" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.gift_wrapping.title') } onCloseClick={ close } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<div className="form-group">
|
||||
<label>{ LocalizeText('catalog.gift_wrapping.receiver') }</label>
|
||||
<input type="text" className="form-control form-control-sm" value={ receiverName } onChange={ (e) => setReceiverName(e.target.value) } />
|
||||
</div>
|
||||
<div className="gift-tag d-flex mt-2">
|
||||
<div className="d-flex align-items-center justify-content-center gift-face flex-shrink-0">
|
||||
{ !showMyFace && <div className="gift-incognito"></div> }
|
||||
{ showMyFace && <div className="gift-avatar">
|
||||
<AvatarImageView figure={ GetSessionDataManager().figure } direction={ 2 } headOnly={ true } />
|
||||
</div> }
|
||||
</div>
|
||||
<div className="d-flex flex-column w-100 pt-4 pb-4 pe-4 ps-3">
|
||||
<textarea className="gift-message" maxLength={ 140 } value={ message } onChange={ (e) => setMessage(e.target.value) } placeholder={ LocalizeText('catalog.gift_wrapping_new.message_hint') }></textarea>
|
||||
{ showMyFace && <div className="mt-auto text-end fst-italic">{ LocalizeText('catalog.gift_wrapping_new.message_from', ['name'], [GetSessionDataManager().userName]) }</div> }
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-check mt-1">
|
||||
<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>
|
||||
</div>
|
||||
<div className="d-flex gap-2 mt-1 align-items-center">
|
||||
<div className="gift-preview">
|
||||
{ selectedColorId && <FurniImageView spriteId={ selectedColorId } type="s" extras={ extraData } /> }
|
||||
</div>
|
||||
<div className="d-flex flex-column gap-2">
|
||||
<div className="d-flex gap-2">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-primary" onClick={ () => handleAction('prev_box') }><i className="fas fa-chevron-left" /></button>
|
||||
<button className="btn btn-primary" onClick={ () => handleAction('next_box') }><i className="fas fa-chevron-right" /></button>
|
||||
</div>
|
||||
<div>
|
||||
<div className="fw-bold">{ LocalizeText(boxName) }</div>
|
||||
<div className="d-flex gap-1">{ LocalizeText(priceText, ['price'], [giftConfiguration.price.toString()]) }<CurrencyIcon type={ -1 } /></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex gap-2 align-items-center">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-primary" onClick={ () => handleAction('prev_ribbon') }><i className="fas fa-chevron-left" /></button>
|
||||
<button className="btn btn-primary" onClick={ () => handleAction('next_ribbon') }><i className="fas fa-chevron-right" /></button>
|
||||
</div>
|
||||
<div>
|
||||
<div className="fw-bold">{ LocalizeText(ribbonName) }</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<div className="fw-bold">{ LocalizeText('catalog.gift_wrapping.pick_color') }</div>
|
||||
<div className="btn-group w-100">
|
||||
{ colors.map(color =>
|
||||
{
|
||||
return <button key={ color.id } className={ 'btn btn-dark btn-sm' + classNames({ ' active': color.id === selectedColorId }) } disabled={ !isColorable } style={{ backgroundColor: color.color }} onClick={ () => setSelectedColorId(color.id) }></button>
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center mt-2">
|
||||
<div className="text-decoration-underline cursor-pointer" onClick={ close }>{ LocalizeText('cancel') }</div>
|
||||
<button className="btn btn-success" onClick={ () => handleAction('buy') }>{ LocalizeText('catalog.gift_wrapping.give_gift') }</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
@ -36,7 +36,6 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
|
||||
|
||||
const purchase = useCallback(() =>
|
||||
{
|
||||
console.log(pageId, offer.offerId, extra, quantity);
|
||||
SendMessageHook(new PurchaseFromCatalogComposer(pageId, offer.offerId, extra, quantity));
|
||||
}, [ pageId, offer, extra, quantity ]);
|
||||
|
||||
|
@ -1,12 +1,19 @@
|
||||
import { FC } from 'react';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { LocalizeText } from '../../../../../../api';
|
||||
import { CatalogInitGiftEvent } from '../../../../../../events/catalog/CatalogInitGiftEvent';
|
||||
import { dispatchUiEvent } from '../../../../../../hooks';
|
||||
import { CatalogPurchaseGiftButtonViewProps } from './CatalogPurchaseGiftButtonView.types';
|
||||
|
||||
export const CatalogPurchaseGiftButtonView: FC<CatalogPurchaseGiftButtonViewProps> = props =>
|
||||
{
|
||||
const { className = '', offer = null, pageId = -1, extra = null, quantity = 1, isPurchaseAllowed = true, beforePurchase = null } = props;
|
||||
|
||||
const initGift = useCallback(() =>
|
||||
{
|
||||
dispatchUiEvent(new CatalogInitGiftEvent(pageId, offer.offerId, extra));
|
||||
}, [ extra, offer, pageId ]);
|
||||
|
||||
return (
|
||||
<button type="button" className={ 'btn btn-secondary ' + className }>{ LocalizeText('catalog.purchase_confirmation.gift') }</button>
|
||||
<button type="button" className={ 'btn btn-secondary ' + className } onClick={ initGift }>{ LocalizeText('catalog.purchase_confirmation.gift') }</button>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
.nitro-currency-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export const FurniImageView: FC<FurniImageViewProps> = props =>
|
||||
if(imageResult)
|
||||
{
|
||||
const image = imageResult.getImage();
|
||||
|
||||
|
||||
image.onload = () => setImageElement(image);
|
||||
}
|
||||
}, [ type, spriteId, direction, extras ]);
|
||||
|
Loading…
Reference in New Issue
Block a user