Purse updates

This commit is contained in:
Bill 2022-02-21 13:26:19 -05:00
parent 410fffc9be
commit 45179a8eda
24 changed files with 219 additions and 273 deletions

View File

@ -13,9 +13,9 @@ import { CatalogEvent } from '../../../../../events/catalog/CatalogEvent';
import { useUiEvent } from '../../../../../hooks';
import { SendMessageHook } from '../../../../../hooks/messages/message-event';
import { LoadingSpinnerView } from '../../../../../layout/loading-spinner/LoadingSpinnerView';
import { GetCurrencyAmount } from '../../../../../views/purse/common/CurrencyHelper';
import { GLOBAL_PURSE } from '../../../../../views/purse/PurseView';
import { CurrencyIcon } from '../../../../../views/shared/currency-icon/CurrencyIcon';
import { GetCurrencyAmount } from '../../../../purse/common/CurrencyHelper';
import { GLOBAL_PURSE } from '../../../../purse/PurseView';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
import { CatalogLayoutProps } from './CatalogLayout.types';

View File

@ -9,7 +9,7 @@ import { Text } from '../../../../../../common/Text';
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../hooks';
import { NotificationAlertType } from '../../../../../../views/notification-center/common/NotificationAlertType';
import { NotificationUtilities } from '../../../../../../views/notification-center/common/NotificationUtilities';
import { GetCurrencyAmount } from '../../../../../../views/purse/common/CurrencyHelper';
import { GetCurrencyAmount } from '../../../../../purse/common/CurrencyHelper';
import { CatalogLayoutProps } from '../CatalogLayout.types';
import { CatalogLayoutMarketplaceItemView, PUBLIC_OFFER } from './CatalogLayoutMarketplaceItemView';
import { SearchFormView } from './CatalogLayoutMarketplaceSearchFormView';

View File

@ -8,7 +8,7 @@ import { CatalogInitPurchaseEvent } from '../../../../../events/catalog/CatalogI
import { CatalogWidgetEvent } from '../../../../../events/catalog/CatalogWidgetEvent';
import { dispatchUiEvent, SendMessageHook, useUiEvent } from '../../../../../hooks';
import { LoadingSpinnerView } from '../../../../../layout';
import { GetCurrencyAmount } from '../../../../../views/purse/common/CurrencyHelper';
import { GetCurrencyAmount } from '../../../../purse/common/CurrencyHelper';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
import { Offer } from '../../../common/Offer';

View File

@ -8,6 +8,7 @@
@import './inventory/InventoryView';
@import './mod-tools/ModToolsView';
@import './navigator/NavigatorView';
@import './purse/PurseView';
@import './toolbar/ToolbarView';
@import './user-profile/UserProfileVew';
@import './user-settings/UserSettingsView';

View File

@ -1,11 +1,16 @@
import { createContext, FC, useContext } from 'react';
import { IPurseContext, PurseContextProps } from './PurseContext.types';
import { createContext, FC, ProviderProps, useContext } from 'react';
import { IPurse } from './common/IPurse';
interface IPurseContext
{
purse: IPurse;
}
const PurseContext = createContext<IPurseContext>({
purse: null
});
export const PurseContextProvider: FC<PurseContextProps> = props =>
export const PurseContextProvider: FC<ProviderProps<IPurseContext>> = props =>
{
return <PurseContext.Provider value={ props.value }>{ props.children }</PurseContext.Provider>
}

View File

