mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-22 22:30:52 +01:00
Updates
This commit is contained in:
parent
6a65398cc6
commit
c697a9aa30
@ -35,11 +35,17 @@
|
||||
"brace-style": [ "error", "allman" ],
|
||||
"template-curly-spacing": [ "error", "always" ],
|
||||
"no-multi-spaces": [ "error" ],
|
||||
"react/prop-types": [ "off" ],
|
||||
"jsx-quotes": [ "error" ],
|
||||
"react/jsx-curly-spacing": [ "error", "always" ],
|
||||
"react/prop-types": [ "off" ],
|
||||
"react/jsx-curly-spacing": [ "error", { "when": "always", "children": true } ],
|
||||
"react/jsx-equals-spacing": [ "error" ],
|
||||
"@typescript-eslint/object-curly-spacing": [ "error", "always", { "arraysInObjects": true, "objectsInObjects": false } ],
|
||||
"react/jsx-newline": [ "error", { "prevent": true } ],
|
||||
"@typescript-eslint/object-curly-spacing": [ "error", "always",
|
||||
{
|
||||
"arraysInObjects": true,
|
||||
"objectsInObjects": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
|
11
src/api/hc-center/GetClubBadge.ts
Normal file
11
src/api/hc-center/GetClubBadge.ts
Normal 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);
|
||||
}
|
2
src/api/hc-center/index.ts
Normal file
2
src/api/hc-center/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './ClubStatus';
|
||||
export * from './GetClubBadge';
|
@ -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';
|
||||
|
@ -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
2
src/api/purse/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './IPurse';
|
||||
export * from './Purse';
|
@ -45,7 +45,7 @@ export const TransitionAnimation: FC<TransitionAnimationProps> = props =>
|
||||
<div className={ (className ?? '') + ' animate__animated' } style={ { ...getTransitionAnimationStyle(type, state, timeout) } }>
|
||||
{ isChildrenVisible && children }
|
||||
</div>
|
||||
)}
|
||||
) }
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ export const AvatarEditorView: FC<{}> = props =>
|
||||
{ LocalizeText(`avatareditor.category.${ category }`) }
|
||||
</NitroCardTabsItemView>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
<NitroCardTabsItemView isActive={ isWardrobeVisible } onClick={ event => setIsWardrobeVisible(true) }>
|
||||
{ LocalizeText('avatareditor.category.wardrobe') }
|
||||
</NitroCardTabsItemView>
|
||||
|
@ -76,7 +76,7 @@ export const AvatarEditorModelView: FC<AvatarEditorModelViewProps> = props =>
|
||||
<AvatarEditorIcon icon={ category.name } selected={ (activeCategory === category) } />
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden">
|
||||
<AvatarEditorFigureSetView model={ model } category={ activeCategory } setMaxPaletteCount={ setMaxPaletteCount } />
|
||||
|
@ -102,7 +102,7 @@ export const CampaignView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
{(calendarData && isCalendarOpen) &&
|
||||
{ (calendarData && isCalendarOpen) &&
|
||||
<CalendarView close={ onCalendarClose } campaignName={ calendarData.campaignName } currentDay={ calendarData.currentDay } numDays={ calendarData.campaignDays } openedDays={ calendarData.openedDays } missedDays={ calendarData.missedDays } openPackage={ openPackage } receivedProducts={ receivedProducts } />
|
||||
}
|
||||
</>
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
||||
{ searchResult && (searchResult.filteredNodes.length > 0) && searchResult.filteredNodes.map((n, index) =>
|
||||
{
|
||||
return <CatalogNavigationItemView key={ index } node={ n } />;
|
||||
})}
|
||||
}) }
|
||||
{ !searchResult &&
|
||||
<CatalogNavigationSetView node={ node } /> }
|
||||
</AutoGrid>
|
||||
|
@ -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(() =>
|
||||
{
|
||||
|
@ -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) =>
|
||||
{
|
||||
|
@ -60,9 +60,9 @@ export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps>
|
||||
</Flex>
|
||||
<Column gap={ 1 }>
|
||||
<CatalogPurchaseWidgetView />
|
||||
{/* <CatalogPurchaseButtonView offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
|
||||
{ /* <CatalogPurchaseButtonView offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
|
||||
{ offer.giftable &&
|
||||
<CatalogPurchaseGiftButtonView offer={ offer } pageId={ pageId } extra={ extraData } disabled={ nameApproved } /> } */}
|
||||
<CatalogPurchaseGiftButtonView offer={ offer } pageId={ pageId } extra={ extraData } disabled={ nameApproved } /> } */ }
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
|
@ -206,7 +206,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
<LayoutPetImageView typeId={ petIndex } paletteId={ palette.paletteId } direction={ 2 } headOnly={ true } />
|
||||
</LayoutGridItem>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) => <LayoutGridItem itemHighlight key={ index } itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } className="clear-bg" onClick={ event => setSelectedColorIndex(index) } />) }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
|
@ -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) =>
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -124,7 +124,7 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
</Flex>
|
||||
</LayoutGridItem>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</Column>
|
||||
</Column>
|
||||
<Column size={ 8 } overflow="hidden">
|
||||
|
@ -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;
|
||||
@ -169,18 +119,14 @@ export const HcCenterView: FC<{}> = props =>
|
||||
const popover = (
|
||||
<Popover id="popover-basic">
|
||||
<Popover.Body className="text-black py-2 px-3">
|
||||
<h5>{LocalizeText('hccenter.breakdown.title')}</h5>
|
||||
<div>{LocalizeText('hccenter.breakdown.creditsspent', [ 'credits' ], [ kickbackData.totalCreditsSpent.toString() ])}</div>
|
||||
<div>{LocalizeText('hccenter.breakdown.paydayfactor.percent', [ 'percent' ], [ (kickbackData.kickbackPercentage * 100).toString() ])}</div>
|
||||
<div>{LocalizeText('hccenter.breakdown.streakbonus', [ 'credits' ], [ kickbackData.creditRewardForStreakBonus.toString() ])}</div>
|
||||
<h5>{ LocalizeText('hccenter.breakdown.title') }</h5>
|
||||
<div>{ LocalizeText('hccenter.breakdown.creditsspent', [ 'credits' ], [ kickbackData.totalCreditsSpent.toString() ]) }</div>
|
||||
<div>{ LocalizeText('hccenter.breakdown.paydayfactor.percent', [ 'percent' ], [ (kickbackData.kickbackPercentage * 100).toString() ]) }</div>
|
||||
<div>{ LocalizeText('hccenter.breakdown.streakbonus', [ 'credits' ], [ kickbackData.creditRewardForStreakBonus.toString() ]) }</div>
|
||||
<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={ () =>
|
||||
{
|
||||
CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['payday.habbopage'])
|
||||
} }>{
|
||||
LocalizeText('hccenter.special.infolink')}
|
||||
<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={ () => CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['payday.habbopage']) }>
|
||||
{ LocalizeText('hccenter.special.infolink') }
|
||||
</div>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
@ -193,10 +139,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>
|
||||
@ -215,58 +158,50 @@ export const HcCenterView: FC<{}> = props =>
|
||||
</Flex>
|
||||
{ GetConfiguration('hc.center')['payday.info'] &&
|
||||
<Flex alignItems="center">
|
||||
|
||||
<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>
|
||||
<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>
|
||||
</Column>
|
||||
<div className="payday flex-shrink-0 p-2">
|
||||
<h5 className="mb-2 ms-2">{LocalizeText('hccenter.special.time.title')}</h5>
|
||||
<h5 className="mb-2 ms-2">{ LocalizeText('hccenter.special.time.title') }</h5>
|
||||
<div className="d-flex flex-row mb-2">
|
||||
<div className="clock me-2" />
|
||||
<h6 className="mb-0 align-self-center">{getHcPaydayTime()}</h6>
|
||||
<h6 className="mb-0 align-self-center">{ getHcPaydayTime() }</h6>
|
||||
</div>
|
||||
{clubStatus === ClubStatus.ACTIVE &&
|
||||
{ clubStatus === ClubStatus.ACTIVE &&
|
||||
<div className="pe-3">
|
||||
<h5 className="ms-2 mb-1 bolder">{LocalizeText('hccenter.special.amount.title')}</h5>
|
||||
<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>
|
||||
<div className="w-100 text-center ms-4n">{ getHcPaydayAmount() }</div>
|
||||
<OverlayTrigger trigger={ [ 'hover', 'focus' ] } placement="left" overlay={ popover }>
|
||||
<div className="btn btn-link align-self-end text-primary">
|
||||
{LocalizeText('hccenter.breakdown.infolink')}
|
||||
{ LocalizeText('hccenter.breakdown.infolink') }
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div> }
|
||||
</div>
|
||||
</Flex>
|
||||
}
|
||||
{GetConfiguration('hc.center')['gift.info'] &&
|
||||
</Flex> }
|
||||
{ GetConfiguration('hc.center')['gift.info'] &&
|
||||
<div className="rounded bg-success p-2 d-flex flex-row mb-0">
|
||||
<div>
|
||||
<h4 className="mb-1">{LocalizeText('hccenter.gift.title')}</h4>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
}
|
||||
{GetConfiguration('hc.center')['benefits.info'] &&
|
||||
<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>
|
||||
</div> }
|
||||
{ GetConfiguration('hc.center')['benefits.info'] &&
|
||||
<div className="benefits text-black py-2">
|
||||
<h5 className="mb-1 text-primary">{LocalizeText('hccenter.general.title')}</h5>
|
||||
<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={ () =>
|
||||
{
|
||||
CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['benefits.habbopage'])
|
||||
} }>{LocalizeText('hccenter.general.infolink')}</button>
|
||||
</div>
|
||||
}
|
||||
<button className="btn btn-link p-0 text-primary" onClick={ () => CreateLinkEvent('habbopages/' + GetConfiguration('hc.center')['benefits.habbopage']) }>
|
||||
{ LocalizeText('hccenter.general.infolink') }
|
||||
</button>
|
||||
</div> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -58,28 +58,28 @@ export const SanctionSatusView:FC<{}> = props =>
|
||||
</Column>
|
||||
<Column justifyContent="between" size={ 7 } overflow="hidden">
|
||||
{ (sanctionInfo.sanctionReason === 'cfh.reason.EMPTY')
|
||||
? <div className="col-12 fw-bold">{LocalizeText('help.sanction.current.none')}</div>
|
||||
? <div className="col-12 fw-bold">{ LocalizeText('help.sanction.current.none') }</div>
|
||||
: <>
|
||||
{((sanctionInfo.probationHoursLeft > 0) || (sanctionInfo.isSanctionActive)) &&
|
||||
<div className="col-12 fw-bold">{LocalizeText('help.sanction.probation.reminder')}</div>
|
||||
{ ((sanctionInfo.probationHoursLeft > 0) || (sanctionInfo.isSanctionActive)) &&
|
||||
<div className="col-12 fw-bold">{ LocalizeText('help.sanction.probation.reminder') }</div>
|
||||
}
|
||||
<div className={ `col-12 fw-bold ${ sanctionInfo.isSanctionNew ? 'text-danger' : '' }` }>
|
||||
{LocalizeText('help.sanction.last.sanction')} {sanctionLocalization('current', sanctionInfo.sanctionName, sanctionInfo.sanctionLengthHours)}
|
||||
{ LocalizeText('help.sanction.last.sanction') } { sanctionLocalization('current', sanctionInfo.sanctionName, sanctionInfo.sanctionLengthHours) }
|
||||
</div>
|
||||
<div className="col-12">{LocalizeText('generic.start.time')} {sanctionInfo.sanctionCreationTime}</div>
|
||||
<div className="col-12">{LocalizeText('generic.reason')} {sanctionInfo.sanctionReason}</div>
|
||||
<div className="col-12">{LocalizeText('help.sanction.probation.days.left')} {Math.trunc((sanctionInfo.probationHoursLeft / 24)) + 1}</div>
|
||||
<div className="col-12">{ LocalizeText('generic.start.time') } { sanctionInfo.sanctionCreationTime }</div>
|
||||
<div className="col-12">{ LocalizeText('generic.reason') } { sanctionInfo.sanctionReason }</div>
|
||||
<div className="col-12">{ LocalizeText('help.sanction.probation.days.left') } { Math.trunc((sanctionInfo.probationHoursLeft / 24)) + 1 }</div>
|
||||
</>
|
||||
}
|
||||
{ ((sanctionInfo.hasCustomMute) && (!(sanctionInfo.isSanctionActive))) &&
|
||||
<div className="col-12 fw-bold">{LocalizeText('help.sanction.custom.mute')}</div>
|
||||
<div className="col-12 fw-bold">{ LocalizeText('help.sanction.custom.mute') }</div>
|
||||
}
|
||||
{ (sanctionInfo.tradeLockExpiryTime && sanctionInfo.tradeLockExpiryTime.length > 0) &&
|
||||
<div className="col-12 fw-bold">{LocalizeText('trade.locked.until')} {sanctionInfo.tradeLockExpiryTime}</div>
|
||||
<div className="col-12 fw-bold">{ LocalizeText('trade.locked.until') } { sanctionInfo.tradeLockExpiryTime }</div>
|
||||
}
|
||||
|
||||
<div className="col-12">{sanctionLocalization('next', sanctionInfo.nextSanctionName, sanctionInfo.nextSanctionLengthHours)}</div>
|
||||
<Button variant="success" onClick={ event => setSanctionInfo(null) }>{LocalizeText('habbo.way.ok.button')}</Button>
|
||||
<div className="col-12">{ sanctionLocalization('next', sanctionInfo.nextSanctionName, sanctionInfo.nextSanctionLengthHours) }</div>
|
||||
<Button variant="success" onClick={ event => setSanctionInfo(null) }>{ LocalizeText('habbo.way.ok.button') }</Button>
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
|
@ -97,11 +97,11 @@ export const HotelView: FC<{}> = props =>
|
||||
<div className="left position-absolute" style={ (left && left.length) ? { backgroundImage: `url(${ left })` } : {} } />
|
||||
<div className="right-repeat position-absolute" style={ (rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${ rightRepeat })` } : {} } />
|
||||
<div className="right position-absolute" style={ (right && right.length) ? { backgroundImage: `url(${ right })` } : {} } />
|
||||
{GetConfiguration('hotelview')['show.avatar'] && (
|
||||
{ GetConfiguration('hotelview')['show.avatar'] && (
|
||||
<div className="avatar-image">
|
||||
<LayoutAvatarImageView figure={ userFigure } direction={ 2 } />
|
||||
</div>
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ export const BonusRareWidgetView: FC<BonusRareWidgetViewProps> = props =>
|
||||
<div className="bonus-rare widget d-flex">
|
||||
{ productType }
|
||||
<div className="bg-light-dark rounded overflow-hidden position-relative bonus-bar-container">
|
||||
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{(totalCoinsForBonus - coinsStillRequiredToBuy) + '/' + totalCoinsForBonus}</div>
|
||||
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{ (totalCoinsForBonus - coinsStillRequiredToBuy) + '/' + totalCoinsForBonus }</div>
|
||||
<div className="small bg-info rounded position-absolute top-0 h-100" style={ { width: ((totalCoinsForBonus - coinsStillRequiredToBuy) / totalCoinsForBonus) * 100 + '%' } }></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@ export const HallOfFameItemView: FC<HallOfFameItemViewProps> = props =>
|
||||
<div className="hof-header">
|
||||
{ level }. { data.userName } <UserProfileIconView userId={ data.userId } />
|
||||
</div>
|
||||
<div className="small text-center text-white">{ LocalizeText('landing.view.competition.hof.points', [ 'points' ], [ LocalizeFormattedNumber(data.currentScore).toString() ])}</div>
|
||||
<div className="small text-center text-white">{ LocalizeText('landing.view.competition.hof.points', [ 'points' ], [ LocalizeFormattedNumber(data.currentScore).toString() ]) }</div>
|
||||
</div>
|
||||
<LayoutAvatarImageView figure={ data.figure } direction={ 2 } />
|
||||
</div>
|
||||
|
@ -33,7 +33,7 @@ export const HallOfFameWidgetView: FC<HallOfFameWidgetViewProps> = props =>
|
||||
{
|
||||
return <HallOfFameItemView key={ index } data={ entry } level={ (index + 1) } />;
|
||||
}
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -30,15 +30,15 @@ export const PromoArticleWidgetView: FC<{}> = props =>
|
||||
<hr className="w-100 my-0"/>
|
||||
</div>
|
||||
<div className="d-flex flex-row mb-1">
|
||||
{articles && (articles.length > 0) && articles.map((article, ind) =>
|
||||
{ articles && (articles.length > 0) && articles.map((article, ind) =>
|
||||
<div className={ 'promo-articles-bullet cursor-pointer ' + (article === articles[index] ? 'promo-articles-bullet-active' : '') } key={ article.id } onClick={ event => setIndex(ind) } />
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
{articles && articles[index] &&
|
||||
{ articles && articles[index] &&
|
||||
<div className="promo-article d-flex flex-row row mx-0">
|
||||
<div className="promo-article-image" style={ { backgroundImage: `url(${ articles[index].imageUrl })` } }/>
|
||||
<div className="col-3 d-flex flex-column h-100">
|
||||
<h3 className="my-0">{articles[index].title}</h3>
|
||||
<h3 className="my-0">{ articles[index].title }</h3>
|
||||
<b>{ articles[index].bodyText }</b>
|
||||
<button className="btn btn-sm mt-auto btn-gainsboro" onClick={ event => NotificationUtilities.openUrl(articles[index].linkContent) }>{ articles[index].buttonText }</button>
|
||||
</div>
|
||||
|
@ -29,7 +29,7 @@ export const WidgetContainerView: FC<WidgetContainerViewProps> = props =>
|
||||
<div className="widgetcontainer widget d-flex flex-row overflow-hidden">
|
||||
<div className="widgetcontainer-image flex-shrink-0" style={ { backgroundImage: `url(${ getOption('image') })` } } />
|
||||
<div className="d-flex flex-column align-self-center">
|
||||
<h3 className="my-0">{LocalizeText(`landing.view.${ getOption('texts') }.header`)}</h3>
|
||||
<h3 className="my-0">{ LocalizeText(`landing.view.${ getOption('texts') }.header`) }</h3>
|
||||
<i>{ LocalizeText(`landing.view.${ getOption('texts') }.body`) }</i>
|
||||
<button className="btn btn-sm btn-gainsboro align-self-start px-3 mt-auto" onClick={ event => NotificationUtilities.openUrl(getOption('btnLink')) }>{ LocalizeText(`landing.view.${ getOption('texts') }.button`) }</button>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@ export const LoadingView: FC<LoadingViewProps> = props =>
|
||||
<Base className="connecting-duck" />
|
||||
<Column size={ 6 } className="text-center py-4">
|
||||
{ isError && (message && message.length) ?
|
||||
<Base className="fs-4 text-shadow">{message}</Base>
|
||||
<Base className="fs-4 text-shadow">{ message }</Base>
|
||||
:
|
||||
<>
|
||||
<Text fontSize={ 4 } variant="white" className="text-shadow">{ percent.toFixed() }%</Text>
|
||||
|
@ -36,7 +36,7 @@ export const CfhChatlogView: FC<CfhChatlogViewProps> = props =>
|
||||
<NitroCardView className="nitro-mod-tools-chatlog" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ 'Issue Chatlog' } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{ chatlogData && <ChatlogView records={ [ chatlogData.chatRecord ] } />}
|
||||
{ chatlogData && <ChatlogView records={ [ chatlogData.chatRecord ] } /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -155,7 +155,7 @@ export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = pro
|
||||
<NitroCardContentView className="text-black">
|
||||
<select className="form-select form-select-sm" value={ selectedTopic } onChange={ event => setSelectedTopic(parseInt(event.target.value)) }>
|
||||
<option value={ -1 } disabled>CFH Topic</option>
|
||||
{ topics.map((topic, index) => <option key={ index } value={ index }>{LocalizeText('help.cfh.topic.' + topic.id)}</option>) }
|
||||
{ topics.map((topic, index) => <option key={ index } value={ index }>{ LocalizeText('help.cfh.topic.' + topic.id) }</option>) }
|
||||
</select>
|
||||
<select className="form-select form-select-sm" value={ selectedAction } onChange={ event => setSelectedAction(parseInt(event.target.value)) }>
|
||||
<option value={ -1 } disabled>Sanction Type</option>
|
||||
|
@ -68,7 +68,7 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
|
||||
</Column>
|
||||
</Flex>
|
||||
<Button disabled={ (selectedUserId <= 0) } onClick={ event => unBanUser(selectedUserId) }>
|
||||
{ LocalizeText('navigator.roomsettings.moderation.unban') } {selectedUserId > 0 && bannedUsers.find(user => (user.userId === selectedUserId))?.userName }
|
||||
{ LocalizeText('navigator.roomsettings.moderation.unban') } { selectedUserId > 0 && bannedUsers.find(user => (user.userId === selectedUserId))?.userName }
|
||||
</Button>
|
||||
</Column>
|
||||
<Column size={ 6 }>
|
||||
@ -80,7 +80,7 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
|
||||
</Flex>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{LocalizeText('navigator.roomsettings.moderation.kick.header')}</Text>
|
||||
<Text bold>{ LocalizeText('navigator.roomsettings.moderation.kick.header') }</Text>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" type="checkbox" checked={ (roomData.moderationSettings.allowKick === 0) } onChange={ event => handleChange('moderation_kick', (event.target.checked ? 0 : 2)) } />
|
||||
<Text>{ LocalizeText('navigator.roomsettings.moderation.all') }</Text>
|
||||
@ -91,7 +91,7 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
|
||||
</Flex>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{LocalizeText('navigator.roomsettings.moderation.ban.header')}</Text>
|
||||
<Text bold>{ LocalizeText('navigator.roomsettings.moderation.ban.header') }</Text>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" type="checkbox" checked={ (roomData.moderationSettings.allowBan === 1) } onChange={ event => handleChange('moderation_ban', (event.target.checked ? 1 : 0)) } />
|
||||
<Text>{ LocalizeText('navigator.roomsettings.moderation.rights') }</Text>
|
||||
|
@ -22,13 +22,13 @@ export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabVie
|
||||
return (
|
||||
<>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold>{LocalizeText('navigator.roomsettings.vip.caption')}</Text>
|
||||
<Text>{LocalizeText('navigator.roomsettings.vip.info')}</Text>
|
||||
<Text bold>{ LocalizeText('navigator.roomsettings.vip.caption') }</Text>
|
||||
<Text>{ LocalizeText('navigator.roomsettings.vip.info') }</Text>
|
||||
</Column>
|
||||
<Grid overflow="auto">
|
||||
<Column size={ 6 } gap={ 1 }>
|
||||
<Text bold>{LocalizeText('navigator.roomsettings.chat_settings')}</Text>
|
||||
<Text>{LocalizeText('navigator.roomsettings.chat_settings.info')}</Text>
|
||||
<Text bold>{ LocalizeText('navigator.roomsettings.chat_settings') }</Text>
|
||||
<Text>{ LocalizeText('navigator.roomsettings.chat_settings.info') }</Text>
|
||||
<select className="form-select form-select-sm" value={ roomData.chatSettings.mode } onChange={ event => handleChange('bubble_mode', event.target.value) }>
|
||||
<option value={ RoomChatSettings.CHAT_MODE_FREE_FLOW }>{ LocalizeText('navigator.roomsettings.chat.mode.free.flow') }</option>
|
||||
<option value={ RoomChatSettings.CHAT_MODE_LINE_BY_LINE }>{ LocalizeText('navigator.roomsettings.chat.mode.line.by.line') }</option>
|
||||
@ -52,10 +52,10 @@ export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabVie
|
||||
<input type="number" min="0" className="form-control form-control-sm" value={ chatDistance } onChange={ event => setChatDistance(event.target.valueAsNumber) } onBlur={ event => handleChange('chat_distance', chatDistance) } />
|
||||
</Column>
|
||||
<Column size={ 6 } gap={ 1 }>
|
||||
<Text bold>{LocalizeText('navigator.roomsettings.vip_settings')}</Text>
|
||||
<Text bold>{ LocalizeText('navigator.roomsettings.vip_settings') }</Text>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" type="checkbox" checked={ roomData.hideWalls } onChange={ event => handleChange('hide_walls', event.target.checked) } />
|
||||
<Text>{LocalizeText('navigator.roomsettings.hide_walls')}</Text>
|
||||
<Text>{ LocalizeText('navigator.roomsettings.hide_walls') }</Text>
|
||||
</Flex>
|
||||
<select className="form-select form-select-sm" value={ roomData.wallThickness } onChange={ event => handleChange('wall_thickness', event.target.value) }>
|
||||
<option value="0">{ LocalizeText('navigator.roomsettings.wall_thickness.normal') }</option>
|
||||
|
@ -69,30 +69,30 @@ export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfo
|
||||
<NitroCardContentView overflow="hidden" className="room-info bg-transparent">
|
||||
<Flex gap={ 2 } overflow="hidden">
|
||||
<LayoutRoomThumbnailView roomId={ roomData.roomId } customUrl={ roomData.officialRoomPicRef } className="d-flex flex-column align-items-center justify-content-end mb-1">
|
||||
{roomData.habboGroupId > 0 && (
|
||||
<LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } isGroup={ true } className={ 'position-absolute top-0 start-0 m-1 ' }/>)}
|
||||
{roomData.doorMode !== RoomDataParser.OPEN_STATE && (
|
||||
<i className={ 'position-absolute end-0 mb-1 me-1 icon icon-navigator-room-' + (roomData.doorMode === RoomDataParser.DOORBELL_STATE ? 'locked' : roomData.doorMode === RoomDataParser.PASSWORD_STATE ? 'password' : roomData.doorMode === RoomDataParser.INVISIBLE_STATE ? 'invisible' : '') }/> )}
|
||||
{ roomData.habboGroupId > 0 && (
|
||||
<LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } isGroup={ true } className={ 'position-absolute top-0 start-0 m-1 ' }/>) }
|
||||
{ roomData.doorMode !== RoomDataParser.OPEN_STATE && (
|
||||
<i className={ 'position-absolute end-0 mb-1 me-1 icon icon-navigator-room-' + (roomData.doorMode === RoomDataParser.DOORBELL_STATE ? 'locked' : roomData.doorMode === RoomDataParser.PASSWORD_STATE ? 'password' : roomData.doorMode === RoomDataParser.INVISIBLE_STATE ? 'invisible' : '') }/> ) }
|
||||
</LayoutRoomThumbnailView>
|
||||
<Column gap={ 1 }>
|
||||
<Text bold truncate className="flex-grow-1" style={ { maxHeight: 13 } }>
|
||||
{roomData.roomName}
|
||||
{ roomData.roomName }
|
||||
</Text>
|
||||
<Flex gap={ 1 }>
|
||||
<Text italics variant="muted">
|
||||
{LocalizeText('navigator.roomownercaption')}
|
||||
{ LocalizeText('navigator.roomownercaption') }
|
||||
</Text>
|
||||
<UserProfileIconView
|
||||
userId={ roomData.ownerId }
|
||||
/>
|
||||
<Text italics>{roomData.ownerName}</Text>
|
||||
<Text italics>{ roomData.ownerName }</Text>
|
||||
</Flex>
|
||||
<Text className="flex-grow-1">
|
||||
{roomData.description}
|
||||
{ roomData.description }
|
||||
</Text>
|
||||
<Flex className={ 'badge p-1 position-absolute m-1 bottom-0 end-0 m-2 ' + getUserCounterColor() } gap={ 1 }>
|
||||
<FontAwesomeIcon icon="user" />
|
||||
{roomData.userCount}
|
||||
{ roomData.userCount }
|
||||
</Flex>
|
||||
</Column>
|
||||
</Flex>
|
||||
|
@ -92,7 +92,7 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
|
||||
<i className={ ('position-absolute end-0 mb-1 me-1 icon icon-navigator-room-' + ((roomData.doorMode === RoomDataParser.DOORBELL_STATE) ? 'locked' : (roomData.doorMode === RoomDataParser.PASSWORD_STATE) ? 'password' : (roomData.doorMode === RoomDataParser.INVISIBLE_STATE) ? 'invisible' : '')) } /> }
|
||||
</LayoutRoomThumbnailView>
|
||||
<Flex className="w-100">
|
||||
<Text truncate className="flex-grow-1">{roomData.roomName}</Text>
|
||||
<Text truncate className="flex-grow-1">{ roomData.roomName }</Text>
|
||||
<Flex reverse alignItems="center" gap={ 1 }>
|
||||
<NavigatorSearchResultItemInfoView roomData={ roomData } />
|
||||
</Flex>
|
||||
|
@ -56,11 +56,11 @@ export const NavigatorSearchResultView: FC<NavigatorSearchResultViewProps> = pro
|
||||
<Text>{ LocalizeText(getResultTitle()) }</Text>
|
||||
</Flex>
|
||||
<FontAwesomeIcon icon={ ((displayMode === NavigatorSearchResultViewDisplayMode.LIST) ? 'th' : (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS) ? 'bars' : null) } className="text-secondary" onClick={ toggleDisplayMode } />
|
||||
</Flex> {isExtended &&
|
||||
</Flex> { isExtended &&
|
||||
<>
|
||||
{
|
||||
gridHasTwoColumns ? <AutoGrid columnCount={ 3 } { ...rest } columnMinWidth={ 110 } columnMinHeight={ 130 } className="mx-2">
|
||||
{searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => <NavigatorSearchResultItemView key={ index } roomData={ room } thumbnail={ true } />) }
|
||||
{ searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => <NavigatorSearchResultItemView key={ index } roomData={ room } thumbnail={ true } />) }
|
||||
</AutoGrid> : <Grid columnCount={ 1 } className="navigator-grid" gap={ 0 }>
|
||||
{ searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => <NavigatorSearchResultItemView key={ index } roomData={ room } />) }
|
||||
</Grid>
|
||||
|
@ -25,30 +25,30 @@ export const NotificationDefaultAlertView: FC<NotificationDefaultAlertViewProps>
|
||||
return (
|
||||
<LayoutNotificationAlertView title={ title } close={ close } { ...rest } type={ hasFrank ? NotificationAlertType.DEFAULT : item.alertType }>
|
||||
<Flex fullHeight overflow="auto" gap={ hasFrank || (item.imageUrl && !imageFailed) ? 2 : 0 }>
|
||||
{hasFrank && !item.imageUrl && <Base className="notification-frank flex-shrink-0" /> }
|
||||
{item.imageUrl && !imageFailed && <img src={ item.imageUrl } alt={ item.title } onError={ () =>
|
||||
{ hasFrank && !item.imageUrl && <Base className="notification-frank flex-shrink-0" /> }
|
||||
{ item.imageUrl && !imageFailed && <img src={ item.imageUrl } alt={ item.title } onError={ () =>
|
||||
{
|
||||
setImageFailed(true)
|
||||
} } className="align-self-baseline" />}
|
||||
} } className="align-self-baseline" /> }
|
||||
<Base classNames={ [ 'notification-text overflow-y-auto d-flex flex-column w-100', (item.clickUrl && !hasFrank) ? 'justify-content-center' : '' ] }>
|
||||
{ (item.messages.length > 0) && item.messages.map((message, index) =>
|
||||
{
|
||||
const htmlText = message.replace(/\r\n|\r|\n/g, '<br />');
|
||||
|
||||
return <Base key={ index } dangerouslySetInnerHTML={ { __html: htmlText } } />;
|
||||
})}
|
||||
{item.clickUrl && (item.clickUrl.length > 0) && (item.imageUrl && !imageFailed) && <>
|
||||
}) }
|
||||
{ item.clickUrl && (item.clickUrl.length > 0) && (item.imageUrl && !imageFailed) && <>
|
||||
<hr className="my-2 w-100" />
|
||||
<Button onClick={ visitUrl } className="align-self-center px-3">{LocalizeText(item.clickUrlText)}</Button>
|
||||
</>}
|
||||
<Button onClick={ visitUrl } className="align-self-center px-3">{ LocalizeText(item.clickUrlText) }</Button>
|
||||
</> }
|
||||
</Base>
|
||||
</Flex>
|
||||
{ (!item.imageUrl || (item.imageUrl && imageFailed)) && <>
|
||||
<Column alignItems="center" center gap={ 0 }>
|
||||
<hr className="my-2 w-100" />
|
||||
{ !item.clickUrl &&
|
||||
<Button onClick={ close }>{LocalizeText('generic.close')}</Button>}
|
||||
{ item.clickUrl && (item.clickUrl.length > 0) && <Button onClick={ visitUrl }>{LocalizeText(item.clickUrlText)}</Button> }
|
||||
<Button onClick={ close }>{ LocalizeText('generic.close') }</Button> }
|
||||
{ item.clickUrl && (item.clickUrl.length > 0) && <Button onClick={ visitUrl }>{ LocalizeText(item.clickUrlText) }</Button> }
|
||||
</Column>
|
||||
</> }
|
||||
</LayoutNotificationAlertView>
|
||||
|
@ -42,10 +42,10 @@ export const NotificationSeachAlertView: FC<NotificationDefaultAlertViewProps> =
|
||||
</Flex>
|
||||
<Column fullHeight className="py-1" overflow="hidden">
|
||||
<AutoGrid gap={ 1 } columnCount={ 1 }>
|
||||
{results && results.map((n, index) =>
|
||||
{ results && results.map((n, index) =>
|
||||
{
|
||||
return <span key={ index }>{ n }</span>
|
||||
})}
|
||||
}) }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
<hr className="my-2"/>
|
||||
|
@ -1,18 +0,0 @@
|
||||
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<ProviderProps<IPurseContext>> = props =>
|
||||
{
|
||||
return <PurseContext.Provider value={ props.value }>{ props.children }</PurseContext.Provider>
|
||||
}
|
||||
|
||||
export const usePurseContext = () => useContext(PurseContext);
|
@ -1,21 +1,14 @@
|
||||
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 { PurseContextProvider } from './PurseContext';
|
||||
import { usePurse } from '../../hooks';
|
||||
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,135 +58,32 @@ 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 (
|
||||
<PurseContextProvider value={ { purse } }>
|
||||
<Column alignItems="end" className="nitro-purse-container" gap={ 1 }>
|
||||
<Flex className="nitro-purse rounded-bottom p-1">
|
||||
<Grid fullWidth gap={ 1 }>
|
||||
<Column justifyContent="center" size={ hcDisabled ? 10 : 6 } gap={ 0 }>
|
||||
<CurrencyView type={ -1 } amount={ purse.credits } short={ currencyDisplayNumberShort } />
|
||||
{ getCurrencyElements(0, 2) }
|
||||
</Column>
|
||||
{ !hcDisabled &&
|
||||
<Column center pointer size={ 4 } gap={ 1 } className="nitro-purse-subscription rounded" onClick={ event => CreateLinkEvent('habboUI/open/hccenter') }>
|
||||
<LayoutCurrencyIcon type="hc" />
|
||||
<Text variant="white">{ getClubText }</Text>
|
||||
</Column> }
|
||||
<Column justifyContent="center" size={ 2 } gap={ 0 }>
|
||||
<Flex center pointer fullHeight className="nitro-purse-button p-1 rounded" onClick={ event => CreateLinkEvent('help/show') }>
|
||||
<i className="icon icon-help"/>
|
||||
</Flex>
|
||||
<Flex center pointer fullHeight className="nitro-purse-button p-1 rounded" onClick={ event => CreateLinkEvent('user-settings/toggle') } >
|
||||
<i className="icon icon-cog"/>
|
||||
</Flex>
|
||||
</Column>
|
||||
</Grid>
|
||||
</Flex>
|
||||
{ getCurrencyElements(2, -1, true) }
|
||||
</Column>
|
||||
</PurseContextProvider>
|
||||
<Column alignItems="end" className="nitro-purse-container" gap={ 1 }>
|
||||
<Flex className="nitro-purse rounded-bottom p-1">
|
||||
<Grid fullWidth gap={ 1 }>
|
||||
<Column justifyContent="center" size={ hcDisabled ? 10 : 6 } gap={ 0 }>
|
||||
<CurrencyView type={ -1 } amount={ purse.credits } short={ currencyDisplayNumberShort } />
|
||||
{ getCurrencyElements(0, 2) }
|
||||
</Column>
|
||||
{ !hcDisabled &&
|
||||
<Column center pointer size={ 4 } gap={ 1 } className="nitro-purse-subscription rounded" onClick={ event => CreateLinkEvent('habboUI/open/hccenter') }>
|
||||
<LayoutCurrencyIcon type="hc" />
|
||||
<Text variant="white">{ getClubText }</Text>
|
||||
</Column> }
|
||||
<Column justifyContent="center" size={ 2 } gap={ 0 }>
|
||||
<Flex center pointer fullHeight className="nitro-purse-button p-1 rounded" onClick={ event => CreateLinkEvent('help/show') }>
|
||||
<i className="icon icon-help"/>
|
||||
</Flex>
|
||||
<Flex center pointer fullHeight className="nitro-purse-button p-1 rounded" onClick={ event => CreateLinkEvent('user-settings/toggle') } >
|
||||
<i className="icon icon-cog"/>
|
||||
</Flex>
|
||||
</Column>
|
||||
</Grid>
|
||||
</Flex>
|
||||
{ getCurrencyElements(2, -1, true) }
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -215,7 +215,7 @@ export const AvatarInfoWidgetAvatarView: FC<AvatarInfoWidgetAvatarViewProps> = p
|
||||
return (
|
||||
<ContextMenuView objectId={ userData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ userData.userType } close={ close }>
|
||||
<ContextMenuHeaderView className="cursor-pointer" onClick={ event => GetUserProfile(userData.webID) }>
|
||||
{userData.name}
|
||||
{ userData.name }
|
||||
</ContextMenuHeaderView>
|
||||
{ (mode === MODE_NORMAL) &&
|
||||
<>
|
||||
@ -232,10 +232,10 @@ export const AvatarInfoWidgetAvatarView: FC<AvatarInfoWidgetAvatarViewProps> = p
|
||||
{ (respectsLeft > 0) &&
|
||||
<ContextMenuListItemView onClick={ event => processAction('respect') }>
|
||||
{ LocalizeText('infostand.button.respect', [ 'count' ], [ respectsLeft.toString() ]) }
|
||||
</ContextMenuListItemView>}
|
||||
</ContextMenuListItemView> }
|
||||
{ !canRequestFriend(userData.webID) &&
|
||||
<ContextMenuListItemView onClick={ event => processAction('relationship') }>
|
||||
{LocalizeText('infostand.link.relationship')}
|
||||
{ LocalizeText('infostand.link.relationship') }
|
||||
<FontAwesomeIcon icon="chevron-right" className="right" />
|
||||
</ContextMenuListItemView> }
|
||||
{ !userData.isIgnored &&
|
||||
@ -356,7 +356,7 @@ export const AvatarInfoWidgetAvatarView: FC<AvatarInfoWidgetAvatarViewProps> = p
|
||||
<FontAwesomeIcon icon="chevron-left" className="left" />
|
||||
{ LocalizeText('generic.back') }
|
||||
</ContextMenuListItemView>
|
||||
</>}
|
||||
</> }
|
||||
{ (mode === MODE_RELATIONSHIP) &&
|
||||
<>
|
||||
<Flex className="menu-list-split-3">
|
||||
|
@ -362,7 +362,7 @@ export const ChatInputView: FC<{}> = props =>
|
||||
{ !floodBlocked &&
|
||||
<input ref={ inputRef } type="text" className="chat-input" placeholder={ LocalizeText('widgets.chatinput.default') } value={ chatValue } maxLength={ maxChatLength } onChange={ event => updateChatInput(event.target.value) } onMouseDown={ event => setInputFocus() } /> }
|
||||
{ floodBlocked &&
|
||||
<Text variant="danger">{ LocalizeText('chat.input.alert.flood', [ 'time' ], [ floodBlockedSeconds.toString() ]) } </Text>}
|
||||
<Text variant="danger">{ LocalizeText('chat.input.alert.flood', [ 'time' ], [ floodBlockedSeconds.toString() ]) } </Text> }
|
||||
</div>
|
||||
<ChatInputStyleSelectorView chatStyleId={ chatStyleId } chatStyleIds={ chatStyleIds } selectChatStyleId={ selectChatStyleId } />
|
||||
</div>, document.getElementById('toolbar-chat-input-container'))
|
||||
|
@ -236,7 +236,7 @@ export const ChatWidgetView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<div ref={ elementRef } className="nitro-chat-widget">
|
||||
{chatMessages.map(chat => <ChatWidgetMessageView key={ chat.id } chat={ chat } makeRoom={ makeRoom } onChatClicked={ onChatClicked } bubbleWidth={ chatSettings.weight } />)}
|
||||
{ chatMessages.map(chat => <ChatWidgetMessageView key={ chat.id } chat={ chat } makeRoom={ makeRoom } onChatClicked={ onChatClicked } bubbleWidth={ chatSettings.weight } />) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ export const FurnitureHighScoreView: FC<{}> = props =>
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</Column>
|
||||
</ContextMenuListView>
|
||||
</Column>
|
||||
|
@ -121,7 +121,7 @@ export const FurnitureStickieView: FC<{}> = props =>
|
||||
{ STICKIE_COLORS.map(color =>
|
||||
{
|
||||
return <div key={ color } className="stickie-color ms-1" onClick={ event => processAction('changeColor', color) } style={ { backgroundColor: ColorUtils.makeColorHex(color) } } />
|
||||
})}
|
||||
}) }
|
||||
</> }
|
||||
</div>
|
||||
<div className="d-flex align-items-center nitro-stickie-image stickie-close header-close" onClick={ event => processAction('close') }></div>
|
||||
|
@ -208,11 +208,11 @@ export const FurnitureYoutubeDisplayView: FC<{}> = props =>
|
||||
<NitroCardContentView>
|
||||
<div className="row w-100 h-100">
|
||||
<div className="youtube-video-container col-9">
|
||||
{(videoId && videoId.length > 0) &&
|
||||
{ (videoId && videoId.length > 0) &&
|
||||
<YouTube videoId={ videoId } opts={ getYoutubeOpts as Options } onReady={ onReady } onStateChange={ onStateChange } containerClassName={ 'youtubeContainer' } />
|
||||
}
|
||||
{(!videoId || videoId.length === 0) &&
|
||||
<div className="empty-video w-100 h-100 justify-content-center align-items-center d-flex">{LocalizeText('widget.furni.video_viewer.no_videos')}</div>
|
||||
{ (!videoId || videoId.length === 0) &&
|
||||
<div className="empty-video w-100 h-100 justify-content-center align-items-center d-flex">{ LocalizeText('widget.furni.video_viewer.no_videos') }</div>
|
||||
}
|
||||
</div>
|
||||
<div className="playlist-container col-3">
|
||||
@ -220,16 +220,16 @@ export const FurnitureYoutubeDisplayView: FC<{}> = props =>
|
||||
<i className="icon icon-youtube-prev cursor-pointer" onClick={ () => processAction('playlist_prev') } />
|
||||
<i className="icon icon-youtube-next cursor-pointer" onClick={ () => processAction('playlist_next') } />
|
||||
</span>
|
||||
<div className="mb-1">{LocalizeText('widget.furni.video_viewer.playlists')}</div>
|
||||
<div className="mb-1">{ LocalizeText('widget.furni.video_viewer.playlists') }</div>
|
||||
<Grid columnCount={ 1 } className="playlist-grid">
|
||||
{playlists && playlists.map((entry, index) =>
|
||||
{ playlists && playlists.map((entry, index) =>
|
||||
{
|
||||
return (
|
||||
<LayoutGridItem key={ index } onClick={ () => processAction(entry.video) } itemActive={ entry.video === selectedItem }>
|
||||
<b>{entry.title}</b> - {entry.description}
|
||||
<b>{ entry.title }</b> - { entry.description }
|
||||
</LayoutGridItem>
|
||||
)
|
||||
})}
|
||||
}) }
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -369,7 +369,7 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
|
||||
{ canUse &&
|
||||
<Button variant="dark" onClick={ event => processButtonAction('use') }>
|
||||
{ LocalizeText('infostand.button.use') }
|
||||
</Button>}
|
||||
</Button> }
|
||||
{ ((furniKeys.length > 0 && furniValues.length > 0) && (furniKeys.length === furniValues.length)) &&
|
||||
<Button variant="dark" onClick={ () => processButtonAction('save_branding_configuration') }>
|
||||
{ LocalizeText('save') }
|
||||
|
@ -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
1
src/hooks/purse/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './usePurse';
|
134
src/hooks/purse/usePurse.ts
Normal file
134
src/hooks/purse/usePurse.ts
Normal 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);
|
Loading…
Reference in New Issue
Block a user