diff --git a/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts b/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts index db1073d5..9365f015 100644 --- a/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts +++ b/src/api/nitro/room/widgets/handlers/RoomWidgetAvatarInfoHandler.ts @@ -120,24 +120,24 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler { if(userData.ownerId === ownerId) { - if(userData.hasSaddle && (specialType === FurniCategory._Str_6096)) replace = true; + if(userData.hasSaddle && (specialType === FurniCategory.PET_SADDLE)) replace = true; const figureParts = userData.figure.split(' '); const figurePart = (figureParts.length ? parseInt(figureParts[0]) : -1); if(figurePart === part) { - if(specialType === FurniCategory._Str_6915) + if(specialType === FurniCategory.MONSTERPLANT_REVIVAL) { if(!userData.canRevive) continue; } - if(specialType === FurniCategory._Str_8726) + if(specialType === FurniCategory.MONSTERPLANT_REBREED) { if((userData.petLevel < 7) || userData.canRevive || userData.canBreed) continue; } - if(specialType === FurniCategory._Str_9449) + if(specialType === FurniCategory.MONSTERPLANT_FERTILIZE) { if((userData.petLevel >= 7) || userData.canRevive) continue; } diff --git a/src/events/catalog/CatalogPostMarketplaceOfferEvent.ts b/src/events/catalog/CatalogPostMarketplaceOfferEvent.ts new file mode 100644 index 00000000..837f1cff --- /dev/null +++ b/src/events/catalog/CatalogPostMarketplaceOfferEvent.ts @@ -0,0 +1,20 @@ +import { CatalogEvent } from '.'; +import { FurnitureItem } from '../../views/inventory/common/FurnitureItem'; + +export class CatalogPostMarketplaceOfferEvent extends CatalogEvent +{ + public static readonly POST_MARKETPLACE = 'CE_POST_MARKETPLACE'; + + private _item: FurnitureItem; + + constructor(item: FurnitureItem) + { + super(CatalogPostMarketplaceOfferEvent.POST_MARKETPLACE); + this._item = item; + } + + public get item(): FurnitureItem + { + return this._item; + } +} diff --git a/src/views/catalog/CatalogMessageHandler.tsx b/src/views/catalog/CatalogMessageHandler.tsx index 8fef7ae0..836de880 100644 --- a/src/views/catalog/CatalogMessageHandler.tsx +++ b/src/views/catalog/CatalogMessageHandler.tsx @@ -1,12 +1,15 @@ -import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer'; +import { ApproveNameMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, HabboClubOffersMessageEvent, LimitedEditionSoldOutEvent, MarketplaceConfigurationEvent, MarketplaceMakeOfferResult, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, SellablePetPalettesMessageEvent, UserSubscriptionEvent } from '@nitrots/nitro-renderer'; import { GuildMembershipsMessageEvent } from '@nitrots/nitro-renderer/src/nitro/communication/messages/incoming/user/GuildMembershipsMessageEvent'; import { FC, useCallback } from 'react'; +import { LocalizeText } from '../../api'; import { CatalogNameResultEvent, CatalogPurchaseFailureEvent } from '../../events'; import { CatalogGiftReceiverNotFoundEvent } from '../../events/catalog/CatalogGiftReceiverNotFoundEvent'; import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEvent'; import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent'; import { dispatchUiEvent } from '../../hooks/events/ui/ui-event'; import { CreateMessageHook } from '../../hooks/messages/message-event'; +import { NotificationAlertType } from '../notification-center/common/NotificationAlertType'; +import { NotificationUtilities } from '../notification-center/common/NotificationUtilities'; import { CatalogMessageHandlerProps } from './CatalogMessageHandler.types'; import { CatalogPetPalette } from './common/CatalogPetPalette'; import { SubscriptionInfo } from './common/SubscriptionInfo'; @@ -164,6 +167,43 @@ export const CatalogMessageHandler: FC = props => }); }, [ dispatchCatalogState ]); + 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 onMarketplaceConfigurationEvent = useCallback((event: MarketplaceConfigurationEvent) => + { + const parser = event.getParser(); + + if(!parser) return; + + console.log(parser); + + dispatchCatalogState({ + type: CatalogActions.SET_MARKETPLACE_CONFIGURATION, + payload: { + marketplaceConfiguration: parser + } + }); + }, [dispatchCatalogState]); + const onClubGiftInfoEvent = useCallback((event: ClubGiftInfoEvent) => { const parser = event.getParser(); @@ -194,6 +234,8 @@ export const CatalogMessageHandler: FC = props => CreateMessageHook(CatalogPublishedMessageEvent, onCatalogPublishedMessageEvent); CreateMessageHook(GiftWrappingConfigurationEvent, onGiftWrappingConfigurationEvent); CreateMessageHook(ClubGiftInfoEvent, onClubGiftInfoEvent); + CreateMessageHook(MarketplaceMakeOfferResult, onMarketplaceMakeOfferResult); + CreateMessageHook(MarketplaceConfigurationEvent, onMarketplaceConfigurationEvent); return null; } diff --git a/src/views/catalog/CatalogView.tsx b/src/views/catalog/CatalogView.tsx index d45ed9e3..739f126d 100644 --- a/src/views/catalog/CatalogView.tsx +++ b/src/views/catalog/CatalogView.tsx @@ -1,8 +1,9 @@ -import { GetCatalogIndexComposer, GetCatalogPageComposer, GetGiftWrappingConfigurationComposer, ILinkEventTracker, INodeData, RoomPreviewer } from '@nitrots/nitro-renderer'; +import { GetCatalogIndexComposer, GetCatalogPageComposer, GetGiftWrappingConfigurationComposer, GetMarketplaceConfigurationMessageComposer, ILinkEventTracker, INodeData, RoomPreviewer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { AddEventLinkTracker, GetRoomEngine, LocalizeText, RemoveLinkEventTracker } from '../../api'; import { CREDITS, PlaySound } from '../../api/utils/PlaySound'; import { CatalogEvent } from '../../events'; +import { UseMountEffect } from '../../hooks'; import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { SendMessageHook } from '../../hooks/messages/message-event'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, NitroLayoutGrid, NitroLayoutGridColumn } from '../../layout'; @@ -14,6 +15,7 @@ import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer'; import { CatalogGiftView } from './views/gift/CatalogGiftView'; import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { CatalogPageView } from './views/page/CatalogPageView'; +import { MarketplacePostOfferView } from './views/page/layout/marketplace/post-offer/MarketplacePostOfferView'; export const CatalogView: FC = props => { @@ -114,7 +116,6 @@ export const CatalogView: FC = props => if(loadCatalog) { SendMessageHook(new GetCatalogIndexComposer(CatalogMode.MODE_NORMAL)); - SendMessageHook(new GetGiftWrappingConfigurationComposer()); return; } @@ -188,6 +189,12 @@ export const CatalogView: FC = props => } }, []); + UseMountEffect(() => + { + SendMessageHook(new GetMarketplaceConfigurationMessageComposer()); + SendMessageHook(new GetGiftWrappingConfigurationComposer()); + }); + const currentNavigationPage = ((searchResult && searchResult.page) || currentTab); const navigationHidden = !!(pageParser && pageParser.frontPageItems.length); @@ -220,6 +227,7 @@ export const CatalogView: FC = props => } + ); } diff --git a/src/views/catalog/reducers/CatalogReducer.tsx b/src/views/catalog/reducers/CatalogReducer.tsx index c9b7b674..ad3878d1 100644 --- a/src/views/catalog/reducers/CatalogReducer.tsx +++ b/src/views/catalog/reducers/CatalogReducer.tsx @@ -1,4 +1,4 @@ -import { CatalogPageMessageOfferData, CatalogPageMessageParser, ClubGiftInfoParser, ClubOfferData, GiftWrappingConfigurationParser, INodeData } from '@nitrots/nitro-renderer'; +import { CatalogPageMessageOfferData, CatalogPageMessageParser, ClubGiftInfoParser, ClubOfferData, GiftWrappingConfigurationParser, INodeData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer'; import { HabboGroupEntryData } from '@nitrots/nitro-renderer/src/nitro/communication/messages/parser/user/HabboGroupEntryData'; import { Reducer } from 'react'; import { CatalogPetPalette } from '../common/CatalogPetPalette'; @@ -20,6 +20,7 @@ export interface ICatalogState clubGifts: ClubGiftInfoParser; subscriptionInfo: SubscriptionInfo; giftConfiguration: GiftWrappingConfiguration; + marketplaceConfiguration: MarketplaceConfigurationMessageParser; } export interface ICatalogAction @@ -38,6 +39,7 @@ export interface ICatalogAction clubGifts?: ClubGiftInfoParser; subscriptionInfo?: SubscriptionInfo; giftConfiguration?: GiftWrappingConfigurationParser; + marketplaceConfiguration?: MarketplaceConfigurationMessageParser; } } @@ -55,6 +57,7 @@ export class CatalogActions public static SET_SUBSCRIPTION_INFO: string = 'CA_SET_SUBSCRIPTION_INFO'; public static SET_GIFT_CONFIGURATION: string = 'CA_SET_GIFT_CONFIGURATION'; public static SET_CLUB_GIFTS: string = 'CA_SET_CLUB_GIFTS'; + public static SET_MARKETPLACE_CONFIGURATION: string = 'CA_SET_MARKETPLACE_CONFIGURATION'; } export const initialCatalog: ICatalogState = { @@ -69,7 +72,8 @@ export const initialCatalog: ICatalogState = { clubOffers: null, clubGifts: null, subscriptionInfo: new SubscriptionInfo(), - giftConfiguration: null + giftConfiguration: null, + marketplaceConfiguration: null } export const CatalogReducer: Reducer = (state, action) => @@ -169,6 +173,11 @@ export const CatalogReducer: Reducer = (state, ac return { ...state, clubGifts }; } + case CatalogActions.SET_MARKETPLACE_CONFIGURATION: { + let marketplaceConfiguration = (action.payload.marketplaceConfiguration || state.marketplaceConfiguration || null); + + return { ...state, marketplaceConfiguration } + } default: return state; } diff --git a/src/views/catalog/views/page/layout/CatalogLayout.scss b/src/views/catalog/views/page/layout/CatalogLayout.scss index d71c9f8a..3302b3b8 100644 --- a/src/views/catalog/views/page/layout/CatalogLayout.scss +++ b/src/views/catalog/views/page/layout/CatalogLayout.scss @@ -1,3 +1,5 @@ @import './frontpage4/CatalogLayoutFrontpage4View'; @import './info-loyalty/CatalogLayoutInfoLoyaltyView.scss'; @import './vip-buy/CatalogLayoutVipBuyView'; +@import './marketplace/marketplace-item/MarketplaceItemView'; +@import './marketplace/post-offer/MarketplacePostOfferView'; diff --git a/src/views/catalog/views/page/layout/GetCatalogLayout.tsx b/src/views/catalog/views/page/layout/GetCatalogLayout.tsx index 4685260e..d5411cf0 100644 --- a/src/views/catalog/views/page/layout/GetCatalogLayout.tsx +++ b/src/views/catalog/views/page/layout/GetCatalogLayout.tsx @@ -7,6 +7,8 @@ import { CatalogLayouGuildCustomFurniView } from './guild-custom-furni/CatalogLa import { CatalogLayouGuildForumView } from './guild-forum/CatalogLayoutGuildForumView'; import { CatalogLayouGuildFrontpageView } from './guild-frontpage/CatalogLayoutGuildFrontpageView'; import { CatalogLayoutInfoLoyaltyView } from './info-loyalty/CatalogLayoutInfoLoyaltyView'; +import { CatalogLayoutMarketplaceOwnItemsView } from './marketplace/own-items/CatalogLayoutMarketplaceOwnItemsView'; +import { CatalogLayoutMarketplacePublicItemsView } from './marketplace/public-items/CatalogLayoutMarketplacePublicItemsView'; import { CatalogLayoutPetView } from './pets/CatalogLayoutPetView'; import { CatalogLayoutPets2View } from './pets2/CatalogLayoutPets2View'; import { CatalogLayoutPets3View } from './pets3/CatalogLayoutPets3View'; @@ -43,9 +45,9 @@ export const GetCatalogLayout = (pageParser: CatalogPageMessageParser, roomPrevi case 'club_gifts': return ; case 'marketplace_own_items': - return null; + return ; case 'marketplace': - return null; + return ; case 'single_bundle': return ; case 'spaces_new': diff --git a/src/views/catalog/views/page/layout/marketplace/common/IMarketplaceSearchOptions.ts b/src/views/catalog/views/page/layout/marketplace/common/IMarketplaceSearchOptions.ts new file mode 100644 index 00000000..77734c3a --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/common/IMarketplaceSearchOptions.ts @@ -0,0 +1,6 @@ +export interface IMarketplaceSearchOptions { + query: string; + type: number; + minPrice: number; + maxPrice: number; +} diff --git a/src/views/catalog/views/page/layout/marketplace/common/MarketplaceConfirmType.ts b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceConfirmType.ts new file mode 100644 index 00000000..dbc17f2a --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceConfirmType.ts @@ -0,0 +1,6 @@ +export class MarketplaceConfirmType +{ + public static readonly PURCHASE_CONFIRM_TYPE_NORMAL = 1; + public static readonly PURCHASE_CONFIRM_TYPE_HIGHER = 2; + public static readonly PURCHASE_CONFIRM_TYPE_INVALID = 3; +} diff --git a/src/views/catalog/views/page/layout/marketplace/common/MarketplaceOfferData.ts b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceOfferData.ts new file mode 100644 index 00000000..ba1fa88b --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceOfferData.ts @@ -0,0 +1,128 @@ +import { IObjectData } from '@nitrots/nitro-renderer'; + +export class MarketplaceOfferData +{ + public static readonly TYPE_FLOOR: number = 1; + public static readonly TYPE_WALL: number = 2; + + private _offerId: number; + private _furniId: number; + private _furniType: number; + private _extraData: string; + private _stuffData: IObjectData; + private _price: number; + private _averagePrice: number; + private _imageCallback: number; + private _status: number; + private _timeLeftMinutes: number = -1; + private _offerCount: number; + private _image: string; + + constructor(offerId: number, furniId: number, furniType: number, extraData: string, stuffData: IObjectData, price: number, status: number, averagePrice: number, offerCount: number = -1) + { + this._offerId = offerId; + this._furniId = furniId; + this._furniType = furniType; + this._extraData = extraData; + this._stuffData = stuffData; + this._price = price; + this._status = status; + this._averagePrice = averagePrice; + this._offerCount = offerCount; + } + + public get offerId(): number + { + return this._offerId; + } + + public set offerId(offerId: number) + { + this._offerId = offerId; + } + + public get furniId(): number + { + return this._furniId; + } + + public get furniType(): number + { + return this._furniType; + } + + public get extraData(): string + { + return this._extraData; + } + + public get stuffData(): IObjectData + { + return this._stuffData; + } + + public get price(): number + { + return this._price; + } + + public set price(price: number) + { + this._price = price; + } + + public get averagePrice(): number + { + return this._averagePrice; + } + + public get image(): string + { + return this._image; + } + + public set image(image: string) + { + this._image = image; + } + + public get imageCallback(): number + { + return this._imageCallback; + } + + public set imageCallback(callback: number) + { + this._imageCallback = callback; + } + + public get status(): number + { + return this._status; + } + + public get timeLeftMinutes(): number + { + return this._timeLeftMinutes; + } + + public set timeLeftMinutes(minutes: number) + { + this._timeLeftMinutes = minutes; + } + + public get offerCount(): number + { + return this._offerCount; + } + + public set offerCount(count: number) + { + this._offerCount = count; + } + + public get isUniqueLimitedItem(): boolean + { + return (this.stuffData && (this.stuffData.uniqueSeries > 0)); + } +} diff --git a/src/views/catalog/views/page/layout/marketplace/common/MarketplaceOfferState.ts b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceOfferState.ts new file mode 100644 index 00000000..20c0e45b --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceOfferState.ts @@ -0,0 +1,7 @@ +export class MarketPlaceOfferState +{ + public static readonly ONGOING = 1; + public static readonly ONGOING_OWN = 1; + public static readonly SOLD = 2; + public static readonly EXPIRED = 3; +} diff --git a/src/views/catalog/views/page/layout/marketplace/common/MarketplaceSearchType.ts b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceSearchType.ts new file mode 100644 index 00000000..ac7a7019 --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/common/MarketplaceSearchType.ts @@ -0,0 +1,6 @@ +export class MarketplaceSearchType +{ + public static readonly BY_ACTIVITY = 1; + public static readonly BY_VALUE = 2; + public static readonly ADVANCED = 3; +} diff --git a/src/views/catalog/views/page/layout/marketplace/marketplace-item/MarketplaceItemView.scss b/src/views/catalog/views/page/layout/marketplace/marketplace-item/MarketplaceItemView.scss new file mode 100644 index 00000000..be2f5222 --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/marketplace-item/MarketplaceItemView.scss @@ -0,0 +1,4 @@ +.marketplace-item { + max-height: 70px; + height: 70px; +} diff --git a/src/views/catalog/views/page/layout/marketplace/marketplace-item/MarketplaceItemView.tsx b/src/views/catalog/views/page/layout/marketplace/marketplace-item/MarketplaceItemView.tsx new file mode 100644 index 00000000..db086b2a --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/marketplace-item/MarketplaceItemView.tsx @@ -0,0 +1,101 @@ +import { FC, useCallback } from 'react'; +import { GetRoomEngine, LocalizeText } from '../../../../../../../api'; +import { NitroCardGridItemView } from '../../../../../../../layout'; +import { MarketplaceOfferData } from '../common/MarketplaceOfferData'; +import { MarketPlaceOfferState } from '../common/MarketplaceOfferState'; + +export const OWN_OFFER = 1; +export const PUBLIC_OFFER = 2; + +export interface MarketplaceItemViewProps +{ + offerData: MarketplaceOfferData; + type?: number; + onClick(offerData: MarketplaceOfferData): void; +} + +export const MarketplaceItemView: FC = props => +{ + const { offerData = null, type = PUBLIC_OFFER, onClick = null } = props; + + const getImageUrlForOffer = useCallback( () => + { + if(!offerData) return ''; + + switch(offerData.furniType) + { + case MarketplaceOfferData.TYPE_FLOOR: + return GetRoomEngine().getFurnitureFloorIconUrl(offerData.furniId); + case MarketplaceOfferData.TYPE_WALL: + return GetRoomEngine().getFurnitureWallIconUrl(offerData.furniId, offerData.extraData); + } + + return ''; + }, [offerData]); + + const getMarketplaceOfferTitle = useCallback(() => + { + if(!offerData) return ''; + + const localizationKey = offerData.furniType === 2 ? 'wallItem.name.' + offerData.furniId: 'roomItem.name.' + offerData.furniId; + + return LocalizeText(localizationKey); + }, [offerData]); + + const getMarketplaceOfferDescription = useCallback( () => + { + if(!offerData) return ''; + + const localizationKey = offerData.furniType === 2 ? 'wallItem.desc.' + offerData.furniId : 'roomItem.desc.' + offerData.furniId; + + return LocalizeText(localizationKey); + }, [offerData]); + + const offerTime = useCallback( () => + { + if(!offerData) return ''; + + if(offerData.status === MarketPlaceOfferState.SOLD) return LocalizeText('catalog.marketplace.offer.sold'); + + if(offerData.timeLeftMinutes <= 0) return LocalizeText('catalog.marketplace.offer.expired'); + + const time = Math.max(1, offerData.timeLeftMinutes); + const hours = Math.floor(time / 60); + const minutes = time - (hours * 60); + + let text = minutes + ' ' + LocalizeText('catalog.marketplace.offer.minutes'); + if(hours > 0) + { + text = hours + ' ' + LocalizeText('catalog.marketplace.offer.hours') + ' ' + text; + } + + return LocalizeText('catalog.marketplace.offer.time_left', ['time'], [text] ); + }, [offerData]); + + return ( + + +
+
{getMarketplaceOfferTitle()}
+
{getMarketplaceOfferDescription()}
+ + { type === OWN_OFFER && <> +
{ LocalizeText('catalog.marketplace.offer.price_own_item', ['price'], [offerData.price.toString()])}
+
{ offerTime() }
+ + } + { type === PUBLIC_OFFER && <> +
{ LocalizeText('catalog.marketplace.offer.price_public_item', ['price', 'average'], [offerData.price.toString(), offerData.averagePrice.toString() ]) }
+
{ LocalizeText('catalog.marketplace.offer_count', ['count'], [offerData.offerCount.toString()]) }
+ + } +
+
+ { (type === OWN_OFFER && offerData.status !== MarketPlaceOfferState.SOLD) && } + { type === PUBLIC_OFFER && <> + + + } +
+
) +} diff --git a/src/views/catalog/views/page/layout/marketplace/own-items/CatalogLayoutMarketplaceOwnItemsView.tsx b/src/views/catalog/views/page/layout/marketplace/own-items/CatalogLayoutMarketplaceOwnItemsView.tsx new file mode 100644 index 00000000..d76ae6f3 --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/own-items/CatalogLayoutMarketplaceOwnItemsView.tsx @@ -0,0 +1,120 @@ +import { CancelMarketplaceOfferMessageComposer, GetMarketplaceOwnOffersMessageComposer, MarketplaceCancelOfferResultEvent, MarketplaceOwnOffersEvent, RedeemMarketplaceOfferCreditsMessageComposer } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { LocalizeText } from '../../../../../../../api'; +import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../../hooks'; +import { NitroCardGridView } from '../../../../../../../layout'; +import { NitroLayoutBase } from '../../../../../../../layout/base'; +import { NotificationAlertType } from '../../../../../../notification-center/common/NotificationAlertType'; +import { NotificationUtilities } from '../../../../../../notification-center/common/NotificationUtilities'; +import { CatalogLayoutProps } from '../../CatalogLayout.types'; +import { MarketplaceOfferData } from '../common/MarketplaceOfferData'; +import { MarketPlaceOfferState } from '../common/MarketplaceOfferState'; +import { MarketplaceItemView, OWN_OFFER } from '../marketplace-item/MarketplaceItemView'; + +export interface CatalogLayoutMarketplaceOwnItemsViewProps extends CatalogLayoutProps +{ + +} + +export const CatalogLayoutMarketplaceOwnItemsView: FC = props => +{ + const [ creditsWaiting, setCreditsWaiting ] = useState(0); + const [ offers, setOffers ] = useState(new Map()); + + UseMountEffect(() => + { + SendMessageHook(new GetMarketplaceOwnOffersMessageComposer()); + }); + + const onMarketPlaceOwnOffersEvent = useCallback((event: MarketplaceOwnOffersEvent) => + { + const parser = event.getParser(); + + if(!parser) return; + + const latestOffers = new Map(); + parser.offers.forEach(entry => + { + const offerEntry = new MarketplaceOfferData(entry.offerId, entry.furniId, entry.furniType, entry.extraData, entry.stuffData, entry.price, entry.status, entry.averagePrice, entry.offerCount); + offerEntry.timeLeftMinutes = entry.timeLeftMinutes; + latestOffers.set(entry.offerId, offerEntry); + }); + + BatchUpdates(() => + { + setCreditsWaiting(parser.creditsWaiting); + setOffers(latestOffers); + }); + }, []); + + const onMarketplaceCancelOfferResultEvent = useCallback((event:MarketplaceCancelOfferResultEvent) => + { + const parser = event.getParser(); + + if(!parser) return; + + if(!parser.success) + { + NotificationUtilities.simpleAlert(LocalizeText('catalog.marketplace.cancel_failed'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.marketplace.operation_failed.topic')); + return; + } + + setOffers( prev => + { + const newVal = new Map(prev); + newVal.delete(parser.offerId); + return newVal; + }); + }, []); + + CreateMessageHook(MarketplaceOwnOffersEvent, onMarketPlaceOwnOffersEvent); + CreateMessageHook(MarketplaceCancelOfferResultEvent, onMarketplaceCancelOfferResultEvent); + + const redeemSoldOffers = useCallback(() => + { + setOffers(prev => + { + const newVal = new Map(prev); + + const idsToDelete = []; + + for(const offer of newVal.values()) + { + if(offer.status === MarketPlaceOfferState.SOLD) + { + idsToDelete.push(offer.offerId); + } + } + + for(const offerId of idsToDelete) + { + newVal.delete(offerId); + } + return newVal; + }) + + SendMessageHook(new RedeemMarketplaceOfferCreditsMessageComposer()); + }, []); + + const takeItemBack = useCallback( (offerData: MarketplaceOfferData) => + { + SendMessageHook(new CancelMarketplaceOfferMessageComposer(offerData.offerId)); + }, []); + + return ( + <> + { (creditsWaiting <= 0) && {LocalizeText('catalog.marketplace.redeem.no_sold_items')}} + + { (creditsWaiting > 0) && {LocalizeText('catalog.marketplace.redeem.get_credits', ['count', 'credits'], [Array.from(offers.values()).filter(value => value.status === MarketPlaceOfferState.SOLD).length.toString(), creditsWaiting.toString()])}} + + + +
{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}
+ + { + Array.from(offers.values()).map( (entry, index) => ) + } + + + ); +} diff --git a/src/views/catalog/views/page/layout/marketplace/post-offer/MarketplacePostOfferView.scss b/src/views/catalog/views/page/layout/marketplace/post-offer/MarketplacePostOfferView.scss new file mode 100644 index 00000000..46306202 --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/post-offer/MarketplacePostOfferView.scss @@ -0,0 +1,12 @@ +.nitro-marketplace-post-offer { + width: 300px; + height: 365px; + + .item-image-container { + width: 75px; + height: 75px; + background-repeat: no-repeat; + background-position: center; + overflow: hidden; + } +} diff --git a/src/views/catalog/views/page/layout/marketplace/post-offer/MarketplacePostOfferView.tsx b/src/views/catalog/views/page/layout/marketplace/post-offer/MarketplacePostOfferView.tsx new file mode 100644 index 00000000..80d9e63c --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/post-offer/MarketplacePostOfferView.tsx @@ -0,0 +1,117 @@ +import { ImageResult, MakeOfferMessageComposer, Vector3d } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { GetRoomEngine, LocalizeText } from '../../../../../../../api'; +import { CatalogPostMarketplaceOfferEvent } from '../../../../../../../events/catalog/CatalogPostMarketplaceOfferEvent'; +import { SendMessageHook, useUiEvent } from '../../../../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../../../../layout'; +import { FurnitureItem } from '../../../../../../inventory/common/FurnitureItem'; +import { NotificationUtilities } from '../../../../../../notification-center/common/NotificationUtilities'; +import { useCatalogContext } from '../../../../../context/CatalogContext'; + +export const MarketplacePostOfferView : FC<{}> = props => +{ + const [ item, setItem ] = useState(null); + const [ askingPrice, setAskingPrice ] = useState(0); + const { catalogState = null, dispatchCatalogState = null } = useCatalogContext(); + + const close = useCallback(() => + { + setItem(null); + setAskingPrice(0); + }, []); + + const onCatalogPostMarketplaceOfferEvent = useCallback( (event: CatalogPostMarketplaceOfferEvent) => + { + setItem(event.item); + }, []); + + useUiEvent(CatalogPostMarketplaceOfferEvent.POST_MARKETPLACE, onCatalogPostMarketplaceOfferEvent); + + const getItemImage = useCallback( () => + { + if(!item) return ''; + + let object: ImageResult; + + if(!item.isWallItem) + { + object = GetRoomEngine().getFurnitureFloorImage(item.type, new Vector3d(90,0,0), 64, this, 4293848814, item.extra.toString()); + } + else + { + object = GetRoomEngine().getFurnitureWallImage(item.type, new Vector3d(90,0,0), 64, this, 4293848814, item.extra.toString()); + } + + if(object) + { + const image = object.getImage(); + + if(image) return image.src; + } + return ''; + }, [item]); + + const getFurniTitle = useCallback( () => + { + if(!item) return ''; + + const localizationKey = item.isWallItem ? 'wallItem.name.' + item.type : 'roomItem.name.' + item.type; + + return LocalizeText(localizationKey); + }, [item]); + + const getFurniDescription = useCallback( () => + { + if(!item) return ''; + + const localizationKey = item.isWallItem ? 'wallItem.desc.' + item.type : 'roomItem.desc.' + item.type; + + return LocalizeText(localizationKey); + }, [item]); + + const postItem = useCallback( () => + { + if(isNaN(askingPrice) || askingPrice <= 0 || !item) return; + + NotificationUtilities.confirm(LocalizeText('inventory.marketplace.confirm_offer.info', ['furniname', 'price'], [getFurniTitle(), askingPrice.toString()]), () => + { + SendMessageHook(new MakeOfferMessageComposer(askingPrice, item.isWallItem ? 2 : 1, item.id)); + setItem(null); + }, + () => { setItem(null)}, null, null, LocalizeText('inventory.marketplace.confirm_offer.title')); + }, [askingPrice, getFurniTitle, item]); + + return ( item && + + + + +
+
+
{getFurniTitle()}
+
{getFurniDescription()}
+
+ +
+ { LocalizeText('inventory.marketplace.make_offer.expiration_info', ['time'], [catalogState.marketplaceConfiguration.offerTime.toString()]) } +
+
+
{ LocalizeText('inventory.marketplace.make_offer.price_request') }
+ setAskingPrice(event.target.valueAsNumber) } /> +
+
+ { (askingPrice < catalogState.marketplaceConfiguration.minimumPrice || isNaN(askingPrice)) && LocalizeText('inventory.marketplace.make_offer.min_price', ['minprice'], [catalogState.marketplaceConfiguration.minimumPrice.toString()]) } + { askingPrice > catalogState.marketplaceConfiguration.maximumPrice && !isNaN(askingPrice) && + LocalizeText('inventory.marketplace.make_offer.max_price', ['maxprice'], [catalogState.marketplaceConfiguration.maximumPrice.toString()]) + } + { !(askingPrice < catalogState.marketplaceConfiguration.minimumPrice || askingPrice > catalogState.marketplaceConfiguration.maximumPrice || isNaN(askingPrice)) && LocalizeText('inventory.marketplace.make_offer.final_price', ['commission', 'finalprice'], [catalogState.marketplaceConfiguration.commission.toString(), (askingPrice + catalogState.marketplaceConfiguration.commission).toString()])} +
+
+ +
+ + + ) +} diff --git a/src/views/catalog/views/page/layout/marketplace/public-items/CatalogLayoutMarketplacePublicItemsView.tsx b/src/views/catalog/views/page/layout/marketplace/public-items/CatalogLayoutMarketplacePublicItemsView.tsx new file mode 100644 index 00000000..69af4473 --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/public-items/CatalogLayoutMarketplacePublicItemsView.tsx @@ -0,0 +1,166 @@ +import { BuyMarketplaceOfferMessageComposer, GetMarketplaceOffersMessageComposer, MarketplaceBuyOfferResultEvent, MarketPlaceOffersEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useMemo, useState } from 'react'; +import { LocalizeText } from '../../../../../../../api'; +import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../../hooks'; +import { NitroCardGridView } from '../../../../../../../layout'; +import { NotificationAlertType } from '../../../../../../notification-center/common/NotificationAlertType'; +import { NotificationUtilities } from '../../../../../../notification-center/common/NotificationUtilities'; +import { GetCurrencyAmount } from '../../../../../../purse/common/CurrencyHelper'; +import { CatalogLayoutProps } from '../../CatalogLayout.types'; +import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions'; +import { MarketplaceOfferData } from '../common/MarketplaceOfferData'; +import { MarketplaceSearchType } from '../common/MarketplaceSearchType'; +import { MarketplaceItemView, PUBLIC_OFFER } from '../marketplace-item/MarketplaceItemView'; +import { SearchFormView } from './SearchFormView'; + +const SORT_TYPES_VALUE = [1, 2]; +const SORT_TYPES_ACTIVITY = [3, 4, 5, 6]; +const SORT_TYPES_ADVANCED = [1, 2, 3, 4, 5, 6]; +export interface CatalogLayoutMarketplacePublicItemsViewProps extends CatalogLayoutProps +{ + +} + +export const CatalogLayoutMarketplacePublicItemsView: FC = props => +{ + const [ searchType, setSearchType ] = useState(MarketplaceSearchType.BY_ACTIVITY); + const [ totalItemsFound, setTotalItemsFound ] = useState(0); + const [ offers, setOffers ] = useState(new Map()); + const [ lastSearch, setLastSearch ] = useState({ minPrice: -1, maxPrice: -1, query: '', type: 3 }); + + const requestOffers = useCallback((options: IMarketplaceSearchOptions) => + { + setLastSearch(options); + SendMessageHook(new GetMarketplaceOffersMessageComposer(options.minPrice, options.maxPrice, options.query, options.type)) + }, []); + + const getSortTypes = useMemo( () => + { + switch(searchType) + { + case MarketplaceSearchType.BY_ACTIVITY: + return SORT_TYPES_ACTIVITY; + case MarketplaceSearchType.BY_VALUE: + return SORT_TYPES_VALUE; + case MarketplaceSearchType.ADVANCED: + return SORT_TYPES_ADVANCED; + } + return []; + }, [searchType]); + + const purchaseItem = useCallback((offerData: MarketplaceOfferData) => + { + if(offerData.price > GetCurrencyAmount(-1)) + { + NotificationUtilities.simpleAlert(LocalizeText('catalog.alert.notenough.credits.description'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.alert.notenough.title')); + return; + } + const offerId = offerData.offerId; + NotificationUtilities.confirm(LocalizeText('catalog.marketplace.confirm_header'), () => + { + SendMessageHook(new BuyMarketplaceOfferMessageComposer(offerId)); + }, + null, null, null, LocalizeText('catalog.marketplace.confirm_title')); + },[]); + + const onMarketPlaceOffersEvent = useCallback( (event: MarketPlaceOffersEvent) => + { + const parser = event.getParser(); + + if(!parser) return; + + const latestOffers = new Map(); + parser.offers.forEach(entry => + { + const offerEntry = new MarketplaceOfferData(entry.offerId, entry.furniId, entry.furniType, entry.extraData, entry.stuffData, entry.price, entry.status, entry.averagePrice, entry.offerCount); + offerEntry.timeLeftMinutes = entry.timeLeftMinutes; + latestOffers.set(entry.offerId, offerEntry); + }); + + BatchUpdates(() => + { + setTotalItemsFound(parser.totalItemsFound); + setOffers(latestOffers); + }); + + }, []); + + const onMarketplaceBuyOfferResultEvent = useCallback( (event: MarketplaceBuyOfferResultEvent) => + { + const parser = event.getParser(); + + if(!parser) return; + + switch(parser.result) + { + case 1: + requestOffers(lastSearch); + break; + case 2: + setOffers(prev => + { + const newVal = new Map(prev); + newVal.delete(parser.requestedOfferId); + return newVal; + }); + NotificationUtilities.simpleAlert(LocalizeText('catalog.marketplace.not_available_header'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.marketplace.not_available_title')); + break; + case 3: + // our shit was updated + // todo: some dialogue modal + setOffers( prev => + { + const newVal = new Map(prev); + + const item = newVal.get(parser.requestedOfferId); + if(item) + { + item.offerId = parser.offerId; + item.price = parser.newPrice; + item.offerCount--; + newVal.set(item.offerId, item); + } + + newVal.delete(parser.requestedOfferId); + return newVal; + }); + + NotificationUtilities.confirm(LocalizeText('catalog.marketplace.confirm_higher_header') + + '\n' + LocalizeText('catalog.marketplace.confirm_price', ['price'], [parser.newPrice.toString()]), () => + { + SendMessageHook(new BuyMarketplaceOfferMessageComposer(parser.offerId)); + }, + null, null, null, LocalizeText('catalog.marketplace.confirm_higher_title')); + break; + case 4: + NotificationUtilities.simpleAlert(LocalizeText('catalog.alert.notenough.credits.description'), NotificationAlertType.DEFAULT, null, null, LocalizeText('catalog.alert.notenough.title')); + break; + } + }, [lastSearch, requestOffers]); + + CreateMessageHook(MarketPlaceOffersEvent, onMarketPlaceOffersEvent); + CreateMessageHook(MarketplaceBuyOfferResultEvent, onMarketplaceBuyOfferResultEvent); + + return (<> +
+ + + +
+ + + +
{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}
+ + { + Array.from(offers.values()).map( (entry, index) => ) + } + + ); +} diff --git a/src/views/catalog/views/page/layout/marketplace/public-items/SearchFormView.tsx b/src/views/catalog/views/page/layout/marketplace/public-items/SearchFormView.tsx new file mode 100644 index 00000000..3fe0d3e6 --- /dev/null +++ b/src/views/catalog/views/page/layout/marketplace/public-items/SearchFormView.tsx @@ -0,0 +1,70 @@ +import { FC, useCallback, useEffect, useState } from 'react'; +import { LocalizeText } from '../../../../../../../api'; +import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions'; +import { MarketplaceSearchType } from '../common/MarketplaceSearchType'; + +export interface SearchFormViewProps +{ + searchType: number; + sortTypes: number[]; + onSearch(options: IMarketplaceSearchOptions): void; +} + +export const SearchFormView: FC = props => +{ + const { searchType = null, sortTypes = null, onSearch = null } = props; + const [ sortType, setSortType ] = useState(sortTypes ? sortTypes[0] : 3); // first item of SORT_TYPES_ACTIVITY + const [ searchQuery, setSearchQuery ] = useState(''); + const [ min, setMin ] = useState(0); + const [ max, setMax ] = useState(0); + + const onSortTypeChange = useCallback((sortType: number) => + { + setSortType(sortType); + if(searchType === MarketplaceSearchType.BY_ACTIVITY || searchType === MarketplaceSearchType.BY_VALUE) + onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType }); + }, [onSearch, searchType]); + + const onClickSearch = useCallback(() => + { + const minPrice = min > 0 ? min : -1; + const maxPrice = max > 0 ? max : -1; + + onSearch({ minPrice: minPrice, maxPrice: maxPrice, type: sortType, query: searchQuery }) + }, [max, min, onSearch, searchQuery, sortType]); + + useEffect( () => + { + if(!sortTypes || !sortTypes.length) return; + + const sortType = sortTypes[0]; + setSortType(sortType); + + if(searchType === MarketplaceSearchType.BY_ACTIVITY || MarketplaceSearchType.BY_VALUE === searchType) + onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType }); + }, [onSearch, searchType, sortTypes]); + + return (<> +
+
{ LocalizeText('catalog.marketplace.sort_order') }
+ +
+ { searchType === MarketplaceSearchType.ADVANCED && <> +
+
{ LocalizeText('catalog.marketplace.search_name') }
+ setSearchQuery(event.target.value)}/> +
+ +
+
{ LocalizeText('catalog.marketplace.search_price') }
+ setMin(event.target.valueAsNumber) } /> + setMax(event.target.valueAsNumber) } /> +
+ + + + } + ); +} diff --git a/src/views/inventory/common/FurniCategory.ts b/src/views/inventory/common/FurniCategory.ts index c4175ab1..65289472 100644 --- a/src/views/inventory/common/FurniCategory.ts +++ b/src/views/inventory/common/FurniCategory.ts @@ -1,26 +1,26 @@ export class FurniCategory { public static DEFAULT: number = 1; - public static _Str_3639: number = 2; - public static _Str_3683: number = 3; - public static _Str_3432: number = 4; - public static _Str_12351: number = 5; - public static _Str_5186: number = 6; - public static _Str_21911: number = 7; - public static _Str_9125: number = 8; - public static _Str_5922: number = 9; - public static _Str_18231: number = 10; - public static _Str_4255: number = 11; - public static _Str_19933: number = 12; - public static _Str_7696: number = 13; - public static _Str_7297: number = 14; - public static _Str_7954: number = 15; - public static _Str_6096: number = 16; - public static _Str_12454: number = 17; - public static _Str_19144: number = 18; + 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 _Str_6915: number = 20; - public static _Str_8726: number = 21; - public static _Str_9449: number = 22; - public static _Str_12534: number = 23; -} \ No newline at end of file + 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; +} diff --git a/src/views/inventory/common/FurnitureUtilities.ts b/src/views/inventory/common/FurnitureUtilities.ts index 96dc07b3..46ba4b7d 100644 --- a/src/views/inventory/common/FurnitureUtilities.ts +++ b/src/views/inventory/common/FurnitureUtilities.ts @@ -1,6 +1,7 @@ import { FurnitureListItemParser, FurniturePlacePaintComposer, IObjectData, RoomObjectCategory, RoomObjectPlacementSource } from '@nitrots/nitro-renderer'; import { GetRoomEngine } from '../../../api'; import { InventoryEvent } from '../../../events'; +import { CatalogPostMarketplaceOfferEvent } from '../../../events/catalog/CatalogPostMarketplaceOfferEvent'; import { dispatchUiEvent } from '../../../hooks/events/ui/ui-event'; import { SendMessageHook } from '../../../hooks/messages/message-event'; import { FurniCategory } from './FurniCategory'; @@ -18,7 +19,7 @@ export function attemptItemPlacement(groupItem: GroupItem, flag: boolean = false if(!item) return false; - if((item.category === FurniCategory._Str_3683) || (item.category === FurniCategory._Str_3639) || (item.category === FurniCategory._Str_3432)) + if((item.category === FurniCategory.FLOOR) || (item.category === FurniCategory.WALL_PAPER) || (item.category === FurniCategory.LANDSCAPE)) { if(flag) return false; @@ -36,7 +37,7 @@ export function attemptItemPlacement(groupItem: GroupItem, flag: boolean = false if(item.isWallItem) category = RoomObjectCategory.WALL; else category = RoomObjectCategory.FLOOR; - if((item.category === FurniCategory._Str_5186)) // or external image from furnidata + if((item.category === FurniCategory.POSTER)) // or external image from furnidata { isMoving = GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.INVENTORY, item.id, category, item.type, item.stuffData.getLegacyString()); } @@ -55,6 +56,17 @@ export function attemptItemPlacement(groupItem: GroupItem, flag: boolean = false return true; } +export function attemptPlaceMarketplaceOffer(groupItem: GroupItem): boolean +{ + const item = groupItem.getLastItem(); + + if(!item) return false; + + if(!item.sellable) return false; + + dispatchUiEvent(new CatalogPostMarketplaceOfferEvent(item)); +} + function cancelRoomObjectPlacement(): void { if(getPlacingItemId() === -1) return; @@ -108,7 +120,7 @@ function getAllItemIds(groupItems: GroupItem[]): number[] { let totalCount = groupItem.getTotalCount(); - if(groupItem.category === FurniCategory._Str_12351) totalCount = 1; + if(groupItem.category === FurniCategory.POST_IT) totalCount = 1; let i = 0; @@ -240,7 +252,7 @@ function addGroupableFurnitureItem(set: GroupItem[], item: FurnitureItem, unseen { if((groupItem.type === item.type) && (groupItem.isWallItem === item.isWallItem) && groupItem.isGroupable) { - if(item.category === FurniCategory._Str_5186) + if(item.category === FurniCategory.POSTER) { if(groupItem.stuffData.getLegacyString() === item.stuffData.getLegacyString()) { @@ -250,7 +262,7 @@ function addGroupableFurnitureItem(set: GroupItem[], item: FurnitureItem, unseen } } - else if(item.category === FurniCategory._Str_12454) + else if(item.category === FurniCategory.GUILD_FURNI) { if(item.stuffData.compare(groupItem.stuffData)) { @@ -309,7 +321,7 @@ export function createGroupItem(type: number, category: number, stuffData: IObje { // const iconImage: HTMLImageElement = null; - if(category === FurniCategory._Str_3639) + if(category === FurniCategory.WALL_PAPER) { // const icon = this._windowManager.assets.getAssetByName("inventory_furni_icon_wallpaper"); // if (icon != null) @@ -318,7 +330,7 @@ export function createGroupItem(type: number, category: number, stuffData: IObje // } } - else if(category === FurniCategory._Str_3683) + else if(category === FurniCategory.FLOOR) { // const icon = this._windowManager.assets.getAssetByName("inventory_furni_icon_floor"); // if (icon != null) @@ -327,7 +339,7 @@ export function createGroupItem(type: number, category: number, stuffData: IObje // } } - else if(category === FurniCategory._Str_3432) + else if(category === FurniCategory.LANDSCAPE) { // const icon = this._windowManager.assets.getAssetByName("inventory_furni_icon_landscape"); // if (icon != null) diff --git a/src/views/inventory/common/GroupItem.ts b/src/views/inventory/common/GroupItem.ts index 8eb40644..2b3598a4 100644 --- a/src/views/inventory/common/GroupItem.ts +++ b/src/views/inventory/common/GroupItem.ts @@ -199,7 +199,7 @@ export class GroupItem public getTotalCount(): number { - if(this._category === FurniCategory._Str_12351) + if(this._category === FurniCategory.POST_IT) { let count = 0; let index = 0; @@ -221,7 +221,7 @@ export class GroupItem public getUnlockedCount(): number { - if(this.category === FurniCategory._Str_12351) return this.getTotalCount(); + if(this.category === FurniCategory.POST_IT) return this.getTotalCount(); let count = 0; let index = 0; @@ -318,10 +318,10 @@ export class GroupItem switch(this._category) { - case FurniCategory._Str_5186: + case FurniCategory.POSTER: key = (('poster_' + k.stuffData.getLegacyString()) + '_name'); break; - case FurniCategory._Str_9125: + case FurniCategory.TRAX_SONG: this._name = 'SONG_NAME'; return; default: @@ -442,6 +442,13 @@ export class GroupItem return (item ? item.isGroupable : false); } + public get isSellable(): boolean + { + const item = this.getItemByIndex(0); + + return (item ? item.sellable : false); + } + public get items(): FurnitureItem[] { return this._items; diff --git a/src/views/inventory/common/TradingUtilities.ts b/src/views/inventory/common/TradingUtilities.ts index 9e6e9204..dbf12e4b 100644 --- a/src/views/inventory/common/TradingUtilities.ts +++ b/src/views/inventory/common/TradingUtilities.ts @@ -30,12 +30,12 @@ export function parseTradeItems(items: ItemDataStructure[], _arg_2: AdvancedMap< name = ('itemid' + item.itemId); } - if(item.category === FurniCategory._Str_5186) + if(item.category === FurniCategory.POSTER) { name = (item.itemId + 'poster' + item.stuffData.getLegacyString()); } - else if(item.category === FurniCategory._Str_12454) + else if(item.category === FurniCategory.GUILD_FURNI) { name = ''; } diff --git a/src/views/inventory/views/furniture/InventoryFurnitureView.tsx b/src/views/inventory/views/furniture/InventoryFurnitureView.tsx index bdddaf0f..3edc27fb 100644 --- a/src/views/inventory/views/furniture/InventoryFurnitureView.tsx +++ b/src/views/inventory/views/furniture/InventoryFurnitureView.tsx @@ -11,7 +11,7 @@ import { LimitedEditionCompactPlateView } from '../../../shared/limited-edition/ import { RarityLevelView } from '../../../shared/rarity-level/RarityLevelView'; import { RoomPreviewerView } from '../../../shared/room-previewer/RoomPreviewerView'; import { FurniCategory } from '../../common/FurniCategory'; -import { attemptItemPlacement } from '../../common/FurnitureUtilities'; +import { attemptItemPlacement, attemptPlaceMarketplaceOffer } from '../../common/FurnitureUtilities'; import { GroupItem } from '../../common/GroupItem'; import { useInventoryContext } from '../../context/InventoryContext'; import { InventoryFurnitureActions } from '../../reducers/InventoryFurnitureReducer'; @@ -76,15 +76,15 @@ export const InventoryFurnitureView: FC = props => roomPreviewer.updateObjectRoom(floorType, wallType, landscapeType); roomPreviewer.updateRoomWallsAndFloorVisibility(true, true); - if((furnitureItem.category === FurniCategory._Str_3639) || (furnitureItem.category === FurniCategory._Str_3683) || (furnitureItem.category === FurniCategory._Str_3432)) + if((furnitureItem.category === FurniCategory.WALL_PAPER) || (furnitureItem.category === FurniCategory.FLOOR) || (furnitureItem.category === FurniCategory.LANDSCAPE)) { - floorType = ((furnitureItem.category === FurniCategory._Str_3683) ? groupItem.stuffData.getLegacyString() : floorType); - wallType = ((furnitureItem.category === FurniCategory._Str_3639) ? groupItem.stuffData.getLegacyString() : wallType); - landscapeType = ((furnitureItem.category === FurniCategory._Str_3432) ? groupItem.stuffData.getLegacyString() : landscapeType); + floorType = ((furnitureItem.category === FurniCategory.FLOOR) ? groupItem.stuffData.getLegacyString() : floorType); + wallType = ((furnitureItem.category === FurniCategory.WALL_PAPER) ? groupItem.stuffData.getLegacyString() : wallType); + landscapeType = ((furnitureItem.category === FurniCategory.LANDSCAPE) ? groupItem.stuffData.getLegacyString() : landscapeType); roomPreviewer.updateObjectRoom(floorType, wallType, landscapeType); - if(furnitureItem.category === FurniCategory._Str_3432) + if(furnitureItem.category === FurniCategory.LANDSCAPE) { const data = GetSessionDataManager().getWallItemDataByName('noob_window_double'); @@ -127,10 +127,15 @@ export const InventoryFurnitureView: FC = props => { groupItem && { groupItem.name } - { !!roomSession && + { roomSession && attemptItemPlacement(groupItem) }> { LocalizeText('inventory.furni.placetoroom') } } + { (groupItem && groupItem.isSellable) && + attemptPlaceMarketplaceOffer(groupItem) }> + { LocalizeText('inventory.marketplace.sell') } + + } } diff --git a/src/views/inventory/views/trade/InventoryTradeView.tsx b/src/views/inventory/views/trade/InventoryTradeView.tsx index 0d7f9797..8ab902e1 100644 --- a/src/views/inventory/views/trade/InventoryTradeView.tsx +++ b/src/views/inventory/views/trade/InventoryTradeView.tsx @@ -8,6 +8,8 @@ import { NitroCardGridItemView } from '../../../../layout/card/grid/item/NitroCa import { NitroCardGridView } from '../../../../layout/card/grid/NitroCardGridView'; import { NitroLayoutGridColumn } from '../../../../layout/grid/column/NitroLayoutGridColumn'; import { NitroLayoutGrid } from '../../../../layout/grid/NitroLayoutGrid'; +import { NotificationAlertType } from '../../../notification-center/common/NotificationAlertType'; +import { NotificationUtilities } from '../../../notification-center/common/NotificationUtilities'; import { FurniCategory } from '../../common/FurniCategory'; import { GroupItem } from '../../common/GroupItem'; import { IFurnitureItem } from '../../common/IFurnitureItem'; @@ -41,13 +43,13 @@ export const InventoryTradeView: FC = props => let type = spriteId.toString(); - if(category === FurniCategory._Str_5186) + if(category === FurniCategory.POSTER) { type = ((type + 'poster') + stuffData.getLegacyString()); } else { - if(category === FurniCategory._Str_12454) + if(category === FurniCategory.GUILD_FURNI) { type = _Str_16998(spriteId, stuffData); } @@ -113,7 +115,7 @@ export const InventoryTradeView: FC = props => } else { - //this._notificationService.alert('${trading.items.too_many_items.desc}', '${trading.items.too_many_items.title}'); + NotificationUtilities.simpleAlert(LocalizeText('trading.items.too_many_items.desc'), NotificationAlertType.DEFAULT, null, null, LocalizeText('trading.items.too_many_items.title')); } }, [ groupItem, tradeData, canTradeItem ]); diff --git a/src/views/mod-tools/ModToolsMessageHandler.tsx b/src/views/mod-tools/ModToolsMessageHandler.tsx index 3357aef8..3863bdb7 100644 --- a/src/views/mod-tools/ModToolsMessageHandler.tsx +++ b/src/views/mod-tools/ModToolsMessageHandler.tsx @@ -1,13 +1,14 @@ import { CfhSanctionMessageEvent, CfhTopicsInitEvent, IssueDeletedMessageEvent, IssueInfoMessageEvent, IssuePickFailedMessageEvent, ModeratorActionResultMessageEvent, ModeratorInitMessageEvent, ModeratorToolPreferencesEvent, RoomEngineEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback } from 'react'; import { MODTOOLS_NEW_TICKET, PlaySound } from '../../api/utils/PlaySound'; -import { NotificationAlertEvent } from '../../events'; import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent'; import { ModToolsOpenRoomChatlogEvent } from '../../events/mod-tools/ModToolsOpenRoomChatlogEvent'; import { ModToolsOpenRoomInfoEvent } from '../../events/mod-tools/ModToolsOpenRoomInfoEvent'; import { ModToolsOpenUserChatlogEvent } from '../../events/mod-tools/ModToolsOpenUserChatlogEvent'; import { ModToolsOpenUserInfoEvent } from '../../events/mod-tools/ModToolsOpenUserInfoEvent'; -import { CreateMessageHook, dispatchUiEvent, useRoomEngineEvent, useUiEvent } from '../../hooks'; +import { CreateMessageHook, useRoomEngineEvent, useUiEvent } from '../../hooks'; +import { NotificationAlertType } from '../notification-center/common/NotificationAlertType'; +import { NotificationUtilities } from '../notification-center/common/NotificationUtilities'; import { SetCfhCategories } from './common/GetCFHCategories'; import { useModToolsContext } from './context/ModToolsContext'; import { ModToolsActions } from './reducers/ModToolsReducer'; @@ -38,8 +39,7 @@ export const ModToolsMessageHandler: FC<{}> = props => tickets: data.issues } }); - - console.log(parser); + }, [dispatchModToolsState]); const onIssueInfoMessageEvent = useCallback((event: IssueInfoMessageEvent) => @@ -68,7 +68,6 @@ export const ModToolsMessageHandler: FC<{}> = props => } }); - console.log(parser); }, [dispatchModToolsState, tickets]); const onModeratorToolPreferencesEvent = useCallback((event: ModeratorToolPreferencesEvent) => @@ -77,7 +76,6 @@ export const ModToolsMessageHandler: FC<{}> = props => if(!parser) return; - console.log(parser); }, []); const onIssuePickFailedMessageEvent = useCallback((event: IssuePickFailedMessageEvent) => @@ -86,8 +84,7 @@ export const ModToolsMessageHandler: FC<{}> = props => if(!parser) return; - // todo: let user know it failed - dispatchUiEvent(new NotificationAlertEvent(['Failed to pick issue'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('Failed to pick issue', NotificationAlertType.DEFAULT, null, null, 'Error') }, []); const onIssueDeletedMessageEvent = useCallback((event: IssueDeletedMessageEvent) => @@ -119,11 +116,11 @@ export const ModToolsMessageHandler: FC<{}> = props => if(parser.success) { - dispatchUiEvent(new NotificationAlertEvent(['Moderation action was successfull'], null, null, null, 'Success', null)); + NotificationUtilities.simpleAlert('Moderation action was successfull', NotificationAlertType.MODERATION, null, null, 'Success'); } else { - dispatchUiEvent(new NotificationAlertEvent(['There was a problem applying that moderation action'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('There was a problem applying tht moderation action', NotificationAlertType.MODERATION, null, null, 'Error'); } }, []); @@ -144,7 +141,6 @@ export const ModToolsMessageHandler: FC<{}> = props => SetCfhCategories(categories); - console.log(parser); }, [dispatchModToolsState]); const onCfhSanctionMessageEvent = useCallback((event: CfhSanctionMessageEvent) => @@ -153,7 +149,7 @@ export const ModToolsMessageHandler: FC<{}> = props => if(!parser) return; - console.log(parser); + // todo: update sanction data }, []); CreateMessageHook(ModeratorInitMessageEvent, onModeratorInitMessageEvent); diff --git a/src/views/mod-tools/views/user/user-mod-action/ModToolsUserModActionView.tsx b/src/views/mod-tools/views/user/user-mod-action/ModToolsUserModActionView.tsx index 09454bdc..88bcbac1 100644 --- a/src/views/mod-tools/views/user/user-mod-action/ModToolsUserModActionView.tsx +++ b/src/views/mod-tools/views/user/user-mod-action/ModToolsUserModActionView.tsx @@ -1,9 +1,10 @@ import { CallForHelpTopicData, DefaultSanctionMessageComposer, ModAlertMessageComposer, ModBanMessageComposer, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModTradingLockMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useMemo, useState } from 'react'; import { LocalizeText } from '../../../../../api'; -import { NotificationAlertEvent } from '../../../../../events'; -import { dispatchUiEvent, SendMessageHook } from '../../../../../hooks'; +import { SendMessageHook } from '../../../../../hooks'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout'; +import { NotificationAlertType } from '../../../../notification-center/common/NotificationAlertType'; +import { NotificationUtilities } from '../../../../notification-center/common/NotificationUtilities'; import { useModToolsContext } from '../../../context/ModToolsContext'; import { ModActionDefinition } from '../../../utils/ModActionDefinition'; import { ModToolsUserModActionViewProps } from './ModToolsUserModActionView.types'; @@ -59,13 +60,13 @@ export const ModToolsUserModActionView: FC = pro { if( (selectedTopic === -1) || (selectedAction === -1) ) { - dispatchUiEvent(new NotificationAlertEvent(['You must select a CFH topic and Sanction'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You must select a CFH topic and Sanction', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } if(!settings || !settings.cfhPermission) { - dispatchUiEvent(new NotificationAlertEvent(['You do not have permission to do this'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You do not have permission to do this', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } @@ -74,13 +75,13 @@ export const ModToolsUserModActionView: FC = pro if(!category) { - dispatchUiEvent(new NotificationAlertEvent(['You must select a CFH topic'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You must select a CFH topic', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } if(!sanction) { - dispatchUiEvent(new NotificationAlertEvent(['You must select a sanction'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You must select a sanction', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } @@ -92,13 +93,13 @@ export const ModToolsUserModActionView: FC = pro if(!settings.alertPermission) { - dispatchUiEvent(new NotificationAlertEvent(['You have insufficient permissions.'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You have insufficient permissions', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } if(message.trim().length === 0) { - dispatchUiEvent(new NotificationAlertEvent(['Please write a message to user.'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('Please write a message to user', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } @@ -113,7 +114,7 @@ export const ModToolsUserModActionView: FC = pro if(!settings.banPermission) { - dispatchUiEvent(new NotificationAlertEvent(['You have insufficient permissions.'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You have insufficient permissions', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } @@ -125,7 +126,7 @@ export const ModToolsUserModActionView: FC = pro if(!settings.kickPermission) { - dispatchUiEvent(new NotificationAlertEvent(['You have insufficient permissions.'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('You have insufficient permissions', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } @@ -144,7 +145,7 @@ export const ModToolsUserModActionView: FC = pro if(message.trim().length === 0) { - dispatchUiEvent(new NotificationAlertEvent(['Please write a message to user.'], null, null, null, 'Error', null)); + NotificationUtilities.simpleAlert('Please write a message to user', NotificationAlertType.DEFAULT, null, null, 'Error'); return; } diff --git a/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx b/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx index 939ce7ad..1f515a21 100644 --- a/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx +++ b/src/views/room/widgets/avatar-info/views/use-product-confirm/AvatarInfoUseProductConfirmView.tsx @@ -48,7 +48,7 @@ export const AvatarInfoUseProductConfirmView: FC } - case FurniCategory._Str_7297: { + case FurniCategory.PET_CUSTOM_PART: { if(customParts.length < 4) return null; const newCustomParts: PetCustomPart[] = []; @@ -95,7 +95,7 @@ export const AvatarInfoUseProductConfirmView: FC; } - case FurniCategory._Str_7954: { + case FurniCategory.PET_CUSTOM_PART_SHAMPOO: { if(customParts.length < 3) return null; const newCustomParts: PetCustomPart[] = []; @@ -121,7 +121,7 @@ export const AvatarInfoUseProductConfirmView: FC; } - case FurniCategory._Str_6096: { + case FurniCategory.PET_SADDLE: { if(customParts.length < 4) return null; const newCustomParts: PetCustomPart[] = []; @@ -149,9 +149,9 @@ export const AvatarInfoUseProductConfirmView: FC; } - case FurniCategory._Str_8726: - case FurniCategory._Str_6915: - case FurniCategory._Str_9449: { + case FurniCategory.MONSTERPLANT_REBREED: + case FurniCategory.MONSTERPLANT_REVIVAL: + case FurniCategory.MONSTERPLANT_FERTILIZE: { let posture = 'rip'; const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, petData.roomIndex, RoomObjectCategory.UNIT); @@ -190,25 +190,25 @@ export const AvatarInfoUseProductConfirmView: FC = props => { @@ -31,30 +31,30 @@ export const AvatarInfoUseProductView: FC = props if(!furniData) return; - let mode = _Str_2906; + let mode = PRODUCT_PAGE_UKNOWN; switch(furniData.specialType) { - case FurniCategory._Str_7696: - mode = _Str_13718; + case FurniCategory.PET_SHAMPOO: + mode = PRODUCT_PAGE_SHAMPOO; break; - case FurniCategory._Str_7297: - mode = _Str_14146; + case FurniCategory.PET_CUSTOM_PART: + mode = PRODUCT_PAGE_CUSTOM_PART; break; - case FurniCategory._Str_7954: - mode = _Str_15667; + case FurniCategory.PET_CUSTOM_PART_SHAMPOO: + mode = PRODUCT_PAGE_CUSTOM_PART_SHAMPOO; break; - case FurniCategory._Str_6096: - mode = _Str_14658; + case FurniCategory.PET_SADDLE: + mode = PRODUCT_PAGE_SADDLE; break; - case FurniCategory._Str_6915: - mode = _Str_14165; + case FurniCategory.MONSTERPLANT_REVIVAL: + mode = PRODUCT_PAGE_REVIVE; break; - case FurniCategory._Str_8726: - mode = _Str_12577; + case FurniCategory.MONSTERPLANT_REBREED: + mode = PRODUCT_PAGE_REBREED; break; - case FurniCategory._Str_9449: - mode = _Str_14611; + case FurniCategory.MONSTERPLANT_FERTILIZE: + mode = PRODUCT_PAGE_FERTILIZE; break; } @@ -86,23 +86,23 @@ export const AvatarInfoUseProductView: FC = props { item.name } - { (mode === _Str_2906) && + { (mode === PRODUCT_PAGE_UKNOWN) && processAction('use_product') }> { LocalizeText('infostand.button.useproduct') } } - { (mode === _Str_13718) && + { (mode === PRODUCT_PAGE_SHAMPOO) && processAction('use_product_shampoo') }> { LocalizeText('infostand.button.useproduct_shampoo') } } - { (mode === _Str_14146) && + { (mode === PRODUCT_PAGE_CUSTOM_PART) && processAction('use_product_custom_part') }> { LocalizeText('infostand.button.useproduct_custom_part') } } - { (mode === _Str_15667) && + { (mode === PRODUCT_PAGE_CUSTOM_PART_SHAMPOO) && processAction('use_product_custom_part_shampoo') }> { LocalizeText('infostand.button.useproduct_custom_part_shampoo') } } - { (mode === _Str_14658) && + { (mode === PRODUCT_PAGE_SADDLE) && <> { item.replace && processAction('replace_product_saddle') }> @@ -113,15 +113,15 @@ export const AvatarInfoUseProductView: FC = props { LocalizeText('infostand.button.useproduct_saddle') } } } - { (mode === _Str_14165) && + { (mode === PRODUCT_PAGE_REVIVE) && processAction('revive_monsterplant') }> { LocalizeText('infostand.button.revive_monsterplant') } } - { (mode === _Str_12577) && + { (mode === PRODUCT_PAGE_REBREED) && processAction('rebreed_monsterplant') }> { LocalizeText('infostand.button.rebreed_monsterplant') } } - { (mode === _Str_14611) && + { (mode === PRODUCT_PAGE_FERTILIZE) && processAction('fertilize_monsterplant') }> { LocalizeText('infostand.button.fertilize_monsterplant') } } diff --git a/src/views/room/widgets/furniture/context-menu/views/purchaseable-clothing/PurchasableClothingConfirmView.tsx b/src/views/room/widgets/furniture/context-menu/views/purchaseable-clothing/PurchasableClothingConfirmView.tsx index c2df26c1..515054cd 100644 --- a/src/views/room/widgets/furniture/context-menu/views/purchaseable-clothing/PurchasableClothingConfirmView.tsx +++ b/src/views/room/widgets/furniture/context-menu/views/purchaseable-clothing/PurchasableClothingConfirmView.tsx @@ -43,7 +43,7 @@ export const PurchasableClothingConfirmView: FC parseInt(part));