@ -2,10 +2,9 @@ import { ActivityPointNotificationMessageEvent, UserCreditsEvent, UserCurrencyEv
import { FC, useCallback } from 'react';
import { CREDITS, DUCKETS, PlaySound } from '../../api/utils/PlaySound';
import { CreateMessageHook } from '../../hooks/messages/message-event';
import { usePurseContext } from './context/PurseContext';
import { PurseMessageHandlerProps } from './PurseMessageHandler.types';
import { usePurseContext } from './PurseContext';
export const PurseMessageHandler: FC<PurseMessageHandlerProps> = props =>
export const PurseMessageHandler: FC<{}> = props =>
{
const { purse = null } = usePurseContext();

View File

@ -3,7 +3,6 @@
font-size: $font-size-sm;
pointer-events: all;
margin-bottom: 5px;
padding: 6px 5px;
box-shadow: inset 0px 5px lighten(rgba($dark, 0.6), 2.5),
inset 0 -4px darken(rgba($dark, 0.6), 4);
@ -17,23 +16,30 @@
.nitro-purse-hc {
background-color: rgba($light, 0.1);
margin: 0 5px;
}
.nitro-purse-button {
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
padding: 2px 3px;
border-radius: $border-radius;
&:last-child {
border-bottom: none;
}
&:hover {
background-color: rgba($light, 0.1);
cursor: pointer;
}
}
}
@import "./views";
.nitro-currency {
pointer-events: all;
position: relative;
}
.nitro-seasonal-currency {
pointer-events: all;
background-color: rgba($dark,.95);
box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4);
font-size: $font-size-sm;
margin-bottom: 5px;
.seasonal-text {
color: rgba($light,.5);
}
}
}

View File

@ -0,0 +1,135 @@
import { FriendlyTime, HabboClubLevelEnum, UserCurrencyComposer, UserSubscriptionComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CreateLinkEvent, GetConfiguration, LocalizeText } from '../../api';
import { Column, Flex, Grid } from '../../common';
import { HcCenterEvent } from '../../events/hc-center/HcCenterEvent';
import { UserSettingsUIEvent } from '../../events/user-settings/UserSettingsUIEvent';
import { dispatchUiEvent } from '../../hooks';
import { SendMessageHook } from '../../hooks/messages/message-event';
import { CurrencyIcon } from '../../views/shared/currency-icon/CurrencyIcon';
import { IPurse } from './common/IPurse';
import { Purse } from './common/Purse';
import { PurseContextProvider } from './PurseContext';
import { PurseMessageHandler } from './PurseMessageHandler';
import { CurrencyView } from './views/CurrencyView';
import { SeasonalView } from './views/SeasonalView';
export let GLOBAL_PURSE: IPurse = null;
export const PurseView: FC<{}> = props =>
{
const [ purse, setPurse ] = useState<IPurse>(new Purse());
const [ updateId, setUpdateId ] = useState(-1);
const handleUserSettingsClick = () => dispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS));
const handleHelpCenterClick = () => CreateLinkEvent('help/show');
const handleHcCenterClick = () => dispatchUiEvent(new HcCenterEvent(HcCenterEvent.TOGGLE_HC_CENTER));
const displayedCurrencies = useMemo(() => GetConfiguration<number[]>('system.currency.types', []), []);
const currencyDisplayNumberShort = useMemo(() => GetConfiguration<boolean>('currency.display.number.short', false), []);
const getClubText = useMemo(() =>
{
const totalDays = ((purse.clubPeriods * 31) + purse.clubDays);
const minutesUntilExpiration = purse.minutesUntilExpiration;
if(purse.clubLevel === HabboClubLevelEnum.NO_CLUB) return LocalizeText('purse.clubdays.zero.amount.text');
else if((minutesUntilExpiration > -1) && (minutesUntilExpiration < (60 * 24))) return FriendlyTime.shortFormat(minutesUntilExpiration * 60);
else return FriendlyTime.shortFormat(totalDays * 86400);
}, [ purse ]);
const getCurrencyElements = useCallback((offset: number, limit: number = -1, seasonal: boolean = false) =>
{
if(!purse.activityPoints.size) return null;
const types = Array.from(purse.activityPoints.keys()).filter(type => (displayedCurrencies.indexOf(type) >= 0));
let count = 0;
while(count < offset)
{
types.shift();
count++;
}
count = 0;
const elements: JSX.Element[] = [];
for(const type of types)
{
if((limit > -1) && (count === limit)) break;
if(seasonal) elements.push(<SeasonalView key={ type } type={ type } amount={ purse.activityPoints.get(type) } />);
else elements.push(<CurrencyView key={ type } type={ type } amount={ purse.activityPoints.get(type) } short={ currencyDisplayNumberShort } />);
count++;
}
return elements;
}, [ purse, displayedCurrencies, currencyDisplayNumberShort ]);
useEffect(() =>
{
const purse = new Purse();
GLOBAL_PURSE = purse;
purse.notifier = () => setUpdateId(prevValue => (prevValue + 1));
setPurse(purse);
return () => (purse.notifier = null);
}, []);
useEffect(() =>
{
if(!purse) return;
SendMessageHook(new UserCurrencyComposer());
}, [ purse ]);
useEffect(() =>
{
SendMessageHook(new UserSubscriptionComposer('habbo_club'));
const interval = setInterval(() => SendMessageHook(new UserSubscriptionComposer('habbo_club')), 50000);
return () => clearInterval(interval);
}, [ purse ]);
if(!purse) return null;
return (
<PurseContextProvider value={ { purse } }>
<PurseMessageHandler />
<Flex className="nitro-purse rounded-bottom p-1">
<Grid gap={ 1 }>
<Column justifyContent="center" size={ 6 } gap={ 1 } className="nitro-currencies">
<CurrencyView type={ -1 } amount={ purse.credits } short={ currencyDisplayNumberShort } />
{ getCurrencyElements(0, 2) }
</Column>
<Column center size={ 4 } gap={ 1 } className="nitro-purse-hc nitro-purse-button rounded" onClick={ handleHcCenterClick }>
<CurrencyIcon className="flex-shrink-0" type="hc" />
<span>{ getClubText }</span>
</Column>
<Column justifyContent="center" size={ 2 } gap={ 1 } className="nitro-purse-buttons">
<Flex center pointer fullHeight className="nitro-purse-button text-white text-center p-1 rounded" onClick={ handleHelpCenterClick }>
<i className="icon icon-help"/>
</Flex>
<Flex center pointer fullHeight className="nitro-purse-button text-white text-center p-1 rounded" onClick={ handleUserSettingsClick } >
<i className="icon icon-cog"/>
</Flex>
</Column>
</Grid>
</Flex>
{ getCurrencyElements(2, -1, true) }
</PurseContextProvider>
);
}

