diff --git a/package.json b/package.json index a15b4ec8..3a1738e8 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/free-solid-svg-icons": "^6.1.0", "@fortawesome/react-fontawesome": "^0.1.17", - "@nitrots/nitro-renderer": "^1.2.2", + "@nitrots/nitro-renderer": "^1.2.3", "animate.css": "^4.1.1", "classnames": "^2.3.1", "cross-env": "^7.0.3", diff --git a/public/ui-config.json.example b/public/ui-config.json.example index e67ee9a9..403bb68a 100644 --- a/public/ui-config.json.example +++ b/public/ui-config.json.example @@ -16,6 +16,43 @@ "badge.descriptions.enabled": true, "motto.max.length": 38, "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": { "show.avatar": true, "widgets": { diff --git a/src/api/catalog/BuilderFurniPlaceableStatus.ts b/src/api/catalog/BuilderFurniPlaceableStatus.ts new file mode 100644 index 00000000..40eb6f65 --- /dev/null +++ b/src/api/catalog/BuilderFurniPlaceableStatus.ts @@ -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; +} diff --git a/src/components/catalog/common/CatalogNode.ts b/src/api/catalog/CatalogNode.ts similarity index 100% rename from src/components/catalog/common/CatalogNode.ts rename to src/api/catalog/CatalogNode.ts diff --git a/src/components/catalog/common/CatalogPage.ts b/src/api/catalog/CatalogPage.ts similarity index 100% rename from src/components/catalog/common/CatalogPage.ts rename to src/api/catalog/CatalogPage.ts diff --git a/src/components/catalog/common/CatalogPageName.ts b/src/api/catalog/CatalogPageName.ts similarity index 100% rename from src/components/catalog/common/CatalogPageName.ts rename to src/api/catalog/CatalogPageName.ts diff --git a/src/components/catalog/common/CatalogPetPalette.ts b/src/api/catalog/CatalogPetPalette.ts similarity index 100% rename from src/components/catalog/common/CatalogPetPalette.ts rename to src/api/catalog/CatalogPetPalette.ts diff --git a/src/components/catalog/common/CatalogPurchaseState.ts b/src/api/catalog/CatalogPurchaseState.ts similarity index 100% rename from src/components/catalog/common/CatalogPurchaseState.ts rename to src/api/catalog/CatalogPurchaseState.ts diff --git a/src/components/catalog/common/CatalogType.ts b/src/api/catalog/CatalogType.ts similarity index 100% rename from src/components/catalog/common/CatalogType.ts rename to src/api/catalog/CatalogType.ts diff --git a/src/components/catalog/common/CatalogUtilities.ts b/src/api/catalog/CatalogUtilities.ts similarity index 99% rename from src/components/catalog/common/CatalogUtilities.ts rename to src/api/catalog/CatalogUtilities.ts index 54319610..5ca8fed5 100644 --- a/src/components/catalog/common/CatalogUtilities.ts +++ b/src/api/catalog/CatalogUtilities.ts @@ -1,5 +1,5 @@ import { SellablePetPaletteData } from '@nitrots/nitro-renderer'; -import { GetRoomEngine } from '../../../api'; +import { GetRoomEngine } from '../nitro'; import { ICatalogNode } from './ICatalogNode'; export const GetPixelEffectIcon = (id: number) => diff --git a/src/components/catalog/common/FurnitureOffer.ts b/src/api/catalog/FurnitureOffer.ts similarity index 99% rename from src/components/catalog/common/FurnitureOffer.ts rename to src/api/catalog/FurnitureOffer.ts index b25c376f..4c9c9f94 100644 --- a/src/components/catalog/common/FurnitureOffer.ts +++ b/src/api/catalog/FurnitureOffer.ts @@ -1,5 +1,5 @@ import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer'; -import { GetProductDataForLocalization, SendMessageComposer } from '../../../api'; +import { GetProductDataForLocalization, SendMessageComposer } from '..'; import { ICatalogPage } from './ICatalogPage'; import { IProduct } from './IProduct'; import { IPurchasableOffer } from './IPurchasableOffer'; diff --git a/src/components/catalog/common/GiftWrappingConfiguration.ts b/src/api/catalog/GiftWrappingConfiguration.ts similarity index 100% rename from src/components/catalog/common/GiftWrappingConfiguration.ts rename to src/api/catalog/GiftWrappingConfiguration.ts diff --git a/src/components/catalog/common/ICatalogNode.ts b/src/api/catalog/ICatalogNode.ts similarity index 100% rename from src/components/catalog/common/ICatalogNode.ts rename to src/api/catalog/ICatalogNode.ts diff --git a/src/components/catalog/common/ICatalogOptions.ts b/src/api/catalog/ICatalogOptions.ts similarity index 100% rename from src/components/catalog/common/ICatalogOptions.ts rename to src/api/catalog/ICatalogOptions.ts diff --git a/src/components/catalog/common/ICatalogPage.ts b/src/api/catalog/ICatalogPage.ts similarity index 100% rename from src/components/catalog/common/ICatalogPage.ts rename to src/api/catalog/ICatalogPage.ts diff --git a/src/components/catalog/common/IPageLocalization.ts b/src/api/catalog/IPageLocalization.ts similarity index 100% rename from src/components/catalog/common/IPageLocalization.ts rename to src/api/catalog/IPageLocalization.ts diff --git a/src/components/catalog/common/IProduct.ts b/src/api/catalog/IProduct.ts similarity index 100% rename from src/components/catalog/common/IProduct.ts rename to src/api/catalog/IProduct.ts diff --git a/src/components/catalog/common/IPurchasableOffer.ts b/src/api/catalog/IPurchasableOffer.ts similarity index 100% rename from src/components/catalog/common/IPurchasableOffer.ts rename to src/api/catalog/IPurchasableOffer.ts diff --git a/src/components/catalog/common/IPurchaseOptions.ts b/src/api/catalog/IPurchaseOptions.ts similarity index 100% rename from src/components/catalog/common/IPurchaseOptions.ts rename to src/api/catalog/IPurchaseOptions.ts diff --git a/src/components/catalog/common/Offer.ts b/src/api/catalog/Offer.ts similarity index 99% rename from src/components/catalog/common/Offer.ts rename to src/api/catalog/Offer.ts index 4b6f4f4e..c14d6ac1 100644 --- a/src/components/catalog/common/Offer.ts +++ b/src/api/catalog/Offer.ts @@ -1,4 +1,4 @@ -import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, ProductTypeEnum } from '../../../api'; +import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, ProductTypeEnum } from '..'; import { ICatalogPage } from './ICatalogPage'; import { IProduct } from './IProduct'; import { IPurchasableOffer } from './IPurchasableOffer'; diff --git a/src/components/catalog/common/PageLocalization.ts b/src/api/catalog/PageLocalization.ts similarity index 94% rename from src/components/catalog/common/PageLocalization.ts rename to src/api/catalog/PageLocalization.ts index 6a51830d..91e3ce6f 100644 --- a/src/components/catalog/common/PageLocalization.ts +++ b/src/api/catalog/PageLocalization.ts @@ -1,4 +1,4 @@ -import { GetConfiguration } from '../../../api'; +import { GetConfiguration } from '../nitro'; import { IPageLocalization } from './IPageLocalization'; export class PageLocalization implements IPageLocalization diff --git a/src/api/catalog/PlacedObjectPurchaseData.ts b/src/api/catalog/PlacedObjectPurchaseData.ts new file mode 100644 index 00000000..84bad8cd --- /dev/null +++ b/src/api/catalog/PlacedObjectPurchaseData.ts @@ -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; + } +} diff --git a/src/components/catalog/common/Product.ts b/src/api/catalog/Product.ts similarity index 97% rename from src/components/catalog/common/Product.ts rename to src/api/catalog/Product.ts index 52c7136c..bfb760fc 100644 --- a/src/components/catalog/common/Product.ts +++ b/src/api/catalog/Product.ts @@ -1,8 +1,9 @@ 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 { IProduct } from './IProduct'; import { IPurchasableOffer } from './IPurchasableOffer'; +import { ProductTypeEnum } from './ProductTypeEnum'; export class Product implements IProduct { diff --git a/src/components/catalog/common/RequestedPage.ts b/src/api/catalog/RequestedPage.ts similarity index 100% rename from src/components/catalog/common/RequestedPage.ts rename to src/api/catalog/RequestedPage.ts diff --git a/src/components/catalog/common/SearchResult.ts b/src/api/catalog/SearchResult.ts similarity index 100% rename from src/components/catalog/common/SearchResult.ts rename to src/api/catalog/SearchResult.ts diff --git a/src/api/catalog/index.ts b/src/api/catalog/index.ts index 9b5a552f..ba084bc8 100644 --- a/src/api/catalog/index.ts +++ b/src/api/catalog/index.ts @@ -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 './RequestedPage'; +export * from './SearchResult'; diff --git a/src/api/inventory/FurnitureItem.ts b/src/api/inventory/FurnitureItem.ts index 4d84cab9..dc055010 100644 --- a/src/api/inventory/FurnitureItem.ts +++ b/src/api/inventory/FurnitureItem.ts @@ -95,7 +95,7 @@ export class FurnitureItem implements IFurnitureItem return this._extra; } - public get _Str_16260(): boolean + public get recyclable(): boolean { return this._recyclable; } @@ -135,17 +135,17 @@ export class FurnitureItem implements IFurnitureItem return time; } - public get _Str_8932(): number + public get creationDay(): number { return this._creationDay; } - public get _Str_9050(): number + public get creationMonth(): number { return this._creationMonth; } - public get _Str_9408(): number + public get creationYear(): number { return this._creationYear; } @@ -155,7 +155,7 @@ export class FurnitureItem implements IFurnitureItem return this._slotId; } - public get _Str_3951(): number + public get songId(): number { return this._songId; } @@ -185,7 +185,7 @@ export class FurnitureItem implements IFurnitureItem return this._hasRentPeriodStarted; } - public get _Str_10616(): number + public get expirationTimeStamp(): number { return this._expirationTimeStamp; } diff --git a/src/api/inventory/IFurnitureItem.ts b/src/api/inventory/IFurnitureItem.ts index 7e030c52..435597d2 100644 --- a/src/api/inventory/IFurnitureItem.ts +++ b/src/api/inventory/IFurnitureItem.ts @@ -8,7 +8,7 @@ export interface IFurnitureItem stuffData: IObjectData; extra: number; category: number; - _Str_16260: boolean; + recyclable: boolean; isTradable: boolean; isGroupable: boolean; sellable: boolean; diff --git a/src/api/navigator/IRoomModel.ts b/src/api/navigator/IRoomModel.ts new file mode 100644 index 00000000..73dfe278 --- /dev/null +++ b/src/api/navigator/IRoomModel.ts @@ -0,0 +1,6 @@ +export interface IRoomModel +{ + clubLevel: number; + tileSize: number; + name: string; +} diff --git a/src/api/navigator/RoomModels.ts b/src/api/navigator/RoomModels.ts deleted file mode 100644 index c2b40066..00000000 --- a/src/api/navigator/RoomModels.ts +++ /dev/null @@ -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' } -]; diff --git a/src/api/navigator/index.ts b/src/api/navigator/index.ts index ade27895..77b0a417 100644 --- a/src/api/navigator/index.ts +++ b/src/api/navigator/index.ts @@ -3,10 +3,10 @@ export * from './INavigatorData'; export * from './INavigatorSearchFilter'; export * from './IRoomChatSettings'; export * from './IRoomData'; +export * from './IRoomModel'; export * from './IRoomModerationSettings'; export * from './NavigatorSearchResultViewDisplayMode'; export * from './RoomInfoData'; -export * from './RoomModels'; export * from './RoomSettingsData'; export * from './RoomSettingsUtils'; export * from './SearchFilterOptions'; diff --git a/src/api/utils/LocalStorageKeys.ts b/src/api/utils/LocalStorageKeys.ts new file mode 100644 index 00000000..6c922790 --- /dev/null +++ b/src/api/utils/LocalStorageKeys.ts @@ -0,0 +1,5 @@ +export class LocalStorageKeys +{ + public static CATALOG_PLACE_MULTIPLE_OBJECTS: string = 'catalogPlaceMultipleObjects'; + public static CATALOG_SKIP_PURCHASE_CONFIRMATION: string = 'catalogSkipPurchaseConfirmation'; +} diff --git a/src/api/utils/index.ts b/src/api/utils/index.ts index 0b5596ee..eeb74fce 100644 --- a/src/api/utils/index.ts +++ b/src/api/utils/index.ts @@ -5,6 +5,7 @@ export * from './LocalizeBageName'; export * from './LocalizeFormattedNumber'; export * from './LocalizeShortNumber'; export * from './LocalizeText'; +export * from './LocalStorageKeys'; export * from './PlaySound'; export * from './ProductImageUtility'; export * from './Randomizer'; diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx index 12d9c153..9bc42217 100644 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx +++ b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx @@ -1,4 +1,5 @@ import { FC, useCallback, useEffect, useState } from 'react'; +import { GetConfiguration } from '../../../../api'; import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; import { AvatarEditorGridPartItem } from '../../common/AvatarEditorGridPartItem'; import { AvatarEditorIcon } from '../AvatarEditorIcon'; @@ -13,6 +14,8 @@ export const AvatarEditorFigureSetItemView: FC('hc.disabled', false); + const rerender = useCallback(() => { setUpdateId(prevValue => (prevValue + 1)); @@ -30,7 +33,7 @@ export const AvatarEditorFigureSetItemView: FC - { partItem.isHC && } + { !hcDisabled && partItem.isHC && } { partItem.isClear && } { partItem.isSellable && } { children } diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx index 94ebb40c..2933c244 100644 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx +++ b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx @@ -1,4 +1,5 @@ import { FC, useCallback, useEffect, useState } from 'react'; +import { GetConfiguration } from '../../../../api'; import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; import { AvatarEditorGridColorItem } from '../../common/AvatarEditorGridColorItem'; @@ -12,6 +13,8 @@ export const AvatarEditorPaletteSetItem: FC = p const { colorItem = null, children = null, ...rest } = props; const [ updateId, setUpdateId ] = useState(-1); + const hcDisabled = GetConfiguration('hc.disabled', false); + const rerender = useCallback(() => { setUpdateId(prevValue => (prevValue + 1)); @@ -26,7 +29,7 @@ export const AvatarEditorPaletteSetItem: FC = p return ( - { colorItem.isHC && } + { !hcDisabled && colorItem.isHC && } { children } ); diff --git a/src/components/avatar-editor/views/wardrobe/AvatarEditorWardrobeView.tsx b/src/components/avatar-editor/views/wardrobe/AvatarEditorWardrobeView.tsx index 96252ff4..6efea5ed 100644 --- a/src/components/avatar-editor/views/wardrobe/AvatarEditorWardrobeView.tsx +++ b/src/components/avatar-editor/views/wardrobe/AvatarEditorWardrobeView.tsx @@ -1,6 +1,6 @@ import { IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer'; 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 { FigureData } from '../../common/FigureData'; @@ -16,6 +16,8 @@ export const AvatarEditorWardrobeView: FC = props { const { figureData = null, savedFigures = [], setSavedFigures = null, loadAvatarInEditor = null } = props; + const hcDisabled = GetConfiguration('hc.disabled', false); + const wearFigureAtIndex = useCallback((index: number) => { if((index >= savedFigures.length) || (index < 0)) return; @@ -57,7 +59,7 @@ export const AvatarEditorWardrobeView: FC = props { figureContainer && } - { (clubLevel > 0) && } + { !hcDisabled && (clubLevel > 0) && } { figureContainer && @@ -68,7 +70,7 @@ export const AvatarEditorWardrobeView: FC = props }); return items; - }, [ savedFigures, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); + }, [ savedFigures, hcDisabled, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); return ( diff --git a/src/components/catalog/CatalogContext.tsx b/src/components/catalog/CatalogContext.tsx deleted file mode 100644 index 6d6f9492..00000000 --- a/src/components/catalog/CatalogContext.tsx +++ /dev/null @@ -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>; - pageId: number; - currentType: string; - setCurrentType: Dispatch>; - rootNode: ICatalogNode; - setRootNode: Dispatch>; - offersToNodes: Map; - setOffersToNodes: Dispatch>>; - currentPage: ICatalogPage; - setCurrentPage: Dispatch>; - currentOffer: IPurchasableOffer; - setCurrentOffer: Dispatch>; - activeNodes: ICatalogNode[]; - setActiveNodes: Dispatch>; - searchResult: SearchResult; - setSearchResult: Dispatch>; - frontPageItems: FrontPageItem[]; - setFrontPageItems: Dispatch>; - roomPreviewer: RoomPreviewer; - purchaseOptions: IPurchaseOptions; - setPurchaseOptions: Dispatch>; - catalogOptions: ICatalogOptions; - setCatalogOptions: Dispatch>; - 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({ - 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> = props => -{ - return { props.children } -} - -export const useCatalogContext = () => useContext(CatalogContext); diff --git a/src/components/catalog/CatalogMessageHandler.tsx b/src/components/catalog/CatalogMessageHandler.tsx deleted file mode 100644 index b1a53941..00000000 --- a/src/components/catalog/CatalogMessageHandler.tsx +++ /dev/null @@ -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 = 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; -} diff --git a/src/components/catalog/CatalogView.tsx b/src/components/catalog/CatalogView.tsx index c8645189..3af9b189 100644 --- a/src/components/catalog/CatalogView.tsx +++ b/src/components/catalog/CatalogView.tsx @@ -1,406 +1,75 @@ -import { CatalogPublishedMessageEvent, FrontPageItem, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetGiftWrappingConfigurationComposer, ILinkEventTracker, RoomPreviewer } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useState } from 'react'; -import { AddEventLinkTracker, GetRoomEngine, LocalizeText, NotificationAlertType, NotificationUtilities, PlaySound, RemoveLinkEventTracker, SendMessageComposer, SoundNames } from '../../api'; +import { ILinkEventTracker } from '@nitrots/nitro-renderer'; +import { FC, useEffect } from 'react'; +import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker } from '../../api'; import { Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; -import { CatalogPurchasedEvent } from '../../events'; -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 { useCatalog } from '../../hooks'; import { CatalogGiftView } from './views/gift/CatalogGiftView'; import { CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { GetCatalogLayout } from './views/page/layout/GetCatalogLayout'; import { MarketplacePostOfferView } from './views/page/layout/marketplace/MarketplacePostOfferView'; -const REQUESTED_PAGE = new RequestedPage(); - export const CatalogView: FC<{}> = props => { - const [ isVisible, setIsVisible ] = useState(false); - 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(null); - const [ offersToNodes, setOffersToNodes ] = useState>(null); - const [ currentPage, setCurrentPage ] = useState(null); - const [ currentOffer, setCurrentOffer ] = useState(null); - const [ activeNodes, setActiveNodes ] = useState([]); - const [ searchResult, setSearchResult ] = useState(null); - const [ frontPageItems, setFrontPageItems ] = useState([]); - const [ roomPreviewer, setRoomPreviewer ] = useState(null); - const [ navigationHidden, setNavigationHidden ] = useState(false); - const [ purchaseOptions, setPurchaseOptions ] = useState({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null }); - const [ catalogOptions, setCatalogOptions ] = useState({}); - - 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 ]); + 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(); useEffect(() => { 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/' }; AddEventLinkTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker); - }, [ linkReceived ]); - - 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 ]); + }, [ setIsVisible, openPageByOfferId, openPageByName ]); return ( - - + <> { isVisible && - - { - setIsVisible(false); - } } /> + setIsVisible(false) } /> { rootNode && (rootNode.children.length > 0) && rootNode.children.map(child => { @@ -433,6 +102,6 @@ export const CatalogView: FC<{}> = props => } - + ); } diff --git a/src/components/catalog/common/AttemptCatalogPlacement.ts b/src/components/catalog/common/AttemptCatalogPlacement.ts deleted file mode 100644 index 2eb418a3..00000000 --- a/src/components/catalog/common/AttemptCatalogPlacement.ts +++ /dev/null @@ -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)) - { - - } -} diff --git a/src/components/catalog/common/FurniCategory.ts b/src/components/catalog/common/FurniCategory.ts deleted file mode 100644 index 07195cb0..00000000 --- a/src/components/catalog/common/FurniCategory.ts +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/src/components/catalog/common/IPurse.ts b/src/components/catalog/common/IPurse.ts deleted file mode 100644 index bd365ba6..00000000 --- a/src/components/catalog/common/IPurse.ts +++ /dev/null @@ -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; -} diff --git a/src/components/catalog/common/IsCatalogOfferDraggable.ts b/src/components/catalog/common/IsCatalogOfferDraggable.ts deleted file mode 100644 index 976bd2e1..00000000 --- a/src/components/catalog/common/IsCatalogOfferDraggable.ts +++ /dev/null @@ -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)) -} diff --git a/src/components/catalog/common/Purse.ts b/src/components/catalog/common/Purse.ts deleted file mode 100644 index eebaacf8..00000000 --- a/src/components/catalog/common/Purse.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { GetNitroInstance } from '../../../api'; -import { IPurse } from './IPurse'; - -export class Purse implements IPurse -{ - private _credits: number = 0; - private _activityPoints: Map; - 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 - { - return this._activityPoints; - } - - public set _Str_18527(k: Map) - { - 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; - } -} diff --git a/src/components/catalog/common/SubscriptionInfo.ts b/src/components/catalog/common/SubscriptionInfo.ts deleted file mode 100644 index 72d16e69..00000000 --- a/src/components/catalog/common/SubscriptionInfo.ts +++ /dev/null @@ -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; - } -} diff --git a/src/components/catalog/views/CatalogPurchaseConfirmView.tsx b/src/components/catalog/views/CatalogPurchaseConfirmView.tsx new file mode 100644 index 00000000..30dcfc3b --- /dev/null +++ b/src/components/catalog/views/CatalogPurchaseConfirmView.tsx @@ -0,0 +1,10 @@ +import { FC } from 'react'; + +export const CatalogPurchaseConfirmView: FC<{}> = props => +{ + const {} = props; + + return ( +
+ ); +} diff --git a/src/components/catalog/views/gift/CatalogGiftView.tsx b/src/components/catalog/views/gift/CatalogGiftView.tsx index d3e94506..74cb4a84 100644 --- a/src/components/catalog/views/gift/CatalogGiftView.tsx +++ b/src/components/catalog/views/gift/CatalogGiftView.tsx @@ -1,12 +1,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer'; +import { GiftReceiverNotFoundEvent, PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer'; import classNames from 'classnames'; import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { GetSessionDataManager, LocalizeText, ProductTypeEnum, SendMessageComposer } from '../../../../api'; import { Base, Button, ButtonGroup, Column, Flex, FormGroup, LayoutCurrencyIcon, LayoutFurniImageView, LayoutGiftTagView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent } from '../../../../events'; -import { UseUiEvent } from '../../../../hooks'; -import { useCatalogContext } from '../../CatalogContext'; +import { useCatalog, UseMessageEventHook, UseUiEvent } from '../../../../hooks'; export const CatalogGiftView: FC<{}> = props => { @@ -24,7 +23,7 @@ export const CatalogGiftView: FC<{}> = props => const [ maxBoxIndex, setMaxBoxIndex ] = useState(0); const [ maxRibbonIndex, setMaxRibbonIndex ] = useState(0); const [ receiverNotFound, setReceiverNotFound ] = useState(false); - const { catalogOptions = null } = useCatalogContext(); + const { catalogOptions = null } = useCatalog(); const { giftConfiguration = null } = catalogOptions; const close = useCallback(() => @@ -59,15 +58,11 @@ export const CatalogGiftView: FC<{}> = props => setExtraData(castedEvent.extraData); setIsVisible(true); return; - case CatalogEvent.GIFT_RECEIVER_NOT_FOUND: - setReceiverNotFound(true); - return; } }, [ close ]); UseUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogEvent); UseUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent); - UseUiEvent(CatalogEvent.GIFT_RECEIVER_NOT_FOUND, onCatalogEvent); const isBoxDefault = useMemo(() => { @@ -118,6 +113,13 @@ export const CatalogGiftView: FC<{}> = props => } }, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]); + const onGiftReceiverNotFoundEvent = useCallback(() => + { + setReceiverNotFound(true); + }, []); + + UseMessageEventHook(GiftReceiverNotFoundEvent, onGiftReceiverNotFoundEvent); + useEffect(() => { setReceiverNotFound(false); diff --git a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx index d3893e86..5eb1c45c 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationItemView.tsx @@ -1,9 +1,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FC } from 'react'; -import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem'; -import { Text } from '../../../../common/Text'; -import { useCatalogContext } from '../../CatalogContext'; -import { ICatalogNode } from '../../common/ICatalogNode'; +import { ICatalogNode } from '../../../../api'; +import { LayoutGridItem, Text } from '../../../../common'; +import { useCatalog } from '../../../../hooks'; import { CatalogIconView } from '../catalog-icon/CatalogIconView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView'; @@ -15,7 +14,7 @@ export interface CatalogNavigationItemViewProps export const CatalogNavigationItemView: FC = props => { const { node = null } = props; - const { activateNode = null } = useCatalogContext(); + const { activateNode = null } = useCatalog(); return ( <> diff --git a/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx b/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx index e1553631..19f71f47 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationSetView.tsx @@ -1,5 +1,5 @@ import { FC } from 'react'; -import { ICatalogNode } from '../../common/ICatalogNode'; +import { ICatalogNode } from '../../../../api'; import { CatalogNavigationItemView } from './CatalogNavigationItemView'; export interface CatalogNavigationSetViewProps diff --git a/src/components/catalog/views/navigation/CatalogNavigationView.tsx b/src/components/catalog/views/navigation/CatalogNavigationView.tsx index c1d187f4..3924928d 100644 --- a/src/components/catalog/views/navigation/CatalogNavigationView.tsx +++ b/src/components/catalog/views/navigation/CatalogNavigationView.tsx @@ -1,8 +1,7 @@ import { FC } from 'react'; -import { AutoGrid } from '../../../../common/AutoGrid'; -import { Column } from '../../../../common/Column'; -import { useCatalogContext } from '../../CatalogContext'; -import { ICatalogNode } from '../../common/ICatalogNode'; +import { ICatalogNode } from '../../../../api'; +import { AutoGrid, Column } from '../../../../common'; +import { useCatalog } from '../../../../hooks'; import { CatalogSearchView } from '../page/common/CatalogSearchView'; import { CatalogNavigationItemView } from './CatalogNavigationItemView'; import { CatalogNavigationSetView } from './CatalogNavigationSetView'; @@ -15,7 +14,7 @@ export interface CatalogNavigationViewProps export const CatalogNavigationView: FC = props => { const { node = null } = props; - const { searchResult = null } = useCatalogContext(); + const { searchResult = null } = useCatalog(); return ( <> diff --git a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx index fa8d3e28..507bb6c7 100644 --- a/src/components/catalog/views/page/common/CatalogGridOfferView.tsx +++ b/src/components/catalog/views/page/common/CatalogGridOfferView.tsx @@ -1,17 +1,21 @@ -import { FC, useMemo } from 'react'; -import { ProductTypeEnum } from '../../../../../api'; +import { MouseEventType } from '@nitrots/nitro-renderer'; +import { FC, MouseEvent, useMemo, useState } from 'react'; +import { IPurchasableOffer, Offer, ProductTypeEnum } from '../../../../../api'; import { LayoutAvatarImageView, LayoutGridItem, LayoutGridItemProps } from '../../../../../common'; -import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; -import { Offer } from '../../../common/Offer'; +import { useCatalog, useInventoryFurni } from '../../../../../hooks'; interface CatalogGridOfferViewProps extends LayoutGridItemProps { offer: IPurchasableOffer; + selectOffer: (offer: IPurchasableOffer) => void; } export const CatalogGridOfferView: FC = 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(() => { @@ -23,12 +27,31 @@ export const CatalogGridOfferView: FC = props => return offer.product.getIconUrl(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; if(!product) return null; return ( - + { (offer.product.productType === ProductTypeEnum.ROBOT) && } diff --git a/src/components/catalog/views/page/common/CatalogSearchView.tsx b/src/components/catalog/views/page/common/CatalogSearchView.tsx index 2a375fbf..bd0f0caf 100644 --- a/src/components/catalog/views/page/common/CatalogSearchView.tsx +++ b/src/components/catalog/views/page/common/CatalogSearchView.tsx @@ -1,25 +1,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { IFurnitureData } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useState } from 'react'; -import { GetSessionDataManager, LocalizeText } from '../../../../../api'; -import { Button } from '../../../../../common/Button'; -import { Flex } from '../../../../../common/Flex'; -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'; +import { CatalogPage, CatalogType, FilterCatalogNode, FurnitureOffer, GetOfferNodes, GetSessionDataManager, ICatalogNode, ICatalogPage, IPurchasableOffer, LocalizeText, PageLocalization, SearchResult } from '../../../../../api'; +import { Button, Flex } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; export const CatalogSearchView: FC<{}> = props => { const [ searchValue, setSearchValue ] = useState(''); 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) => { diff --git a/src/components/catalog/views/page/layout/CatalogLayout.types.ts b/src/components/catalog/views/page/layout/CatalogLayout.types.ts index a64e9412..b05bccf9 100644 --- a/src/components/catalog/views/page/layout/CatalogLayout.types.ts +++ b/src/components/catalog/views/page/layout/CatalogLayout.types.ts @@ -1,4 +1,4 @@ -import { ICatalogPage } from '../../../common/ICatalogPage'; +import { ICatalogPage } from '../../../../../api'; export interface CatalogLayoutProps { diff --git a/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx index 849fff8d..b3a619f3 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx @@ -1,7 +1,7 @@ import { FC } from 'react'; import { LocalizeText } from '../../../../../api'; import { Base, Column, Flex, Grid, Text } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; import { CatalogBadgeSelectorWidgetView } from '../widgets/CatalogBadgeSelectorWidgetView'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; @@ -14,7 +14,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutBadgeDisplayView: FC = props => { const { page = null } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); return ( <> diff --git a/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx index 8aa7a913..969b536b 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; import { Base, Column, Flex, Grid, Text } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogLimitedItemWidgetView } from '../widgets/CatalogLimitedItemWidgetView'; @@ -13,7 +13,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutDefaultView: FC = props => { const { page = null } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); return ( diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx index 9016f004..cf129517 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx @@ -1,10 +1,6 @@ import { FC } from 'react'; -import { Base } from '../../../../../common/Base'; -import { Column } from '../../../../../common/Column'; -import { Flex } from '../../../../../common/Flex'; -import { Grid } from '../../../../../common/Grid'; -import { Text } from '../../../../../common/Text'; -import { useCatalogContext } from '../../../CatalogContext'; +import { Base, Column, Flex, Grid, Text } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; @@ -16,7 +12,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayouGuildCustomFurniView: FC = props => { const { page = null } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); return ( diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx index 2e63e14d..b5a89ca6 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx @@ -2,7 +2,7 @@ import { CatalogGroupsComposer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; import { SendMessageComposer } from '../../../../../api'; import { Base, Column, Flex, Grid, Text } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; @@ -13,7 +13,7 @@ export const CatalogLayouGuildForumView: FC = props => { const { page = null } = props; const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { currentOffer = null, setCurrentOffer = null, catalogOptions = null } = useCatalogContext(); + const { currentOffer = null, setCurrentOffer = null, catalogOptions = null } = useCatalog(); const { groups = null } = catalogOptions; useEffect(() => diff --git a/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx index d83b2d8d..88cd30b6 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx @@ -1,11 +1,7 @@ import { NitroPoint } from '@nitrots/nitro-renderer'; import { FC, useEffect } from 'react'; -import { Base } from '../../../../../common/Base'; -import { Column } from '../../../../../common/Column'; -import { Flex } from '../../../../../common/Flex'; -import { Grid } from '../../../../../common/Grid'; -import { Text } from '../../../../../common/Text'; -import { useCatalogContext } from '../../../CatalogContext'; +import { Base, Column, Flex, Grid, Text } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; import { CatalogSpacesWidgetView } from '../widgets/CatalogSpacesWidgetView'; import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget'; @@ -15,7 +11,7 @@ import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutSpacesView: FC = props => { const { page = null } = props; - const { currentOffer = null, roomPreviewer = null } = useCatalogContext(); + const { currentOffer = null, roomPreviewer = null } = useCatalog(); useEffect(() => { diff --git a/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx index 1ecb9192..5db43b78 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx @@ -1,9 +1,6 @@ import { FC, useEffect, useState } from 'react'; -import { Column } from '../../../../../common/Column'; -import { Flex } from '../../../../../common/Flex'; -import { Grid } from '../../../../../common/Grid'; -import { Text } from '../../../../../common/Text'; -import { useCatalogContext } from '../../../CatalogContext'; +import { Column, Flex, Grid, Text } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; import { CatalogTotalPriceWidget } from '../widgets/CatalogTotalPriceWidget'; @@ -14,7 +11,7 @@ export const CatalogLayoutTrophiesView: FC = props => { const { page = null } = props; const [ trophyText, setTrophyText ] = useState(''); - const { currentOffer = null, setPurchaseOptions = null } = useCatalogContext(); + const { currentOffer = null, setPurchaseOptions = null } = useCatalog(); useEffect(() => { diff --git a/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx index 00aaad8e..36a1a72b 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx @@ -1,18 +1,16 @@ import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer'; 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 { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent } from '../../../../../events'; -import { usePurse, UseUiEvent } from '../../../../../hooks'; -import { useCatalogContext } from '../../../CatalogContext'; -import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState'; +import { useCatalog, usePurse, UseUiEvent } from '../../../../../hooks'; import { CatalogLayoutProps } from './CatalogLayout.types'; export const CatalogLayoutVipBuyView: FC = props => { const [ pendingOffer, setPendingOffer ] = useState(null); 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 { clubOffers = null } = catalogOptions; diff --git a/src/components/catalog/views/page/layout/GetCatalogLayout.tsx b/src/components/catalog/views/page/layout/GetCatalogLayout.tsx index 78674428..5748ef88 100644 --- a/src/components/catalog/views/page/layout/GetCatalogLayout.tsx +++ b/src/components/catalog/views/page/layout/GetCatalogLayout.tsx @@ -1,4 +1,4 @@ -import { ICatalogPage } from '../../../common/ICatalogPage'; +import { ICatalogPage } from '../../../../../api'; import { CatalogLayoutProps } from './CatalogLayout.types'; import { CatalogLayoutBadgeDisplayView } from './CatalogLayoutBadgeDisplayView'; import { CatalogLayoutDefaultView } from './CatalogLayoutDefaultView'; diff --git a/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx b/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx index 807adbf8..f2e1da6c 100644 --- a/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx +++ b/src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx @@ -1,9 +1,8 @@ import { FrontPageItem } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect } from 'react'; import { CreateLinkEvent } from '../../../../../../api'; -import { Column } from '../../../../../../common/Column'; -import { Grid } from '../../../../../../common/Grid'; -import { useCatalogContext } from '../../../../CatalogContext'; +import { Column, Grid } from '../../../../../../common'; +import { useCatalog } from '../../../../../../hooks'; import { CatalogRedeemVoucherView } from '../../common/CatalogRedeemVoucherView'; import { CatalogLayoutProps } from '../CatalogLayout.types'; import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView'; @@ -11,7 +10,7 @@ import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView export const CatalogLayoutFrontpage4View: FC = props => { const { page = null, hideNavigation = null } = props; - const { frontPageItems = [] } = useCatalogContext(); + const { frontPageItems = [] } = useCatalog(); const selectItem = useCallback((item: FrontPageItem) => { diff --git a/src/components/catalog/views/page/layout/marketplace/MarketplacePostOfferView.tsx b/src/components/catalog/views/page/layout/marketplace/MarketplacePostOfferView.tsx index 9b73be89..4ad0545f 100644 --- a/src/components/catalog/views/page/layout/marketplace/MarketplacePostOfferView.tsx +++ b/src/components/catalog/views/page/layout/marketplace/MarketplacePostOfferView.tsx @@ -3,14 +3,13 @@ import { FC, useCallback, useEffect, useState } from 'react'; import { FurnitureItem, LocalizeText, NotificationUtilities, ProductTypeEnum, SendMessageComposer } from '../../../../../../api'; import { Base, Button, Column, Grid, LayoutFurniImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../../common'; import { CatalogPostMarketplaceOfferEvent } from '../../../../../../events'; -import { UseMessageEventHook, UseUiEvent } from '../../../../../../hooks'; -import { useCatalogContext } from '../../../../CatalogContext'; +import { useCatalog, UseMessageEventHook, UseUiEvent } from '../../../../../../hooks'; export const MarketplacePostOfferView : FC<{}> = props => { const [ item, setItem ] = useState(null); const [ askingPrice, setAskingPrice ] = useState(0); - const { catalogOptions = null, setCatalogOptions = null } = useCatalogContext(); + const { catalogOptions = null, setCatalogOptions = null } = useCatalog(); const { marketplaceConfiguration = null } = catalogOptions; const onMarketplaceConfigurationEvent = useCallback((event: MarketplaceConfigurationEvent) => diff --git a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx deleted file mode 100644 index a9bd44a5..00000000 --- a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetPurchaseView.tsx +++ /dev/null @@ -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 = 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 ( - -
- -
- -
- { LocalizeText('catalog.bundlewidget.price') } -
- - { ((offer.priceType === Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS) || (offer.priceType === Offer.PRICE_TYPE_CREDITS)) && - - { offer.priceInCredits } - - } - { ((offer.priceType === Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS) || (offer.priceType === Offer.PRICE_TYPE_ACTIVITYPOINTS)) && - - { offer.priceInActivityPoints } - - } - -
- - - { /* - { offer.giftable && - } */ } - -
- ); -} diff --git a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx index 665a7a4a..39d0c95e 100644 --- a/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx +++ b/src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx @@ -1,12 +1,10 @@ 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 { LocalizeText, SendMessageComposer } from '../../../../../../api'; +import { GetPetAvailableColors, GetPetIndexFromLocalization, LocalizeText, SendMessageComposer } from '../../../../../../api'; import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, LayoutPetImageView, Text } from '../../../../../../common'; -import { CatalogNameResultEvent, CatalogPurchaseFailureEvent, CatalogWidgetEvent } from '../../../../../../events'; -import { DispatchUiEvent, UseUiEvent } from '../../../../../../hooks'; -import { useCatalogContext } from '../../../../CatalogContext'; -import { GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities'; +import { CatalogPurchaseFailureEvent } from '../../../../../../events'; +import { DispatchUiEvent, useCatalog, UseMessageEventHook } from '../../../../../../hooks'; import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView'; import { CatalogPurchaseWidgetView } from '../../widgets/CatalogPurchaseWidgetView'; import { CatalogTotalPriceWidget } from '../../widgets/CatalogTotalPriceWidget'; @@ -25,7 +23,7 @@ export const CatalogLayoutPetView: FC = props => const [ petName, setPetName ] = useState(''); const [ approvalPending, setApprovalPending ] = useState(true); 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 getColor = useMemo(() => @@ -106,15 +104,17 @@ export const CatalogLayoutPetView: FC = props => } }, [ 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)); }, [ purchasePet ]); - UseUiEvent(CatalogWidgetEvent.APPROVE_RESULT, onCatalogNameResultEvent); + UseMessageEventHook(ApproveNameMessageEvent, onApproveNameMessageEvent); useEffect(() => { diff --git a/src/components/catalog/views/page/layout/pets/CatalogPetNameApprovalView.tsx b/src/components/catalog/views/page/layout/pets/CatalogPetNameApprovalView.tsx deleted file mode 100644 index ca22262d..00000000 --- a/src/components/catalog/views/page/layout/pets/CatalogPetNameApprovalView.tsx +++ /dev/null @@ -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>; - nameApproved: boolean; - setNameApproved: Dispatch>; -} - -export const CatalogPetNameApprovalView: FC = 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 ( -
- 0) ? 'is-invalid ' : '') } placeholder={ LocalizeText('widgets.petpackage.name.title') } value={ petNameValue } onChange={ event => setPetNameValue(event.target.value) } /> - { (validationResult > 0) && -
{ validationErrorMessage }
} -
- ); -} diff --git a/src/components/catalog/views/page/layout/vip-gifts/CatalogLayoutVipGiftsView.tsx b/src/components/catalog/views/page/layout/vip-gifts/CatalogLayoutVipGiftsView.tsx index 3b760d27..e339ec80 100644 --- a/src/components/catalog/views/page/layout/vip-gifts/CatalogLayoutVipGiftsView.tsx +++ b/src/components/catalog/views/page/layout/vip-gifts/CatalogLayoutVipGiftsView.tsx @@ -2,15 +2,14 @@ import { SelectClubGiftComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback } from 'react'; import { LocalizeText, NotificationUtilities, SendMessageComposer } from '../../../../../../api'; import { AutoGrid, Text } from '../../../../../../common'; -import { usePurse } from '../../../../../../hooks'; -import { useCatalogContext } from '../../../../CatalogContext'; +import { useCatalog, usePurse } from '../../../../../../hooks'; import { CatalogLayoutProps } from '../CatalogLayout.types'; import { VipGiftItem } from './VipGiftItemView'; export const CatalogLayoutVipGiftsView: FC = props => { const { purse = null } = usePurse(); - const { catalogOptions = null, setCatalogOptions = null } = useCatalogContext(); + const { catalogOptions = null, setCatalogOptions = null } = useCatalog(); const { clubGifts = null } = catalogOptions; const giftsAvailable = useCallback(() => diff --git a/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx index 0b041536..38892cea 100644 --- a/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; import { BaseProps, LayoutBadgeImageView } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; interface CatalogAddOnBadgeWidgetViewProps extends BaseProps { @@ -10,7 +10,7 @@ interface CatalogAddOnBadgeWidgetViewProps extends BaseProps export const CatalogAddOnBadgeWidgetView: FC = props => { const { ...rest } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); if(!currentOffer || !currentOffer.badgeCode || !currentOffer.badgeCode.length) return null; diff --git a/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx index b3c6c028..04891dde 100644 --- a/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx @@ -1,8 +1,7 @@ import { StringDataType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; import { AutoGrid, AutoGridProps, LayoutBadgeImageView, LayoutGridItem } from '../../../../../common'; -import { useInventoryBadges } from '../../../../../hooks'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog, useInventoryBadges } from '../../../../../hooks'; const EXCLUDED_BADGE_CODES: string[] = []; @@ -16,7 +15,7 @@ export const CatalogBadgeSelectorWidgetView: FC(null); - const { currentOffer = null, setPurchaseOptions = null } = useCatalogContext(); + const { currentOffer = null, setPurchaseOptions = null } = useCatalog(); const { badgeCodes = [], activate = null, deactivate = null } = useInventoryBadges(); const previewStuffData = useMemo(() => diff --git a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx index e684387b..d98ff1dd 100644 --- a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx @@ -1,7 +1,6 @@ import { FC } from 'react'; -import { AutoGrid, AutoGridProps } from '../../../../../common/AutoGrid'; -import { LayoutGridItem } from '../../../../../common/layout/LayoutGridItem'; -import { useCatalogContext } from '../../../CatalogContext'; +import { AutoGrid, AutoGridProps, LayoutGridItem } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; interface CatalogBundleGridWidgetViewProps extends AutoGridProps { @@ -11,7 +10,7 @@ interface CatalogBundleGridWidgetViewProps extends AutoGridProps export const CatalogBundleGridWidgetView: FC = props => { const { columnCount = 5, children = null, ...rest } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); if(!currentOffer) return null; diff --git a/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx index dfab4d85..a0d71033 100644 --- a/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx @@ -1,9 +1,9 @@ import { FC, useEffect } from 'react'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; export const CatalogFirstProductSelectorWidgetView: FC<{}> = props => { - const { currentPage = null, setCurrentOffer = null } = useCatalogContext(); + const { currentPage = null, setCurrentOffer = null } = useCatalog(); useEffect(() => { diff --git a/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx index ae7deb55..db893ad3 100644 --- a/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx @@ -1,7 +1,7 @@ import { StringDataType } from '@nitrots/nitro-renderer'; import { FC, useMemo } from 'react'; import { BaseProps, LayoutBadgeImageView } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; interface CatalogGuildBadgeWidgetViewProps extends BaseProps { @@ -11,7 +11,7 @@ interface CatalogGuildBadgeWidgetViewProps extends BaseProps export const CatalogGuildBadgeWidgetView: FC = props => { const { ...rest } = props; - const { currentOffer = null, purchaseOptions = null } = useCatalogContext(); + const { currentOffer = null, purchaseOptions = null } = useCatalog(); const { previewStuffData = null } = purchaseOptions; const badgeCode = useMemo(() => diff --git a/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx index 919fa038..d097bd36 100644 --- a/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx @@ -2,12 +2,12 @@ import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; import { LocalizeText, SendMessageComposer } from '../../../../../api'; import { Base, Button, Flex } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; +import { useCatalog } from '../../../../../hooks'; export const CatalogGuildSelectorWidgetView: FC<{}> = props => { const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { currentOffer = null, catalogOptions = null, setPurchaseOptions = null } = useCatalogContext(); + const { currentOffer = null, catalogOptions = null, setPurchaseOptions = null } = useCatalog(); const { groups = null } = catalogOptions; const previewStuffData = useMemo(() => diff --git a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx index d5e941ca..933dede6 100644 --- a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx @@ -1,8 +1,7 @@ import { FC } from 'react'; -import { ProductTypeEnum } from '../../../../../api'; +import { IPurchasableOffer, ProductTypeEnum } from '../../../../../api'; import { AutoGrid, AutoGridProps } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; -import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; +import { useCatalog } from '../../../../../hooks'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; interface CatalogItemGridWidgetViewProps extends AutoGridProps @@ -13,7 +12,7 @@ interface CatalogItemGridWidgetViewProps extends AutoGridProps export const CatalogItemGridWidgetView: FC = 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; @@ -40,7 +39,7 @@ export const CatalogItemGridWidgetView: FC = pro return ( - { currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => selectOffer(offer) } />) } + { currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => ) } { children } ); diff --git a/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx index 035d7d44..70b52cbd 100644 --- a/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx @@ -1,12 +1,12 @@ import { FC } from 'react'; +import { Offer } from '../../../../../api'; import { Base, BaseProps, LayoutLimitedEditionCompletePlateView } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; -import { Offer } from '../../../common/Offer'; +import { useCatalog } from '../../../../../hooks'; export const CatalogLimitedItemWidgetView: FC> = 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; diff --git a/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx index 7cf8c76b..0ca15604 100644 --- a/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx @@ -1,8 +1,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FC } from 'react'; +import { IPurchasableOffer } from '../../../../../api'; import { Flex, LayoutCurrencyIcon, Text } from '../../../../../common'; -import { useCatalogContext } from '../../../CatalogContext'; -import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; +import { useCatalog } from '../../../../../hooks'; interface CatalogPriceDisplayWidgetViewProps { @@ -13,7 +13,7 @@ interface CatalogPriceDisplayWidgetViewProps export const CatalogPriceDisplayWidgetView: FC = props => { const { offer = null, separator = false } = props; - const { purchaseOptions = null } = useCatalogContext(); + const { purchaseOptions = null } = useCatalog(); const { quantity = 1 } = purchaseOptions; if(!offer) return null; diff --git a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx index 7a874acc..93637305 100644 --- a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx @@ -1,12 +1,9 @@ import { PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer'; 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 { CatalogEvent, CatalogInitGiftEvent, CatalogInitPurchaseEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent, CatalogWidgetEvent } from '../../../../../events'; -import { DispatchUiEvent, usePurse, UseUiEvent } from '../../../../../hooks'; -import { useCatalogContext } from '../../../CatalogContext'; -import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState'; -import { Offer } from '../../../common/Offer'; +import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent } from '../../../../../events'; +import { DispatchUiEvent, useCatalog, useLocalStorage, usePurse, UseUiEvent } from '../../../../../hooks'; interface CatalogPurchaseWidgetViewProps { @@ -19,19 +16,10 @@ export const CatalogPurchaseWidgetView: FC = pro const { noGiftOption = false, purchaseCallback = null } = props; const [ purchaseWillBeGift, setPurchaseWillBeGift ] = useState(false); 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 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) => { switch(event.type) diff --git a/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx index 80b5dafb..694507df 100644 --- a/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; -import { Flex, FlexProps } from '../../../../../common/Flex'; -import { useCatalogContext } from '../../../CatalogContext'; +import { Flex, FlexProps } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; interface CatalogSimplePriceWidgetViewProps extends FlexProps @@ -11,7 +11,7 @@ interface CatalogSimplePriceWidgetViewProps extends FlexProps export const CatalogSimplePriceWidgetView: FC = props => { const { gap = 1, ...rest } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); return ( diff --git a/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx index d2dc9550..fdc3f5f4 100644 --- a/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx @@ -1,9 +1,7 @@ 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 { useCatalogContext } from '../../../CatalogContext'; -import { IPurchasableOffer } from '../../../common/IPurchasableOffer'; -import { Offer } from '../../../common/Offer'; +import { useCatalog } from '../../../../../hooks'; import { CatalogGridOfferView } from '../common/CatalogGridOfferView'; interface CatalogSpacesWidgetViewProps extends AutoGridProps @@ -19,7 +17,21 @@ export const CatalogSpacesWidgetView: FC = props = const [ groupedOffers, setGroupedOffers ] = useState(null); const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(-1); const [ selectedOfferForGroup, setSelectedOfferForGroup ] = useState(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(() => { @@ -89,22 +101,7 @@ export const CatalogSpacesWidgetView: FC = props = { SPACES_GROUP_NAMES.map((name, index) => ) } - { offers && (offers.length > 0) && offers.map((offer, index) => - { - const setSelectedOffer = () => - { - setSelectedOfferForGroup(prevValue => - { - const newValue = [ ...prevValue ]; - - newValue[selectedGroupIndex] = offer; - - return newValue; - }); - } - - return ; - }) } + { offers && (offers.length > 0) && offers.map((offer, index) => setSelectedOffer(offer) } />) } { children } diff --git a/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx index df50e347..2749fa7b 100644 --- a/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx @@ -1,16 +1,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FC } from 'react'; import { LocalizeText } from '../../../../../api'; -import { Flex } from '../../../../../common/Flex'; -import { Text } from '../../../../../common/Text'; -import { useCatalogContext } from '../../../CatalogContext'; +import { Flex, Text } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; const MIN_VALUE: number = 1; const MAX_VALUE: number = 100; 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 updateQuantity = (value: number) => diff --git a/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx b/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx index ae10d810..b3f935ae 100644 --- a/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx +++ b/src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; -import { Column, ColumnProps } from '../../../../../common/Column'; -import { useCatalogContext } from '../../../CatalogContext'; +import { Column, ColumnProps } from '../../../../../common'; +import { useCatalog } from '../../../../../hooks'; import { CatalogPriceDisplayWidgetView } from './CatalogPriceDisplayWidgetView'; interface CatalogSimplePriceWidgetViewProps extends ColumnProps @@ -10,7 +10,7 @@ interface CatalogSimplePriceWidgetViewProps extends ColumnProps export const CatalogTotalPriceWidget: FC = props => { const { gap = 1, ...rest } = props; - const { currentOffer = null } = useCatalogContext(); + const { currentOffer = null } = useCatalog(); return ( diff --git a/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx index 27e21c23..620ece15 100644 --- a/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx @@ -1,14 +1,12 @@ import { Vector3d } from '@nitrots/nitro-renderer'; 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 { useCatalogContext } from '../../../CatalogContext'; -import { FurniCategory } from '../../../common/FurniCategory'; -import { Offer } from '../../../common/Offer'; +import { useCatalog } from '../../../../../hooks'; 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; useEffect(() => diff --git a/src/components/groups/views/GroupInformationView.tsx b/src/components/groups/views/GroupInformationView.tsx index 1021bf4b..e330898a 100644 --- a/src/components/groups/views/GroupInformationView.tsx +++ b/src/components/groups/views/GroupInformationView.tsx @@ -1,8 +1,7 @@ import { GroupInformationParser, GroupRemoveMemberComposer } from '@nitrots/nitro-renderer'; 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 { CatalogPageName } from '../../catalog/common/CatalogPageName'; import { GroupMembershipType } from '../common/GroupMembershipType'; import { GroupType } from '../common/GroupType'; diff --git a/src/components/navigator/views/NavigatorRoomCreatorView.tsx b/src/components/navigator/views/NavigatorRoomCreatorView.tsx index 97b979c0..90ac95b2 100644 --- a/src/components/navigator/views/NavigatorRoomCreatorView.tsx +++ b/src/components/navigator/views/NavigatorRoomCreatorView.tsx @@ -1,7 +1,7 @@ /* eslint-disable no-template-curly-in-string */ import { HabboClubLevelEnum, RoomCreateComposer } from '@nitrots/nitro-renderer'; 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 { useNavigatorContext } from '../NavigatorContext'; @@ -13,22 +13,25 @@ export const NavigatorRoomCreatorView: FC<{}> = props => const [ category, setCategory ] = useState(null); const [ visitorsCount, setVisitorsCount ] = useState(null); const [ tradesSetting, setTradesSetting ] = useState(0); - const [ selectedModelName, setSelectedModelName ] = useState(RoomModels[0].name); + const [ roomModels, setRoomModels ] = useState([]); + const [ selectedModelName, setSelectedModelName ] = useState(''); const { categories = null } = useNavigatorContext(); + const hcDisabled = GetConfiguration('hc.disabled', false); + const getRoomModelImage = (name: string) => GetConfiguration('images.url') + `/navigator/models/model_${ name }.png`; const selectModel = (model: IRoomModel, index: number) => { if(!model || (model.clubLevel > GetClubMemberLevel())) return; - setSelectedModelName(RoomModels[index].name); - } + setSelectedModelName(roomModels[index].name); + }; const createRoom = () => { SendMessageComposer(new RoomCreateComposer(name, description, 'model_' + selectedModelName, Number(category), Number(visitorsCount), tradesSetting)); - } + }; useEffect(() => { @@ -48,6 +51,17 @@ export const NavigatorRoomCreatorView: FC<{}> = props => if(categories && categories.length) setCategory(categories[0].id); }, [ categories ]); + useEffect(() => + { + const models = GetConfiguration('navigator.room.models'); + + if(models && models.length) + { + setRoomModels(models); + setSelectedModelName(models[0].name); + } + }, []); + return ( @@ -89,14 +103,14 @@ export const NavigatorRoomCreatorView: FC<{}> = props => { - RoomModels.map((model, index )=> + roomModels.map((model, index )=> { return ( selectModel(model, index) } itemActive={ (selectedModelName === model.name) } overflow="unset" gap={ 0 } className="p-1" disabled={ (GetClubMemberLevel() < model.clubLevel) }> { model.tileSize } { LocalizeText('navigator.createroom.tilesize') } - { model.clubLevel > HabboClubLevelEnum.NO_CLUB && } + { !hcDisabled && model.clubLevel > HabboClubLevelEnum.NO_CLUB && } ); }) } diff --git a/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx b/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx index adb7cf19..432fce48 100644 --- a/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx +++ b/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 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 { LocalizeText } from '../../../../api'; import { Base, Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common'; @@ -13,8 +13,8 @@ interface NavigatorSearchResultItemInfoViewProps export const NavigatorSearchResultItemInfoView: FC = props => { const { roomData = null } = props; - const [ target, setTarget ] = useState<(EventTarget & HTMLElement)>(null); const [ isVisible, setIsVisible ] = useState(false); + const elementRef = useRef(); const getUserCounterColor = () => { @@ -38,33 +38,10 @@ export const NavigatorSearchResultItemInfoView: FC) => - { - 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 ( <> - - + setIsVisible(true) } onMouseLeave={ event => setIsVisible(false) } /> + @@ -78,14 +55,14 @@ export const NavigatorSearchResultItemInfoView: FC { roomData.roomName } - + { LocalizeText('navigator.roomownercaption') } - - { roomData.ownerName } + + + { roomData.ownerName } + { roomData.description } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoUseProductView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoUseProductView.tsx index 9f308dd9..1dce4787 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoUseProductView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoUseProductView.tsx @@ -87,7 +87,7 @@ export const AvatarInfoUseProductView: FC = props }, [ item, updateConfirmingProduct ]); return ( - + { item.name } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx index c655baef..7a919b80 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetAvatarView.tsx @@ -213,7 +213,7 @@ export const AvatarInfoWidgetAvatarView: FC = p }, [ userData ]); return ( - + GetUserProfile(userData.webID) }> { userData.name } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnAvatarView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnAvatarView.tsx index 700289f7..00aa89c0 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnAvatarView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnAvatarView.tsx @@ -121,7 +121,8 @@ export const AvatarInfoWidgetOwnAvatarView: FC + + GetUserProfile(userData.webID) }> { userData.name } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnPetView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnPetView.tsx index bb119714..56ebac5c 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnPetView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetOwnPetView.tsx @@ -142,7 +142,7 @@ export const AvatarInfoWidgetOwnPetView: FC = p }, [ petData ]); return ( - + { petData.name } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetPetView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetPetView.tsx index 60f19fa3..93faffc3 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetPetView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetPetView.tsx @@ -109,7 +109,7 @@ export const AvatarInfoWidgetPetView: FC = props = }, [ petData ]); return ( - + { petData.name } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetRentableBotView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetRentableBotView.tsx index 73d9e985..dcc452e4 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetRentableBotView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetRentableBotView.tsx @@ -136,7 +136,7 @@ export const AvatarInfoWidgetRentableBotView: FC + { rentableBotData.name } diff --git a/src/components/room/widgets/context-menu/ContextMenu.scss b/src/components/room/widgets/context-menu/ContextMenu.scss index e57027c4..b3e67f67 100644 --- a/src/components/room/widgets/context-menu/ContextMenu.scss +++ b/src/components/room/widgets/context-menu/ContextMenu.scss @@ -20,9 +20,11 @@ font-size: 18px; } - &:not(.name-only) { + &:not(.name-only):not(.menu-hidden) { min-width: 125px; + } + &:not(.name-only) { &:after { content: ""; position: absolute; @@ -49,6 +51,22 @@ 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-item { diff --git a/src/components/room/widgets/context-menu/ContextMenuCaretView.tsx b/src/components/room/widgets/context-menu/ContextMenuCaretView.tsx new file mode 100644 index 00000000..4cfb3fee --- /dev/null +++ b/src/components/room/widgets/context-menu/ContextMenuCaretView.tsx @@ -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 = 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 + + +} diff --git a/src/components/room/widgets/context-menu/ContextMenuView.tsx b/src/components/room/widgets/context-menu/ContextMenuView.tsx index 3700f79d..344e0f2b 100644 --- a/src/components/room/widgets/context-menu/ContextMenuView.tsx +++ b/src/components/room/widgets/context-menu/ContextMenuView.tsx @@ -2,6 +2,7 @@ import { FixedSizeStack, NitroPoint, NitroRectangle, RoomObjectType } from '@nit import { CSSProperties, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { GetNitroInstance, GetRoomObjectBounds, GetRoomObjectScreenLocation, GetRoomSession, GetTicker } from '../../../../api'; import { Base, BaseProps } from '../../../../common'; +import { ContextMenuCaretView } from './ContextMenuCaretView'; interface ContextMenuViewProps extends BaseProps { @@ -10,6 +11,7 @@ interface ContextMenuViewProps extends BaseProps userType?: number; fades?: boolean; close: () => void; + collapsable?: boolean; } const LOCATION_STACK_SIZE: number = 25; @@ -18,9 +20,11 @@ const fadeDelay = 3000; const fadeLength = 75; const SPACE_AROUND_EDGES = 10; +let COLLAPSED = false; + export const ContextMenuView: FC = 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 [ deltaYStack, setDeltaYStack ] = useState(null); const [ currentDeltaY, setCurrentDeltaY ] = useState(-1000000); @@ -30,6 +34,8 @@ export const ContextMenuView: FC = props => const [ isFrozen, setIsFrozen ] = useState(false); const elementRef = useRef(); + const [ collapsed, setCollapsed ] = useState(COLLAPSED); + const getOffset = useCallback((bounds: NitroRectangle) => { let height = -(elementRef.current.offsetHeight); @@ -118,13 +124,15 @@ export const ContextMenuView: FC = props => const getClassNames = useMemo(() => { const newClassNames: string[] = [ 'nitro-context-menu' ]; + + if (collapsed) newClassNames.push('menu-hidden'); newClassNames.push((pos.x !== null) ? 'visible' : 'invisible'); if(classNames.length) newClassNames.push(...classNames); return newClassNames; - }, [ pos, classNames ]); + }, [ pos, classNames, collapsed ]); const getStyle = useMemo(() => { @@ -170,6 +178,15 @@ export const ContextMenuView: FC = props => return () => clearTimeout(timeout); }, [ fades ]); + - return setIsFrozen(true) } onMouseOut={ event => setIsFrozen(false) } { ...rest } />; + const toggleCollapse = () => + { + COLLAPSED = !COLLAPSED; + setCollapsed(COLLAPSED) + } + return setIsFrozen(true) } onMouseOut={ event => setIsFrozen(false) } { ...rest }> + { !(collapsable && COLLAPSED) && children } + { collapsable && toggleCollapse() } collapsed={ collapsed } /> } + ; } diff --git a/src/components/user-settings/UserSettingsView.tsx b/src/components/user-settings/UserSettingsView.tsx index 6768c898..3564203b 100644 --- a/src/components/user-settings/UserSettingsView.tsx +++ b/src/components/user-settings/UserSettingsView.tsx @@ -3,12 +3,14 @@ import { ILinkEventTracker, NitroSettingsEvent, UserSettingsCameraFollowComposer import { FC, useCallback, useEffect, useState } from 'react'; import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api'; 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 => { const [ isVisible, setIsVisible ] = useState(false); const [ userSettings, setUserSettings ] = useState(null); + const [ catalogPlaceMultipleObjects, setCatalogPlaceMultipleObjects ] = useCatalogPlaceMultipleItems(); + const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useCatalogSkipPurchaseConfirmation(); const onUserSettingsEvent = useCallback((event: UserSettingsEvent) => { @@ -141,6 +143,14 @@ export const UserSettingsView: FC<{}> = props => processAction('camera_follow', event.target.checked) } /> { LocalizeText('memenu.settings.other.disable.room.camera.follow') } + + setCatalogPlaceMultipleObjects(event.target.checked) } /> + { LocalizeText('memenu.settings.other.place.multiple.objects') } + + + setCatalogSkipPurchaseConfirmation(event.target.checked) } /> + { LocalizeText('memenu.settings.other.skip.purchase.confirmation') } + { LocalizeText('widget.memenu.settings.volume') } diff --git a/src/events/catalog/CatalogEvent.ts b/src/events/catalog/CatalogEvent.ts index 58ef7b7b..893775a5 100644 --- a/src/events/catalog/CatalogEvent.ts +++ b/src/events/catalog/CatalogEvent.ts @@ -7,7 +7,6 @@ export class CatalogEvent extends NitroEvent public static TOGGLE_CATALOG: string = 'CE_TOGGLE_CATALOG'; public static SOLD_OUT: string = 'CE_SOLD_OUT'; 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 INIT_GIFT: string = 'CE_INIT_GIFT'; public static CATALOG_RESET: string = 'CE_RESET'; diff --git a/src/events/catalog/CatalogGiftReceiverNotFoundEvent.ts b/src/events/catalog/CatalogGiftReceiverNotFoundEvent.ts deleted file mode 100644 index 611eaff7..00000000 --- a/src/events/catalog/CatalogGiftReceiverNotFoundEvent.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { CatalogEvent } from './CatalogEvent'; - -export class CatalogGiftReceiverNotFoundEvent extends CatalogEvent -{ - constructor() - { - super(CatalogEvent.GIFT_RECEIVER_NOT_FOUND); - } -} diff --git a/src/events/catalog/CatalogInitPurchaseEvent.ts b/src/events/catalog/CatalogInitPurchaseEvent.ts deleted file mode 100644 index 2b2661fa..00000000 --- a/src/events/catalog/CatalogInitPurchaseEvent.ts +++ /dev/null @@ -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; - } -} diff --git a/src/events/catalog/CatalogNameResultEvent.ts b/src/events/catalog/CatalogNameResultEvent.ts deleted file mode 100644 index 70e4c0ec..00000000 --- a/src/events/catalog/CatalogNameResultEvent.ts +++ /dev/null @@ -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; - } -} diff --git a/src/events/catalog/CatalogWidgetEvent.ts b/src/events/catalog/CatalogWidgetEvent.ts index 57df2d7b..fd0e6026 100644 --- a/src/events/catalog/CatalogWidgetEvent.ts +++ b/src/events/catalog/CatalogWidgetEvent.ts @@ -11,10 +11,8 @@ export class CatalogWidgetEvent extends NitroEvent public static COLOUR_INDEX: string = 'CWE_COLOUR_INDEX'; public static TEXT_INPUT: string = 'CWE_TEXT_INPUT'; 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 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 GUILD_SELECTED: string = 'CWE_GUILD_SELECTED'; public static TOTAL_PRICE_WIDGET_INITIALIZED: string = 'CWE_TOTAL_PRICE_WIDGET_INITIALIZED'; diff --git a/src/events/catalog/SetRoomPreviewerStuffDataEvent.ts b/src/events/catalog/SetRoomPreviewerStuffDataEvent.ts index 03e11862..5332dfd9 100644 --- a/src/events/catalog/SetRoomPreviewerStuffDataEvent.ts +++ b/src/events/catalog/SetRoomPreviewerStuffDataEvent.ts @@ -1,5 +1,5 @@ import { IObjectData, NitroEvent } from '@nitrots/nitro-renderer'; -import { IPurchasableOffer } from '../../components/catalog/common/IPurchasableOffer'; +import { IPurchasableOffer } from '../../api'; export class SetRoomPreviewerStuffDataEvent extends NitroEvent { diff --git a/src/events/catalog/index.ts b/src/events/catalog/index.ts index 1e2cd919..a7c15725 100644 --- a/src/events/catalog/index.ts +++ b/src/events/catalog/index.ts @@ -1,8 +1,5 @@ export * from './CatalogEvent'; -export * from './CatalogGiftReceiverNotFoundEvent'; export * from './CatalogInitGiftEvent'; -export * from './CatalogInitPurchaseEvent'; -export * from './CatalogNameResultEvent'; export * from './CatalogPostMarketplaceOfferEvent'; export * from './CatalogPurchasedEvent'; export * from './CatalogPurchaseFailureEvent'; diff --git a/src/events/index.ts b/src/events/index.ts index 288b2364..40e7a162 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -1,6 +1,7 @@ export * from './catalog'; export * from './guide-tool'; export * from './help'; +export * from './inventory'; export * from './mod-tools'; export * from './notification-center'; export * from './room-widgets'; diff --git a/src/events/inventory/InventoryFurniAddedEvent.ts b/src/events/inventory/InventoryFurniAddedEvent.ts new file mode 100644 index 00000000..409f0be8 --- /dev/null +++ b/src/events/inventory/InventoryFurniAddedEvent.ts @@ -0,0 +1,14 @@ +import { NitroEvent } from '@nitrots/nitro-renderer'; + +export class InventoryFurniAddedEvent extends NitroEvent +{ + public static FURNI_ADDED: string = 'IFAE_FURNI_ADDED'; + + constructor( + public readonly id: number, + public readonly spriteId: number, + public readonly category: number) + { + super(InventoryFurniAddedEvent.FURNI_ADDED); + } +} diff --git a/src/events/inventory/index.ts b/src/events/inventory/index.ts new file mode 100644 index 00000000..58503ead --- /dev/null +++ b/src/events/inventory/index.ts @@ -0,0 +1 @@ +export * from './InventoryFurniAddedEvent'; diff --git a/src/hooks/catalog/index.ts b/src/hooks/catalog/index.ts new file mode 100644 index 00000000..75d29849 --- /dev/null +++ b/src/hooks/catalog/index.ts @@ -0,0 +1,3 @@ +export * from './useCatalog'; +export * from './useCatalogPlaceMultipleItems'; +export * from './useCatalogSkipPurchaseConfirmation'; diff --git a/src/hooks/catalog/useCatalog.ts b/src/hooks/catalog/useCatalog.ts new file mode 100644 index 00000000..2e90d17a --- /dev/null +++ b/src/hooks/catalog/useCatalog.ts @@ -0,0 +1,946 @@ +import { BuildersClubFurniCountMessageEvent, BuildersClubPlaceRoomItemMessageComposer, BuildersClubPlaceWallItemMessageComposer, BuildersClubQueryFurniCountMessageComposer, BuildersClubSubscriptionStatusMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, FrontPageItem, FurniturePlaceComposer, FurniturePlacePaintComposer, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetGiftWrappingConfigurationComposer, GiftWrappingConfigurationEvent, GuildMembershipsMessageEvent, HabboClubOffersMessageEvent, LegacyDataType, LimitedEditionSoldOutEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseFromCatalogComposer, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, RoomControllerLevel, RoomEngineObjectPlacedEvent, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType, RoomObjectVariable, RoomPreviewer, SellablePetPalettesMessageEvent, Vector3d } from '@nitrots/nitro-renderer'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useBetween } from 'use-between'; +import { BuilderFurniPlaceableStatus, CatalogNode, CatalogPage, CatalogPetPalette, CatalogType, CreateLinkEvent, FurniCategory, GetFurnitureData, GetNitroInstance, GetProductDataForLocalization, GetRoomEngine, GetRoomSession, GiftWrappingConfiguration, ICatalogNode, ICatalogOptions, ICatalogPage, IPageLocalization, IProduct, IPurchasableOffer, IPurchaseOptions, LocalizeText, NotificationAlertType, NotificationUtilities, Offer, PageLocalization, PlacedObjectPurchaseData, PlaySound, Product, ProductTypeEnum, RequestedPage, SearchResult, SendMessageComposer, SoundNames } from '../../api'; +import { CatalogPurchasedEvent, CatalogPurchaseFailureEvent, CatalogPurchaseNotAllowedEvent, CatalogPurchaseSoldOutEvent, InventoryFurniAddedEvent } from '../../events'; +import { DispatchUiEvent, UseRoomEngineEvent, UseUiEvent } from '../events'; +import { UseMessageEventHook } from '../messages'; +import { useCatalogPlaceMultipleItems } from './useCatalogPlaceMultipleItems'; +import { useCatalogSkipPurchaseConfirmation } from './useCatalogSkipPurchaseConfirmation'; + +const DUMMY_PAGE_ID_FOR_OFFER_SEARCH = -12345678; +const DRAG_AND_DROP_ENABLED = true; + +const useCatalogState = () => +{ + const [ isVisible, setIsVisible ] = useState(false); + 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(null); + const [ offersToNodes, setOffersToNodes ] = useState>(null); + const [ currentPage, setCurrentPage ] = useState(null); + const [ currentOffer, setCurrentOffer ] = useState(null); + const [ activeNodes, setActiveNodes ] = useState([]); + const [ searchResult, setSearchResult ] = useState(null); + const [ frontPageItems, setFrontPageItems ] = useState([]); + const [ roomPreviewer, setRoomPreviewer ] = useState(null); + const [ navigationHidden, setNavigationHidden ] = useState(false); + const [ purchaseOptions, setPurchaseOptions ] = useState({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null }); + const [ catalogOptions, setCatalogOptions ] = useState({}); + const [ objectMoverRequested, setObjectMoverRequested ] = useState(false); + const [ catalogPlaceMultipleObjects, setCatalogPlaceMultipleObjects ] = useCatalogPlaceMultipleItems(); + const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useCatalogSkipPurchaseConfirmation(); + const [ purchasableOffer, setPurchaseableOffer ] = useState(null); + const [ placedObjectPurchaseData, setPlacedObjectPurchaseData ] = useState(null); + const [ furniCount, setFurniCount ] = useState(0); + const [ furniLimit, setFurniLimit ] = useState(0); + const [ maxFurniLimit, setMaxFurniLimit ] = useState(0); + const [ secondsLeft, setSecondsLeft ] = useState(0); + const [ updateTime, setUpdateTime ] = useState(0); + const [ secondsLeftWithGrace, setSecondsLeftWithGrace ] = useState(0); + const requestedPage = useRef(new RequestedPage()); + + const resetState = useCallback(() => + { + setPageId(-1); + setPreviousPageId(-1); + setRootNode(null); + setOffersToNodes(null); + setCurrentPage(null); + setCurrentOffer(null); + setActiveNodes([]); + setSearchResult(null); + setFrontPageItems([]); + setIsVisible(false); + }, []); + + const getBuilderFurniPlaceableStatus = useCallback((offer: IPurchasableOffer) => + { + if(!offer) return BuilderFurniPlaceableStatus.MISSING_OFFER; + + if((furniCount < 0) || (furniCount >= furniLimit)) return BuilderFurniPlaceableStatus.FURNI_LIMIT_REACHED; + + const roomSession = GetRoomSession(); + + if(!roomSession) return BuilderFurniPlaceableStatus.NOT_IN_ROOM; + + if(!roomSession.isRoomOwner) return BuilderFurniPlaceableStatus.NOT_ROOM_OWNER; + + if(secondsLeft <= 0) + { + const roomEngine = GetRoomEngine(); + + let objectCount = roomEngine.getRoomObjectCount(roomSession.roomId, RoomObjectCategory.UNIT); + + while(objectCount > 0) + { + const roomObject = roomEngine.getRoomObjectByIndex(roomSession.roomId, objectCount, RoomObjectCategory.UNIT); + const userData = roomSession.userDataManager.getUserDataByIndex(roomObject.id); + + if(userData && (userData.type === RoomObjectType.USER) && (userData.roomIndex !== roomSession.ownRoomIndex) && !userData.isModerator) return BuilderFurniPlaceableStatus.VISITORS_IN_ROOM; + + objectCount--; + } + } + + return BuilderFurniPlaceableStatus.OKAY; + }, [ furniCount, furniLimit, secondsLeft ]); + + const isDraggable = useCallback((offer: IPurchasableOffer) => + { + const roomSession = GetRoomSession(); + + if(((DRAG_AND_DROP_ENABLED && roomSession && offer.page && (offer.page.layoutCode !== 'sold_ltd_items') && (currentType === CatalogType.NORMAL) && (roomSession.isRoomOwner || (roomSession.isGuildRoom && (roomSession.controllerLevel >= RoomControllerLevel.GUILD_MEMBER)))) || ((currentType === CatalogType.BUILDER) && (getBuilderFurniPlaceableStatus(offer) === BuilderFurniPlaceableStatus.OKAY))) && (offer.pricingModel !== Offer.PRICING_MODEL_BUNDLE) && (offer.product.productType !== ProductTypeEnum.EFFECT) && (offer.product.productType !== ProductTypeEnum.HABBO_CLUB)) return true; + + return false; + }, [ currentType, getBuilderFurniPlaceableStatus ]); + + const requestOfferToMover = useCallback((offer: IPurchasableOffer) => + { + if(!isDraggable(offer)) return; + + const product = offer.product; + + if(!product) return; + + let category = 0; + + switch(product.productType) + { + case ProductTypeEnum.FLOOR: + category = RoomObjectCategory.FLOOR; + break; + case ProductTypeEnum.WALL: + category = RoomObjectCategory.WALL; + break; + } + + if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.CATALOG, -(offer.offerId), category, product.productClassId, product.extraParam)) + { + setPurchaseableOffer(offer); + setObjectMoverRequested(true); + + setIsVisible(false); + } + }, [ isDraggable ]); + + const resetRoomPaint = useCallback((planeType: string, type: string) => + { + const roomEngine = GetRoomEngine(); + + let wallType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE); + let floorType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE); + let landscapeType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE); + + wallType = (wallType && wallType.length) ? wallType : '101'; + floorType = (floorType && floorType.length) ? floorType : '101'; + landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1'; + + switch(planeType) + { + case 'floor': + roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, type, wallType, landscapeType, true); + return; + case 'wallpaper': + roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, floorType, type, landscapeType, true); + return; + case 'landscape': + roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, floorType, wallType, type, true); + return; + default: + roomEngine.updateRoomInstancePlaneType(roomEngine.activeRoomId, floorType, wallType, landscapeType, true); + return; + } + }, []); + + const cancelObjectMover = useCallback(() => + { + if(!purchasableOffer) return; + + GetRoomEngine().cancelRoomObjectInsert(); + + setObjectMoverRequested(false); + setPurchaseableOffer(null); + }, [ purchasableOffer ]); + + const resetObjectMover = useCallback((flag: boolean = true) => + { + setObjectMoverRequested(prevValue => + { + if(prevValue && flag) + { + CreateLinkEvent('catalog/open'); + } + + return false; + }); + }, []); + + const resetPlacedOfferData = useCallback((flag: boolean = false) => + { + if(!flag) resetObjectMover(); + + setPlacedObjectPurchaseData(prevValue => + { + if(prevValue) + { + switch(prevValue.category) + { + case RoomObjectCategory.FLOOR: + GetRoomEngine().removeRoomObjectFloor(prevValue.roomId, prevValue.objectId); + break; + case RoomObjectCategory.WALL: { + + switch(prevValue.furniData.className) + { + case 'floor': + case 'wallpaper': + case 'landscape': + resetRoomPaint('reset', ''); + break; + default: + GetRoomEngine().removeRoomObjectWall(prevValue.roomId, prevValue.objectId); + break; + } + break; + } + default: + GetRoomEngine().deleteRoomObject(prevValue.objectId, prevValue.category); + break; + } + } + + return null; + }); + }, [ resetObjectMover, resetRoomPaint ]); + + 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) => + { + cancelObjectMover(); + + 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, cancelObjectMover ]); + + const openPageById = useCallback((id: number) => + { + setSearchResult(null); + + if(!isVisible) + { + requestedPage.current.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) + { + requestedPage.current.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) + { + requestedPage.current.requestedByOfferId = offerId; + + setIsVisible(true); + } + else + { + const nodes = getNodesByOfferId(offerId); + + if(!nodes || !nodes.length) return; + + activateNode(nodes[0], offerId); + } + }, [ isVisible, getNodesByOfferId, activateNode ]); + + const refreshBuilderStatus = useCallback(() => + { + + }, []); + + const onCatalogPagesListEvent = useCallback((event: CatalogPagesListEvent) => + { + const parser = event.getParser(); + const offers: Map = 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 ]); + + UseMessageEventHook(CatalogPagesListEvent, onCatalogPagesListEvent); + + 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 ]); + + UseMessageEventHook(CatalogPageMessageEvent, onCatalogPageMessageEvent); + + const onPurchaseOKMessageEvent = useCallback((event: PurchaseOKMessageEvent) => + { + const parser = event.getParser(); + + DispatchUiEvent(new CatalogPurchasedEvent(parser.offer)); + }, []); + + UseMessageEventHook(PurchaseOKMessageEvent, onPurchaseOKMessageEvent); + + const onPurchaseErrorMessageEvent = useCallback((event: PurchaseErrorMessageEvent) => + { + const parser = event.getParser(); + + DispatchUiEvent(new CatalogPurchaseFailureEvent(parser.code)); + }, []); + + UseMessageEventHook(PurchaseErrorMessageEvent, onPurchaseErrorMessageEvent); + + const onPurchaseNotAllowedMessageEvent = useCallback((event: PurchaseNotAllowedMessageEvent) => + { + const parser = event.getParser(); + + DispatchUiEvent(new CatalogPurchaseNotAllowedEvent(parser.code)); + }, []); + + UseMessageEventHook(PurchaseNotAllowedMessageEvent, onPurchaseNotAllowedMessageEvent); + + const onLimitedEditionSoldOutEvent = useCallback((event: LimitedEditionSoldOutEvent) => + { + const parser = event.getParser(); + + DispatchUiEvent(new CatalogPurchaseSoldOutEvent()); + }, []); + + UseMessageEventHook(LimitedEditionSoldOutEvent, onLimitedEditionSoldOutEvent); + + 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)) + { + setPurchaseOptions(prevValue => + { + const newValue = { ...prevValue }; + + newValue.extraData =( offer.product.extraParam || null); + + return newValue; + }); + } + + // (this._isObjectMoverRequested) && (this._purchasableOffer) + }, [ currentType, currentPage, setCurrentOffer, setPurchaseOptions ]); + + UseMessageEventHook(ProductOfferEvent, onProductOfferEvent); + + 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 ]); + + UseMessageEventHook(SellablePetPalettesMessageEvent, onSellablePetPalettesMessageEvent); + + const onHabboClubOffersMessageEvent = useCallback((event: HabboClubOffersMessageEvent) => + { + const parser = event.getParser(); + + setCatalogOptions(prevValue => + { + const clubOffers = parser.offers; + + return { ...prevValue, clubOffers }; + }); + }, [ setCatalogOptions ]); + + UseMessageEventHook(HabboClubOffersMessageEvent, onHabboClubOffersMessageEvent); + + const onGuildMembershipsMessageEvent = useCallback((event: GuildMembershipsMessageEvent) => + { + const parser = event.getParser(); + + setCatalogOptions(prevValue => + { + const groups = parser.groups; + + return { ...prevValue, groups }; + }); + }, [ setCatalogOptions ]); + + UseMessageEventHook(GuildMembershipsMessageEvent, onGuildMembershipsMessageEvent); + + const onGiftWrappingConfigurationEvent = useCallback((event: GiftWrappingConfigurationEvent) => + { + const parser = event.getParser(); + + setCatalogOptions(prevValue => + { + const giftConfiguration = new GiftWrappingConfiguration(parser); + + return { ...prevValue, giftConfiguration }; + }); + }, [ setCatalogOptions ]); + + UseMessageEventHook(GiftWrappingConfigurationEvent, onGiftWrappingConfigurationEvent); + + 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); + }, []); + + UseMessageEventHook(MarketplaceMakeOfferResult, onMarketplaceMakeOfferResult); + + const onClubGiftInfoEvent = useCallback((event: ClubGiftInfoEvent) => + { + const parser = event.getParser(); + + setCatalogOptions(prevValue => + { + const clubGifts = parser; + + return { ...prevValue, clubGifts }; + }); + }, [ setCatalogOptions ]); + + UseMessageEventHook(ClubGiftInfoEvent, onClubGiftInfoEvent); + + 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 onBuildersClubFurniCountMessageEvent = useCallback((event: BuildersClubFurniCountMessageEvent) => + { + const parser = event.getParser(); + + setFurniCount(parser.furniCount); + + refreshBuilderStatus(); + }, [ refreshBuilderStatus ]); + + UseMessageEventHook(BuildersClubFurniCountMessageEvent, onBuildersClubFurniCountMessageEvent); + + const onBuildersClubSubscriptionStatusMessageEvent = useCallback((event: BuildersClubSubscriptionStatusMessageEvent) => + { + const parser = event.getParser(); + + setFurniLimit(parser._Str_15864); + setMaxFurniLimit(parser._Str_24094); + setSecondsLeft(parser._Str_3709); + setUpdateTime(GetNitroInstance().time); + setSecondsLeftWithGrace(parser._Str_24379); + + refreshBuilderStatus(); + }, [ refreshBuilderStatus ]); + + UseMessageEventHook(BuildersClubSubscriptionStatusMessageEvent, onBuildersClubSubscriptionStatusMessageEvent); + + const onCatalogPurchasedEvent = useCallback((event: CatalogPurchasedEvent) => + { + PlaySound(SoundNames.CREDITS); + }, []); + + UseUiEvent(CatalogPurchasedEvent.PURCHASE_SUCCESS, onCatalogPurchasedEvent); + + const onRoomEngineObjectPlacedEvent = useCallback((event: RoomEngineObjectPlacedEvent) => + { + if(!objectMoverRequested || (event.type !== RoomEngineObjectPlacedEvent.PLACED)) return; + + resetPlacedOfferData(true); + + if(!purchasableOffer) + { + resetObjectMover(); + + return; + } + + let placed = false; + + const product = purchasableOffer.product; + + if(event.category === RoomObjectCategory.WALL) + { + switch(product.furnitureData.className) + { + case 'floor': + case 'wallpaper': + case 'landscape': + placed = (event.placedOnFloor || event.placedOnWall); + break; + default: + placed = event.placedInRoom; + break; + } + } + else + { + placed = event.placedInRoom; + } + + if(!placed) + { + resetObjectMover(); + + return; + } + + setPlacedObjectPurchaseData(new PlacedObjectPurchaseData(event.roomId, event.objectId, event.category, event.wallLocation, event.x, event.y, event.direction, purchasableOffer)); + + switch(currentType) + { + case CatalogType.NORMAL: { + switch(event.category) + { + case RoomObjectCategory.FLOOR: + GetRoomEngine().addFurnitureFloor(event.roomId, event.objectId, product.productClassId, new Vector3d(event.x, event.y, event.z), new Vector3d(event.direction), 0, new LegacyDataType()); + break; + case RoomObjectCategory.WALL: { + switch(product.furnitureData.className) + { + case 'floor': + case 'wallpaper': + case 'landscape': + resetRoomPaint(product.furnitureData.className, product.extraParam); + break; + default: + GetRoomEngine().addFurnitureWall(event.roomId, event.objectId, product.productClassId, new Vector3d(event.x, event.y, event.z), new Vector3d(event.direction * 45), 0, event.instanceData, 0); + break; + } + } + } + + const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category); + + if(roomObject) roomObject.model.setValue(RoomObjectVariable.FURNITURE_ALPHA_MULTIPLIER, 0.5); + + if(catalogSkipPurchaseConfirmation) + { + SendMessageComposer(new PurchaseFromCatalogComposer(pageId, purchasableOffer.offerId, product.extraParam, 1)); + + if(catalogPlaceMultipleObjects) requestOfferToMover(purchasableOffer); + } + else + { + // confirm + + if(catalogPlaceMultipleObjects) requestOfferToMover(purchasableOffer); + } + break; + } + case CatalogType.BUILDER: { + let pageId = purchasableOffer.page.pageId; + + if(pageId === DUMMY_PAGE_ID_FOR_OFFER_SEARCH) + { + pageId = -1; + } + + switch(event.category) + { + case RoomObjectCategory.FLOOR: + SendMessageComposer(new BuildersClubPlaceRoomItemMessageComposer(pageId, purchasableOffer.offerId, product.extraParam, event.x, event.y, event.direction)); + break; + case RoomObjectCategory.WALL: + SendMessageComposer(new BuildersClubPlaceWallItemMessageComposer(pageId, purchasableOffer.offerId, product.extraParam, event.wallLocation)); + break; + } + + if(catalogPlaceMultipleObjects) requestOfferToMover(purchasableOffer); + break; + } + } + }, [ objectMoverRequested, purchasableOffer, catalogPlaceMultipleObjects, catalogSkipPurchaseConfirmation, currentType, pageId, resetPlacedOfferData, resetObjectMover, resetRoomPaint, requestOfferToMover ]); + + UseRoomEngineEvent(RoomEngineObjectPlacedEvent.PLACED, onRoomEngineObjectPlacedEvent); + + const onInventoryFurniAddedEvent = useCallback((event: InventoryFurniAddedEvent) => + { + const roomEngine = GetRoomEngine(); + + if(!placedObjectPurchaseData || (placedObjectPurchaseData.productClassId !== event.spriteId) || (placedObjectPurchaseData.roomId !== roomEngine.activeRoomId)) return; + + switch(event.category) + { + case FurniCategory.FLOOR: { + const floorType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE); + + if(placedObjectPurchaseData.extraParam !== floorType) SendMessageComposer(new FurniturePlacePaintComposer(event.id)); + break; + } + case FurniCategory.WALL_PAPER: { + const wallType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE); + + if(placedObjectPurchaseData.extraParam !== wallType) SendMessageComposer(new FurniturePlacePaintComposer(event.id)); + break; + } + case FurniCategory.LANDSCAPE: { + const landscapeType = roomEngine.getRoomInstanceVariable(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE); + + if(placedObjectPurchaseData.extraParam !== landscapeType) SendMessageComposer(new FurniturePlacePaintComposer(event.id)); + break; + } + default: + SendMessageComposer(new FurniturePlaceComposer(event.id, placedObjectPurchaseData.category, placedObjectPurchaseData.wallLocation, placedObjectPurchaseData.x, placedObjectPurchaseData.y, placedObjectPurchaseData.direction)); + } + + if(!catalogPlaceMultipleObjects) resetPlacedOfferData(); + }, [ placedObjectPurchaseData, catalogPlaceMultipleObjects, resetPlacedOfferData ]); + + UseUiEvent(InventoryFurniAddedEvent.FURNI_ADDED, onInventoryFurniAddedEvent); + + useEffect(() => + { + return () => setCurrentOffer(null); + }, [ currentPage ]); + + useEffect(() => + { + if(!isVisible || !rootNode || !requestedPage.current) return; + + switch(requestedPage.current.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(requestedPage.current.requestById); + requestedPage.current.resetRequest(); + return; + case RequestedPage.REQUEST_TYPE_OFFER: + openPageByOfferId(requestedPage.current.requestedByOfferId); + requestedPage.current.resetRequest(); + return; + case RequestedPage.REQUEST_TYPE_NAME: + openPageByName(requestedPage.current.requestByName); + requestedPage.current.resetRequest(); + return; + } + }, [ isVisible, rootNode, activeNodes, activateNode, openPageById, openPageByOfferId, openPageByName ]); + + useEffect(() => + { + if(!searchResult && currentPage && (currentPage.pageId === -1)) openPageById(previousPageId); + }, [ searchResult, currentPage, previousPageId, openPageById ]); + + useEffect(() => + { + if(!isVisible || rootNode) return; + + SendMessageComposer(new GetGiftWrappingConfigurationComposer()); + SendMessageComposer(new GetClubGiftInfo()); + SendMessageComposer(new GetCatalogIndexComposer(currentType)); + SendMessageComposer(new BuildersClubQueryFurniCountMessageComposer()); + }, [ isVisible, rootNode, currentType ]); + + useEffect(() => + { + setRoomPreviewer(new RoomPreviewer(GetRoomEngine(), ++RoomPreviewer.PREVIEW_COUNTER)); + + return () => + { + setRoomPreviewer(prevValue => + { + prevValue.dispose(); + + return null; + }); + } + }, []); + + return { isVisible, setIsVisible, isBusy, pageId, previousPageId, currentType, rootNode, offersToNodes, currentPage, setCurrentPage, currentOffer, setCurrentOffer, activeNodes, searchResult, setSearchResult, frontPageItems, roomPreviewer, navigationHidden, setNavigationHidden, purchaseOptions, setPurchaseOptions, catalogOptions, setCatalogOptions, getNodeById, getNodeByName, activateNode, openPageById, openPageByName, openPageByOfferId, requestOfferToMover }; +} + +export const useCatalog = () => useBetween(useCatalogState); diff --git a/src/hooks/catalog/useCatalogPlaceMultipleItems.ts b/src/hooks/catalog/useCatalogPlaceMultipleItems.ts new file mode 100644 index 00000000..39cfd28c --- /dev/null +++ b/src/hooks/catalog/useCatalogPlaceMultipleItems.ts @@ -0,0 +1,7 @@ +import { useBetween } from 'use-between'; +import { LocalStorageKeys } from '../../api'; +import { useLocalStorage } from '../useLocalStorage'; + +const useCatalogPlaceMultipleItemsState = () => useLocalStorage(LocalStorageKeys.CATALOG_PLACE_MULTIPLE_OBJECTS, false); + +export const useCatalogPlaceMultipleItems = () => useBetween(useCatalogPlaceMultipleItemsState); diff --git a/src/hooks/catalog/useCatalogSkipPurchaseConfirmation.ts b/src/hooks/catalog/useCatalogSkipPurchaseConfirmation.ts new file mode 100644 index 00000000..b2d69a2a --- /dev/null +++ b/src/hooks/catalog/useCatalogSkipPurchaseConfirmation.ts @@ -0,0 +1,7 @@ +import { useBetween } from 'use-between'; +import { LocalStorageKeys } from '../../api'; +import { useLocalStorage } from '../useLocalStorage'; + +const useCatalogSkipPurchaseConfirmationState = () => useLocalStorage(LocalStorageKeys.CATALOG_SKIP_PURCHASE_CONFIRMATION, false); + +export const useCatalogSkipPurchaseConfirmation = () => useBetween(useCatalogSkipPurchaseConfirmationState); diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 8713e5db..44d7ec57 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,4 +1,5 @@ export * from './achievements'; +export * from './catalog'; export * from './events'; export * from './events/core'; export * from './events/nitro'; @@ -10,5 +11,6 @@ export * from './navigator'; export * from './purse'; export * from './rooms'; export * from './session'; +export * from './useLocalStorage'; export * from './UseMountEffect'; export * from './useSharedVisibility'; diff --git a/src/hooks/inventory/useInventoryFurni.ts b/src/hooks/inventory/useInventoryFurni.ts index 8ce8379d..18c0b740 100644 --- a/src/hooks/inventory/useInventoryFurni.ts +++ b/src/hooks/inventory/useInventoryFurni.ts @@ -4,6 +4,8 @@ import { useBetween } from 'use-between'; import { useInventoryUnseenTracker } from '.'; import { UseMessageEventHook } from '..'; import { addFurnitureItem, attemptItemPlacement, cancelRoomObjectPlacement, CloneObject, CreateLinkEvent, FurnitureItem, getAllItemIds, getPlacingItemId, GroupItem, mergeFurniFragments, SendMessageComposer, UnseenItemCategory } from '../../api'; +import { InventoryFurniAddedEvent } from '../../events'; +import { DispatchUiEvent } from '../events'; import { useSharedVisibility } from '../useSharedVisibility'; let furniMsgFragments: Map[] = null; @@ -73,6 +75,8 @@ const useInventoryFurniState = () => const furniture = new FurnitureItem(item); addFurnitureItem(newValue, furniture, isUnseen(UnseenItemCategory.FURNI, item.itemId)); + + DispatchUiEvent(new InventoryFurniAddedEvent(furniture.id, furniture.type, furniture.category)); } } @@ -148,6 +152,8 @@ const useInventoryFurniState = () => addFurnitureItem(newValue, item, isUnseen(UnseenItemCategory.FURNI, itemId)); + DispatchUiEvent(new InventoryFurniAddedEvent(item.id, item.type, item.category)); + } return newValue; @@ -262,7 +268,7 @@ const useInventoryFurniState = () => setNeedsUpdate(false); }, [ isVisible, needsUpdate ]); - return { groupItems, setGroupItems, selectedItem, setSelectedItem, activate, deactivate }; + return { isVisible, groupItems, setGroupItems, selectedItem, setSelectedItem, activate, deactivate }; } export const useInventoryFurni = () => useBetween(useInventoryFurniState); diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts new file mode 100644 index 00000000..e231cc29 --- /dev/null +++ b/src/hooks/useLocalStorage.ts @@ -0,0 +1,43 @@ +import { NitroLogger } from '@nitrots/nitro-renderer'; +import { Dispatch, SetStateAction, useState } from 'react'; + +const useLocalStorageState = (key: string, initialValue: T): [ T, Dispatch>] => +{ + const [ storedValue, setStoredValuie ] = useState(() => + { + if(typeof window === 'undefined') return initialValue; + + try + { + const item = window.localStorage.getItem(key); + + return item ? JSON.parse(item) : initialValue; + } + + catch(error) + { + return initialValue; + } + }); + + const setValue = (value: T) => + { + try + { + const valueToStore = value instanceof Function ? value(storedValue) : value; + + setStoredValuie(valueToStore); + + if(typeof window !== 'undefined') window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } + + catch(error) + { + NitroLogger.error(error); + } + } + + return [ storedValue, setValue ]; +} + +export const useLocalStorage = useLocalStorageState; diff --git a/yarn.lock b/yarn.lock index b6ee0612..b9048174 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1450,10 +1450,10 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@nitrots/nitro-renderer@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@nitrots/nitro-renderer/-/nitro-renderer-1.2.2.tgz#9dabba24dc701162cc1f1980aa54d20d72532ef2" - integrity sha512-fIBcBhUp1bQexbh2L77bcmkWDQZ/LwTsy+S7KwNxbIRS5B+WVRqDqsZeTaCcaoB2H+GBYakhah5akVMKeos9Wg== +"@nitrots/nitro-renderer@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@nitrots/nitro-renderer/-/nitro-renderer-1.2.3.tgz#692fbec1a8bf3b81af46788be56ace5d393100cf" + integrity sha512-v+tJA6QyTWPbbl/cEtYB3478OkhmB18BKFrGkL7dKIgN97sCQToTvbmfVXyyn3X3k81JIifApSb+vxyXvc0QXA== dependencies: "@pixi/app" "^6.3.0" "@pixi/basis" "^6.3.0"