Merge branch 'dev' into @update/room-widgets

This commit is contained in:
Bill 2022-04-20 19:46:40 -04:00
commit f57df3b57f
113 changed files with 1509 additions and 1493 deletions

View File

@ -15,7 +15,7 @@
"@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.1.0", "@fortawesome/free-solid-svg-icons": "^6.1.0",
"@fortawesome/react-fontawesome": "^0.1.17", "@fortawesome/react-fontawesome": "^0.1.17",
"@nitrots/nitro-renderer": "^1.2.2", "@nitrots/nitro-renderer": "^1.2.3",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",

View File

@ -16,6 +16,43 @@
"badge.descriptions.enabled": true, "badge.descriptions.enabled": true,
"motto.max.length": 38, "motto.max.length": 38,
"bot.name.max.length": 15, "bot.name.max.length": 15,
"navigator.room.models": [
{ "clubLevel": 0, "tileSize": 104, "name": "a" },
{ "clubLevel": 0, "tileSize": 94, "name": "b" },
{ "clubLevel": 0, "tileSize": 36, "name": "c" },
{ "clubLevel": 0, "tileSize": 84, "name": "d" },
{ "clubLevel": 0, "tileSize": 80, "name": "e" },
{ "clubLevel": 0, "tileSize": 80, "name": "f" },
{ "clubLevel": 0, "tileSize": 416, "name": "i" },
{ "clubLevel": 0, "tileSize": 320, "name": "j" },
{ "clubLevel": 0, "tileSize": 448, "name": "k" },
{ "clubLevel": 0, "tileSize": 352, "name": "l" },
{ "clubLevel": 0, "tileSize": 384, "name": "m" },
{ "clubLevel": 0, "tileSize": 372, "name": "n" },
{ "clubLevel": 1, "tileSize": 80, "name": "g" },
{ "clubLevel": 1, "tileSize": 74, "name": "h" },
{ "clubLevel": 1, "tileSize": 416, "name": "o" },
{ "clubLevel": 1, "tileSize": 352, "name": "p" },
{ "clubLevel": 1, "tileSize": 304, "name": "q" },
{ "clubLevel": 1, "tileSize": 336, "name": "r" },
{ "clubLevel": 1, "tileSize": 748, "name": "u" },
{ "clubLevel": 1, "tileSize": 438, "name": "v" },
{ "clubLevel": 2, "tileSize": 540, "name": "t" },
{ "clubLevel": 2, "tileSize": 512, "name": "w" },
{ "clubLevel": 2, "tileSize": 396, "name": "x" },
{ "clubLevel": 2, "tileSize": 440, "name": "y" },
{ "clubLevel": 2, "tileSize": 456, "name": "z" },
{ "clubLevel": 2, "tileSize": 208, "name": "0" },
{ "clubLevel": 2, "tileSize": 1009, "name": "1" },
{ "clubLevel": 2, "tileSize": 1044, "name": "2" },
{ "clubLevel": 2, "tileSize": 183, "name": "3" },
{ "clubLevel": 2, "tileSize": 254, "name": "4" },
{ "clubLevel": 2, "tileSize": 1024, "name": "5" },
{ "clubLevel": 2, "tileSize": 801, "name": "6" },
{ "clubLevel": 2, "tileSize": 354, "name": "7" },
{ "clubLevel": 2, "tileSize": 888, "name": "8" },
{ "clubLevel": 2, "tileSize": 926, "name": "9" }
],
"hotelview": { "hotelview": {
"show.avatar": true, "show.avatar": true,
"widgets": { "widgets": {

View File

@ -0,0 +1,10 @@
export class BuilderFurniPlaceableStatus
{
public static OKAY: number = 0;
public static MISSING_OFFER: number = 1;
public static FURNI_LIMIT_REACHED: number = 2;
public static NOT_IN_ROOM: number = 3;
public static NOT_ROOM_OWNER: number = 4;
public static GUILD_ROOM: number = 5;
public static VISITORS_IN_ROOM: number = 6;
}

View File

@ -1,5 +1,5 @@
import { SellablePetPaletteData } from '@nitrots/nitro-renderer'; import { SellablePetPaletteData } from '@nitrots/nitro-renderer';
import { GetRoomEngine } from '../../../api'; import { GetRoomEngine } from '../nitro';
import { ICatalogNode } from './ICatalogNode'; import { ICatalogNode } from './ICatalogNode';
export const GetPixelEffectIcon = (id: number) => export const GetPixelEffectIcon = (id: number) =>

View File

@ -1,5 +1,5 @@
import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer'; import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer';
import { GetProductDataForLocalization, SendMessageComposer } from '../../../api'; import { GetProductDataForLocalization, SendMessageComposer } from '..';
import { ICatalogPage } from './ICatalogPage'; import { ICatalogPage } from './ICatalogPage';
import { IProduct } from './IProduct'; import { IProduct } from './IProduct';
import { IPurchasableOffer } from './IPurchasableOffer'; import { IPurchasableOffer } from './IPurchasableOffer';

View File

@ -1,4 +1,4 @@
import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, ProductTypeEnum } from '../../../api'; import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, ProductTypeEnum } from '..';
import { ICatalogPage } from './ICatalogPage'; import { ICatalogPage } from './ICatalogPage';
import { IProduct } from './IProduct'; import { IProduct } from './IProduct';
import { IPurchasableOffer } from './IPurchasableOffer'; import { IPurchasableOffer } from './IPurchasableOffer';

View File

@ -1,4 +1,4 @@
import { GetConfiguration } from '../../../api'; import { GetConfiguration } from '../nitro';
import { IPageLocalization } from './IPageLocalization'; import { IPageLocalization } from './IPageLocalization';
export class PageLocalization implements IPageLocalization export class PageLocalization implements IPageLocalization

View File

@ -0,0 +1,41 @@
import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer';
import { IPurchasableOffer } from './IPurchasableOffer';
export class PlacedObjectPurchaseData
{
constructor(
public readonly roomId: number,
public readonly objectId: number,
public readonly category: number,
public readonly wallLocation: string,
public readonly x: number,
public readonly y: number,
public readonly direction: number,
public readonly offer: IPurchasableOffer)
{}
public get offerId(): number
{
return this.offer.offerId;
}
public get productClassId(): number
{
return this.offer.product.productClassId;
}
public get productData(): IProductData
{
return this.offer.product.productData;
}
public get furniData(): IFurnitureData
{
return this.offer.product.furnitureData;
}
public get extraParam(): string
{
return this.offer.product.extraParam;
}
}

View File

@ -1,8 +1,9 @@
import { IFurnitureData, IObjectData, IProductData } from '@nitrots/nitro-renderer'; import { IFurnitureData, IObjectData, IProductData } from '@nitrots/nitro-renderer';
import { GetConfiguration, GetRoomEngine, GetSessionDataManager, ProductTypeEnum } from '../../../api'; import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../nitro';
import { GetPixelEffectIcon, GetSubscriptionProductIcon } from './CatalogUtilities'; import { GetPixelEffectIcon, GetSubscriptionProductIcon } from './CatalogUtilities';
import { IProduct } from './IProduct'; import { IProduct } from './IProduct';
import { IPurchasableOffer } from './IPurchasableOffer'; import { IPurchasableOffer } from './IPurchasableOffer';
import { ProductTypeEnum } from './ProductTypeEnum';
export class Product implements IProduct export class Product implements IProduct
{ {

View File

@ -1 +1,24 @@
export * from './BuilderFurniPlaceableStatus';
export * from './CatalogNode';
export * from './CatalogPage';
export * from './CatalogPageName';
export * from './CatalogPetPalette';
export * from './CatalogPurchaseState';
export * from './CatalogType';
export * from './CatalogUtilities';
export * from './FurnitureOffer';
export * from './GiftWrappingConfiguration';
export * from './ICatalogNode';
export * from './ICatalogOptions';
export * from './ICatalogPage';
export * from './IPageLocalization';
export * from './IProduct';
export * from './IPurchasableOffer';
export * from './IPurchaseOptions';
export * from './Offer';
export * from './PageLocalization';
export * from './PlacedObjectPurchaseData';
export * from './Product';
export * from './ProductTypeEnum'; export * from './ProductTypeEnum';
export * from './RequestedPage';
export * from './SearchResult';

View File

@ -95,7 +95,7 @@ export class FurnitureItem implements IFurnitureItem
return this._extra; return this._extra;
} }
public get _Str_16260(): boolean public get recyclable(): boolean
{ {
return this._recyclable; return this._recyclable;
} }
@ -135,17 +135,17 @@ export class FurnitureItem implements IFurnitureItem
return time; return time;
} }
public get _Str_8932(): number public get creationDay(): number
{ {
return this._creationDay; return this._creationDay;
} }
public get _Str_9050(): number public get creationMonth(): number
{ {
return this._creationMonth; return this._creationMonth;
} }
public get _Str_9408(): number public get creationYear(): number
{ {
return this._creationYear; return this._creationYear;
} }
@ -155,7 +155,7 @@ export class FurnitureItem implements IFurnitureItem
return this._slotId; return this._slotId;
} }
public get _Str_3951(): number public get songId(): number
{ {
return this._songId; return this._songId;
} }
@ -185,7 +185,7 @@ export class FurnitureItem implements IFurnitureItem
return this._hasRentPeriodStarted; return this._hasRentPeriodStarted;
} }
public get _Str_10616(): number public get expirationTimeStamp(): number
{ {
return this._expirationTimeStamp; return this._expirationTimeStamp;
} }

View File

@ -8,7 +8,7 @@ export interface IFurnitureItem
stuffData: IObjectData; stuffData: IObjectData;
extra: number; extra: number;
category: number; category: number;
_Str_16260: boolean; recyclable: boolean;
isTradable: boolean; isTradable: boolean;
isGroupable: boolean; isGroupable: boolean;
sellable: boolean; sellable: boolean;

View File

@ -0,0 +1,6 @@
export interface IRoomModel
{
clubLevel: number;
tileSize: number;
name: string;
}

View File

@ -1,46 +0,0 @@
import { HabboClubLevelEnum } from '@nitrots/nitro-renderer';
export interface IRoomModel
{
clubLevel: number;
tileSize: number;
name: string;
}
export const RoomModels: IRoomModel[] = [
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 104, name: 'a' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 94, name: 'b' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 36, name: 'c' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 84, name: 'd' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 80, name: 'e' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 80, name: 'f' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 416, name: 'i' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 320, name: 'j' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 448, name: 'k' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 352, name: 'l' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 384, name: 'm' },
{ clubLevel: HabboClubLevelEnum.NO_CLUB, tileSize: 372, name: 'n' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 80, name: 'g' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 74, name: 'h' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 416, name: 'o' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 352, name: 'p' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 304, name: 'q' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 336, name: 'r' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 748, name: 'u' },
{ clubLevel: HabboClubLevelEnum.CLUB, tileSize: 438, name: 'v' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 540, name: 't' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 512, name: 'w' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 396, name: 'x' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 440, name: 'y' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 456, name: 'z' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 208, name: '0' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 1009, name: '1' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 1044, name: '2' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 183, name: '3' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 254, name: '4' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 1024, name: '5' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 801, name: '6' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 354, name: '7' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 888, name: '8' },
{ clubLevel: HabboClubLevelEnum.VIP, tileSize: 926, name: '9' }
];

View File

@ -3,10 +3,10 @@ export * from './INavigatorData';
export * from './INavigatorSearchFilter'; export * from './INavigatorSearchFilter';
export * from './IRoomChatSettings'; export * from './IRoomChatSettings';
export * from './IRoomData'; export * from './IRoomData';
export * from './IRoomModel';
export * from './IRoomModerationSettings'; export * from './IRoomModerationSettings';
export * from './NavigatorSearchResultViewDisplayMode'; export * from './NavigatorSearchResultViewDisplayMode';
export * from './RoomInfoData'; export * from './RoomInfoData';
export * from './RoomModels';
export * from './RoomSettingsData'; export * from './RoomSettingsData';
export * from './RoomSettingsUtils'; export * from './RoomSettingsUtils';
export * from './SearchFilterOptions'; export * from './SearchFilterOptions';

View File

@ -0,0 +1,5 @@
export class LocalStorageKeys
{
public static CATALOG_PLACE_MULTIPLE_OBJECTS: string = 'catalogPlaceMultipleObjects';
public static CATALOG_SKIP_PURCHASE_CONFIRMATION: string = 'catalogSkipPurchaseConfirmation';
}

View File

@ -5,6 +5,7 @@ export * from './LocalizeBageName';
export * from './LocalizeFormattedNumber'; export * from './LocalizeFormattedNumber';
export * from './LocalizeShortNumber'; export * from './LocalizeShortNumber';
export * from './LocalizeText'; export * from './LocalizeText';
export * from './LocalStorageKeys';
export * from './PlaySound'; export * from './PlaySound';
export * from './ProductImageUtility'; export * from './ProductImageUtility';
export * from './Randomizer'; export * from './Randomizer';

View File