View File

@ -0,0 +1,40 @@
import { FC, useMemo } from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { LocalizeFormattedNumber, LocalizeShortNumber } from '../../../api';
import { Flex } from '../../../common';
import { CurrencyIcon } from '../../../views/shared/currency-icon/CurrencyIcon';
interface CurrencyViewProps
{
type: number;
amount: number;
short: boolean;
}
export const CurrencyView: FC<CurrencyViewProps> = props =>
{
const { type = -1, amount = -1, short = false } = props;
const element = useMemo(() =>
{
return (
<Flex justifyContent="end" className="nitro-currency nitro-purse-button rounded">
<div className="px-1 text-end text-truncate nitro-currency-text align-self-center">{ short ? LocalizeShortNumber(amount) : LocalizeFormattedNumber(amount) }</div>
<CurrencyIcon className="flex-shrink-0" type={ type } />
</Flex>);
}, [ amount, short, type ]);
if(!short) return element;
return (
<OverlayTrigger
placement="left"
overlay={
<Tooltip id={ `tooltip-${ type }` }>
{ LocalizeFormattedNumber(amount) }
</Tooltip>
}>
{ element }
</OverlayTrigger>
);
}

View File

@ -1,14 +1,20 @@
import { FC } from 'react';
import { LocalizeFormattedNumber, LocalizeText } from '../../../../api';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { SeasonalViewProps } from './SeasonalView.types';
import { LocalizeFormattedNumber, LocalizeText } from '../../../api';
import { Flex } from '../../../common';
import { CurrencyIcon } from '../../../views/shared/currency-icon/CurrencyIcon';
interface SeasonalViewProps
{
type: number;
amount: number;
}
export const SeasonalView: FC<SeasonalViewProps> = props =>
{
const { type = -1, amount = -1 } = props;
return (
<div className="nitro-seasonal-currency rounded d-flex justify-content-end">
<Flex justifyContent="end" className="nitro-seasonal-currency rounded">
<div className="nitro-currency-text w-100 px-1 d-flex justify-content-between">
<span className="seasonal-text">{ LocalizeText(`purse.seasonal.currency.${ type }`) }</span>
<span>{ LocalizeFormattedNumber(amount) }</span>
@ -16,6 +22,6 @@ export const SeasonalView: FC<SeasonalViewProps> = props =>
<div>
<CurrencyIcon type={ type } />
</div>
</div>
</Flex>
);
}

View File

