targeted offers (#89)

* targeted offers

* remove comments
This commit is contained in:
Layne 2022-12-14 00:11:37 -05:00 committed by GitHub
parent 9f46f75a1e
commit fc6852ee2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 170 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,18 +1,23 @@
import { FC, useMemo } from 'react';
import { Flex, FlexProps } from '..';
export const NitroCardSubHeaderView: FC<FlexProps> = props =>
interface NitroCardSubHeaderProps extends FlexProps {
variant?: string;
}
export const NitroCardSubHeaderView: FC<NitroCardSubHeaderProps> = props =>
{
const { justifyContent = 'center', classNames = [], ...rest } = props;
const { justifyContent = 'center', classNames = [], variant = 'muted', ...rest } = props;
const getClassNames = useMemo(() =>
{
const newClassNames: string[] = [ 'container-fluid', 'bg-muted', 'p-1' ];
const newClassNames: string[] = [ 'container-fluid', 'p-1' ];
if(classNames.length) newClassNames.push(...classNames);
newClassNames.push('bg-' + variant);
return newClassNames;
}, [ classNames ]);
}, [ classNames, variant ]);
return <Flex justifyContent={ justifyContent } classNames={ getClassNames } { ...rest } />;
}

View File

@ -126,3 +126,5 @@
width: 290px;
height: 60px;
}
@import "./views/targeted-offer/Offer.scss";

View File

@ -0,0 +1,27 @@
.nitro-targeted-offer {
width: 500px;
height: 350px;
a {
color: orangered;
}
.price-ray {
bottom: 20px;
left: 40px;
right: 0;
margin: auto;
width: 136px;
height: 136px;
padding: 10px;
background-image: url("../../../../assets/images/catalog/target-price.png");
font-size: 18px;
}
}
.nitro-targeted-offer-icon {
height: 40px;
width: 40px;
background-position: center;
background-repeat: no-repeat;
}

View File

@ -0,0 +1,16 @@
import { TargetedOfferData } from '@nitrots/nitro-renderer';
import { Dispatch, SetStateAction } from 'react';
import { GetConfiguration } from '../../../../api';
import { Base, LayoutNotificationBubbleView, Text } from '../../../../common';
export const OfferBubbleView = (props: { offer: TargetedOfferData, setOpen: Dispatch<SetStateAction<boolean>> }) =>
{
const { offer = null, setOpen = null } = props;
if (!offer) return;
return <LayoutNotificationBubbleView fadesOut={ false } onClose={ null } onClick={ evt => setOpen(true) } gap={ 2 }>
<Base className="nitro-targeted-offer-icon" style={ { backgroundImage: `url(${ GetConfiguration('image.library.url') + offer._Str_13452 })` } }/>
<Text variant="light" className="ubuntu-bold">{ offer.title }</Text>
</LayoutNotificationBubbleView>;
}

View File

@ -0,0 +1,32 @@
import { GetTargetedOfferComposer, TargetedOfferData, TargetedOfferEvent } from '@nitrots/nitro-renderer';
import { useState } from 'react';
import { SendMessageComposer } from '../../../../api';
import { useMessageEvent, UseMountEffect } from '../../../../hooks';
import { OfferBubbleView } from './OfferBubbleView';
import { OfferWindowView } from './OfferWindowView';
export const OfferView = () =>
{
const [ offer, setOffer ] = useState<TargetedOfferData>(null);
const [ opened, setOpened ] = useState<boolean>(false);
useMessageEvent<TargetedOfferEvent>(TargetedOfferEvent, evt =>
{
let parser = evt.getParser();
if (!parser) return;
setOffer(parser.data);
});
UseMountEffect(() =>
{
SendMessageComposer(new GetTargetedOfferComposer());
})
if (!offer) return;
return <>
{ opened ? <OfferWindowView offer={ offer } setOpen={ setOpened } /> : <OfferBubbleView offer={ offer } setOpen={ setOpened } /> }
</>
}

View File