@ -1,4 +1,5 @@
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetConfiguration } from '../../../../api';
import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common';
import { AvatarEditorGridPartItem } from '../../common/AvatarEditorGridPartItem'; import { AvatarEditorGridPartItem } from '../../common/AvatarEditorGridPartItem';
import { AvatarEditorIcon } from '../AvatarEditorIcon'; import { AvatarEditorIcon } from '../AvatarEditorIcon';
@ -13,6 +14,8 @@ export const AvatarEditorFigureSetItemView: FC<AvatarEditorFigureSetItemViewProp
const { partItem = null, children = null, ...rest } = props; const { partItem = null, children = null, ...rest } = props;
const [ updateId, setUpdateId ] = useState(-1); const [ updateId, setUpdateId ] = useState(-1);
const hcDisabled = GetConfiguration<boolean>('hc.disabled', false);
const rerender = useCallback(() => const rerender = useCallback(() =>
{ {
setUpdateId(prevValue => (prevValue + 1)); setUpdateId(prevValue => (prevValue + 1));
@ -30,7 +33,7 @@ export const AvatarEditorFigureSetItemView: FC<AvatarEditorFigureSetItemViewProp
return ( return (
<LayoutGridItem itemImage={ (partItem.isClear ? undefined : partItem.imageUrl) } itemActive={ partItem.isSelected } { ...rest }> <LayoutGridItem itemImage={ (partItem.isClear ? undefined : partItem.imageUrl) } itemActive={ partItem.isSelected } { ...rest }>
{ partItem.isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type={ 'hc' } /> } { !hcDisabled && partItem.isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type="hc" /> }
{ partItem.isClear && <AvatarEditorIcon icon="clear" /> } { partItem.isClear && <AvatarEditorIcon icon="clear" /> }
{ partItem.isSellable && <AvatarEditorIcon icon="sellable" position="absolute" className="end-1 bottom-1" /> } { partItem.isSellable && <AvatarEditorIcon icon="sellable" position="absolute" className="end-1 bottom-1" /> }
{ children } { children }

View File

@ -1,4 +1,5 @@
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetConfiguration } from '../../../../api';
import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common';
import { AvatarEditorGridColorItem } from '../../common/AvatarEditorGridColorItem'; import { AvatarEditorGridColorItem } from '../../common/AvatarEditorGridColorItem';
@ -12,6 +13,8 @@ export const AvatarEditorPaletteSetItem: FC<AvatarEditorPaletteSetItemProps> = p
const { colorItem = null, children = null, ...rest } = props; const { colorItem = null, children = null, ...rest } = props;
const [ updateId, setUpdateId ] = useState(-1); const [ updateId, setUpdateId ] = useState(-1);
const hcDisabled = GetConfiguration<boolean>('hc.disabled', false);
const rerender = useCallback(() => const rerender = useCallback(() =>
{ {
setUpdateId(prevValue => (prevValue + 1)); setUpdateId(prevValue => (prevValue + 1));
@ -26,7 +29,7 @@ export const AvatarEditorPaletteSetItem: FC<AvatarEditorPaletteSetItemProps> = p
return ( return (
<LayoutGridItem itemHighlight itemColor={ colorItem.color } itemActive={ colorItem.isSelected } className="clear-bg" { ...rest }> <LayoutGridItem itemHighlight itemColor={ colorItem.color } itemActive={ colorItem.isSelected } className="clear-bg" { ...rest }>
{ colorItem.isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type={ 'hc' } /> } { !hcDisabled && colorItem.isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type="hc" /> }
{ children } { children }
</LayoutGridItem> </LayoutGridItem>
); );

View File

@ -1,6 +1,6 @@
import { IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer'; import { IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer';
import { Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react'; import { Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react';
import { GetAvatarRenderManager, GetClubMemberLevel, LocalizeText, SendMessageComposer } from '../../../../api'; import { GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, LocalizeText, SendMessageComposer } from '../../../../api';
import { AutoGrid, Base, Button, Flex, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../../../common'; import { AutoGrid, Base, Button, Flex, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../../../common';
import { FigureData } from '../../common/FigureData'; import { FigureData } from '../../common/FigureData';
@ -16,6 +16,8 @@ export const AvatarEditorWardrobeView: FC<AvatarEditorWardrobeViewProps> = props
{ {
const { figureData = null, savedFigures = [], setSavedFigures = null, loadAvatarInEditor = null } = props; const { figureData = null, savedFigures = [], setSavedFigures = null, loadAvatarInEditor = null } = props;
const hcDisabled = GetConfiguration<boolean>('hc.disabled', false);
const wearFigureAtIndex = useCallback((index: number) => const wearFigureAtIndex = useCallback((index: number) =>
{ {
if((index >= savedFigures.length) || (index < 0)) return; if((index >= savedFigures.length) || (index < 0)) return;
@ -57,7 +59,7 @@ export const AvatarEditorWardrobeView: FC<AvatarEditorWardrobeViewProps> = props
{ figureContainer && { figureContainer &&
<LayoutAvatarImageView figure={ figureContainer.getFigureString() } gender={ gender } direction={ 2 } /> } <LayoutAvatarImageView figure={ figureContainer.getFigureString() } gender={ gender } direction={ 2 } /> }
<Base className="avatar-shadow" /> <Base className="avatar-shadow" />
{ (clubLevel > 0) && <LayoutCurrencyIcon className="position-absolute top-1 start-1" type="hc" /> } { !hcDisabled && (clubLevel > 0) && <LayoutCurrencyIcon className="position-absolute top-1 start-1" type="hc" /> }
<Flex gap={ 1 } className="button-container"> <Flex gap={ 1 } className="button-container">
<Button variant="link" fullWidth onClick={ event => saveFigureAtWardrobeIndex(index) }>{ LocalizeText('avatareditor.wardrobe.save') }</Button> <Button variant="link" fullWidth onClick={ event => saveFigureAtWardrobeIndex(index) }>{ LocalizeText('avatareditor.wardrobe.save') }</Button>
{ figureContainer && { figureContainer &&
@ -68,7 +70,7 @@ export const AvatarEditorWardrobeView: FC<AvatarEditorWardrobeViewProps> = props
}); });
return items; return items;
}, [ savedFigures, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); }, [ savedFigures, hcDisabled, saveFigureAtWardrobeIndex, wearFigureAtIndex ]);
return ( return (
<AutoGrid columnCount={ 5 } columnMinWidth={ 80 } columnMinHeight={ 140 }> <AutoGrid columnCount={ 5 } columnMinWidth={ 80 } columnMinHeight={ 140 }>

View File

@ -1,83 +0,0 @@
import { FrontPageItem, RoomPreviewer } from '@nitrots/nitro-renderer';
import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react';
import { ICatalogNode } from './common/ICatalogNode';
import { ICatalogOptions } from './common/ICatalogOptions';
import { ICatalogPage } from './common/ICatalogPage';
import { IPageLocalization } from './common/IPageLocalization';
import { IPurchasableOffer } from './common/IPurchasableOffer';
import { IPurchaseOptions } from './common/IPurchaseOptions';
import { SearchResult } from './common/SearchResult';
interface ICatalogContext
{
isVisible: boolean;
isBusy: boolean;
setIsBusy: Dispatch<SetStateAction<boolean>>;
pageId: number;
currentType: string;
setCurrentType: Dispatch<SetStateAction<string>>;
rootNode: ICatalogNode;
setRootNode: Dispatch<SetStateAction<ICatalogNode>>;
offersToNodes: Map<number, ICatalogNode[]>;
setOffersToNodes: Dispatch<SetStateAction<Map<number, ICatalogNode[]>>>;
currentPage: ICatalogPage;
setCurrentPage: Dispatch<SetStateAction<ICatalogPage>>;
currentOffer: IPurchasableOffer;
setCurrentOffer: Dispatch<SetStateAction<IPurchasableOffer>>;
activeNodes: ICatalogNode[];
setActiveNodes: Dispatch<SetStateAction<ICatalogNode[]>>;
searchResult: SearchResult;
setSearchResult: Dispatch<SetStateAction<SearchResult>>;
frontPageItems: FrontPageItem[];
setFrontPageItems: Dispatch<SetStateAction<FrontPageItem[]>>;
roomPreviewer: RoomPreviewer;
purchaseOptions: IPurchaseOptions;
setPurchaseOptions: Dispatch<SetStateAction<IPurchaseOptions>>;
catalogOptions: ICatalogOptions;
setCatalogOptions: Dispatch<SetStateAction<ICatalogOptions>>;
resetState: () => void;
getNodesByOfferId: (offerId: number, flag?: boolean) => ICatalogNode[];
loadCatalogPage: (pageId: number, offerId: number) => void;
showCatalogPage: (pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) => void;
activateNode: (targetNode: ICatalogNode) => void;
}
const CatalogContext = createContext<ICatalogContext>({
isVisible: null,
isBusy: null,
setIsBusy: null,
pageId: null,
currentType: null,
setCurrentType: null,
rootNode: null,
setRootNode: null,
offersToNodes: null,
setOffersToNodes: null,
currentPage: null,
setCurrentPage: null,
currentOffer: null,
setCurrentOffer: null,
activeNodes: null,
setActiveNodes: null,
searchResult: null,
setSearchResult: null,
frontPageItems: null,
setFrontPageItems: null,
roomPreviewer: null,
purchaseOptions: null,
setPurchaseOptions: null,
catalogOptions: null,
setCatalogOptions: null,
resetState: null,
getNodesByOfferId: null,
loadCatalogPage: null,
showCatalogPage: null,
activateNode: null
});
export const CatalogContextProvider: FC<ProviderProps<ICatalogContext>> = props =>
{
return <CatalogContext.Provider value={ props.value }>{ props.children }</CatalogContext.Provider>
}
export const useCatalogContext = () => useContext(CatalogContext);

View File

@ -1,291 +0,0 @@
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';
import { CatalogGiftReceiverNotFoundEvent, CatalogNameResultEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent } from '../../events';
import { DispatchUiEvent, UseMessageEventHook } from '../../hooks';
import { useCatalogContext } from './CatalogContext';
import { CatalogNode } from './common/CatalogNode';
import { CatalogPetPalette } from './common/CatalogPetPalette';
import { CatalogType } from './common/CatalogType';
import { GiftWrappingConfiguration } from './common/GiftWrappingConfiguration';
import { ICatalogNode } from './common/ICatalogNode';
import { IProduct } from './common/IProduct';
import { IPurchasableOffer } from './common/IPurchasableOffer';
import { Offer } from './common/Offer';
import { PageLocalization } from './common/PageLocalization';
import { Product } from './common/Product';
export const CatalogMessageHandler: FC<{}> = props =>
{
const { setIsBusy, pageId, currentType, setRootNode, setOffersToNodes, currentPage, setCurrentOffer, setFrontPageItems, resetState, showCatalogPage, setCatalogOptions, setPurchaseOptions } = useCatalogContext();
const onCatalogPagesListEvent = useCallback((event: CatalogPagesListEvent) =>
{
const parser = event.getParser();
const offers: Map<number, ICatalogNode[]> = new Map();
const getCatalogNode = (node: NodeData, depth: number, parent: ICatalogNode) =>
{
const catalogNode = (new CatalogNode(node, depth, parent) as ICatalogNode);
for(const offerId of catalogNode.offerIds)
{
if(offers.has(offerId)) offers.get(offerId).push(catalogNode);
else offers.set(offerId, [ catalogNode ]);
}
depth++;
for(const child of node.children) catalogNode.addChild(getCatalogNode(child, depth, catalogNode));
return catalogNode;
}
setRootNode(getCatalogNode(parser.root, 0, null));
setOffersToNodes(offers);
}, [ setRootNode, setOffersToNodes ]);
const onCatalogPageMessageEvent = useCallback((event: CatalogPageMessageEvent) =>
{
const parser = event.getParser();
if(parser.catalogType !== currentType) return;
const purchasableOffers: IPurchasableOffer[] = [];
for(const offer of parser.offers)
{
const products: IProduct[] = [];
const productData = GetProductDataForLocalization(offer.localizationId);
for(const product of offer.products)
{
const furnitureData = GetFurnitureData(product.furniClassId, product.productType);
products.push(new Product(product.productType, product.furniClassId, product.extraParam, product.productCount, productData, furnitureData, product.uniqueLimitedItem, product.uniqueLimitedSeriesSize, product.uniqueLimitedItemsLeft));
}
if(!products.length) continue;
const purchasableOffer = new Offer(offer.offerId, offer.localizationId, offer.rent, offer.priceCredits, offer.priceActivityPoints, offer.priceActivityPointsType, offer.giftable, offer.clubLevel, products, offer.bundlePurchaseAllowed);
if((currentType === CatalogType.NORMAL) || ((purchasableOffer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (purchasableOffer.pricingModel !== Offer.PRICING_MODEL_MULTI))) purchasableOffers.push(purchasableOffer);
}
if(parser.frontPageItems && parser.frontPageItems.length) setFrontPageItems(parser.frontPageItems);
setIsBusy(false);
if(pageId === parser.pageId)
{
showCatalogPage(parser.pageId, parser.layoutCode, new PageLocalization(parser.localization.images.concat(), parser.localization.texts.concat()), purchasableOffers, parser.offerId, parser.acceptSeasonCurrencyAsCredits);
}
}, [ currentType, pageId, setFrontPageItems, setIsBusy, showCatalogPage ]);
const onPurchaseOKMessageEvent = useCallback((event: PurchaseOKMessageEvent) =>
{
const parser = event.getParser();
DispatchUiEvent(new CatalogPurchasedEvent(parser.offer));
}, []);
const onPurchaseErrorMessageEvent = useCallback((event: PurchaseErrorMessageEvent) =>
{
const parser = event.getParser();
DispatchUiEvent(new CatalogPurchaseFailureEvent(parser.code));
}, []);
const onPurchaseNotAllowedMessageEvent = useCallback((event: PurchaseNotAllowedMessageEvent) =>
{
const parser = event.getParser();
DispatchUiEvent(new CatalogPurchaseNotAllowedEvent(parser.code));
}, []);
const onLimitedEditionSoldOutEvent = useCallback((event: LimitedEditionSoldOutEvent) =>
{
const parser = event.getParser();
DispatchUiEvent(new CatalogPurchaseSoldOutEvent());
}, []);
const onProductOfferEvent = useCallback((event: ProductOfferEvent) =>
{
const parser = event.getParser();
const offerData = parser.offer;
if(!offerData || !offerData.products.length) return;
const offerProductData = offerData.products[0];
if(offerProductData.uniqueLimitedItem)
{
// update unique
}
const products: IProduct[] = [];
const productData = GetProductDataForLocalization(offerData.localizationId);
for(const product of offerData.products)
{
const furnitureData = GetFurnitureData(product.furniClassId, product.productType);
products.push(new Product(product.productType, product.furniClassId, product.extraParam, product.productCount, productData, furnitureData, product.uniqueLimitedItem, product.uniqueLimitedSeriesSize, product.uniqueLimitedItemsLeft));
}
const offer = new Offer(offerData.offerId, offerData.localizationId, offerData.rent, offerData.priceCredits, offerData.priceActivityPoints, offerData.priceActivityPointsType, offerData.giftable, offerData.clubLevel, products, offerData.bundlePurchaseAllowed);
if(!((currentType === CatalogType.NORMAL) || ((offer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (offer.pricingModel !== Offer.PRICING_MODEL_MULTI)))) return;
offer.page = currentPage;
setCurrentOffer(offer);
if(offer.product && (offer.product.productType === ProductTypeEnum.WALL))
{
if(offer.product && (offer.product.productType === ProductTypeEnum.WALL))
{
setPurchaseOptions(prevValue =>
{
const newValue = { ...prevValue };
newValue.extraData =( offer.product.extraParam || null);
return newValue;
});
}
}
// (this._isObjectMoverRequested) && (this._purchasableOffer)
}, [ currentType, currentPage, setCurrentOffer, setPurchaseOptions ]);
const onSellablePetPalettesMessageEvent = useCallback((event: SellablePetPalettesMessageEvent) =>
{
const parser = event.getParser();
const petPalette = new CatalogPetPalette(parser.productCode, parser.palettes.slice());
setCatalogOptions(prevValue =>
{
const petPalettes = [];
if(prevValue.petPalettes) petPalettes.push(...prevValue.petPalettes);
for(let i = 0; i < petPalettes.length; i++)
{
const palette = petPalettes[i];
if(palette.breed === petPalette.breed)
{
petPalettes.splice(i, 1);
break;
}
}
petPalettes.push(petPalette);
return { ...prevValue, petPalettes };
});
}, [ setCatalogOptions ]);
const onApproveNameMessageEvent = useCallback((event: ApproveNameMessageEvent) =>
{
const parser = event.getParser();
DispatchUiEvent(new CatalogNameResultEvent(parser.result, parser.validationInfo));
}, []);
const onGiftReceiverNotFoundEvent = useCallback(() =>
{
DispatchUiEvent(new CatalogGiftReceiverNotFoundEvent());
}, []);
const onHabboClubOffersMessageEvent = useCallback((event: HabboClubOffersMessageEvent) =>
{
const parser = event.getParser();
setCatalogOptions(prevValue =>
{
const clubOffers = parser.offers;
return { ...prevValue, clubOffers };
});
}, [ setCatalogOptions ]);
const onGuildMembershipsMessageEvent = useCallback((event: GuildMembershipsMessageEvent) =>
{
const parser = event.getParser();
setCatalogOptions(prevValue =>
{
const groups = parser.groups;
return { ...prevValue, groups };
});
}, [ setCatalogOptions ]);
const onGiftWrappingConfigurationEvent = useCallback((event: GiftWrappingConfigurationEvent) =>
{
const parser = event.getParser();
setCatalogOptions(prevValue =>
{
const giftConfiguration = new GiftWrappingConfiguration(parser);
return { ...prevValue, giftConfiguration };
});
}, [ setCatalogOptions ]);
const onMarketplaceMakeOfferResult = useCallback((event: MarketplaceMakeOfferResult) =>
{
const parser = event.getParser();
if(!parser) return;
let title = '';
if(parser.result === 1)
{
title = LocalizeText('inventory.marketplace.result.title.success');
}
else
{
title = LocalizeText('inventory.marketplace.result.title.failure');
}
const message = LocalizeText(`inventory.marketplace.result.${ parser.result }`);
NotificationUtilities.simpleAlert(message, NotificationAlertType.DEFAULT, null, null, title);
}, []);
const onClubGiftInfoEvent = useCallback((event: ClubGiftInfoEvent) =>
{
const parser = event.getParser();
setCatalogOptions(prevValue =>
{
const clubGifts = parser;
return { ...prevValue, clubGifts };
});
}, [ setCatalogOptions ]);
UseMessageEventHook(CatalogPagesListEvent, onCatalogPagesListEvent);
UseMessageEventHook(CatalogPageMessageEvent, onCatalogPageMessageEvent);
UseMessageEventHook(PurchaseOKMessageEvent, onPurchaseOKMessageEvent);
UseMessageEventHook(PurchaseErrorMessageEvent, onPurchaseErrorMessageEvent);
UseMessageEventHook(PurchaseNotAllowedMessageEvent, onPurchaseNotAllowedMessageEvent);
UseMessageEventHook(LimitedEditionSoldOutEvent, onLimitedEditionSoldOutEvent);
UseMessageEventHook(ProductOfferEvent, onProductOfferEvent);
UseMessageEventHook(GuildMembershipsMessageEvent, onGuildMembershipsMessageEvent);
UseMessageEventHook(SellablePetPalettesMessageEvent, onSellablePetPalettesMessageEvent);
UseMessageEventHook(ApproveNameMessageEvent, onApproveNameMessageEvent);
UseMessageEventHook(GiftReceiverNotFoundEvent, onGiftReceiverNotFoundEvent);
UseMessageEventHook(HabboClubOffersMessageEvent, onHabboClubOffersMessageEvent);
UseMessageEventHook(GiftWrappingConfigurationEvent, onGiftWrappingConfigurationEvent);
UseMessageEventHook(ClubGiftInfoEvent, onClubGiftInfoEvent);
UseMessageEventHook(MarketplaceMakeOfferResult, onMarketplaceMakeOfferResult);
return null;
}

View File

@ -1,406 +1,75 @@
import { CatalogPublishedMessageEvent, FrontPageItem, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetGiftWrappingConfigurationComposer, ILinkEventTracker, RoomPreviewer } from '@nitrots/nitro-renderer'; import { ILinkEventTracker } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useEffect } from 'react';
import { AddEventLinkTracker, GetRoomEngine, LocalizeText, NotificationAlertType, NotificationUtilities, PlaySound, RemoveLinkEventTracker, SendMessageComposer, SoundNames } from '../../api'; import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker } from '../../api';
import { Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; import { Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
import { CatalogPurchasedEvent } from '../../events'; import { useCatalog } from '../../hooks';
import { UseMessageEventHook, UseUiEvent } from '../../hooks';
import { CatalogContextProvider } from './CatalogContext';
import { CatalogMessageHandler } from './CatalogMessageHandler';
import { CatalogPage } from './common/CatalogPage';
import { CatalogType } from './common/CatalogType';
import { ICatalogNode } from './common/ICatalogNode';
import { ICatalogOptions } from './common/ICatalogOptions';
import { ICatalogPage } from './common/ICatalogPage';
import { IPageLocalization } from './common/IPageLocalization';
import { IPurchasableOffer } from './common/IPurchasableOffer';
import { IPurchaseOptions } from './common/IPurchaseOptions';
import { RequestedPage } from './common/RequestedPage';
import { SearchResult } from './common/SearchResult';
import { CatalogGiftView } from './views/gift/CatalogGiftView'; import { CatalogGiftView } from './views/gift/CatalogGiftView';
import { CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { CatalogNavigationView } from './views/navigation/CatalogNavigationView';
import { GetCatalogLayout } from './views/page/layout/GetCatalogLayout'; import { GetCatalogLayout } from './views/page/layout/GetCatalogLayout';
import { MarketplacePostOfferView } from './views/page/layout/marketplace/MarketplacePostOfferView'; import { MarketplacePostOfferView } from './views/page/layout/marketplace/MarketplacePostOfferView';
const REQUESTED_PAGE = new RequestedPage();
export const CatalogView: FC<{}> = props => export const CatalogView: FC<{}> = props =>
{ {
const [ isVisible, setIsVisible ] = useState(false); const { isVisible = false, setIsVisible = null, rootNode = null, currentPage = null, navigationHidden = false, setNavigationHidden = null, activeNodes = [], searchResult = null, setSearchResult = null, openPageByName = null, openPageByOfferId = null, activateNode = null } = useCatalog();
const [ isBusy, setIsBusy ] = useState(false);
const [ pageId, setPageId ] = useState(-1);
const [ previousPageId, setPreviousPageId ] = useState(-1);
const [ currentType, setCurrentType ] = useState(CatalogType.NORMAL);
const [ rootNode, setRootNode ] = useState<ICatalogNode>(null);
const [ offersToNodes, setOffersToNodes ] = useState<Map<number, ICatalogNode[]>>(null);
const [ currentPage, setCurrentPage ] = useState<ICatalogPage>(null);
const [ currentOffer, setCurrentOffer ] = useState<IPurchasableOffer>(null);
const [ activeNodes, setActiveNodes ] = useState<ICatalogNode[]>([]);
const [ searchResult, setSearchResult ] = useState<SearchResult>(null);
const [ frontPageItems, setFrontPageItems ] = useState<FrontPageItem[]>([]);
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
const [ navigationHidden, setNavigationHidden ] = useState(false);
const [ purchaseOptions, setPurchaseOptions ] = useState<IPurchaseOptions>({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null });
const [ catalogOptions, setCatalogOptions ] = useState<ICatalogOptions>({});
const resetState = useCallback(() =>
{
setPageId(-1);
setPreviousPageId(-1);
setRootNode(null);
setOffersToNodes(null);
setCurrentPage(null);
setCurrentOffer(null);
setActiveNodes([]);
setSearchResult(null);
setFrontPageItems([]);
setIsVisible(false);
}, []);
const onCatalogPublishedMessageEvent = useCallback((event: CatalogPublishedMessageEvent) =>
{
const wasVisible = isVisible;
resetState();
if(wasVisible) NotificationUtilities.simpleAlert(LocalizeText('catalog.alert.published.description'), NotificationAlertType.ALERT, null, null, LocalizeText('catalog.alert.published.title'));
}, [ isVisible, resetState ]);
UseMessageEventHook(CatalogPublishedMessageEvent, onCatalogPublishedMessageEvent);
const getNodeById = useCallback((id: number, node: ICatalogNode) =>
{
if((node.pageId === id) && (node !== rootNode)) return node;
for(const child of node.children)
{
const found = (getNodeById(id, child) as ICatalogNode);
if(found) return found;
}
return null;
}, [ rootNode ]);
const getNodeByName = useCallback((name: string, node: ICatalogNode) =>
{
if((node.pageName === name) && (node !== rootNode)) return node;
for(const child of node.children)
{
const found = (getNodeByName(name, child) as ICatalogNode);
if(found) return found;
}
return null;
}, [ rootNode ]);
const getNodesByOfferId = useCallback((offerId: number, flag: boolean = false) =>
{
if(!offersToNodes || !offersToNodes.size) return null;
if(flag)
{
const nodes: ICatalogNode[] = [];
const offers = offersToNodes.get(offerId);
if(offers && offers.length) for(const offer of offers) (offer.isVisible && nodes.push(offer));
if(nodes.length) return nodes;
}
return offersToNodes.get(offerId);
}, [ offersToNodes ]);
const loadCatalogPage = useCallback((pageId: number, offerId: number) =>
{
if(pageId < 0) return;
setIsBusy(true);
setPageId(pageId);
if(pageId > -1) SendMessageComposer(new GetCatalogPageComposer(pageId, offerId, currentType));
}, [ currentType ]);
const showCatalogPage = useCallback((pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], offerId: number, acceptSeasonCurrencyAsCredits: boolean) =>
{
const catalogPage = (new CatalogPage(pageId, layoutCode, localization, offers, acceptSeasonCurrencyAsCredits) as ICatalogPage);
setCurrentPage(catalogPage);
setPreviousPageId(prevValue => ((pageId !== -1) ? pageId : prevValue));
setNavigationHidden(false);
if((offerId > -1) && catalogPage.offers.length)
{
for(const offer of catalogPage.offers)
{
if(offer.offerId !== offerId) continue;
setCurrentOffer(offer)
break;
}
}
}, []);
const activateNode = useCallback((targetNode: ICatalogNode, offerId: number = -1) =>
{
if(targetNode.parent.pageName === 'root')
{
if(targetNode.children.length)
{
for(const child of targetNode.children)
{
if(!child.isVisible) continue;
targetNode = child;
break;
}
}
}
const nodes: ICatalogNode[] = [];
let node = targetNode;
while(node && (node.pageName !== 'root'))
{
nodes.push(node);
node = node.parent;
}
nodes.reverse();
setActiveNodes(prevValue =>
{
const isActive = (prevValue.indexOf(targetNode) >= 0);
const isOpen = targetNode.isOpen;
for(const existing of prevValue)
{
existing.deactivate();
if(nodes.indexOf(existing) === -1) existing.close();
}
for(const n of nodes)
{
n.activate();
if(n.parent) n.open();
if((n === targetNode.parent) && n.children.length) n.open();
}
if(isActive && isOpen) targetNode.close();
else targetNode.open();
return nodes;
});
if(targetNode.pageId > -1) loadCatalogPage(targetNode.pageId, offerId);
}, [ setActiveNodes, loadCatalogPage ]);
const openPageById = useCallback((id: number) =>
{
setSearchResult(null);
if(!isVisible)
{
REQUESTED_PAGE.requestById = id;
setIsVisible(true);
}
else
{
const node = getNodeById(id, rootNode);
if(node) activateNode(node);
}
}, [ isVisible, rootNode, getNodeById, activateNode ]);
const openPageByName = useCallback((name: string) =>
{
setSearchResult(null);
if(!isVisible)
{
REQUESTED_PAGE.requestByName = name;
setIsVisible(true);
}
else
{
const node = getNodeByName(name, rootNode);
if(node) activateNode(node);
}
}, [ isVisible, rootNode, getNodeByName, activateNode ]);
const openPageByOfferId = useCallback((offerId: number) =>
{
setSearchResult(null);
if(!isVisible)
{
REQUESTED_PAGE.requestedByOfferId = offerId;
setIsVisible(true);
}
else
{
const nodes = getNodesByOfferId(offerId);
if(!nodes || !nodes.length) return;
activateNode(nodes[0], offerId);
}
}, [ isVisible, getNodesByOfferId, activateNode ]);
const onCatalogPurchasedEvent = useCallback((event: CatalogPurchasedEvent) =>
{
PlaySound(SoundNames.CREDITS);
}, []);
UseUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogPurchasedEvent);
const linkReceived = useCallback((url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'show':
setIsVisible(true);
return;
case 'hide':
setIsVisible(false);
return;
case 'toggle':
setIsVisible(prevValue => !prevValue);
return;
case 'open':
if(parts.length > 2)
{
if(parts.length === 4)
{
switch(parts[2])
{
case 'offerId':
openPageByOfferId(parseInt(parts[3]));
return;
}
}
else
{
openPageByName(parts[2]);
}
}
else
{
setIsVisible(true);
}
return;
}
}, [ openPageByOfferId, openPageByName ]);
useEffect(() => useEffect(() =>
{ {
const linkTracker: ILinkEventTracker = { const linkTracker: ILinkEventTracker = {
linkReceived, linkReceived: (url: string) =>
{
const parts = url.split('/');
if(parts.length < 2) return;
switch(parts[1])
{
case 'show':
setIsVisible(true);
return;
case 'hide':
setIsVisible(false);
return;
case 'toggle':
setIsVisible(prevValue => !prevValue);
return;
case 'open':
if(parts.length > 2)
{
if(parts.length === 4)
{
switch(parts[2])
{
case 'offerId':
openPageByOfferId(parseInt(parts[3]));
return;
}
}
else
{
openPageByName(parts[2]);
}
}
else
{
setIsVisible(true);
}
return;
}
},
eventUrlPrefix: 'catalog/' eventUrlPrefix: 'catalog/'
}; };
AddEventLinkTracker(linkTracker); AddEventLinkTracker(linkTracker);
return () => RemoveLinkEventTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker);
}, [ linkReceived ]); }, [ setIsVisible, openPageByOfferId, openPageByName ]);
useEffect(() =>
{
setRoomPreviewer(new RoomPreviewer(GetRoomEngine(), ++RoomPreviewer.PREVIEW_COUNTER));
return () =>
{
setRoomPreviewer(prevValue =>
{
prevValue.dispose();
return null;
});
}
}, []);
useEffect(() =>
{
if(!isVisible || rootNode) return;
SendMessageComposer(new GetGiftWrappingConfigurationComposer());
SendMessageComposer(new GetClubGiftInfo());
SendMessageComposer(new GetCatalogIndexComposer(currentType));
}, [ isVisible, rootNode, currentType ]);
useEffect(() =>
{
if(!isVisible || !rootNode) return;
switch(REQUESTED_PAGE.requestType)
{
case RequestedPage.REQUEST_TYPE_NONE:
if(activeNodes && activeNodes.length) return;
if(rootNode.isBranch)
{
for(const child of rootNode.children)
{
if(child && child.isVisible)
{
activateNode(child);
return;
}
}
}
return;
case RequestedPage.REQUEST_TYPE_ID:
openPageById(REQUESTED_PAGE.requestById);
REQUESTED_PAGE.resetRequest();
return;
case RequestedPage.REQUEST_TYPE_OFFER:
openPageByOfferId(REQUESTED_PAGE.requestedByOfferId);
REQUESTED_PAGE.resetRequest();
return;
case RequestedPage.REQUEST_TYPE_NAME:
openPageByName(REQUESTED_PAGE.requestByName);
REQUESTED_PAGE.resetRequest();
return;
}
}, [ isVisible, rootNode, activeNodes, activateNode, openPageById, openPageByOfferId, openPageByName ]);
useEffect(() =>
{
if(!searchResult && currentPage && (currentPage.pageId === -1)) openPageById(previousPageId);
}, [ searchResult, currentPage, previousPageId, openPageById ]);
useEffect(() =>
{
return () => setCurrentOffer(null);
}, [ currentPage ]);
return ( return (
<CatalogContextProvider value={ { isVisible, isBusy, setIsBusy, pageId, currentType, setCurrentType, rootNode, setRootNode, offersToNodes, setOffersToNodes, currentPage, setCurrentPage, currentOffer, setCurrentOffer, activeNodes, setActiveNodes, searchResult, setSearchResult, frontPageItems, setFrontPageItems, roomPreviewer, purchaseOptions, setPurchaseOptions, catalogOptions, setCatalogOptions, resetState, getNodesByOfferId, loadCatalogPage, showCatalogPage, activateNode } }> <>
<CatalogMessageHandler />
{ isVisible && { isVisible &&
<NitroCardView uniqueKey="catalog" className="nitro-catalog"> <NitroCardView uniqueKey="catalog" className="nitro-catalog">
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => <NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => setIsVisible(false) } />
{
setIsVisible(false);
} } />
<NitroCardTabsView> <NitroCardTabsView>
{ rootNode && (rootNode.children.length > 0) && rootNode.children.map(child => { rootNode && (rootNode.children.length > 0) && rootNode.children.map(child =>
{ {
@ -433,6 +102,6 @@ export const CatalogView: FC<{}> = props =>
</NitroCardView> } </NitroCardView> }
<CatalogGiftView /> <CatalogGiftView />
<MarketplacePostOfferView /> <MarketplacePostOfferView />
</CatalogContextProvider> </>
); );
} }

View File

@ -1,29 +0,0 @@
import { CatalogPageMessageOfferData, RoomObjectCategory, RoomObjectPlacementSource } from '@nitrots/nitro-renderer';
import { GetRoomEngine, ProductTypeEnum } from '../../../api';
import { IsCatalogOfferDraggable } from './IsCatalogOfferDraggable';
export const AttemptCatalogPlacement = (offer: CatalogPageMessageOfferData) =>
{
if(!IsCatalogOfferDraggable(offer)) return;
const product = offer.products[0];
let category: number = -1;
switch(product.productType)
{
case ProductTypeEnum.FLOOR:
category = RoomObjectCategory.FLOOR;
break;
case ProductTypeEnum.WALL:
category = RoomObjectCategory.WALL;
break;
}
if(category === -1) return;
if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.CATALOG, -(offer.offerId), category, product.furniClassId, (product.extraParam) ? product.extraParam.toString() : null))
{
}
}

View File

@ -1,26 +0,0 @@
export class FurniCategory
{
public static DEFAULT: number = 1;
public static WALL_PAPER: number = 2;
public static FLOOR: number = 3;
public static LANDSCAPE: number = 4;
public static POST_IT: number = 5;
public static POSTER: number = 6;
public static SOUND_SET: number = 7;
public static TRAX_SONG: number = 8;
public static PRESENT: number = 9;
public static ECOTRON_BOX: number = 10;
public static TROPHY: number = 11;
public static CREDIT_FURNI: number = 12;
public static PET_SHAMPOO: number = 13;
public static PET_CUSTOM_PART: number = 14;
public static PET_CUSTOM_PART_SHAMPOO: number = 15;
public static PET_SADDLE: number = 16;
public static GUILD_FURNI: number = 17;
public static GAME_FURNI: number = 18;
public static MONSTERPLANT_SEED: number = 19;
public static MONSTERPLANT_REVIVAL: number = 20;
public static MONSTERPLANT_REBREED: number = 21;
public static MONSTERPLANT_FERTILIZE: number = 22;
public static FIGURE_PURCHASABLE_SET: number = 23;
}

View File

@ -1,14 +0,0 @@
export interface IPurse
{
_Str_14389: boolean;
_Str_4458: number;
credits: number;
clubDays: number;
clubPeriods: number;
_Str_13571: boolean;
_Str_3738: boolean;
_Str_6288: number;
_Str_4605: number;
_Str_6312: number;
_Str_5590(_arg_1: number): number;
}

View File

@ -1,7 +0,0 @@
import { CatalogPageMessageOfferData, RoomControllerLevel } from '@nitrots/nitro-renderer';
import { GetRoomSession, ProductTypeEnum } from '../../../api';
export const IsCatalogOfferDraggable = (offer: CatalogPageMessageOfferData) =>
{
return ((GetRoomSession().isRoomOwner || (GetRoomSession().isGuildRoom && (GetRoomSession().controllerLevel >= RoomControllerLevel.GUILD_MEMBER))) && (offer.products.length === 1) && (offer.products[0].productType !== ProductTypeEnum.EFFECT) && (offer.products[0].productType !== ProductTypeEnum.HABBO_CLUB))
}

View File

@ -1,144 +0,0 @@
import { GetNitroInstance } from '../../../api';
import { IPurse } from './IPurse';
export class Purse implements IPurse
{
private _credits: number = 0;
private _activityPoints: Map<number, number>;
private _clubDays: number = 0;
private _clubPeriods: number = 0;
private _isVIP: boolean = false;
private _pastClubDays: number = 0;
private _pastVipDays: number = 0;
private _isExpiring: boolean = false;
private _minutesUntilExpiration: number = 0;
private _minutesSinceLastModified: number;
private _lastUpdated: number;
public get credits(): number
{
return this._credits;
}
public set credits(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._credits = k;
}
public get clubDays(): number
{
return this._clubDays;
}
public set clubDays(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._clubDays = k;
}
public get clubPeriods(): number
{
return this._clubPeriods;
}
public set clubPeriods(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._clubPeriods = k;
}
public get _Str_13571(): boolean
{
return (this._clubDays > 0) || (this._clubPeriods > 0);
}
public get _Str_3738(): boolean
{
return this._isVIP;
}
public get _Str_14389(): boolean
{
return this._isExpiring;
}
public set _Str_14389(k: boolean)
{
this._isExpiring = k;
}
public set _Str_3738(k: boolean)
{
this._isVIP = k;
}
public get _Str_6288(): number
{
return this._pastClubDays;
}
public set _Str_6288(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._pastClubDays = k;
}
public get _Str_4605(): number
{
return this._pastVipDays;
}
public set _Str_4605(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._pastVipDays = k;
}
public get _Str_18527(): Map<number, number>
{
return this._activityPoints;
}
public set _Str_18527(k: Map<number, number>)
{
this._lastUpdated = GetNitroInstance().time;
this._activityPoints = k;
}
public _Str_5590(k: number): number
{
return this._activityPoints[k];
}
public set _Str_4458(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._minutesUntilExpiration = k;
}
public get _Str_4458(): number
{
const k = ((GetNitroInstance().time - this._lastUpdated) / (1000 * 60));
const _local_2 = (this._minutesUntilExpiration - k);
return (_local_2 > 0) ? _local_2 : 0;
}
public set _Str_6312(k: number)
{
this._lastUpdated = GetNitroInstance().time;
this._minutesSinceLastModified = k;
}
public get _Str_6312(): number
{
return this._minutesSinceLastModified;
}
public get _Str_26225(): number
{
return this._lastUpdated;
}
}

View File

@ -1,18 +0,0 @@
export class SubscriptionInfo
{
private _lastUpdated: number;
constructor(
public readonly clubDays: number = 0,
public readonly clubPeriods: number = 0,
public readonly isVip: boolean = false,
public readonly pastDays: number = 0,
public readonly pastVipDays: number = 0)
{}
public get lastUpdated(): number
{
return this._lastUpdated;
}
}

View File

@ -0,0 +1,10 @@
import { FC } from 'react';
export const CatalogPurchaseConfirmView: FC<{}> = props =>
{
const {} = props;
return (
<div></div>
);
}

View File

@ -1,12 +1,11 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer'; import { GiftReceiverNotFoundEvent, PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames'; import classNames from 'classnames';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetSessionDataManager, LocalizeText, ProductTypeEnum, SendMessageComposer } from '../../../../api'; import { GetSessionDataManager, LocalizeText, ProductTypeEnum, SendMessageComposer } from '../../../../api';
import { Base, Button, ButtonGroup, Column, Flex, FormGroup, LayoutCurrencyIcon, LayoutFurniImageView, LayoutGiftTagView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; import { Base, Button, ButtonGroup, Column, Flex, FormGroup, LayoutCurrencyIcon, LayoutFurniImageView, LayoutGiftTagView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent } from '../../../../events'; import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent } from '../../../../events';
import { UseUiEvent } from '../../../../hooks'; import { useCatalog, UseMessageEventHook, UseUiEvent } from '../../../../hooks';
import { useCatalogContext } from '../../CatalogContext';
export const CatalogGiftView: FC<{}> = props => export const CatalogGiftView: FC<{}> = props =>
{ {
@ -24,7 +23,7 @@ export const CatalogGiftView: FC<{}> = props =>
const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0); const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0);
const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0); const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0);
const [ receiverNotFound, setReceiverNotFound ] = useState<boolean>(false); const [ receiverNotFound, setReceiverNotFound ] = useState<boolean>(false);
const { catalogOptions = null } = useCatalogContext(); const { catalogOptions = null } = useCatalog();
const { giftConfiguration = null } = catalogOptions; const { giftConfiguration = null } = catalogOptions;
const close = useCallback(() => const close = useCallback(() =>
@ -59,15 +58,11 @@ export const CatalogGiftView: FC<{}> = props =>
setExtraData(castedEvent.extraData); setExtraData(castedEvent.extraData);
setIsVisible(true); setIsVisible(true);
return; return;
case CatalogEvent.GIFT_RECEIVER_NOT_FOUND:
setReceiverNotFound(true);
return;
} }
}, [ close ]); }, [ close ]);
UseUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogEvent); UseUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogEvent);
UseUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent); UseUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent);
UseUiEvent(CatalogEvent.GIFT_RECEIVER_NOT_FOUND, onCatalogEvent);
const isBoxDefault = useMemo(() => const isBoxDefault = useMemo(() =>
{ {
@ -118,6 +113,13 @@ export const CatalogGiftView: FC<{}> = props =>
} }
}, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]); }, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]);
const onGiftReceiverNotFoundEvent = useCallback(() =>
{
setReceiverNotFound(true);
}, []);
UseMessageEventHook(GiftReceiverNotFoundEvent, onGiftReceiverNotFoundEvent);
useEffect(() => useEffect(() =>
{ {
setReceiverNotFound(false); setReceiverNotFound(false);

View File

@ -1,9 +1,8 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC } from 'react'; import { FC } from 'react';
import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem'; import { ICatalogNode } from '../../../../api';
import { Text } from '../../../../common/Text'; import { LayoutGridItem, Text } from '../../../../common';
import { useCatalogContext } from '../../CatalogContext'; import { useCatalog } from '../../../../hooks';
import { ICatalogNode } from '../../common/ICatalogNode';
import { CatalogIconView } from '../catalog-icon/CatalogIconView'; import { CatalogIconView } from '../catalog-icon/CatalogIconView';
import { CatalogNavigationSetView } from './CatalogNavigationSetView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView';
@ -15,7 +14,7 @@ export interface CatalogNavigationItemViewProps
export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = props => export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = props =>
{ {
const { node = null } = props; const { node = null } = props;
const { activateNode = null } = useCatalogContext(); const { activateNode = null } = useCatalog();
return ( return (
<> <>

View File

@ -1,5 +1,5 @@
import { FC } from 'react'; import { FC } from 'react';
import { ICatalogNode } from '../../common/ICatalogNode'; import { ICatalogNode } from '../../../../api';
import { CatalogNavigationItemView } from './CatalogNavigationItemView'; import { CatalogNavigationItemView } from './CatalogNavigationItemView';
export interface CatalogNavigationSetViewProps export interface CatalogNavigationSetViewProps

View File

@ -1,8 +1,7 @@
import { FC } from 'react'; import { FC } from 'react';
import { AutoGrid } from '../../../../common/AutoGrid'; import { ICatalogNode } from '../../../../api';
import { Column } from '../../../../common/Column'; import { AutoGrid, Column } from '../../../../common';
import { useCatalogContext } from '../../CatalogContext'; import { useCatalog } from '../../../../hooks';
import { ICatalogNode } from '../../common/ICatalogNode';
import { CatalogSearchView } from '../page/common/CatalogSearchView'; import { CatalogSearchView } from '../page/common/CatalogSearchView';
import { CatalogNavigationItemView } from './CatalogNavigationItemView'; import { CatalogNavigationItemView } from './CatalogNavigationItemView';
import { CatalogNavigationSetView } from './CatalogNavigationSetView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView';
@ -15,7 +14,7 @@ export interface CatalogNavigationViewProps
export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props => export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
{ {
const { node = null } = props; const { node = null } = props;
const { searchResult = null } = useCatalogContext(); const { searchResult = null } = useCatalog();
return ( return (
<> <>

View File

@ -1,17 +1,21 @@
import { FC, useMemo } from 'react'; import { MouseEventType } from '@nitrots/nitro-renderer';
import { ProductTypeEnum } from '../../../../../api'; import { FC, MouseEvent, useMemo, useState } from 'react';
import { IPurchasableOffer, Offer, ProductTypeEnum } from '../../../../../api';
import { LayoutAvatarImageView, LayoutGridItem, LayoutGridItemProps } from '../../../../../common'; import { LayoutAvatarImageView, LayoutGridItem, LayoutGridItemProps } from '../../../../../common';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; import { useCatalog, useInventoryFurni } from '../../../../../hooks';
import { Offer } from '../../../common/Offer';
interface CatalogGridOfferViewProps extends LayoutGridItemProps interface CatalogGridOfferViewProps extends LayoutGridItemProps
{ {
offer: IPurchasableOffer; offer: IPurchasableOffer;
selectOffer: (offer: IPurchasableOffer) => void;
} }
export const CatalogGridOfferView: FC<CatalogGridOfferViewProps> = props => export const CatalogGridOfferView: FC<CatalogGridOfferViewProps> = props =>
{ {
const { offer = null, ...rest } = props; const { offer = null, selectOffer = null, itemActive = false, ...rest } = props;
const [ isMouseDown, setMouseDown ] = useState(false);
const { requestOfferToMover = null } = useCatalog();
const { isVisible = false } = useInventoryFurni();
const iconUrl = useMemo(() => const iconUrl = useMemo(() =>
{ {
@ -23,12 +27,31 @@ export const CatalogGridOfferView: FC<CatalogGridOfferViewProps> = props =>
return offer.product.getIconUrl(offer); return offer.product.getIconUrl(offer);
}, [ offer ]); }, [ offer ]);
const onMouseEvent = (event: MouseEvent) =>
{
switch(event.type)
{
case MouseEventType.MOUSE_DOWN:
selectOffer(offer);
setMouseDown(true);
return;
case MouseEventType.MOUSE_UP:
setMouseDown(false);
return;
case MouseEventType.ROLL_OUT:
if(!isMouseDown || !itemActive || !isVisible) return;
requestOfferToMover(offer);
return;
}
}
const product = offer.product; const product = offer.product;
if(!product) return null; if(!product) return null;
return ( return (
<LayoutGridItem itemImage={ iconUrl } itemCount={ ((offer.pricingModel === Offer.PRICING_MODEL_MULTI) ? product.productCount : 1) } itemUniqueSoldout={ (product.uniqueLimitedItemSeriesSize && !product.uniqueLimitedItemsLeft) } itemUniqueNumber={ product.uniqueLimitedItemSeriesSize } { ...rest }> <LayoutGridItem itemImage={ iconUrl } itemCount={ ((offer.pricingModel === Offer.PRICING_MODEL_MULTI) ? product.productCount : 1) } itemUniqueSoldout={ (product.uniqueLimitedItemSeriesSize && !product.uniqueLimitedItemsLeft) } itemUniqueNumber={ product.uniqueLimitedItemSeriesSize } itemActive={ itemActive } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } { ...rest }>
{ (offer.product.productType === ProductTypeEnum.ROBOT) && { (offer.product.productType === ProductTypeEnum.ROBOT) &&
<LayoutAvatarImageView figure={ offer.product.extraParam } headOnly={ true } direction={ 3 } /> } <LayoutAvatarImageView figure={ offer.product.extraParam } headOnly={ true } direction={ 3 } /> }
</LayoutGridItem> </LayoutGridItem>

View File

@ -1,25 +1,15 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IFurnitureData } from '@nitrots/nitro-renderer'; import { IFurnitureData } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetSessionDataManager, LocalizeText } from '../../../../../api'; import { CatalogPage, CatalogType, FilterCatalogNode, FurnitureOffer, GetOfferNodes, GetSessionDataManager, ICatalogNode, ICatalogPage, IPurchasableOffer, LocalizeText, PageLocalization, SearchResult } from '../../../../../api';
import { Button } from '../../../../../common/Button'; import { Button, Flex } from '../../../../../common';
import { Flex } from '../../../../../common/Flex'; import { useCatalog } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPage } from '../../../common/CatalogPage';
import { CatalogType } from '../../../common/CatalogType';
import { FilterCatalogNode, GetOfferNodes } from '../../../common/CatalogUtilities';
import { FurnitureOffer } from '../../../common/FurnitureOffer';
import { ICatalogNode } from '../../../common/ICatalogNode';
import { ICatalogPage } from '../../../common/ICatalogPage';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { PageLocalization } from '../../../common/PageLocalization';
import { SearchResult } from '../../../common/SearchResult';
export const CatalogSearchView: FC<{}> = props => export const CatalogSearchView: FC<{}> = props =>
{ {
const [ searchValue, setSearchValue ] = useState(''); const [ searchValue, setSearchValue ] = useState('');
const [ needsProcessing, setNeedsProcessing ] = useState(false); const [ needsProcessing, setNeedsProcessing ] = useState(false);
const { currentType = null, rootNode = null, setActiveNodes = null, offersToNodes = null, searchResult = null, setSearchResult = null, setCurrentPage = null } = useCatalogContext(); const { currentType = null, rootNode = null, offersToNodes = null, searchResult = null, setSearchResult = null, setCurrentPage = null } = useCatalog();
const updateSearchValue = (value: string) => const updateSearchValue = (value: string) =>
{ {

View File

@ -1,4 +1,4 @@
import { ICatalogPage } from '../../../common/ICatalogPage'; import { ICatalogPage } from '../../../../../api';
export interface CatalogLayoutProps export interface CatalogLayoutProps
{ {

View File

@ -1,7 +1,7 @@
import { FC } from 'react'; import { FC } from 'react';
import { LocalizeText } from '../../../../../api'; import { LocalizeText } from '../../../../../api';
import { Base, Column, Flex, Grid, Text } from '../../../../../common'; import { Base, Column, Flex, Grid, Text } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { CatalogBadgeSelectorWidgetView } from '../widgets/CatalogBadgeSelectorWidgetView'; import { CatalogBadgeSelectorWidgetView } from '../widgets/CatalogBadgeSelectorWidgetView';
import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView';
import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView';
@ -14,7 +14,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props => export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props =>
{ {
const { page = null } = props; const { page = null } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
return ( return (
<> <>

View File

@ -1,6 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { Base, Column, Flex, Grid, Text } from '../../../../../common'; import { Base, Column, Flex, Grid, Text } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView';
import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView';
import { CatalogLimitedItemWidgetView } from '../widgets/CatalogLimitedItemWidgetView'; import { CatalogLimitedItemWidgetView } from '../widgets/CatalogLimitedItemWidgetView';
@ -13,7 +13,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props => export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
{ {
const { page = null } = props; const { page = null } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
return ( return (
<Grid> <Grid>

View File

@ -1,10 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { Base } from '../../../../../common/Base'; import { Base, Column, Flex, Grid, Text } from '../../../../../common';
import { Column } from '../../../../../common/Column'; import { useCatalog } from '../../../../../hooks';
import { Flex } from '../../../../../common/Flex';
import { Grid } from '../../../../../common/Grid';
import { Text } from '../../../../../common/Text';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView'; import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView';
import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView';
import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView';
@ -16,7 +12,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props => export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
{ {
const { page = null } = props; const { page = null } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
return ( return (
<Grid> <Grid>

View File

@ -2,7 +2,7 @@ import { CatalogGroupsComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { SendMessageComposer } from '../../../../../api'; import { SendMessageComposer } from '../../../../../api';
import { Base, Column, Flex, Grid, Text } from '../../../../../common'; import { Base, Column, Flex, Grid, Text } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView';
import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView';
import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView';
@ -13,7 +13,7 @@ export const CatalogLayouGuildForumView: FC<CatalogLayoutProps> = props =>
{ {
const { page = null } = props; const { page = null } = props;
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0); const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
const { currentOffer = null, setCurrentOffer = null, catalogOptions = null } = useCatalogContext(); const { currentOffer = null, setCurrentOffer = null, catalogOptions = null } = useCatalog();
const { groups = null } = catalogOptions; const { groups = null } = catalogOptions;
useEffect(() => useEffect(() =>

View File

@ -1,11 +1,7 @@
import { NitroPoint } from '@nitrots/nitro-renderer'; import { NitroPoint } from '@nitrots/nitro-renderer';
import { FC, useEffect } from 'react'; import { FC, useEffect } from 'react';
import { Base } from '../../../../../common/Base'; import { Base, Column, Flex, Grid, Text } from '../../../../../common';
import { Column } from '../../../../../common/Column'; import { useCatalog } from '../../../../../hooks';
import { Flex } from '../../../../../common/Flex';
import { Grid } from '../../../../../common/Grid';
import { Text } from '../../../../../common/Text';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView';
import { CatalogSpacesWidgetView } from '../widgets/CatalogSpacesWidgetView'; import { CatalogSpacesWidgetView } from '../widgets/CatalogSpacesWidgetView';
import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget'; import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget';
@ -15,7 +11,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types';
export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props => export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
{ {
const { page = null } = props; const { page = null } = props;
const { currentOffer = null, roomPreviewer = null } = useCatalogContext(); const { currentOffer = null, roomPreviewer = null } = useCatalog();
useEffect(() => useEffect(() =>
{ {

View File

@ -1,9 +1,6 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { Column } from '../../../../../common/Column'; import { Column, Flex, Grid, Text } from '../../../../../common';
import { Flex } from '../../../../../common/Flex'; import { useCatalog } from '../../../../../hooks';
import { Grid } from '../../../../../common/Grid';
import { Text } from '../../../../../common/Text';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView';
import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView';
import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget'; import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget';
@ -14,7 +11,7 @@ export const CatalogLayoutTrophiesView: FC<CatalogLayoutProps> = props =>
{ {
const { page = null } = props; const { page = null } = props;
const [ trophyText, setTrophyText ] = useState<string>(''); const [ trophyText, setTrophyText ] = useState<string>('');
const { currentOffer = null, setPurchaseOptions = null } = useCatalogContext(); const { currentOffer = null, setPurchaseOptions = null } = useCatalog();
useEffect(() => useEffect(() =>
{ {

View File

@ -1,18 +1,16 @@
import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer'; import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../../api'; import { CatalogPurchaseState, LocalizeText, SendMessageComposer } from '../../../../../api';
import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common'; import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common';
import { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent } from '../../../../../events'; import { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent } from '../../../../../events';
import { usePurse, UseUiEvent } from '../../../../../hooks'; import { useCatalog, usePurse, UseUiEvent } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
import { CatalogLayoutProps } from './CatalogLayout.types'; import { CatalogLayoutProps } from './CatalogLayout.types';
export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props => export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
{ {
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null); const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
const { currentPage = null, catalogOptions = null } = useCatalogContext(); const { currentPage = null, catalogOptions = null } = useCatalog();
const { purse = null, getCurrencyAmount = null } = usePurse(); const { purse = null, getCurrencyAmount = null } = usePurse();
const { clubOffers = null } = catalogOptions; const { clubOffers = null } = catalogOptions;

View File

@ -1,4 +1,4 @@
import { ICatalogPage } from '../../../common/ICatalogPage'; import { ICatalogPage } from '../../../../../api';
import { CatalogLayoutProps } from './CatalogLayout.types'; import { CatalogLayoutProps } from './CatalogLayout.types';
import { CatalogLayoutBadgeDisplayView } from './CatalogLayoutBadgeDisplayView'; import { CatalogLayoutBadgeDisplayView } from './CatalogLayoutBadgeDisplayView';
import { CatalogLayoutDefaultView } from './CatalogLayoutDefaultView'; import { CatalogLayoutDefaultView } from './CatalogLayoutDefaultView';

View File

@ -1,9 +1,8 @@
import { FrontPageItem } from '@nitrots/nitro-renderer'; import { FrontPageItem } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect } from 'react'; import { FC, useCallback, useEffect } from 'react';
import { CreateLinkEvent } from '../../../../../../api'; import { CreateLinkEvent } from '../../../../../../api';
import { Column } from '../../../../../../common/Column'; import { Column, Grid } from '../../../../../../common';
import { Grid } from '../../../../../../common/Grid'; import { useCatalog } from '../../../../../../hooks';
import { useCatalogContext } from '../../../../CatalogContext';
import { CatalogRedeemVoucherView } from '../../common/CatalogRedeemVoucherView'; import { CatalogRedeemVoucherView } from '../../common/CatalogRedeemVoucherView';
import { CatalogLayoutProps } from '../CatalogLayout.types'; import { CatalogLayoutProps } from '../CatalogLayout.types';
import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView'; import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView';
@ -11,7 +10,7 @@ import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView
export const CatalogLayoutFrontpage4View: FC<CatalogLayoutProps> = props => export const CatalogLayoutFrontpage4View: FC<CatalogLayoutProps> = props =>
{ {
const { page = null, hideNavigation = null } = props; const { page = null, hideNavigation = null } = props;
const { frontPageItems = [] } = useCatalogContext(); const { frontPageItems = [] } = useCatalog();
const selectItem = useCallback((item: FrontPageItem) => const selectItem = useCallback((item: FrontPageItem) =>
{ {

View File

@ -3,14 +3,13 @@ import { FC, useCallback, useEffect, useState } from 'react';
import { FurnitureItem, LocalizeText, NotificationUtilities, ProductTypeEnum, SendMessageComposer } from '../../../../../../api'; import { FurnitureItem, LocalizeText, NotificationUtilities, ProductTypeEnum, SendMessageComposer } from '../../../../../../api';
import { Base, Button, Column, Grid, LayoutFurniImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../../common'; import { Base, Button, Column, Grid, LayoutFurniImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../../common';
import { CatalogPostMarketplaceOfferEvent } from '../../../../../../events'; import { CatalogPostMarketplaceOfferEvent } from '../../../../../../events';
import { UseMessageEventHook, UseUiEvent } from '../../../../../../hooks'; import { useCatalog, UseMessageEventHook, UseUiEvent } from '../../../../../../hooks';
import { useCatalogContext } from '../../../../CatalogContext';
export const MarketplacePostOfferView : FC<{}> = props => export const MarketplacePostOfferView : FC<{}> = props =>
{ {
const [ item, setItem ] = useState<FurnitureItem>(null); const [ item, setItem ] = useState<FurnitureItem>(null);
const [ askingPrice, setAskingPrice ] = useState(0); const [ askingPrice, setAskingPrice ] = useState(0);
const { catalogOptions = null, setCatalogOptions = null } = useCatalogContext(); const { catalogOptions = null, setCatalogOptions = null } = useCatalog();
const { marketplaceConfiguration = null } = catalogOptions; const { marketplaceConfiguration = null } = catalogOptions;
const onMarketplaceConfigurationEvent = useCallback((event: MarketplaceConfigurationEvent) => const onMarketplaceConfigurationEvent = useCallback((event: MarketplaceConfigurationEvent) =>

View File

@ -1,69 +0,0 @@
import { ApproveNameMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../../../api';
import { Column, Flex, LayoutCurrencyIcon, Text } from '../../../../../../common';
import { CatalogPurchasedEvent } from '../../../../../../events';
import { UseUiEvent } from '../../../../../../hooks';
import { IPurchasableOffer } from '../../../../common/IPurchasableOffer';
import { Offer } from '../../../../common/Offer';
import { CatalogPurchaseWidgetView } from '../../widgets/CatalogPurchaseWidgetView';
import { CatalogPetNameApprovalView } from './CatalogPetNameApprovalView';
export interface CatalogLayoutPetPurchaseViewProps
{
offer: IPurchasableOffer;
pageId: number;
extra?: string;
}
export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps> = props =>
{
const { offer = null, pageId = -1, extra = '' } = props;
const [ petNameValue, setPetNameValue ] = useState('');
const [ nameApproved, setNameApproved ] = useState(false);
const onCatalogPurchasedEvent = useCallback((event: CatalogPurchasedEvent) =>
{
setNameApproved(false);
}, []);
UseUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogPurchasedEvent);
const beforePurchase = useCallback(() =>
{
SendMessageComposer(new ApproveNameMessageComposer(petNameValue, 1));
}, [ petNameValue ]);
const extraData = `${ petNameValue }\n${ extra }`;
return (
<Column fullWidth grow justifyContent="end">
<div className="d-flex flex-grow-1 justify-content-center align-items-center">
<CatalogPetNameApprovalView petNameValue={ petNameValue } setPetNameValue={ setPetNameValue } nameApproved={ nameApproved } setNameApproved={ setNameApproved } />
</div>
<Flex alignItems="end">
<div className="flex-grow-1 align-items-end">
<Text>{ LocalizeText('catalog.bundlewidget.price') }</Text>
</div>
<Column gap={ 1 }>
{ ((offer.priceType === Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS) || (offer.priceType === Offer.PRICE_TYPE_CREDITS)) &&
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
<Text>{ offer.priceInCredits }</Text>
<LayoutCurrencyIcon type={ -1 } />
</Flex> }
{ ((offer.priceType === Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS) || (offer.priceType === Offer.PRICE_TYPE_ACTIVITYPOINTS)) &&
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
<Text>{ offer.priceInActivityPoints }</Text>
<LayoutCurrencyIcon type={ offer.activityPointType } />
</Flex> }
</Column>
</Flex>
<Column gap={ 1 }>
<CatalogPurchaseWidgetView />
{ /* <CatalogPurchaseButtonView offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
{ offer.giftable &&
<CatalogPurchaseGiftButtonView offer={ offer } pageId={ pageId } extra={ extraData } disabled={ nameApproved } /> } */ }
</Column>
</Column>
);
}

View File

@ -1,12 +1,10 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ApproveNameMessageComposer, ColorConverter, GetSellablePetPalettesComposer, PurchaseFromCatalogComposer, SellablePetPaletteData } from '@nitrots/nitro-renderer'; import { ApproveNameMessageComposer, ApproveNameMessageEvent, ColorConverter, GetSellablePetPalettesComposer, PurchaseFromCatalogComposer, SellablePetPaletteData } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../../../api'; import { GetPetAvailableColors, GetPetIndexFromLocalization, LocalizeText, SendMessageComposer } from '../../../../../../api';
import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, LayoutPetImageView, Text } from '../../../../../../common'; import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, LayoutPetImageView, Text } from '../../../../../../common';
import { CatalogNameResultEvent, CatalogPurchaseFailureEvent, CatalogWidgetEvent } from '../../../../../../events'; import { CatalogPurchaseFailureEvent } from '../../../../../../events';
import { DispatchUiEvent, UseUiEvent } from '../../../../../../hooks'; import { DispatchUiEvent, useCatalog, UseMessageEventHook } from '../../../../../../hooks';
import { useCatalogContext } from '../../../../CatalogContext';
import { GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities';
import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView';
import { CatalogPurchaseWidgetView } from '../../widgets/CatalogPurchaseWidgetView'; import { CatalogPurchaseWidgetView } from '../../widgets/CatalogPurchaseWidgetView';
import { CatalogTotalPriceWidget } from '../../widgets/CatalogTotalPriceWidget'; import { CatalogTotalPriceWidget } from '../../widgets/CatalogTotalPriceWidget';
@ -25,7 +23,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
const [ petName, setPetName ] = useState(''); const [ petName, setPetName ] = useState('');
const [ approvalPending, setApprovalPending ] = useState(true); const [ approvalPending, setApprovalPending ] = useState(true);
const [ approvalResult, setApprovalResult ] = useState(-1); const [ approvalResult, setApprovalResult ] = useState(-1);
const { currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null, catalogOptions = null, roomPreviewer = null } = useCatalogContext(); const { currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null, catalogOptions = null, roomPreviewer = null } = useCatalog();
const { petPalettes = null } = catalogOptions; const { petPalettes = null } = catalogOptions;
const getColor = useMemo(() => const getColor = useMemo(() =>
@ -106,15 +104,17 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
} }
}, [ page, currentOffer, petName, petPurchaseString, approvalResult ]); }, [ page, currentOffer, petName, petPurchaseString, approvalResult ]);
const onCatalogNameResultEvent = useCallback((event: CatalogNameResultEvent) => const onApproveNameMessageEvent = useCallback((event: ApproveNameMessageEvent) =>
{ {
setApprovalResult(event.result); const parser = event.getParser();
if(event.result === 0) purchasePet(); setApprovalResult(parser.result);
if(parser.result === 0) purchasePet();
else DispatchUiEvent(new CatalogPurchaseFailureEvent(-1)); else DispatchUiEvent(new CatalogPurchaseFailureEvent(-1));
}, [ purchasePet ]); }, [ purchasePet ]);
UseUiEvent(CatalogWidgetEvent.APPROVE_RESULT, onCatalogNameResultEvent); UseMessageEventHook(ApproveNameMessageEvent, onApproveNameMessageEvent);
useEffect(() => useEffect(() =>
{ {

View File

@ -1,68 +0,0 @@
import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react';
import { LocalizeText } from '../../../../../../api';
import { CatalogEvent, CatalogNameResultEvent } from '../../../../../../events';
import { UseUiEvent } from '../../../../../../hooks';
export interface CatalogPetNameApprovalViewProps
{
petNameValue: string;
setPetNameValue: Dispatch<SetStateAction<string>>;
nameApproved: boolean;
setNameApproved: Dispatch<SetStateAction<boolean>>;
}
export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = props =>
{
const { petNameValue = null, setPetNameValue = null, nameApproved = false, setNameApproved = null } = props;
const [ validationResult, setValidationResult ] = useState(-1);
const onCatalogNameResultEvent = useCallback((event: CatalogNameResultEvent) =>
{
if(event.result === 0)
{
setNameApproved(true);
return;
}
setValidationResult(event.result);
}, [ setNameApproved ]);
UseUiEvent(CatalogEvent.APPROVE_NAME_RESULT, onCatalogNameResultEvent);
const validationErrorMessage = () =>
{
let key: string = '';
switch(validationResult)
{
case 1:
key = 'catalog.alert.petname.long';
break;
case 2:
key = 'catalog.alert.petname.short';
break;
case 3:
key = 'catalog.alert.petname.chars';
break;
case 4:
key = 'catalog.alert.petname.bobba';
break;
}
return LocalizeText(key);
}
useEffect(() =>
{
setValidationResult(-1);
}, [ petNameValue ]);
return (
<div className="input-group has-validation">
<input type="text" className={ 'form-control form-control-sm '+ ((validationResult > 0) ? 'is-invalid ' : '') } placeholder={ LocalizeText('widgets.petpackage.name.title') } value={ petNameValue } onChange={ event => setPetNameValue(event.target.value) } />
{ (validationResult > 0) &&
<div className="invalid-feedback">{ validationErrorMessage }</div> }
</div>
);
}

View File

@ -2,15 +2,14 @@ import { SelectClubGiftComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { LocalizeText, NotificationUtilities, SendMessageComposer } from '../../../../../../api'; import { LocalizeText, NotificationUtilities, SendMessageComposer } from '../../../../../../api';
import { AutoGrid, Text } from '../../../../../../common'; import { AutoGrid, Text } from '../../../../../../common';
import { usePurse } from '../../../../../../hooks'; import { useCatalog, usePurse } from '../../../../../../hooks';
import { useCatalogContext } from '../../../../CatalogContext';
import { CatalogLayoutProps } from '../CatalogLayout.types'; import { CatalogLayoutProps } from '../CatalogLayout.types';
import { VipGiftItem } from './VipGiftItemView'; import { VipGiftItem } from './VipGiftItemView';
export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props => export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
{ {
const { purse = null } = usePurse(); const { purse = null } = usePurse();
const { catalogOptions = null, setCatalogOptions = null } = useCatalogContext(); const { catalogOptions = null, setCatalogOptions = null } = useCatalog();
const { clubGifts = null } = catalogOptions; const { clubGifts = null } = catalogOptions;
const giftsAvailable = useCallback(() => const giftsAvailable = useCallback(() =>

View File

@ -1,6 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { BaseProps, LayoutBadgeImageView } from '../../../../../common'; import { BaseProps, LayoutBadgeImageView } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
interface CatalogAddOnBadgeWidgetViewProps extends BaseProps<HTMLDivElement> interface CatalogAddOnBadgeWidgetViewProps extends BaseProps<HTMLDivElement>
{ {
@ -10,7 +10,7 @@ interface CatalogAddOnBadgeWidgetViewProps extends BaseProps<HTMLDivElement>
export const CatalogAddOnBadgeWidgetView: FC<CatalogAddOnBadgeWidgetViewProps> = props => export const CatalogAddOnBadgeWidgetView: FC<CatalogAddOnBadgeWidgetViewProps> = props =>
{ {
const { ...rest } = props; const { ...rest } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
if(!currentOffer || !currentOffer.badgeCode || !currentOffer.badgeCode.length) return null; if(!currentOffer || !currentOffer.badgeCode || !currentOffer.badgeCode.length) return null;

View File

@ -1,8 +1,7 @@
import { StringDataType } from '@nitrots/nitro-renderer'; import { StringDataType } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useState } from 'react'; import { FC, useEffect, useMemo, useState } from 'react';
import { AutoGrid, AutoGridProps, LayoutBadgeImageView, LayoutGridItem } from '../../../../../common'; import { AutoGrid, AutoGridProps, LayoutBadgeImageView, LayoutGridItem } from '../../../../../common';
import { useInventoryBadges } from '../../../../../hooks'; import { useCatalog, useInventoryBadges } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
const EXCLUDED_BADGE_CODES: string[] = []; const EXCLUDED_BADGE_CODES: string[] = [];
@ -16,7 +15,7 @@ export const CatalogBadgeSelectorWidgetView: FC<CatalogBadgeSelectorWidgetViewPr
const { columnCount = 5, ...rest } = props; const { columnCount = 5, ...rest } = props;
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ currentBadgeCode, setCurrentBadgeCode ] = useState<string>(null); const [ currentBadgeCode, setCurrentBadgeCode ] = useState<string>(null);
const { currentOffer = null, setPurchaseOptions = null } = useCatalogContext(); const { currentOffer = null, setPurchaseOptions = null } = useCatalog();
const { badgeCodes = [], activate = null, deactivate = null } = useInventoryBadges(); const { badgeCodes = [], activate = null, deactivate = null } = useInventoryBadges();
const previewStuffData = useMemo(() => const previewStuffData = useMemo(() =>

View File

@ -1,7 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { AutoGrid, AutoGridProps } from '../../../../../common/AutoGrid'; import { AutoGrid, AutoGridProps, LayoutGridItem } from '../../../../../common';
import { LayoutGridItem } from '../../../../../common/layout/LayoutGridItem'; import { useCatalog } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
interface CatalogBundleGridWidgetViewProps extends AutoGridProps interface CatalogBundleGridWidgetViewProps extends AutoGridProps
{ {
@ -11,7 +10,7 @@ interface CatalogBundleGridWidgetViewProps extends AutoGridProps
export const CatalogBundleGridWidgetView: FC<CatalogBundleGridWidgetViewProps> = props => export const CatalogBundleGridWidgetView: FC<CatalogBundleGridWidgetViewProps> = props =>
{ {
const { columnCount = 5, children = null, ...rest } = props; const { columnCount = 5, children = null, ...rest } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
if(!currentOffer) return null; if(!currentOffer) return null;

View File

@ -1,9 +1,9 @@
import { FC, useEffect } from 'react'; import { FC, useEffect } from 'react';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
export const CatalogFirstProductSelectorWidgetView: FC<{}> = props => export const CatalogFirstProductSelectorWidgetView: FC<{}> = props =>
{ {
const { currentPage = null, setCurrentOffer = null } = useCatalogContext(); const { currentPage = null, setCurrentOffer = null } = useCatalog();
useEffect(() => useEffect(() =>
{ {

View File

@ -1,7 +1,7 @@
import { StringDataType } from '@nitrots/nitro-renderer'; import { StringDataType } from '@nitrots/nitro-renderer';
import { FC, useMemo } from 'react'; import { FC, useMemo } from 'react';
import { BaseProps, LayoutBadgeImageView } from '../../../../../common'; import { BaseProps, LayoutBadgeImageView } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
interface CatalogGuildBadgeWidgetViewProps extends BaseProps<HTMLDivElement> interface CatalogGuildBadgeWidgetViewProps extends BaseProps<HTMLDivElement>
{ {
@ -11,7 +11,7 @@ interface CatalogGuildBadgeWidgetViewProps extends BaseProps<HTMLDivElement>
export const CatalogGuildBadgeWidgetView: FC<CatalogGuildBadgeWidgetViewProps> = props => export const CatalogGuildBadgeWidgetView: FC<CatalogGuildBadgeWidgetViewProps> = props =>
{ {
const { ...rest } = props; const { ...rest } = props;
const { currentOffer = null, purchaseOptions = null } = useCatalogContext(); const { currentOffer = null, purchaseOptions = null } = useCatalog();
const { previewStuffData = null } = purchaseOptions; const { previewStuffData = null } = purchaseOptions;
const badgeCode = useMemo(() => const badgeCode = useMemo(() =>

View File

@ -2,12 +2,12 @@ import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useState } from 'react'; import { FC, useEffect, useMemo, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../../api'; import { LocalizeText, SendMessageComposer } from '../../../../../api';
import { Base, Button, Flex } from '../../../../../common'; import { Base, Button, Flex } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
export const CatalogGuildSelectorWidgetView: FC<{}> = props => export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
{ {
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0); const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
const { currentOffer = null, catalogOptions = null, setPurchaseOptions = null } = useCatalogContext(); const { currentOffer = null, catalogOptions = null, setPurchaseOptions = null } = useCatalog();
const { groups = null } = catalogOptions; const { groups = null } = catalogOptions;
const previewStuffData = useMemo(() => const previewStuffData = useMemo(() =>

View File

@ -1,8 +1,7 @@
import { FC } from 'react'; import { FC } from 'react';
import { ProductTypeEnum } from '../../../../../api'; import { IPurchasableOffer, ProductTypeEnum } from '../../../../../api';
import { AutoGrid, AutoGridProps } from '../../../../../common'; import { AutoGrid, AutoGridProps } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView';
interface CatalogItemGridWidgetViewProps extends AutoGridProps interface CatalogItemGridWidgetViewProps extends AutoGridProps
@ -13,7 +12,7 @@ interface CatalogItemGridWidgetViewProps extends AutoGridProps
export const CatalogItemGridWidgetView: FC<CatalogItemGridWidgetViewProps> = props => export const CatalogItemGridWidgetView: FC<CatalogItemGridWidgetViewProps> = props =>
{ {
const { columnCount = 5, children = null, ...rest } = props; const { columnCount = 5, children = null, ...rest } = props;
const { currentOffer = null, setCurrentOffer = null, currentPage = null, setPurchaseOptions = null } = useCatalogContext(); const { currentOffer = null, setCurrentOffer = null, currentPage = null, setPurchaseOptions = null } = useCatalog();
if(!currentPage) return null; if(!currentPage) return null;
@ -40,7 +39,7 @@ export const CatalogItemGridWidgetView: FC<CatalogItemGridWidgetViewProps> = pro
return ( return (
<AutoGrid columnCount={ columnCount } { ...rest }> <AutoGrid columnCount={ columnCount } { ...rest }>
{ currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer.offerId === offer.offerId)) } offer={ offer } onClick={ event => selectOffer(offer) } />) } { currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer.offerId === offer.offerId)) } offer={ offer } selectOffer={ selectOffer } />) }
{ children } { children }
</AutoGrid> </AutoGrid>
); );

View File

@ -1,12 +1,12 @@
import { FC } from 'react'; import { FC } from 'react';
import { Offer } from '../../../../../api';
import { Base, BaseProps, LayoutLimitedEditionCompletePlateView } from '../../../../../common'; import { Base, BaseProps, LayoutLimitedEditionCompletePlateView } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { Offer } from '../../../common/Offer';
export const CatalogLimitedItemWidgetView: FC<BaseProps<HTMLDivElement>> = props => export const CatalogLimitedItemWidgetView: FC<BaseProps<HTMLDivElement>> = props =>
{ {
const { children = null, ...rest } = props; const { children = null, ...rest } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
if(!currentOffer || (currentOffer.pricingModel !== Offer.PRICING_MODEL_SINGLE) || !currentOffer.product.isUniqueLimitedItem) return null; if(!currentOffer || (currentOffer.pricingModel !== Offer.PRICING_MODEL_SINGLE) || !currentOffer.product.isUniqueLimitedItem) return null;

View File

@ -1,8 +1,8 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC } from 'react'; import { FC } from 'react';
import { IPurchasableOffer } from '../../../../../api';
import { Flex, LayoutCurrencyIcon, Text } from '../../../../../common'; import { Flex, LayoutCurrencyIcon, Text } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
interface CatalogPriceDisplayWidgetViewProps interface CatalogPriceDisplayWidgetViewProps
{ {
@ -13,7 +13,7 @@ interface CatalogPriceDisplayWidgetViewProps
export const CatalogPriceDisplayWidgetView: FC<CatalogPriceDisplayWidgetViewProps> = props => export const CatalogPriceDisplayWidgetView: FC<CatalogPriceDisplayWidgetViewProps> = props =>
{ {
const { offer = null, separator = false } = props; const { offer = null, separator = false } = props;
const { purchaseOptions = null } = useCatalogContext(); const { purchaseOptions = null } = useCatalog();
const { quantity = 1 } = purchaseOptions; const { quantity = 1 } = purchaseOptions;
if(!offer) return null; if(!offer) return null;

View File

@ -1,12 +1,9 @@
import { PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer'; import { PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { CreateLinkEvent, GetClubMemberLevel, LocalizeText, SendMessageComposer } from '../../../../../api'; import { CatalogPurchaseState, CreateLinkEvent, GetClubMemberLevel, LocalizeText, LocalStorageKeys, Offer, SendMessageComposer } from '../../../../../api';
import { Button, LayoutLoadingSpinnerView } from '../../../../../common'; import { Button, LayoutLoadingSpinnerView } from '../../../../../common';
import { CatalogEvent, CatalogInitGiftEvent, CatalogInitPurchaseEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent, CatalogWidgetEvent } from '../../../../../events'; import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent } from '../../../../../events';
import { DispatchUiEvent, usePurse, UseUiEvent } from '../../../../../hooks'; import { DispatchUiEvent, useCatalog, useLocalStorage, usePurse, UseUiEvent } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
import { Offer } from '../../../common/Offer';
interface CatalogPurchaseWidgetViewProps interface CatalogPurchaseWidgetViewProps
{ {
@ -19,19 +16,10 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
const { noGiftOption = false, purchaseCallback = null } = props; const { noGiftOption = false, purchaseCallback = null } = props;
const [ purchaseWillBeGift, setPurchaseWillBeGift ] = useState(false); const [ purchaseWillBeGift, setPurchaseWillBeGift ] = useState(false);
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE); const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
const { currentOffer = null, currentPage = null, purchaseOptions = null, setPurchaseOptions = null } = useCatalogContext(); const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useLocalStorage(LocalStorageKeys.CATALOG_SKIP_PURCHASE_CONFIRMATION, false);
const { currentOffer = null, currentPage = null, purchaseOptions = null, setPurchaseOptions = null } = useCatalog();
const { getCurrencyAmount = null } = usePurse(); const { getCurrencyAmount = null } = usePurse();
const onCatalogInitPurchaseEvent = useCallback((event: CatalogInitPurchaseEvent) =>
{
if(!currentOffer) return;
// show purchase confirmation
// offer, page.pageId, extraData, quantity, previewStuffData, null, true, null
}, [ currentOffer ]);
UseUiEvent(CatalogWidgetEvent.INIT_PURCHASE, onCatalogInitPurchaseEvent);
const onCatalogEvent = useCallback((event: CatalogEvent) => const onCatalogEvent = useCallback((event: CatalogEvent) =>
{ {
switch(event.type) switch(event.type)

View File

@ -1,6 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { Flex, FlexProps } from '../../../../../common/Flex'; import { Flex, FlexProps } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView';
interface CatalogSimplePriceWidgetViewProps extends FlexProps interface CatalogSimplePriceWidgetViewProps extends FlexProps
@ -11,7 +11,7 @@ interface CatalogSimplePriceWidgetViewProps extends FlexProps
export const CatalogSimplePriceWidgetView: FC<CatalogSimplePriceWidgetViewProps> = props => export const CatalogSimplePriceWidgetView: FC<CatalogSimplePriceWidgetViewProps> = props =>
{ {
const { gap = 1, ...rest } = props; const { gap = 1, ...rest } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
return ( return (
<Flex gap={ gap } alignItems="center" classNames={ [ 'bg-muted', 'p-1', 'rounded' ] } { ...rest }> <Flex gap={ gap } alignItems="center" classNames={ [ 'bg-muted', 'p-1', 'rounded' ] } { ...rest }>

View File

@ -1,9 +1,7 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { LocalizeText, ProductTypeEnum } from '../../../../../api'; import { IPurchasableOffer, LocalizeText, Offer, ProductTypeEnum } from '../../../../../api';
import { AutoGrid, AutoGridProps, Button, ButtonGroup } from '../../../../../common'; import { AutoGrid, AutoGridProps, Button, ButtonGroup } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { IPurchasableOffer } from '../../../common/IPurchasableOffer';
import { Offer } from '../../../common/Offer';
import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView';
interface CatalogSpacesWidgetViewProps extends AutoGridProps interface CatalogSpacesWidgetViewProps extends AutoGridProps
@ -19,7 +17,21 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
const [ groupedOffers, setGroupedOffers ] = useState<IPurchasableOffer[][]>(null); const [ groupedOffers, setGroupedOffers ] = useState<IPurchasableOffer[][]>(null);
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(-1); const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(-1);
const [ selectedOfferForGroup, setSelectedOfferForGroup ] = useState<IPurchasableOffer[]>(null); const [ selectedOfferForGroup, setSelectedOfferForGroup ] = useState<IPurchasableOffer[]>(null);
const { currentPage = null, currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null } = useCatalogContext(); const { currentPage = null, currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null } = useCatalog();
const setSelectedOffer = (offer: IPurchasableOffer) =>
{
if(!offer) return;
setSelectedOfferForGroup(prevValue =>
{
const newValue = [ ...prevValue ];
newValue[selectedGroupIndex] = offer;
return newValue;
});
}
useEffect(() => useEffect(() =>
{ {
@ -89,22 +101,7 @@ export const CatalogSpacesWidgetView: FC<CatalogSpacesWidgetViewProps> = props =
{ SPACES_GROUP_NAMES.map((name, index) => <Button key={ index } active={ (selectedGroupIndex === index) } onClick={ event => setSelectedGroupIndex(index) }>{ LocalizeText(`catalog.spaces.tab.${ name }`) }</Button>) } { SPACES_GROUP_NAMES.map((name, index) => <Button key={ index } active={ (selectedGroupIndex === index) } onClick={ event => setSelectedGroupIndex(index) }>{ LocalizeText(`catalog.spaces.tab.${ name }`) }</Button>) }
</ButtonGroup> </ButtonGroup>
<AutoGrid columnCount={ columnCount } { ...rest }> <AutoGrid columnCount={ columnCount } { ...rest }>
{ offers && (offers.length > 0) && offers.map((offer, index) => { offers && (offers.length > 0) && offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer === offer)) } offer={ offer } selectOffer={ offer => setSelectedOffer(offer) } />) }
{
const setSelectedOffer = () =>
{
setSelectedOfferForGroup(prevValue =>
{
const newValue = [ ...prevValue ];
newValue[selectedGroupIndex] = offer;
return newValue;
});
}
return <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer === offer)) } offer={ offer } onClick={ setSelectedOffer } />;
}) }
{ children } { children }
</AutoGrid> </AutoGrid>
</> </>

View File

@ -1,16 +1,15 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC } from 'react'; import { FC } from 'react';
import { LocalizeText } from '../../../../../api'; import { LocalizeText } from '../../../../../api';
import { Flex } from '../../../../../common/Flex'; import { Flex, Text } from '../../../../../common';
import { Text } from '../../../../../common/Text'; import { useCatalog } from '../../../../../hooks';
import { useCatalogContext } from '../../../CatalogContext';
const MIN_VALUE: number = 1; const MIN_VALUE: number = 1;
const MAX_VALUE: number = 100; const MAX_VALUE: number = 100;
export const CatalogSpinnerWidgetView: FC<{}> = props => export const CatalogSpinnerWidgetView: FC<{}> = props =>
{ {
const { currentOffer = null, purchaseOptions = null, setPurchaseOptions = null } = useCatalogContext(); const { currentOffer = null, purchaseOptions = null, setPurchaseOptions = null } = useCatalog();
const { quantity = 1 } = purchaseOptions; const { quantity = 1 } = purchaseOptions;
const updateQuantity = (value: number) => const updateQuantity = (value: number) =>

View File

@ -1,6 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { Column, ColumnProps } from '../../../../../common/Column'; import { Column, ColumnProps } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView';
interface CatalogSimplePriceWidgetViewProps extends ColumnProps interface CatalogSimplePriceWidgetViewProps extends ColumnProps
@ -10,7 +10,7 @@ interface CatalogSimplePriceWidgetViewProps extends ColumnProps
export const CatalogTotalPriceWidget: FC<CatalogSimplePriceWidgetViewProps> = props => export const CatalogTotalPriceWidget: FC<CatalogSimplePriceWidgetViewProps> = props =>
{ {
const { gap = 1, ...rest } = props; const { gap = 1, ...rest } = props;
const { currentOffer = null } = useCatalogContext(); const { currentOffer = null } = useCatalog();
return ( return (
<Column gap={ gap } { ...rest }> <Column gap={ gap } { ...rest }>

View File

@ -1,14 +1,12 @@
import { Vector3d } from '@nitrots/nitro-renderer'; import { Vector3d } from '@nitrots/nitro-renderer';
import { FC, useEffect } from 'react'; import { FC, useEffect } from 'react';
import { GetAvatarRenderManager, GetSessionDataManager, ProductTypeEnum } from '../../../../../api'; import { FurniCategory, GetAvatarRenderManager, GetSessionDataManager, Offer, ProductTypeEnum } from '../../../../../api';
import { AutoGrid, Column, LayoutGridItem, LayoutRoomPreviewerView } from '../../../../../common'; import { AutoGrid, Column, LayoutGridItem, LayoutRoomPreviewerView } from '../../../../../common';
import { useCatalogContext } from '../../../CatalogContext'; import { useCatalog } from '../../../../../hooks';
import { FurniCategory } from '../../../common/FurniCategory';
import { Offer } from '../../../common/Offer';
export const CatalogViewProductWidgetView: FC<{}> = props => export const CatalogViewProductWidgetView: FC<{}> = props =>
{ {
const { currentOffer = null, roomPreviewer = null, purchaseOptions = null } = useCatalogContext(); const { currentOffer = null, roomPreviewer = null, purchaseOptions = null } = useCatalog();
const { previewStuffData = null } = purchaseOptions; const { previewStuffData = null } = purchaseOptions;
useEffect(() => useEffect(() =>

View File

@ -1,8 +1,7 @@
import { GroupInformationParser, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; import { GroupInformationParser, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { CreateLinkEvent, GetGroupManager, GetGroupMembers, GetSessionDataManager, LocalizeText, NotificationUtilities, SendMessageComposer, TryJoinGroup, TryVisitRoom } from '../../../api'; import { CatalogPageName, CreateLinkEvent, GetGroupManager, GetGroupMembers, GetSessionDataManager, LocalizeText, NotificationUtilities, SendMessageComposer, TryJoinGroup, TryVisitRoom } from '../../../api';
import { Button, Column, Flex, Grid, GridProps, LayoutBadgeImageView, Text } from '../../../common'; import { Button, Column, Flex, Grid, GridProps, LayoutBadgeImageView, Text } from '../../../common';
import { CatalogPageName } from '../../catalog/common/CatalogPageName';
import { GroupMembershipType } from '../common/GroupMembershipType'; import { GroupMembershipType } from '../common/GroupMembershipType';
import { GroupType } from '../common/GroupType'; import { GroupType } from '../common/GroupType';

View File

@ -1,7 +1,7 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
import { HabboClubLevelEnum, RoomCreateComposer } from '@nitrots/nitro-renderer'; import { HabboClubLevelEnum, RoomCreateComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { GetClubMemberLevel, GetConfiguration, IRoomModel, LocalizeText, RoomModels, SendMessageComposer } from '../../../api'; import { GetClubMemberLevel, GetConfiguration, IRoomModel, LocalizeText, SendMessageComposer } from '../../../api';
import { Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, Text } from '../../../common'; import { Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, Text } from '../../../common';
import { useNavigatorContext } from '../NavigatorContext'; import { useNavigatorContext } from '../NavigatorContext';
@ -13,22 +13,25 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
const [ category, setCategory ] = useState<number>(null); const [ category, setCategory ] = useState<number>(null);
const [ visitorsCount, setVisitorsCount ] = useState<number>(null); const [ visitorsCount, setVisitorsCount ] = useState<number>(null);
const [ tradesSetting, setTradesSetting ] = useState<number>(0); const [ tradesSetting, setTradesSetting ] = useState<number>(0);
const [ selectedModelName, setSelectedModelName ] = useState<string>(RoomModels[0].name); const [ roomModels, setRoomModels ] = useState<IRoomModel[]>([]);
const [ selectedModelName, setSelectedModelName ] = useState<string>('');
const { categories = null } = useNavigatorContext(); const { categories = null } = useNavigatorContext();
const hcDisabled = GetConfiguration<boolean>('hc.disabled', false);
const getRoomModelImage = (name: string) => GetConfiguration<string>('images.url') + `/navigator/models/model_${ name }.png`; const getRoomModelImage = (name: string) => GetConfiguration<string>('images.url') + `/navigator/models/model_${ name }.png`;
const selectModel = (model: IRoomModel, index: number) => const selectModel = (model: IRoomModel, index: number) =>
{ {
if(!model || (model.clubLevel > GetClubMemberLevel())) return; if(!model || (model.clubLevel > GetClubMemberLevel())) return;
setSelectedModelName(RoomModels[index].name); setSelectedModelName(roomModels[index].name);
} };
const createRoom = () => const createRoom = () =>
{ {
SendMessageComposer(new RoomCreateComposer(name, description, 'model_' + selectedModelName, Number(category), Number(visitorsCount), tradesSetting)); SendMessageComposer(new RoomCreateComposer(name, description, 'model_' + selectedModelName, Number(category), Number(visitorsCount), tradesSetting));
} };
useEffect(() => useEffect(() =>
{ {
@ -48,6 +51,17 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
if(categories && categories.length) setCategory(categories[0].id); if(categories && categories.length) setCategory(categories[0].id);
}, [ categories ]); }, [ categories ]);
useEffect(() =>
{
const models = GetConfiguration<IRoomModel[]>('navigator.room.models');
if(models && models.length)
{
setRoomModels(models);
setSelectedModelName(models[0].name);
}
}, []);
return ( return (
<Column overflow="hidden"> <Column overflow="hidden">
<Grid overflow="hidden"> <Grid overflow="hidden">
@ -89,14 +103,14 @@ export const NavigatorRoomCreatorView: FC<{}> = props =>
</Column> </Column>
<Column size={ 6 } gap={ 1 } overflow="auto"> <Column size={ 6 } gap={ 1 } overflow="auto">
{ {
RoomModels.map((model, index )=> roomModels.map((model, index )=>
{ {
return (<LayoutGridItem fullHeight key={ model.name } onClick={ () => selectModel(model, index) } itemActive={ (selectedModelName === model.name) } overflow="unset" gap={ 0 } className="p-1" disabled={ (GetClubMemberLevel() < model.clubLevel) }> return (<LayoutGridItem fullHeight key={ model.name } onClick={ () => selectModel(model, index) } itemActive={ (selectedModelName === model.name) } overflow="unset" gap={ 0 } className="p-1" disabled={ (GetClubMemberLevel() < model.clubLevel) }>
<Flex fullHeight center overflow="hidden"> <Flex fullHeight center overflow="hidden">
<img alt="" src={ getRoomModelImage(model.name) } /> <img alt="" src={ getRoomModelImage(model.name) } />
</Flex> </Flex>
<Text bold>{ model.tileSize } { LocalizeText('navigator.createroom.tilesize') }</Text> <Text bold>{ model.tileSize } { LocalizeText('navigator.createroom.tilesize') }</Text>
{ model.clubLevel > HabboClubLevelEnum.NO_CLUB && <LayoutCurrencyIcon position="absolute" className="top-1 end-1" type="hc" /> } { !hcDisabled && model.clubLevel > HabboClubLevelEnum.NO_CLUB && <LayoutCurrencyIcon position="absolute" className="top-1 end-1" type="hc" /> }
</LayoutGridItem>); </LayoutGridItem>);
}) })
} }

View File

@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RoomDataParser } from '@nitrots/nitro-renderer'; import { RoomDataParser } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, useEffect, useState } from 'react'; import { FC, useRef, useState } from 'react';
import { Overlay, Popover } from 'react-bootstrap'; import { Overlay, Popover } from 'react-bootstrap';
import { LocalizeText } from '../../../../api'; import { LocalizeText } from '../../../../api';
import { Base, Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common'; import { Base, Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common';
@ -13,8 +13,8 @@ interface NavigatorSearchResultItemInfoViewProps
export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfoViewProps> = props => export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfoViewProps> = props =>
{ {
const { roomData = null } = props; const { roomData = null } = props;
const [ target, setTarget ] = useState<(EventTarget & HTMLElement)>(null);
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const elementRef = useRef<HTMLDivElement>();
const getUserCounterColor = () => const getUserCounterColor = () =>
{ {
@ -38,33 +38,10 @@ export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfo
return bg; return bg;
} }
const toggle = (event: MouseEvent<HTMLElement>) =>
{
event.stopPropagation();
let visible = false;
setIsVisible(prevValue =>
{
visible = !prevValue;
return visible;
});
if(visible) setTarget((event.target as (EventTarget & HTMLElement)));
}
useEffect(() =>
{
if(isVisible) return;
setTarget(null);
}, [ isVisible ]);
return ( return (
<> <>
<Base pointer className="icon icon-navigator-info" onClick={ toggle }/> <Base pointer innerRef={ elementRef } className="icon icon-navigator-info" onMouseOver={ event => setIsVisible(true) } onMouseLeave={ event => setIsVisible(false) } />
<Overlay show={ isVisible } target={ target } placement="right"> <Overlay show={ isVisible } target={ elementRef.current } placement="right">
<Popover> <Popover>
<NitroCardContentView overflow="hidden" className="room-info bg-transparent"> <NitroCardContentView overflow="hidden" className="room-info bg-transparent">
<Flex gap={ 2 } overflow="hidden"> <Flex gap={ 2 } overflow="hidden">
@ -78,14 +55,14 @@ export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfo
<Text bold truncate className="flex-grow-1" style={ { maxHeight: 13 } }> <Text bold truncate className="flex-grow-1" style={ { maxHeight: 13 } }>
{ roomData.roomName } { roomData.roomName }
</Text> </Text>
<Flex gap={ 1 }> <Flex gap={ 2 }>
<Text italics variant="muted"> <Text italics variant="muted">
{ LocalizeText('navigator.roomownercaption') } { LocalizeText('navigator.roomownercaption') }
</Text> </Text>
<UserProfileIconView <Flex alignItems="center" gap={ 1 }>
userId={ roomData.ownerId } <UserProfileIconView userId={ roomData.ownerId } />
/> <Text italics>{ roomData.ownerName }</Text>
<Text italics>{ roomData.ownerName }</Text> </Flex>
</Flex> </Flex>
<Text className="flex-grow-1"> <Text className="flex-grow-1">
{ roomData.description } { roomData.description }

View File

@ -87,7 +87,7 @@ export const AvatarInfoUseProductView: FC<AvatarInfoUseProductViewProps> = props
}, [ item, updateConfirmingProduct ]); }, [ item, updateConfirmingProduct ]);
return ( return (
<ContextMenuView objectId={ item.id } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.PET } close={ close }> <ContextMenuView objectId={ item.id } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.PET } close={ close } collapsable={ true }>
<ContextMenuHeaderView> <ContextMenuHeaderView>
{ item.name } { item.name }
</ContextMenuHeaderView> </ContextMenuHeaderView>

View File

@ -213,7 +213,7 @@ export const AvatarInfoWidgetAvatarView: FC<AvatarInfoWidgetAvatarViewProps> = p
}, [ userData ]); }, [ userData ]);
return ( return (
<ContextMenuView objectId={ userData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ userData.userType } close={ close }> <ContextMenuView objectId={ userData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ userData.userType } close={ close } collapsable={ true }>
<ContextMenuHeaderView className="cursor-pointer" onClick={ event => GetUserProfile(userData.webID) }> <ContextMenuHeaderView className="cursor-pointer" onClick={ event => GetUserProfile(userData.webID) }>
{ userData.name } { userData.name }
</ContextMenuHeaderView> </ContextMenuHeaderView>

View File

@ -121,7 +121,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
const isRidingHorse = IsRidingHorse(); const isRidingHorse = IsRidingHorse();
return ( return (
<ContextMenuView objectId={ userData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ userData.userType } close={ close }> <ContextMenuView objectId={ userData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ userData.userType } close={ close } collapsable={ true }>
<ContextMenuHeaderView className="cursor-pointer" onClick={ event => GetUserProfile(userData.webID) }> <ContextMenuHeaderView className="cursor-pointer" onClick={ event => GetUserProfile(userData.webID) }>
{ userData.name } { userData.name }
</ContextMenuHeaderView> </ContextMenuHeaderView>

View File

@ -142,7 +142,7 @@ export const AvatarInfoWidgetOwnPetView: FC<AvatarInfoWidgetOwnPetViewProps> = p
}, [ petData ]); }, [ petData ]);
return ( return (
<ContextMenuView objectId={ petData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.PET } close={ close }> <ContextMenuView objectId={ petData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.PET } close={ close } collapsable={ true }>
<ContextMenuHeaderView> <ContextMenuHeaderView>
{ petData.name } { petData.name }
</ContextMenuHeaderView> </ContextMenuHeaderView>

View File

@ -109,7 +109,7 @@ export const AvatarInfoWidgetPetView: FC<AvatarInfoWidgetPetViewProps> = props =
}, [ petData ]); }, [ petData ]);
return ( return (
<ContextMenuView objectId={ petData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.PET } close={ close }> <ContextMenuView objectId={ petData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.PET } close={ close } collapsable={ true }>
<ContextMenuHeaderView> <ContextMenuHeaderView>
{ petData.name } { petData.name }
</ContextMenuHeaderView> </ContextMenuHeaderView>

View File

@ -136,7 +136,7 @@ export const AvatarInfoWidgetRentableBotView: FC<AvatarInfoWidgetRentableBotView
const canControl = (rentableBotData.amIOwner || rentableBotData.amIAnyRoomController); const canControl = (rentableBotData.amIOwner || rentableBotData.amIAnyRoomController);
return ( return (
<ContextMenuView objectId={ rentableBotData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.RENTABLE_BOT } close={ close }> <ContextMenuView objectId={ rentableBotData.roomIndex } category={ RoomObjectCategory.UNIT } userType={ RoomObjectType.RENTABLE_BOT } close={ close } collapsable={ true }>
<ContextMenuHeaderView> <ContextMenuHeaderView>
{ rentableBotData.name } { rentableBotData.name }
</ContextMenuHeaderView> </ContextMenuHeaderView>

View File

@ -20,9 +20,11 @@
font-size: 18px; font-size: 18px;
} }
&:not(.name-only) { &:not(.name-only):not(.menu-hidden) {
min-width: 125px; min-width: 125px;
}
&:not(.name-only) {
&:after { &:after {
content: ""; content: "";
position: absolute; position: absolute;
@ -49,6 +51,22 @@
margin-bottom: 2px; margin-bottom: 2px;
} }
&.menu-hidden {
.menu-footer {
padding-top:2px;
padding-bottom:0;
}
}
.menu-footer {
color: $white;
font-size: 16px;
width:100%;
min-width: 25px;
padding-bottom:2px;
cursor: pointer;
}
.menu-list-split-3 { .menu-list-split-3 {
.menu-item { .menu-item {

View File

@ -0,0 +1,25 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC, useMemo } from 'react';
import { Flex, FlexProps } from '../../../../common';
interface CaretViewProps extends FlexProps
{
collapsed?: boolean;
}
export const ContextMenuCaretView: FC<CaretViewProps> = props =>
{
const { justifyContent = 'center', alignItems = 'center', classNames = [], collapsed = true, ...rest } = props;
const getClassNames = useMemo(() =>
{
const newClassNames: string[] = [ 'menu-footer' ];
if(classNames.length) newClassNames.push(...classNames);
return newClassNames;
}, [ classNames ]);
return <Flex justifyContent={ justifyContent } alignItems={ alignItems } classNames={ getClassNames } { ...rest }>
<FontAwesomeIcon icon={ !collapsed ? 'caret-down' : 'caret-up' } className="align-self-center" />
</Flex>
}

View File

@ -2,6 +2,7 @@ import { FixedSizeStack, NitroPoint, NitroRectangle, RoomObjectType } from '@nit
import { CSSProperties, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { CSSProperties, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { GetNitroInstance, GetRoomObjectBounds, GetRoomObjectScreenLocation, GetRoomSession, GetTicker } from '../../../../api'; import { GetNitroInstance, GetRoomObjectBounds, GetRoomObjectScreenLocation, GetRoomSession, GetTicker } from '../../../../api';
import { Base, BaseProps } from '../../../../common'; import { Base, BaseProps } from '../../../../common';
import { ContextMenuCaretView } from './ContextMenuCaretView';
interface ContextMenuViewProps extends BaseProps<HTMLDivElement> interface ContextMenuViewProps extends BaseProps<HTMLDivElement>
{ {
@ -10,6 +11,7 @@ interface ContextMenuViewProps extends BaseProps<HTMLDivElement>
userType?: number; userType?: number;
fades?: boolean; fades?: boolean;
close: () => void; close: () => void;
collapsable?: boolean;
} }
const LOCATION_STACK_SIZE: number = 25; const LOCATION_STACK_SIZE: number = 25;
@ -18,9 +20,11 @@ const fadeDelay = 3000;
const fadeLength = 75; const fadeLength = 75;
const SPACE_AROUND_EDGES = 10; const SPACE_AROUND_EDGES = 10;
let COLLAPSED = false;
export const ContextMenuView: FC<ContextMenuViewProps> = props => export const ContextMenuView: FC<ContextMenuViewProps> = props =>
{ {
const { objectId = -1, category = -1, userType = -1, fades = false, close = null, position = 'absolute', classNames = [], style = {}, ...rest } = props; const { objectId = -1, category = -1, userType = -1, fades = false, close = null, position = 'absolute', classNames = [], style = {}, children = null, collapsable = false, ...rest } = props;
const [ pos, setPos ] = useState<{ x: number, y: number }>({ x: null, y: null }); const [ pos, setPos ] = useState<{ x: number, y: number }>({ x: null, y: null });
const [ deltaYStack, setDeltaYStack ] = useState<FixedSizeStack>(null); const [ deltaYStack, setDeltaYStack ] = useState<FixedSizeStack>(null);
const [ currentDeltaY, setCurrentDeltaY ] = useState(-1000000); const [ currentDeltaY, setCurrentDeltaY ] = useState(-1000000);
@ -30,6 +34,8 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
const [ isFrozen, setIsFrozen ] = useState(false); const [ isFrozen, setIsFrozen ] = useState(false);
const elementRef = useRef<HTMLDivElement>(); const elementRef = useRef<HTMLDivElement>();
const [ collapsed, setCollapsed ] = useState(COLLAPSED);
const getOffset = useCallback((bounds: NitroRectangle) => const getOffset = useCallback((bounds: NitroRectangle) =>
{ {
let height = -(elementRef.current.offsetHeight); let height = -(elementRef.current.offsetHeight);
@ -118,13 +124,15 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
const getClassNames = useMemo(() => const getClassNames = useMemo(() =>
{ {
const newClassNames: string[] = [ 'nitro-context-menu' ]; const newClassNames: string[] = [ 'nitro-context-menu' ];
if (collapsed) newClassNames.push('menu-hidden');
newClassNames.push((pos.x !== null) ? 'visible' : 'invisible'); newClassNames.push((pos.x !== null) ? 'visible' : 'invisible');
if(classNames.length) newClassNames.push(...classNames); if(classNames.length) newClassNames.push(...classNames);
return newClassNames; return newClassNames;
}, [ pos, classNames ]); }, [ pos, classNames, collapsed ]);
const getStyle = useMemo(() => const getStyle = useMemo(() =>
{ {
@ -170,6 +178,15 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [ fades ]); }, [ fades ]);
return <Base innerRef={ elementRef } position={ position } classNames={ getClassNames } style={ getStyle } onMouseOver={ event => setIsFrozen(true) } onMouseOut={ event => setIsFrozen(false) } { ...rest } />; const toggleCollapse = () =>
{
COLLAPSED = !COLLAPSED;
setCollapsed(COLLAPSED)
}
return <Base innerRef={ elementRef } position={ position } classNames={ getClassNames } style={ getStyle } onMouseOver={ event => setIsFrozen(true) } onMouseOut={ event => setIsFrozen(false) } { ...rest }>
{ !(collapsable && COLLAPSED) && children }
{ collapsable && <ContextMenuCaretView onClick={ () => toggleCollapse() } collapsed={ collapsed } /> }
</Base>;
} }

View File

@ -3,12 +3,14 @@ import { ILinkEventTracker, NitroSettingsEvent, UserSettingsCameraFollowComposer
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api'; import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api';
import { Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; import { Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
import { DispatchMainEvent, DispatchUiEvent, UseMessageEventHook } from '../../hooks'; import { DispatchMainEvent, DispatchUiEvent, useCatalogPlaceMultipleItems, useCatalogSkipPurchaseConfirmation, UseMessageEventHook } from '../../hooks';
export const UserSettingsView: FC<{}> = props => export const UserSettingsView: FC<{}> = props =>
{ {
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ userSettings, setUserSettings ] = useState<NitroSettingsEvent>(null); const [ userSettings, setUserSettings ] = useState<NitroSettingsEvent>(null);
const [ catalogPlaceMultipleObjects, setCatalogPlaceMultipleObjects ] = useCatalogPlaceMultipleItems();
const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useCatalogSkipPurchaseConfirmation();
const onUserSettingsEvent = useCallback((event: UserSettingsEvent) => const onUserSettingsEvent = useCallback((event: UserSettingsEvent) =>
{ {
@ -141,6 +143,14 @@ export const UserSettingsView: FC<{}> = props =>
<input className="form-check-input" type="checkbox" checked={ userSettings.cameraFollow } onChange={ event => processAction('camera_follow', event.target.checked) } /> <input className="form-check-input" type="checkbox" checked={ userSettings.cameraFollow } onChange={ event => processAction('camera_follow', event.target.checked) } />
<Text>{ LocalizeText('memenu.settings.other.disable.room.camera.follow') }</Text> <Text>{ LocalizeText('memenu.settings.other.disable.room.camera.follow') }</Text>
</Flex> </Flex>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="checkbox" checked={ catalogPlaceMultipleObjects } onChange={ event => setCatalogPlaceMultipleObjects(event.target.checked) } />
<Text>{ LocalizeText('memenu.settings.other.place.multiple.objects') }</Text>
</Flex>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="checkbox" checked={ catalogSkipPurchaseConfirmation } onChange={ event => setCatalogSkipPurchaseConfirmation(event.target.checked) } />
<Text>{ LocalizeText('memenu.settings.other.skip.purchase.confirmation') }</Text>
</Flex>
</Column> </Column>
<Column> <Column>
<Text bold>{ LocalizeText('widget.memenu.settings.volume') }</Text> <Text bold>{ LocalizeText('widget.memenu.settings.volume') }</Text>

View File

@ -7,7 +7,6 @@ export class CatalogEvent extends NitroEvent
public static TOGGLE_CATALOG: string = 'CE_TOGGLE_CATALOG'; public static TOGGLE_CATALOG: string = 'CE_TOGGLE_CATALOG';
public static SOLD_OUT: string = 'CE_SOLD_OUT'; public static SOLD_OUT: string = 'CE_SOLD_OUT';
public static APPROVE_NAME_RESULT: string = 'CE_APPROVE_NAME_RESULT'; public static APPROVE_NAME_RESULT: string = 'CE_APPROVE_NAME_RESULT';
public static GIFT_RECEIVER_NOT_FOUND: string = 'CE_GIFT_RECEIVER_NOT_FOUND';
public static PURCHASE_APPROVED: string = 'CE_PURCHASE_APPROVED'; public static PURCHASE_APPROVED: string = 'CE_PURCHASE_APPROVED';
public static INIT_GIFT: string = 'CE_INIT_GIFT'; public static INIT_GIFT: string = 'CE_INIT_GIFT';
public static CATALOG_RESET: string = 'CE_RESET'; public static CATALOG_RESET: string = 'CE_RESET';

View File

@ -1,9 +0,0 @@
import { CatalogEvent } from './CatalogEvent';
export class CatalogGiftReceiverNotFoundEvent extends CatalogEvent
{
constructor()
{
super(CatalogEvent.GIFT_RECEIVER_NOT_FOUND);
}
}

View File

@ -1,26 +0,0 @@
import { NitroEvent } from '@nitrots/nitro-renderer';
import { CatalogWidgetEvent } from './CatalogWidgetEvent';
export class CatalogInitPurchaseEvent extends NitroEvent
{
private _enableBuyAsGift: boolean = true;
private _userName: string;
constructor(enableBuyAsGift: boolean = true, userName: string = null)
{
super(CatalogWidgetEvent.INIT_PURCHASE);
this._enableBuyAsGift = enableBuyAsGift;
this._userName = userName;
}
public get enableBuyAsGift(): boolean
{
return this._enableBuyAsGift;
}
public get userName(): string
{
return this._userName;
}
}

View File

@ -1,26 +0,0 @@
import { NitroEvent } from '@nitrots/nitro-renderer';
import { CatalogWidgetEvent } from './CatalogWidgetEvent';
export class CatalogNameResultEvent extends NitroEvent
{
private _result: number;
private _validationInfo: string;
constructor(result: number, validationInfo: string)
{
super(CatalogWidgetEvent.APPROVE_RESULT);
this._result = result;
this._validationInfo = validationInfo;
}
public get result(): number
{
return this._result;
}
public get validationInfo(): string
{
return this._validationInfo;
}
}

View File

@ -11,10 +11,8 @@ export class CatalogWidgetEvent extends NitroEvent
public static COLOUR_INDEX: string = 'CWE_COLOUR_INDEX'; public static COLOUR_INDEX: string = 'CWE_COLOUR_INDEX';
public static TEXT_INPUT: string = 'CWE_TEXT_INPUT'; public static TEXT_INPUT: string = 'CWE_TEXT_INPUT';
public static DROPMENU_SELECT: string = 'CWE_CWE_DROPMENU_SELECT'; public static DROPMENU_SELECT: string = 'CWE_CWE_DROPMENU_SELECT';
public static APPROVE_RESULT: string = 'CWE_CWE_APPROVE_RESULT';
public static PURCHASE_OVERRIDE: string = 'CWE_PURCHASE_OVERRIDE'; public static PURCHASE_OVERRIDE: string = 'CWE_PURCHASE_OVERRIDE';
public static SELLABLE_PET_PALETTES: string = 'CWE_SELLABLE_PET_PALETTES'; public static SELLABLE_PET_PALETTES: string = 'CWE_SELLABLE_PET_PALETTES';
public static INIT_PURCHASE: string = 'CWE_INIT_PURCHASE';
public static UPDATE_ROOM_PREVIEW: string = 'CWE_UPDATE_ROOM_PREVIEW'; public static UPDATE_ROOM_PREVIEW: string = 'CWE_UPDATE_ROOM_PREVIEW';
public static GUILD_SELECTED: string = 'CWE_GUILD_SELECTED'; public static GUILD_SELECTED: string = 'CWE_GUILD_SELECTED';
public static TOTAL_PRICE_WIDGET_INITIALIZED: string = 'CWE_TOTAL_PRICE_WIDGET_INITIALIZED'; public static TOTAL_PRICE_WIDGET_INITIALIZED: string = 'CWE_TOTAL_PRICE_WIDGET_INITIALIZED';

Some files were not shown because too many files have changed in this diff Show More