@ -4,7 +4,6 @@
@import "./loading/LoadingView";
@import "./main/MainView";
@import "./notification-center/NotificationCenterView";
@import "./purse/PurseView";
@import "./right-side/RightSideView";
@import "./room/RoomView";
@import "./room-host/RoomHostView";

View File

@ -1,4 +0,0 @@
export interface PurseMessageHandlerProps
{
}

View File

@ -1,167 +0,0 @@
import { FriendlyTime, HabboClubLevelEnum, UserCurrencyComposer, UserSubscriptionComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CreateLinkEvent, GetConfiguration, LocalizeText } from '../../api';
import { HcCenterEvent } from '../../events/hc-center/HcCenterEvent';
import { UserSettingsUIEvent } from '../../events/user-settings/UserSettingsUIEvent';
import { dispatchUiEvent } from '../../hooks';
import { SendMessageHook } from '../../hooks/messages/message-event';
import { CurrencyIcon } from '../shared/currency-icon/CurrencyIcon';
import { IPurse } from './common/IPurse';
import { Purse } from './common/Purse';
import { PurseContextProvider } from './context/PurseContext';
import { PurseMessageHandler } from './PurseMessageHandler';
import { CurrencyView } from './views/currency/CurrencyView';
import { SeasonalView } from './views/seasonal/SeasonalView';
export let GLOBAL_PURSE: IPurse = null;
export const PurseView: FC<{}> = props =>
{
const [ purse, setPurse ] = useState<IPurse>(new Purse());
const [ updateId, setUpdateId ] = useState(-1);
const handleUserSettingsClick = useCallback(() =>
{
dispatchUiEvent(new UserSettingsUIEvent(UserSettingsUIEvent.TOGGLE_USER_SETTINGS));
}, []);
const handleHelpCenterClick = useCallback(() =>
{
CreateLinkEvent('help/show');
}, []);
const handleHcCenterClick = useCallback(() =>
{
dispatchUiEvent(new HcCenterEvent(HcCenterEvent.TOGGLE_HC_CENTER));
}, []);
const displayedCurrencies = useMemo(() =>
{
return GetConfiguration<number[]>('system.currency.types', []);
}, []);
const currencyDisplayNumberShort = useMemo(() =>
{
return GetConfiguration<boolean>('currency.display.number.short', false);
}, []);
const getCurrencyElements = useCallback((offset: number, limit: number = -1, seasonal: boolean = false) =>
{
if(!purse.activityPoints.size) return null;
const types = Array.from(purse.activityPoints.keys()).filter(type => (displayedCurrencies.indexOf(type) >= 0));
let count = 0;
while(count < offset)
{
types.shift();
count++;
}
count = 0;
const elements: JSX.Element[] = [];
for(const type of types)
{
if((limit > -1) && (count === limit)) break;
if(seasonal) elements.push(<SeasonalView key={ type } type={ type } amount={ purse.activityPoints.get(type) } />);
else elements.push(<CurrencyView key={ type } type={ type } amount={ purse.activityPoints.get(type) } short={ currencyDisplayNumberShort } />);
count++;
}
return elements;
}, [ purse, displayedCurrencies, currencyDisplayNumberShort ]);
const getClubText = useCallback(() =>
{
const totalDays = ((purse.clubPeriods * 31) + purse.clubDays);
const minutesUntilExpiration = purse.minutesUntilExpiration;
if(purse.clubLevel === HabboClubLevelEnum.NO_CLUB)
{
return LocalizeText('purse.clubdays.zero.amount.text');
}
else if((minutesUntilExpiration > -1) && (minutesUntilExpiration < (60 * 24)))
{
return FriendlyTime.shortFormat(minutesUntilExpiration * 60);
}
else
{
return FriendlyTime.shortFormat(totalDays * 86400);
}
}, [ purse ]);
useEffect(() =>
{
const purse = new Purse();
GLOBAL_PURSE = purse;
purse.notifier = () => setUpdateId(prevValue => (prevValue + 1));
setPurse(purse);
return () => (purse.notifier = null);
}, []);
useEffect(() =>
{
if(!purse) return;
SendMessageHook(new UserCurrencyComposer());
}, [ purse ]);
useEffect(() =>
{
SendMessageHook(new UserSubscriptionComposer('habbo_club'));
const interval = setInterval(() =>
{
SendMessageHook(new UserSubscriptionComposer('habbo_club'));
}, 50000);
return () => clearInterval(interval);
}, [ purse ]);
if(!purse) return null;
return (
<PurseContextProvider value={ { purse } }>
<PurseMessageHandler />
<div className="nitro-purse rounded-bottom d-flex flex-row justify-content-between">
<div className="row mx-0 w-100">
<div className="col-6 px-0">
<div className="d-flex flex-column nitro-currencies">
<CurrencyView type={ -1 } amount={ purse.credits } short={ currencyDisplayNumberShort } />
{ getCurrencyElements(0, 2) }
</div>
</div>
<div className="col-4 px-0">
<div className="nitro-purse-hc nitro-purse-button rounded mx-1 p-1 d-flex flex-column justify-content-center align-items-center h-100"
onClick={ handleHcCenterClick }>
<CurrencyIcon className="flex-shrink-0" type="hc" />
<span>{ getClubText() }</span>
</div>
</div>
<div className="col-2 px-0">
<div className="d-flex flex-column nitro-purse-buttons h-100 justify-content-center">
<div className="nitro-purse-button text-white h-100 text-center d-flex align-items-center justify-content-center cursor-pointer" onClick={ handleHelpCenterClick }><i className="icon icon-help"/></div>
<div className="nitro-purse-button text-white h-100 text-center d-flex align-items-center justify-content-center cursor-pointer" onClick={ handleUserSettingsClick } ><i className="fas fa-cogs"/></div>
</div>
</div>
</div>
{/*<div className="notification-button px-2" onClick={toggleNotificationCenter}>
<i className="fas fa-bars" />
</div>*/}
</div>
{ getCurrencyElements(2, -1, true) }
</PurseContextProvider>
);
}

