Start updates

This commit is contained in:
Bill 2022-04-03 20:27:30 -04:00
parent fd77cf7bf0
commit 1abadf1b55
20 changed files with 241 additions and 313 deletions

View File

@ -0,0 +1,11 @@
const DEFAULT_BADGE: string = 'HC1';
const BADGES: string[] = [ 'ACH_VipHC1', 'ACH_VipHC2', 'ACH_VipHC3', 'ACH_VipHC4', 'ACH_VipHC5', 'HC1', 'HC2', 'HC3', 'HC4', 'HC5' ];
export const GetClubBadge = (badgeCodes: string[]) =>
{
let badgeCode: string = null;
BADGES.forEach(badge => ((badgeCodes.indexOf(badge) > -1) && (badgeCode = badge)));
return (badgeCode || DEFAULT_BADGE);
}

View File

@ -0,0 +1,2 @@
export * from './ClubStatus';
export * from './GetClubBadge';

View File

@ -6,6 +6,7 @@ export * from './friends';
export * from './GetRendererVersion';
export * from './GetUIVersion';
export * from './groups';
export * from './hc-center';
export * from './inventory';
export * from './inventory/unseen';
export * from './navigator';
@ -19,6 +20,7 @@ export * from './nitro/room/widgets/handlers';
export * from './nitro/room/widgets/messages';
export * from './nitro/session';
export * from './notification';
export * from './purse';
export * from './user';
export * from './utils';
export * from './wired';

View File

@ -1,5 +1,5 @@
import { HabboClubLevelEnum } from '@nitrots/nitro-renderer';
import { GetNitroInstance } from '../../../api';
import { GetNitroInstance } from '..';
import { IPurse } from './IPurse';
export class Purse implements IPurse

2
src/api/purse/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './IPurse';
export * from './Purse';

View File

