diff --git a/public/ui-config.json.example b/public/ui-config.json.example index de2d3f2f..8b1ab50b 100644 --- a/public/ui-config.json.example +++ b/public/ui-config.json.example @@ -11,6 +11,7 @@ "widget.dimmer.colorwheel": false, "avatar.wardrobe.max.slots": 10, "user.badges.max.slots": 5, + "user.tags.enabled": false, "camera.publish.disabled": false, "hc.disabled": false, "badge.descriptions.enabled": true, diff --git a/src/api/purse/IPurse.ts b/src/api/purse/IPurse.ts index b08ded9b..9fffb188 100644 --- a/src/api/purse/IPurse.ts +++ b/src/api/purse/IPurse.ts @@ -4,7 +4,7 @@ export interface IPurse activityPoints: Map; clubDays: number; clubPeriods: number; - _Str_13571: boolean; + hasClubLeft: boolean; isVip: boolean; pastClubDays: number; pastVipDays: number; diff --git a/src/api/purse/Purse.ts b/src/api/purse/Purse.ts index 25192420..6970e59c 100644 --- a/src/api/purse/Purse.ts +++ b/src/api/purse/Purse.ts @@ -78,7 +78,7 @@ export class Purse implements IPurse this._clubPeriods = k; } - public get _Str_13571(): boolean + public get hasClubLeft(): boolean { return (this._clubDays > 0) || (this._clubPeriods > 0); } diff --git a/src/api/room/widgets/AvatarInfoUtilities.ts b/src/api/room/widgets/AvatarInfoUtilities.ts index ea2123bd..7c489448 100644 --- a/src/api/room/widgets/AvatarInfoUtilities.ts +++ b/src/api/room/widgets/AvatarInfoUtilities.ts @@ -162,7 +162,7 @@ export class AvatarInfoUtilities if(guildId !== 0) { furniInfo.groupId = guildId; - //this.container.connection.send(new _Str_2863(guildId, false)); + //this.container.connection.send(new GroupInformationComposer(guildId, false)); } if(IsOwnerOfFurniture(roomObject)) furniInfo.isOwner = true; @@ -250,7 +250,7 @@ export class AvatarInfoUtilities if(tradeMode !== RoomTradingLevelEnum.FREE_TRADING) userInfo.canTradeReason = AvatarInfoUser.TRADE_REASON_NO_TRADING; // const _local_12 = GetSessionDataManager().userId; - // _local_13 = GetSessionDataManager()._Str_18437(_local_12); + // _local_13 = GetSessionDataManager().getUserTags(_local_12); // this._Str_16287(_local_12, _local_13); } @@ -259,10 +259,10 @@ export class AvatarInfoUtilities userInfo.groupName = userData.groupName; userInfo.badges = roomSession.userDataManager.getUserBadges(userData.webID); userInfo.figure = userData.figure; - //var _local_8:Array = GetSessionDataManager()._Str_18437(userData.webID); - //this._Str_16287(userData._Str_2394, _local_8); - //this._container._Str_8097._Str_14387(userData.webID); - //this._container.connection.send(new _Str_8049(userData._Str_2394)); + //var _local_8:Array = GetSessionDataManager().getUserTags(userData.webID); + //this._Str_16287(userData.webId, _local_8); + //this._container.habboGroupsManager.updateVisibleExtendedProfile(userData.webID); + //this._container.connection.send(new GetRelationshipStatusInfoMessageComposer(userData.webId)); return userInfo; } diff --git a/src/components/catalog/views/targeted-offer/OfferBubbleView.tsx b/src/components/catalog/views/targeted-offer/OfferBubbleView.tsx index 003b8a18..ed6cf016 100644 --- a/src/components/catalog/views/targeted-offer/OfferBubbleView.tsx +++ b/src/components/catalog/views/targeted-offer/OfferBubbleView.tsx @@ -10,7 +10,7 @@ export const OfferBubbleView = (props: { offer: TargetedOfferData, setOpen: Disp if (!offer) return; return setOpen(true) } gap={ 2 }> - + { offer.title } ; } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoUseProductConfirmView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoUseProductConfirmView.tsx index 5b63dee2..c6bd09a0 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoUseProductConfirmView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoUseProductConfirmView.tsx @@ -10,19 +10,19 @@ interface AvatarInfoUseProductConfirmViewProps onClose: () => void; } -const _Str_5091: number = -1; -const _Str_11906: number = 0; -const _Str_11214: number = 1; -const _Str_11733: number = 2; -const _Str_11369: number = 3; -const _Str_8759: number = 4; -const _Str_8432: number = 5; -const _Str_9653: number = 6; +const PRODUCT_PAGE_UKNOWN: number = -1; +const PRODUCT_PAGE_SHAMPOO: number = 0; +const PRODUCT_PAGE_CUSTOM_PART: number = 1; +const PRODUCT_PAGE_CUSTOM_PART_SHAMPOO: number = 2; +const PRODUCT_PAGE_SADDLE: number = 3; +const PRODUCT_PAGE_REVIVE: number = 4; +const PRODUCT_PAGE_REBREED: number = 5; +const PRODUCT_PAGE_FERTILIZE: number = 6; export const AvatarInfoUseProductConfirmView: FC = props => { const { item = null, onClose = null } = props; - const [ mode, setMode ] = useState(_Str_5091); + const [ mode, setMode ] = useState(PRODUCT_PAGE_UKNOWN); const [ petData, setPetData ] = useState(null); const [ furniData, setFurniData ] = useState(null); const { roomSession = null } = useRoom(); @@ -189,30 +189,30 @@ export const AvatarInfoUseProductConfirmView: FC - { (mode === _Str_11906) && + { (mode === PRODUCT_PAGE_SHAMPOO) && <> { LocalizeText('useproduct.widget.text.shampoo', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.shampoo') } } - { (mode === _Str_11214) && + { (mode === PRODUCT_PAGE_CUSTOM_PART) && <> { LocalizeText('useproduct.widget.text.custompart', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.custompart') } } - { (mode === _Str_11733) && + { (mode === PRODUCT_PAGE_CUSTOM_PART_SHAMPOO) && <> { LocalizeText('useproduct.widget.text.custompartshampoo', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.custompartshampoo') } } - { (mode === _Str_11369) && + { (mode === PRODUCT_PAGE_SADDLE) && <> { LocalizeText('useproduct.widget.text.saddle', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.saddle') } } - { (mode === _Str_8759) && + { (mode === PRODUCT_PAGE_REVIVE) && <> { LocalizeText('useproduct.widget.text.revive_monsterplant', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.revive_monsterplant') } } - { (mode === _Str_8432) && + { (mode === PRODUCT_PAGE_REBREED) && <> { LocalizeText('useproduct.widget.text.rebreed_monsterplant', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.rebreed_monsterplant') } } - { (mode === _Str_9653) && + { (mode === PRODUCT_PAGE_FERTILIZE) && <> { LocalizeText('useproduct.widget.text.fertilize_monsterplant', [ 'productName' ], [ furniData.name ] ) } { LocalizeText('useproduct.widget.info.fertilize_monsterplant') } diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss b/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss index 558b0297..e207786b 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss @@ -106,6 +106,22 @@ font-style: italic; } } + + .flex-tags + { + flex-wrap: wrap; + margin-bottom: -10px; + + .text-tags + { + padding: 2px; + border-radius: 3px; + background: #333; + margin-right: 5px; + margin-bottom: 10px; + cursor: pointer; + } + } } .button-container { diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.tsx b/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.tsx index 410f515e..f70c2b0a 100644 --- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.tsx +++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.tsx @@ -1,6 +1,6 @@ import { RoomEngineEvent, RoomEnterEffect, RoomSessionDanceEvent } from '@nitrots/nitro-renderer'; import { FC, useState } from 'react'; -import { AvatarInfoFurni, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, GetSessionDataManager, RoomWidgetUpdateRentableBotChatEvent } from '../../../../api'; +import { AvatarInfoFurni, AvatarInfoPet, AvatarInfoRentableBot, AvatarInfoUser, GetConfiguration, GetSessionDataManager, RoomWidgetUpdateRentableBotChatEvent } from '../../../../api'; import { Column } from '../../../../common'; import { useAvatarInfoWidget, useRoom, useRoomEngineEvent, useRoomSessionManagerEvent, useUiEvent } from '../../../../hooks'; import { AvatarInfoRentableBotChatView } from './AvatarInfoRentableBotChatView'; @@ -67,6 +67,7 @@ export const AvatarInfoWidgetView: FC<{}> = props => case AvatarInfoUser.OWN_USER: case AvatarInfoUser.PEER: { const info = (avatarInfo as AvatarInfoUser); + if (GetConfiguration('user.tags.enabled')) GetSessionDataManager().getUserTags(info.roomIndex); if(info.isSpectatorMode) return null; @@ -111,7 +112,7 @@ export const AvatarInfoWidgetView: FC<{}> = props => case AvatarInfoRentableBot.RENTABLE_BOT: return setAvatarInfo(null) } />; case AvatarInfoPet.PET_INFO: - return setAvatarInfo(null) } /> + return setAvatarInfo(null) } /> } } diff --git a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserTagsView.tsx b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserTagsView.tsx new file mode 100644 index 00000000..b5fc7036 --- /dev/null +++ b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserTagsView.tsx @@ -0,0 +1,31 @@ +import { NavigatorSearchComposer } from '@nitrots/nitro-renderer'; +import { FC } from 'react'; +import { CreateLinkEvent, SendMessageComposer } from '../../../../../api'; +import { Flex, Text } from '../../../../../common'; + +interface InfoStandWidgetUserTagsViewProps +{ + tags: string[]; +} + +const processAction = (tag: string) => +{ + CreateLinkEvent(`navigator/search/${ tag }`); + SendMessageComposer(new NavigatorSearchComposer('hotel_view', `tag:${ tag }`)); +} + +export const InfoStandWidgetUserTagsView: FC = props => +{ + const { tags = null } = props; + + if(!tags || !tags.length) return null; + + return ( + <> +
+ + { tags && (tags.length > 0) && tags.map((tag, index) => processAction(tag) }>{ tag }) } + + + ); +} diff --git a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx index 238ffae1..7c7a7c82 100644 --- a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx +++ b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx @@ -5,6 +5,7 @@ import { AvatarInfoUser, CloneObject, GetConfiguration, GetGroupInformation, Get import { Column, Flex, LayoutAvatarImageView, LayoutBadgeImageView, Text, UserProfileIconView } from '../../../../../common'; import { useMessageEvent, useRoom, useRoomSessionManagerEvent } from '../../../../../hooks'; import { InfoStandWidgetUserRelationshipsView } from './InfoStandWidgetUserRelationshipsView'; +import { InfoStandWidgetUserTagsView } from './InfoStandWidgetUserTagsView'; interface InfoStandWidgetUserViewProps { @@ -35,7 +36,7 @@ export const InfoStandWidgetUserView: FC = props = const onMottoKeyDown = (event: KeyboardEvent) => { event.stopPropagation(); - + switch(event.key) { case 'Enter': @@ -49,7 +50,7 @@ export const InfoStandWidgetUserView: FC = props = if(!avatarInfo || (avatarInfo.webID !== event.userId)) return; const oldBadges = avatarInfo.badges.join(''); - + if(oldBadges === event.badges.join('')) return; setAvatarInfo(prevValue => @@ -108,10 +109,10 @@ export const InfoStandWidgetUserView: FC = props = { setIsEditingMotto(false); setMotto(avatarInfo.motto); - + SendMessageComposer(new UserRelationshipsComposer(avatarInfo.webID)); - return () => + return () => { setIsEditingMotto(false); setMotto(null); @@ -203,6 +204,11 @@ export const InfoStandWidgetUserView: FC = props = + { GetConfiguration('user.tags.enabled') && + + + + }
); diff --git a/src/components/user-profile/UserProfileView.tsx b/src/components/user-profile/UserProfileView.tsx index c40abf81..6327c293 100644 --- a/src/components/user-profile/UserProfileView.tsx +++ b/src/components/user-profile/UserProfileView.tsx @@ -1,4 +1,4 @@ -import { RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, RoomEngineObjectEvent, RoomObjectCategory, RoomObjectType, UserCurrentBadgesComposer, UserCurrentBadgesEvent, UserProfileEvent, UserProfileParser, UserRelationshipsComposer } from '@nitrots/nitro-renderer'; +import { ExtendedProfileChangedMessageEvent, RelationshipStatusInfoEvent, RelationshipStatusInfoMessageParser, RoomEngineObjectEvent, RoomObjectCategory, RoomObjectType, UserCurrentBadgesComposer, UserCurrentBadgesEvent, UserProfileEvent, UserProfileParser, UserRelationshipsComposer } from '@nitrots/nitro-renderer'; import { FC, useState } from 'react'; import { CreateLinkEvent, GetRoomSession, GetSessionDataManager, GetUserProfile, LocalizeText, SendMessageComposer } from '../../api'; import { Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; @@ -24,7 +24,7 @@ export const UserProfileView: FC<{}> = props => const onLeaveGroup = () => { if(!userProfile || (userProfile.id !== GetSessionDataManager().userId)) return; - + GetUserProfile(userProfile.id); } @@ -33,7 +33,7 @@ export const UserProfileView: FC<{}> = props => const parser = event.getParser(); if(!userProfile || (parser.userId !== userProfile.id)) return; - + setUserBadges(parser.badges); }); @@ -42,7 +42,7 @@ export const UserProfileView: FC<{}> = props => const parser = event.getParser(); if(!userProfile || (parser.userId !== userProfile.id)) return; - + setUserRelationships(parser); }); @@ -51,7 +51,7 @@ export const UserProfileView: FC<{}> = props => const parser = event.getParser(); let isSameProfile = false; - + setUserProfile(prevValue => { if(prevValue && prevValue.id) isSameProfile = (prevValue.id === parser.id); @@ -69,10 +69,19 @@ export const UserProfileView: FC<{}> = props => SendMessageComposer(new UserRelationshipsComposer(parser.id)); }); + useMessageEvent(ExtendedProfileChangedMessageEvent, event => + { + const parser = event.getParser(); + + if(parser.userId != userProfile?.id) return; + + GetUserProfile(parser.userId); + }); + useRoomEngineEvent(RoomEngineObjectEvent.SELECTED, event => { if(!userProfile) return; - + if(event.category !== RoomObjectCategory.UNIT) return; const userData = GetRoomSession().userDataManager.getUserDataByIndex(event.objectId); diff --git a/src/hooks/catalog/useCatalog.ts b/src/hooks/catalog/useCatalog.ts index 9317cc3f..6572baa6 100644 --- a/src/hooks/catalog/useCatalog.ts +++ b/src/hooks/catalog/useCatalog.ts @@ -265,7 +265,7 @@ const useCatalogState = () => const loadCatalogPage = useCallback((pageId: number, offerId: number) => { if(pageId < 0) return; - + setIsBusy(true); setPageId(pageId); @@ -285,9 +285,9 @@ const useCatalogState = () => for(const offer of catalogPage.offers) { if(offer.offerId !== offerId) continue; - + setCurrentOffer(offer) - + break; } } @@ -351,7 +351,7 @@ const useCatalogState = () => return nodes; }); - + if(targetNode.pageId > -1) loadCatalogPage(targetNode.pageId, offerId); }, [ setActiveNodes, loadCatalogPage, cancelObjectMover ]); @@ -544,9 +544,9 @@ const useCatalogState = () => setPurchaseOptions(prevValue => { const newValue = { ...prevValue }; - + newValue.extraData =( offer.product.extraParam || null); - + return newValue; }); } @@ -576,7 +576,7 @@ const useCatalogState = () => break; } } - + petPalettes.push(petPalette); return { ...prevValue, petPalettes }; @@ -636,7 +636,7 @@ const useCatalogState = () => } const message = LocalizeText(`inventory.marketplace.result.${ parser.result }`); - + simpleAlert(message, NotificationAlertType.DEFAULT, null, null, title); }); @@ -674,11 +674,11 @@ const useCatalogState = () => { const parser = event.getParser(); - setFurniLimit(parser._Str_15864); - setMaxFurniLimit(parser._Str_24094); - setSecondsLeft(parser._Str_3709); + setFurniLimit(parser.furniLimit); + setMaxFurniLimit(parser.maxFurniLimit); + setSecondsLeft(parser.secondsLeft); setUpdateTime(GetTickerTime()); - setSecondsLeftWithGrace(parser._Str_24379); + setSecondsLeftWithGrace(parser.secondsLeftWithGrace); refreshBuilderStatus(); }); @@ -757,7 +757,7 @@ const useCatalogState = () => if(roomObject) roomObject.model.setValue(RoomObjectVariable.FURNITURE_ALPHA_MULTIPLIER, 0.5); - if(catalogSkipPurchaseConfirmation) + if(catalogSkipPurchaseConfirmation) { SendMessageComposer(new PurchaseFromCatalogComposer(pageId, purchasableOffer.offerId, product.extraParam, 1)); @@ -832,7 +832,7 @@ const useCatalogState = () => { return () => setCurrentOffer(null); }, [ currentPage ]); - + useEffect(() => { if(!isVisible || !rootNode || !offersToNodes || !requestedPage.current) return; @@ -881,7 +881,7 @@ const useCatalogState = () => setPurchaseOptions({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null }); }, [ currentOffer ]); - + useEffect(() => { if(!isVisible || rootNode) return;