View File

@ -1,12 +0,0 @@
import { ProviderProps } from 'react';
import { IPurse } from '../common/IPurse';
export interface IPurseContext
{
purse: IPurse;
}
export interface PurseContextProps extends ProviderProps<IPurseContext>
{
}

View File

@ -1,4 +0,0 @@
.nitro-currency {
pointer-events: all;
position: relative;
}

View File

@ -1,32 +0,0 @@
import { FC, useMemo } from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { LocalizeFormattedNumber, LocalizeShortNumber } from '../../../../api';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
import { CurrencyViewProps } from './CurrencyView.types';
export const CurrencyView: FC<CurrencyViewProps> = props =>
{
const { type = -1, amount = -1, short = false } = props;
const element = useMemo(() =>
{
return (<div className="nitro-currency d-flex justify-content-end nitro-purse-button">
<div className="px-1 text-end text-truncate nitro-currency-text align-self-center">{ short ? LocalizeShortNumber(amount) : LocalizeFormattedNumber(amount) }</div>
<CurrencyIcon className="flex-shrink-0" type={ type } />
</div>);
}, [ amount, short, type ]);
if(!short) return element;
return (
<OverlayTrigger
placement="left"
overlay={
<Tooltip id={`tooltip-${ type }`}>
{ LocalizeFormattedNumber(amount) }
</Tooltip>
}>
{ element }
</OverlayTrigger>
);
}

View File

@ -1,6 +0,0 @@
export interface CurrencyViewProps
{
type: number;
amount: number;
short: boolean;
}

View File

@ -1,2 +0,0 @@
@import "./currency/CurrencyView";
@import "./seasonal/SeasonalView";

View File

@ -1,12 +0,0 @@
.nitro-seasonal-currency {
pointer-events: all;
padding: 6px 5px;
background-color: rgba($dark,.95);
box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4);
font-size: $font-size-sm;
margin-bottom: 5px;
.seasonal-text {
color: rgba($light,.5);
}
}

View File

@ -1,6 +0,0 @@
export interface SeasonalViewProps
{
type: number;
amount: number;
}

View File

@ -1,7 +1,7 @@
import { FC } from 'react';
import { GroupRoomInformationView } from '../../components/groups/views/room-information/GroupRoomInformationView';
import { PurseView } from '../../components/purse/PurseView';
import { NotificationCenterView } from '../notification-center/NotificationCenterView';
import { PurseView } from '../purse/PurseView';
import { RightSideProps } from './RightSideView.types';
export const RightSideView: FC<RightSideProps> = props =>