@ -1,4 +1,4 @@
import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, ClubGiftInfoEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer';
import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, ClubGiftInfoEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent } from '@nitrots/nitro-renderer';
import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent';
import { FC, useCallback } from 'react';
import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, NotificationAlertType, NotificationUtilities, ProductTypeEnum } from '../../api';
@ -15,7 +15,6 @@ import { IPurchasableOffer } from './common/IPurchasableOffer';
import { Offer } from './common/Offer';
import { PageLocalization } from './common/PageLocalization';
import { Product } from './common/Product';
import { SubscriptionInfo } from './common/SubscriptionInfo';
export const CatalogMessageHandler: FC<{}> = props =>
{
@ -227,23 +226,6 @@ export const CatalogMessageHandler: FC<{}> = props =>
});
}, [ setCatalogOptions ]);
const onUserSubscriptionEvent = useCallback((event: UserSubscriptionEvent) =>
{
const parser = event.getParser();
setCatalogOptions(prevValue =>
{
const subscriptionInfo = new SubscriptionInfo(
Math.max(0, parser.daysToPeriodEnd),
Math.max(0, parser.periodsSubscribedAhead),
parser.isVip,
parser.pastClubDays,
parser.pastVipDays);
return { ...prevValue, subscriptionInfo };
});
}, [ setCatalogOptions ]);
const onGiftWrappingConfigurationEvent = useCallback((event: GiftWrappingConfigurationEvent) =>
{
const parser = event.getParser();
@ -301,7 +283,6 @@ export const CatalogMessageHandler: FC<{}> = props =>
UseMessageEventHook(ApproveNameMessageEvent, onApproveNameMessageEvent);
UseMessageEventHook(GiftReceiverNotFoundEvent, onGiftReceiverNotFoundEvent);
UseMessageEventHook(HabboClubOffersMessageEvent, onHabboClubOffersMessageEvent);
UseMessageEventHook(UserSubscriptionEvent, onUserSubscriptionEvent);
UseMessageEventHook(GiftWrappingConfigurationEvent, onGiftWrappingConfigurationEvent);
UseMessageEventHook(ClubGiftInfoEvent, onClubGiftInfoEvent);
UseMessageEventHook(MarketplaceMakeOfferResult, onMarketplaceMakeOfferResult);

View File

@ -1,7 +1,6 @@
import { ClubGiftInfoParser, ClubOfferData, HabboGroupEntryData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer';
import { CatalogPetPalette } from './CatalogPetPalette';
import { GiftWrappingConfiguration } from './GiftWrappingConfiguration';
import { SubscriptionInfo } from './SubscriptionInfo';
export interface ICatalogOptions
{
@ -9,7 +8,6 @@ export interface ICatalogOptions
petPalettes?: CatalogPetPalette[];
clubOffers?: ClubOfferData[];
clubGifts?: ClubGiftInfoParser;
subscriptionInfo?: SubscriptionInfo;
giftConfiguration?: GiftWrappingConfiguration;
marketplaceConfiguration?: MarketplaceConfigurationMessageParser;
}

View File

@ -3,9 +3,7 @@ import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../../api';
import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common';
import { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent } from '../../../../../events';
import { UseUiEvent } from '../../../../../hooks';
import { GetCurrencyAmount } from '../../../../purse/common/CurrencyHelper';
import { GLOBAL_PURSE } from '../../../../purse/PurseView';
import { usePurse, UseUiEvent } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
import { CatalogLayoutProps } from './CatalogLayout.types';
@ -15,7 +13,8 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
const { currentPage = null, catalogOptions = null } = useCatalogContext();
const { clubOffers = null, subscriptionInfo = null } = catalogOptions;
const { purse = null, getCurrencyAmount = null } = usePurse();
const { clubOffers = null } = catalogOptions;
const onCatalogEvent = useCallback((event: CatalogEvent) =>
{
@ -54,8 +53,6 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
const getPurchaseHeader = useCallback(() =>
{
const purse = GLOBAL_PURSE;
if(!purse) return '';
const extensionOrSubscription = (purse.clubDays > 0 || purse.clubPeriods > 0) ? 'extension.' : 'subscription.';
@ -64,7 +61,7 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
const locale = LocalizeText('catalog.vip.buy.confirm.' + extensionOrSubscription + daysOrMonths);
return locale.replace('%NUM_' + daysOrMonths.toUpperCase() + '%', daysOrMonthsText.toString());
}, [ pendingOffer ]);
}, [ pendingOffer, purse ]);
const getPurchaseValidUntil = useCallback(() =>
{
@ -79,14 +76,12 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
const getSubscriptionDetails = useMemo(() =>
{
if(!subscriptionInfo) return '';
const clubDays = subscriptionInfo.clubDays;
const clubPeriods = subscriptionInfo.clubPeriods;
const clubDays = purse.clubDays;
const clubPeriods = purse.clubPeriods;
const totalDays = (clubPeriods * 31) + clubDays;
return LocalizeText('catalog.vip.extend.info', [ 'days' ], [ totalDays.toString() ]);
}, [ subscriptionInfo ]);
}, [ purse ]);
const purchaseSubscription = useCallback(() =>
{
@ -106,12 +101,12 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
{
if(!pendingOffer) return null;
if(pendingOffer.priceCredits > GetCurrencyAmount(-1))
if(pendingOffer.priceCredits > getCurrencyAmount(-1))
{
return <Button fullWidth variant="danger">{ LocalizeText('catalog.alert.notenough.title') }</Button>;
}
if(pendingOffer.priceActivityPoints > GetCurrencyAmount(pendingOffer.priceActivityPointsType))
if(pendingOffer.priceActivityPoints > getCurrencyAmount(pendingOffer.priceActivityPointsType))
{
return <Button fullWidth variant="danger">{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + pendingOffer.priceActivityPointsType) }</Button>;
}
@ -128,7 +123,7 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
default:
return <Button fullWidth variant="success" onClick={ () => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</Button>;
}
}, [ pendingOffer, purchaseState, purchaseSubscription ]);
}, [ pendingOffer, purchaseState, purchaseSubscription, getCurrencyAmount ]);
useEffect(() =>
{

View File

@ -2,8 +2,7 @@ import { BuyMarketplaceOfferMessageComposer, GetMarketplaceOffersMessageComposer
import { FC, useCallback, useMemo, useState } from 'react';
import { LocalizeText, NotificationAlertType, NotificationUtilities, SendMessageComposer } from '../../../../../../api';
import { Button, ButtonGroup, Column, Text } from '../../../../../../common';
import { UseMessageEventHook } from '../../../../../../hooks';
import { GetCurrencyAmount } from '../../../../../purse/common/CurrencyHelper';
import { UseMessageEventHook, usePurse } from '../../../../../../hooks';
import { CatalogLayoutProps } from '../CatalogLayout.types';
import { CatalogLayoutMarketplaceItemView, PUBLIC_OFFER } from './CatalogLayoutMarketplaceItemView';
import { SearchFormView } from './CatalogLayoutMarketplaceSearchFormView';
@ -25,6 +24,7 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
const [ totalItemsFound, setTotalItemsFound ] = useState(0);
const [ offers, setOffers ] = useState(new Map<number, MarketplaceOfferData>());
const [ lastSearch, setLastSearch ] = useState<IMarketplaceSearchOptions>({ minPrice: -1, maxPrice: -1, query: '', type: 3 });
const { getCurrencyAmount = null } = usePurse();
const requestOffers = useCallback((options: IMarketplaceSearchOptions) =>
{
@ -48,18 +48,20 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
const purchaseItem = useCallback((offerData: MarketplaceOfferData) =>
{
if(offerData.price > GetCurrencyAmount(-1))
if(offerData.price > getCurrencyAmount(-1))
{
NotificationUtilities.simpleAlert(LocalizeText('catalog.alert.notenough.credits.description'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.alert.notenough.title'));
return;
}
const offerId = offerData.offerId;
NotificationUtilities.confirm(LocalizeText('catalog.marketplace.confirm_header'), () =>
{
SendMessageComposer(new BuyMarketplaceOfferMessageComposer(offerId));
},
null, null, null, LocalizeText('catalog.marketplace.confirm_title'));
},[]);
}, [ getCurrencyAmount ]);
const onMarketPlaceOffersEvent = useCallback( (event: MarketPlaceOffersEvent) =>
{

View File

@ -2,14 +2,16 @@ import { SelectClubGiftComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react';
import { LocalizeText, NotificationUtilities, SendMessageComposer } from '../../../../../../api';
import { AutoGrid, Text } from '../../../../../../common';
import { usePurse } from '../../../../../../hooks';
import { useCatalogContext } from '../../../../CatalogContext';
import { CatalogLayoutProps } from '../CatalogLayout.types';
import { VipGiftItem } from './VipGiftItemView';
export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
{
const { purse = null } = usePurse();
const { catalogOptions = null, setCatalogOptions = null } = useCatalogContext();
const { clubGifts = null, subscriptionInfo = null } = catalogOptions;
const { clubGifts = null } = catalogOptions;
const giftsAvailable = useCallback(() =>
{
@ -19,10 +21,10 @@ export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
if(clubGifts.daysUntilNextGift > 0) return LocalizeText('catalog.club_gift.days_until_next', [ 'days' ], [ clubGifts.daysUntilNextGift.toString() ]);
if(subscriptionInfo.isVip) return LocalizeText('catalog.club_gift.not_available');
if(purse.isVip) return LocalizeText('catalog.club_gift.not_available');
return LocalizeText('catalog.club_gift.no_club');
}, [ clubGifts, subscriptionInfo ]);
}, [ clubGifts, purse ]);
const selectGift = useCallback((localizationId: string) =>
{

View File

@ -3,8 +3,7 @@ import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CreateLinkEvent, GetClubMemberLevel, LocalizeText, SendMessageComposer } from '../../../../../api';
import { Button, LayoutLoadingSpinnerView } from '../../../../../common';
import { CatalogEvent, CatalogInitGiftEvent, CatalogInitPurchaseEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent, CatalogWidgetEvent } from '../../../../../events';
import { DispatchUiEvent, UseUiEvent } from '../../../../../hooks';
import { GetCurrencyAmount } from '../../../../purse/common/CurrencyHelper';
import { DispatchUiEvent, usePurse, UseUiEvent } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
import { Offer } from '../../../common/Offer';
@ -21,6 +20,7 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
const [ purchaseWillBeGift, setPurchaseWillBeGift ] = useState(false);
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
const { currentOffer = null, currentPage = null, purchaseOptions = null, setPurchaseOptions = null } = useCatalogContext();
const { getCurrencyAmount = null } = usePurse();
const onCatalogInitPurchaseEvent = useCallback((event: CatalogInitPurchaseEvent) =>
{
@ -148,9 +148,9 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
if(isLimitedSoldOut) return <Button variant="danger" disabled>{ LocalizeText('catalog.alert.limited_edition_sold_out.title') }</Button>;
if(priceCredits > GetCurrencyAmount(-1)) return <Button variant="danger" disabled>{ LocalizeText('catalog.alert.notenough.title') }</Button>;
if(priceCredits > getCurrencyAmount(-1)) return <Button variant="danger" disabled>{ LocalizeText('catalog.alert.notenough.title') }</Button>;
if(pricePoints > GetCurrencyAmount(currentOffer.activityPointType)) return <Button variant="danger" disabled>{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + currentOffer.activityPointType) }</Button>;
if(pricePoints > getCurrencyAmount(currentOffer.activityPointType)) return <Button variant="danger" disabled>{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + currentOffer.activityPointType) }</Button>;
switch(purchaseState)
{

View File

@ -1,42 +1,31 @@
import { BadgesEvent, ClubGiftInfoEvent, FriendlyTime, GetClubGiftInfo, ILinkEventTracker, RequestBadgesComposer, ScrGetKickbackInfoMessageComposer, ScrKickbackData, ScrSendKickbackInfoMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer';
import { ClubGiftInfoEvent, FriendlyTime, GetClubGiftInfo, ILinkEventTracker, ScrGetKickbackInfoMessageComposer, ScrKickbackData, ScrSendKickbackInfoMessageEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { OverlayTrigger, Popover } from 'react-bootstrap';
import { AddEventLinkTracker, CreateLinkEvent, GetConfiguration, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api';
import { AddEventLinkTracker, ClubStatus, CreateLinkEvent, GetClubBadge, GetConfiguration, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api';
import { Base, Button, Column, Flex, LayoutAvatarImageView, LayoutBadgeImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
import { UseMessageEventHook, useSessionInfo } from '../../hooks';
import { BadgeResolver } from './common/BadgeResolver';
import { ClubStatus } from './common/ClubStatus';
import { useInventoryBadges, UseMessageEventHook, usePurse, useSessionInfo } from '../../hooks';
export const HcCenterView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ kickbackData, setKickbackData ] = useState<ScrKickbackData>(null);
const [ clubDays, setClubDays ] = useState(0);
const [ pastClubDays, setPastClubDays ] = useState(0);
const [ clubPeriods, setPastClubPeriods ] = useState(0);
const [ minsTillExpire, setMinsTillExpire ] = useState(0);
const [ clubStatus, setClubStatus ] = useState(ClubStatus.NONE);
const [ unclaimedGifts, setUnclaimedGifts ] = useState(0);
const [ badgeCode, setBadgeCode ] = useState(BadgeResolver.default_badge);
const [ badgeCode, setBadgeCode ] = useState(null);
const { userFigure = null } = useSessionInfo();
const { purse = null, clubStatus = null } = usePurse();
const { badgeCodes = [], activate = null, deactivate = null } = useInventoryBadges();
const getClubText = () =>
{
const totalDays = ((clubPeriods * 31) + clubDays);
const minutesUntilExpiration = minsTillExpire;
if(clubStatus !== ClubStatus.ACTIVE)
if(purse.clubDays <= 0) return LocalizeText('purse.clubdays.zero.amount.text');
if((purse.minutesUntilExpiration > -1) && (purse.minutesUntilExpiration < (60 * 24)))
{
return LocalizeText('purse.clubdays.zero.amount.text');
return FriendlyTime.shortFormat(purse.minutesUntilExpiration * 60);
}
if((minutesUntilExpiration > -1) && (minutesUntilExpiration < (60 * 24)))
{
return FriendlyTime.shortFormat(minutesUntilExpiration * 60);
}
return FriendlyTime.shortFormat(totalDays * 86400);
return FriendlyTime.shortFormat(((purse.clubPeriods * 31) + purse.clubDays) * 86400);
}
const getInfoText = () =>
@ -52,16 +41,8 @@ export const HcCenterView: FC<{}> = props =>
}
}
const getHcPaydayTime = () =>
{
if(kickbackData.timeUntilPayday < 60) return LocalizeText('hccenter.special.time.soon');
return FriendlyTime.shortFormat(kickbackData.timeUntilPayday * 60);
}
const getHcPaydayAmount = () =>
{
return LocalizeText('hccenter.special.sum', [ 'credits' ], [ (kickbackData.creditRewardForStreakBonus + kickbackData.creditRewardForMonthlySpent).toString() ]);
}
const getHcPaydayTime = () => (kickbackData.timeUntilPayday < 60) ? LocalizeText('hccenter.special.time.soon') : FriendlyTime.shortFormat(kickbackData.timeUntilPayday * 60);
const getHcPaydayAmount = () => LocalizeText('hccenter.special.sum', [ 'credits' ], [ (kickbackData.creditRewardForStreakBonus + kickbackData.creditRewardForMonthlySpent).toString() ]);
const onClubGiftInfoEvent = useCallback((event: ClubGiftInfoEvent) =>
{
@ -81,87 +62,56 @@ export const HcCenterView: FC<{}> = props =>
UseMessageEventHook(ScrSendKickbackInfoMessageEvent, onScrSendKickbackInfo);
const onBadges = useCallback((event: BadgesEvent) =>
{
const parser = event.getParser();
setBadgeCode(BadgeResolver.getClubBadge(parser.getAllBadgeCodes()));
}, []);
UseMessageEventHook(BadgesEvent, onBadges);
const onUserSubscriptionEvent = useCallback((event: UserSubscriptionEvent) =>
{
const parser = event.getParser();
const productName = parser.productName;
if((productName !== 'club_habbo') && (productName !== 'habbo_club')) return;
setClubDays(Math.max(0, parser.daysToPeriodEnd));
setPastClubPeriods(Math.max(0, parser.periodsSubscribedAhead));
setPastClubDays(Math.max(0, parser.pastClubDays));
setMinsTillExpire(Math.max(0, parser.minutesUntilExpiration));
}, []);
UseMessageEventHook(UserSubscriptionEvent, onUserSubscriptionEvent);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'open':
if(parts.length > 2)
{
switch(parts[2])
{
case 'hccenter':
setIsVisible(true);
break;
}
}
return;
}
}, []);
useEffect(() =>
{
const linkTracker: ILinkEventTracker = {
linkReceived,
linkReceived: (url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'open':
if(parts.length > 2)
{
switch(parts[2])
{
case 'hccenter':
setIsVisible(true);
break;
}
}
return;
}
},
eventUrlPrefix: 'habboUI/'
};
AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived ]);
}, []);
useEffect(() =>
{
if(clubDays > 0)
{
setClubStatus(ClubStatus.ACTIVE);
setBadgeCode(GetClubBadge(badgeCodes));
}, [ badgeCodes ]);
return;
}
useEffect(() =>
{
if(!isVisible) return;
if(pastClubDays > 0)
{
setClubStatus(ClubStatus.EXPIRED);
const id = activate();
return;
}
}, [ clubDays, pastClubDays ]);
return () => deactivate(id);
}, [ isVisible, activate, deactivate ]);
useEffect(() =>
{
SendMessageComposer(new GetClubGiftInfo());
SendMessageComposer(new ScrGetKickbackInfoMessageComposer());
SendMessageComposer(new RequestBadgesComposer());
}, []);
if(!isVisible) return null;
@ -176,10 +126,10 @@ export const HcCenterView: FC<{}> = props =>
<hr className="w-100 text-black my-1" />
<div>{LocalizeText('hccenter.breakdown.total', [ 'credits', 'actual' ], [ getHcPaydayAmount(),
((((kickbackData.kickbackPercentage * kickbackData.totalCreditsSpent) + kickbackData.creditRewardForStreakBonus) * 100) / 100).toString() ])}</div>
<div className="btn btn-link text-primary p-0" onClick={() =>
<div className="btn btn-link text-primary p-0" onClick={ () =>
{
CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['payday.habbopage'])
}}>{
} }>{
LocalizeText('hccenter.special.infolink')}
</div>
</Popover.Body>
@ -193,10 +143,7 @@ export const HcCenterView: FC<{}> = props =>
<Column gap={ 1 }>
<div className="hc-logo" />
<Flex>
<Button variant="success" onClick={ event =>
{
CreateLinkEvent('catalog/open/' + GetConfiguration('catalog.links')['hc.buy_hc'])
} }>
<Button variant="success" onClick={ event => CreateLinkEvent('catalog/open/' + GetConfiguration('catalog.links')['hc.buy_hc']) }>
{ LocalizeText((clubStatus === ClubStatus.ACTIVE) ? 'hccenter.btn.extend' : 'hccenter.btn.buy') }
</Button>
</Flex>
@ -207,10 +154,10 @@ export const HcCenterView: FC<{}> = props =>
</Flex>
<NitroCardContentView>
<Flex gap={ 2 }>
<LayoutBadgeImageView badgeCode={badgeCode} className="align-self-center flex-shrink-0 me-1" />
<LayoutBadgeImageView badgeCode={ badgeCode } className="align-self-center flex-shrink-0 me-1" />
<Column size={ 5 } className="streak-info" gap={ 0 }>
<Text>{ LocalizeText('hccenter.status.' + clubStatus) }</Text>
<Text dangerouslySetInnerHTML={{ __html: getInfoText() }} />
<Text dangerouslySetInnerHTML={ { __html: getInfoText() } } />
</Column>
</Flex>
{ GetConfiguration('hc.center')['payday.info'] &&
@ -218,10 +165,7 @@ export const HcCenterView: FC<{}> = props =>
<Column className="rounded-start bg-primary p-2 payday-special mb-1">
<h4 className="mb-1">{LocalizeText('hccenter.special.title')}</h4>
<div>{LocalizeText('hccenter.special.info')}</div>
<div className="btn btn-link text-white p-0 mt-auto align-self-baseline" onClick={() =>
{
CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['payday.habbopage'])
}}>{LocalizeText('hccenter.special.infolink')}</div>
<div className="btn btn-link text-white p-0 mt-auto align-self-baseline" onClick={ () => CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['payday.habbopage']) }>{LocalizeText('hccenter.special.infolink')}</div>
</Column>
<div className="payday flex-shrink-0 p-2">
<h5 className="mb-2 ms-2">{LocalizeText('hccenter.special.time.title')}</h5>
@ -234,7 +178,7 @@ export const HcCenterView: FC<{}> = props =>
<h5 className="ms-2 mb-1 bolder">{LocalizeText('hccenter.special.amount.title')}</h5>
<div className="d-flex flex-column">
<div className="w-100 text-center ms-4n">{getHcPaydayAmount()}</div>
<OverlayTrigger trigger={ [ 'hover', 'focus' ] } placement="left" overlay={popover}>
<OverlayTrigger trigger={ [ 'hover', 'focus' ] } placement="left" overlay={ popover }>
<div className="btn btn-link align-self-end text-primary">
{LocalizeText('hccenter.breakdown.infolink')}
</div>
@ -249,22 +193,22 @@ export const HcCenterView: FC<{}> = props =>
<div className="rounded bg-success p-2 d-flex flex-row mb-0">
<div>
<h4 className="mb-1">{LocalizeText('hccenter.gift.title')}</h4>
<div dangerouslySetInnerHTML={{ __html: unclaimedGifts > 0 ? LocalizeText('hccenter.unclaimedgifts', [ 'unclaimedgifts' ], [ unclaimedGifts.toString() ]) : LocalizeText('hccenter.gift.info') }}></div>
<div dangerouslySetInnerHTML={ { __html: unclaimedGifts > 0 ? LocalizeText('hccenter.unclaimedgifts', [ 'unclaimedgifts' ], [ unclaimedGifts.toString() ]) : LocalizeText('hccenter.gift.info') } }></div>
</div>
<button className="btn btn-primary btn-lg align-self-center ms-auto" onClick={() =>
<button className="btn btn-primary btn-lg align-self-center ms-auto" onClick={ () =>
{
CreateLinkEvent('catalog/open/' + GetConfiguration('catalog.links')['hc.hc_gifts'])
}}>{LocalizeText(clubStatus === ClubStatus.ACTIVE ? 'hccenter.btn.gifts.redeem' : 'hccenter.btn.gifts.view')}</button>
} }>{LocalizeText(clubStatus === ClubStatus.ACTIVE ? 'hccenter.btn.gifts.redeem' : 'hccenter.btn.gifts.view')}</button>
</div>
}
{GetConfiguration('hc.center')['benefits.info'] &&
<div className="benefits text-black py-2">
<h5 className="mb-1 text-primary">{LocalizeText('hccenter.general.title')}</h5>
<div className="mb-2" dangerouslySetInnerHTML={{ __html: LocalizeText('hccenter.general.info') }} />
<button className="btn btn-link p-0 text-primary" onClick={() =>
<div className="mb-2" dangerouslySetInnerHTML={ { __html: LocalizeText('hccenter.general.info') } } />
<button className="btn btn-link p-0 text-primary" onClick={ () =>
{
CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['benefits.habbopage'])
}}>{LocalizeText('hccenter.general.infolink')}</button>
} }>{LocalizeText('hccenter.general.infolink')}</button>
</div>
}
</NitroCardContentView>