@ -0,0 +1,82 @@
import { FriendlyTime, GetTargetedOfferComposer, PurchaseTargetedOfferComposer, TargetedOfferData } from '@nitrots/nitro-renderer';
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
import { GetConfiguration, LocalizeText, SendMessageComposer } from '../../../../api';
import { Base, Button, Column, Flex, LayoutCurrencyIcon, NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, Text } from '../../../../common';
import { usePurse } from '../../../../hooks';
export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Dispatch<SetStateAction<boolean>> }) =>
{
const { offer = null, setOpen = null } = props;
const { getCurrencyAmount } = usePurse();
const [ amount, setAmount ] = useState<number>(1);
const canPurchase = useMemo(() =>
{
let credits = false;
let points = false;
let limit = false;
if (offer.priceInCredits > 0) credits = getCurrencyAmount(-1) >= offer.priceInCredits;
if (offer.priceInActivityPoints > 0) points = getCurrencyAmount(offer.activityPointType) >= offer.priceInActivityPoints;
else points = true;
if (offer.purchaseLimit > 0) limit = true;
return (credits && points && limit);
},[ offer,getCurrencyAmount ])
const expirationTime = () =>
{
let expirationTime = Math.max(0, (offer.expirationTime - Date.now() ) / 1000)
return FriendlyTime.format(expirationTime);
}
const buyOffer = () =>
{
SendMessageComposer(new PurchaseTargetedOfferComposer(offer.id, amount));
SendMessageComposer(new GetTargetedOfferComposer());
}
if (!offer) return;
return <NitroCardView theme="primary-slim" uniqueKey="targeted-offer" className="nitro-targeted-offer">
<NitroCardHeaderView headerText={ LocalizeText(offer.title) } onCloseClick={ event => setOpen(false) } />
<NitroCardSubHeaderView position="relative" className="justify-content-center align-items-center cursor-pointer" variant="danger" gap={ 3 }>
{ LocalizeText('targeted.offer.timeleft',[ 'timeleft' ],[ expirationTime() ]) }
</NitroCardSubHeaderView>
<NitroCardContentView gap={ 1 }>
<Flex gap={ 1 } fullHeight>
<Flex gap={ 1 } column className="w-75 text-black">
<Column className="bg-warning p-2" fullHeight>
<h4>
{ LocalizeText(offer.title) }
</h4>
<Base dangerouslySetInnerHTML={ { __html: offer.description } }/>
</Column>
<Flex alignSelf="center" alignItems="center" justifyContent="center" gap={ 2 }>
{ offer.purchaseLimit > 1 && <Flex gap={ 1 }>
<Text variant="muted">{ LocalizeText('catalog.bundlewidget.quantity') }</Text>
<input type="number" value={ amount } onChange={ evt => setAmount(parseInt(evt.target.value)) } min={ 1 } max={ offer.purchaseLimit } />
</Flex> }
<Button variant="primary" disabled={ !canPurchase } onClick={ () => buyOffer() }>{ LocalizeText('targeted.offer.button.buy') }</Button>
</Flex>
</Flex>
<Base className="w-50" fullHeight style={ { background: `url(${ GetConfiguration('image.library.url') + offer.imageUrl }) no-repeat center` } } />
</Flex>
<Flex className="price-ray position-absolute" alignItems="center" justifyContent="center" column>
<Text>{ LocalizeText('targeted.offer.price.label') }</Text>
{ offer.priceInCredits > 0 && <Flex gap={ 1 }>
<Text variant="light">{ offer.priceInCredits }</Text>
<LayoutCurrencyIcon type={ -1 } />
</Flex> }
{ offer.priceInActivityPoints > 0 && <Flex gap={ 1 }>
<Text className="ubuntu-bold" variant="light">+{ offer.priceInActivityPoints }</Text> <LayoutCurrencyIcon type={ offer.activityPointType }/>
</Flex> }
</Flex>
</NitroCardContentView>
</NitroCardView>;
}

View File

@ -1,5 +1,6 @@
import { FC } from 'react';
import { Column } from '../../common';
import { OfferView } from '../catalog/views/targeted-offer/OfferView';
import { GroupRoomInformationView } from '../groups/views/GroupRoomInformationView';
import { NotificationCenterView } from '../notification-center/NotificationCenterView';
import { PurseView } from '../purse/PurseView';
@ -12,6 +13,7 @@ export const RightSideView: FC<{}> = props =>
<Column position="relative" gap={ 1 }>
<PurseView />
<GroupRoomInformationView />
<OfferView/>
<RoomPromotesWidgetView />
<NotificationCenterView />
</Column>