View File

@ -1,23 +0,0 @@
export class BadgeResolver
{
public static default_badge: string = 'HC1';
public static badges: string[] = [ 'ACH_VipHC1', 'ACH_VipHC2', 'ACH_VipHC3', 'ACH_VipHC4', 'ACH_VipHC5', 'HC1', 'HC2', 'HC3', 'HC4', 'HC5' ];
public static getClubBadge(k: string[]): string
{
var badgeCode: string = null;
this.badges.forEach(badge =>
{
if (k.indexOf(badge) > -1)
{
badgeCode = badge;
}
});
return badgeCode || this.default_badge;
}
}

View File

@ -1,21 +1,15 @@
import { ActivityPointNotificationMessageEvent, FriendlyTime, HabboClubLevelEnum, UserCreditsEvent, UserCurrencyComposer, UserCurrencyEvent, UserSubscriptionComposer, UserSubscriptionEvent, UserSubscriptionParser } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CreateLinkEvent, GetConfiguration, LocalizeText, PlaySound, SendMessageComposer, SoundNames } from '../../api';
import { FriendlyTime, HabboClubLevelEnum } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo } from 'react';
import { CreateLinkEvent, GetConfiguration, LocalizeText } from '../../api';
import { Column, Flex, Grid, LayoutCurrencyIcon, Text } from '../../common';
import { UseMessageEventHook } from '../../hooks';
import { IPurse } from './common/IPurse';
import { Purse } from './common/Purse';
import { usePurse } from '../../hooks';
import { PurseContextProvider } from './PurseContext';
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 hcDisabled = useMemo(() => GetConfiguration<boolean>('hc.disabled', false), []);
const { purse = null, hcDisabled = false } = usePurse();
const displayedCurrencies = useMemo(() => GetConfiguration<number[]>('system.currency.types', []), []);
const currencyDisplayNumberShort = useMemo(() => GetConfiguration<boolean>('currency.display.number.short', false), []);
@ -65,107 +59,6 @@ export const PurseView: FC<{}> = props =>
return elements;
}, [ purse, displayedCurrencies, currencyDisplayNumberShort ]);
const onUserCreditsEvent = useCallback((event: UserCreditsEvent) =>
{
const parser = event.getParser();
setPurse(prevValue =>
{
const newValue = Purse.from(prevValue as Purse);
newValue.credits = parseFloat(parser.credits);
if(prevValue.credits !== newValue.credits) PlaySound(SoundNames.CREDITS);
return newValue;
});
}, []);
UseMessageEventHook(UserCreditsEvent, onUserCreditsEvent);
const onUserCurrencyEvent = useCallback((event: UserCurrencyEvent) =>
{
const parser = event.getParser();
setPurse(prevValue =>
{
const newValue = Purse.from(prevValue as Purse);
newValue.activityPoints = parser.currencies;
return newValue;
});
}, []);
UseMessageEventHook(UserCurrencyEvent, onUserCurrencyEvent);
const onActivityPointNotificationMessageEvent = useCallback((event: ActivityPointNotificationMessageEvent) =>
{
const parser = event.getParser();
setPurse(prevValue =>
{
const newValue = Purse.from(prevValue as Purse);
newValue.activityPoints = new Map(newValue.activityPoints);
newValue.activityPoints.set(parser.type, parser.amount);
if(parser.type === 0) PlaySound(SoundNames.DUCKETS)
return newValue;
});
}, []);
UseMessageEventHook(ActivityPointNotificationMessageEvent, onActivityPointNotificationMessageEvent);
const onUserSubscriptionEvent = useCallback((event: UserSubscriptionEvent) =>
{
const parser = event.getParser();
const productName = parser.productName;
if((productName !== 'club_habbo') && (productName !== 'habbo_club')) return;
setPurse(prevValue =>
{
const newValue = Purse.from(prevValue as Purse);
newValue.clubDays = Math.max(0, parser.daysToPeriodEnd);
newValue.clubPeriods = Math.max(0, parser.periodsSubscribedAhead);
newValue.isVip = parser.isVip;
newValue.pastClubDays = parser.pastClubDays;
newValue.pastVipDays = parser.pastVipDays;
newValue.isExpiring = ((parser.responseType === UserSubscriptionParser.RESPONSE_TYPE_DISCOUNT_AVAILABLE) ? true : false);
newValue.minutesUntilExpiration = parser.minutesUntilExpiration;
newValue.minutesSinceLastModified = parser.minutesSinceLastModified;
return newValue;
});
}, []);
UseMessageEventHook(UserSubscriptionEvent, onUserSubscriptionEvent);
useEffect(() =>
{
GLOBAL_PURSE = purse;
}, [ purse ]);
useEffect(() =>
{
if(hcDisabled) return;
SendMessageComposer(new UserSubscriptionComposer('habbo_club'));
const interval = setInterval(() => SendMessageComposer(new UserSubscriptionComposer('habbo_club')), 50000);
return () => clearInterval(interval);
}, [ hcDisabled ]);
useEffect(() =>
{
SendMessageComposer(new UserCurrencyComposer());
}, []);
if(!purse) return null;
return (

View File

@ -1,17 +0,0 @@
import { GLOBAL_PURSE } from '../PurseView';
export function GetCurrencyAmount(type: number): number
{
const purse = GLOBAL_PURSE;
if(type === -1) return purse.credits;
for(const [ key, value ] of purse.activityPoints.entries())
{
if(key !== type) continue;
return value;
}
return 0;
}

View File

@ -7,6 +7,7 @@ export * from './friends';
export * from './inventory';
export * from './messages';
export * from './navigator';
export * from './purse';
export * from './session';
export * from './UseMountEffect';
export * from './useSharedVisibility';

1
src/hooks/purse/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './usePurse';

134
src/hooks/purse/usePurse.ts Normal file
View File

@ -0,0 +1,134 @@
import { ActivityPointNotificationMessageEvent, UserCreditsEvent, UserCurrencyComposer, UserCurrencyEvent, UserSubscriptionComposer, UserSubscriptionEvent, UserSubscriptionParser } from '@nitrots/nitro-renderer';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useBetween } from 'use-between';
import { CloneObject, ClubStatus, GetConfiguration, IPurse, PlaySound, Purse, SendMessageComposer, SoundNames } from '../../api';
import { UseMessageEventHook } from '../messages';
const usePurseState = () =>
{
const [ purse, setPurse ] = useState<IPurse>(new Purse());
const hcDisabled = useMemo(() => GetConfiguration<boolean>('hc.disabled', false), []);
const clubStatus = useMemo(() =>
{
if(hcDisabled || (purse.clubDays > 0)) return ClubStatus.ACTIVE;
if((purse.pastVipDays > 0) || (purse.pastVipDays > 0)) return ClubStatus.EXPIRED;
return ClubStatus.NONE;
}, [ purse, hcDisabled ]);
const getCurrencyAmount = (type: number) =>
{
if(type === -1) return purse.credits;
for(const [ key, value ] of purse.activityPoints.entries())
{
if(key !== type) continue;
return value;
}
return 0;
}
const onUserCreditsEvent = useCallback((event: UserCreditsEvent) =>
{
const parser = event.getParser();
setPurse(prevValue =>
{
const newValue = CloneObject(prevValue);
newValue.credits = parseFloat(parser.credits);
if(prevValue.credits !== newValue.credits) PlaySound(SoundNames.CREDITS);
return newValue;
});
}, []);
UseMessageEventHook(UserCreditsEvent, onUserCreditsEvent);
const onUserCurrencyEvent = useCallback((event: UserCurrencyEvent) =>
{
const parser = event.getParser();
setPurse(prevValue =>
{
const newValue = CloneObject(prevValue);
newValue.activityPoints = parser.currencies;
return newValue;
});
}, []);
UseMessageEventHook(UserCurrencyEvent, onUserCurrencyEvent);
const onActivityPointNotificationMessageEvent = useCallback((event: ActivityPointNotificationMessageEvent) =>
{
const parser = event.getParser();
setPurse(prevValue =>
{
const newValue = CloneObject(prevValue);
newValue.activityPoints = new Map(newValue.activityPoints);
newValue.activityPoints.set(parser.type, parser.amount);
if(parser.type === 0) PlaySound(SoundNames.DUCKETS)
return newValue;
});
}, []);
UseMessageEventHook(ActivityPointNotificationMessageEvent, onActivityPointNotificationMessageEvent);
const onUserSubscriptionEvent = useCallback((event: UserSubscriptionEvent) =>
{
const parser = event.getParser();
const productName = parser.productName;
if((productName !== 'club_habbo') && (productName !== 'habbo_club')) return;
setPurse(prevValue =>
{
const newValue = CloneObject(prevValue);
newValue.clubDays = Math.max(0, parser.daysToPeriodEnd);
newValue.clubPeriods = Math.max(0, parser.periodsSubscribedAhead);
newValue.isVip = parser.isVip;
newValue.pastClubDays = parser.pastClubDays;
newValue.pastVipDays = parser.pastVipDays;
newValue.isExpiring = ((parser.responseType === UserSubscriptionParser.RESPONSE_TYPE_DISCOUNT_AVAILABLE) ? true : false);
newValue.minutesUntilExpiration = parser.minutesUntilExpiration;
newValue.minutesSinceLastModified = parser.minutesSinceLastModified;
return newValue;
});
}, []);
UseMessageEventHook(UserSubscriptionEvent, onUserSubscriptionEvent);
useEffect(() =>
{
if(hcDisabled) return;
SendMessageComposer(new UserSubscriptionComposer('habbo_club'));
const interval = setInterval(() => SendMessageComposer(new UserSubscriptionComposer('habbo_club')), 50000);
return () => clearInterval(interval);
}, [ hcDisabled ]);
useEffect(() =>
{
SendMessageComposer(new UserCurrencyComposer());
}, []);
return { purse, hcDisabled, clubStatus, getCurrencyAmount };
}
export const usePurse = () => useBetween(usePurseState);