-
setAskingPrice(parseInt(event.target.value)) } placeholder={ LocalizeText('inventory.marketplace.make_offer.price_request') } />
+
updateAskingPrice(event.target.value) } placeholder={ LocalizeText('inventory.marketplace.make_offer.price_request') } />
{ ((askingPrice < marketplaceConfiguration.minimumPrice) || isNaN(askingPrice)) &&
{ LocalizeText('inventory.marketplace.make_offer.min_price', [ 'minprice' ], [ marketplaceConfiguration.minimumPrice.toString() ]) }
diff --git a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx
index d98ff1dd..84039c93 100644
--- a/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx
+++ b/src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx
@@ -1,4 +1,4 @@
-import { FC } from 'react';
+import { FC, useEffect, useRef } from 'react';
import { AutoGrid, AutoGridProps, LayoutGridItem } from '../../../../../common';
import { useCatalog } from '../../../../../hooks';
@@ -11,11 +11,17 @@ export const CatalogBundleGridWidgetView: FC
=
{
const { columnCount = 5, children = null, ...rest } = props;
const { currentOffer = null } = useCatalog();
+ const elementRef = useRef();
+
+ useEffect(() =>
+ {
+ if(elementRef && elementRef.current) elementRef.current.scrollTop = 0;
+ }, [ currentOffer ]);
if(!currentOffer) return null;
return (
-
+
{ currentOffer.products && (currentOffer.products.length > 0) && currentOffer.products.map((product, index) => ) }
{ children }
diff --git a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx
index 933dede6..e4660545 100644
--- a/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx
+++ b/src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx
@@ -1,4 +1,4 @@
-import { FC } from 'react';
+import { FC, useEffect, useRef } from 'react';
import { IPurchasableOffer, ProductTypeEnum } from '../../../../../api';
import { AutoGrid, AutoGridProps } from '../../../../../common';
import { useCatalog } from '../../../../../hooks';
@@ -13,6 +13,12 @@ export const CatalogItemGridWidgetView: FC = pro
{
const { columnCount = 5, children = null, ...rest } = props;
const { currentOffer = null, setCurrentOffer = null, currentPage = null, setPurchaseOptions = null } = useCatalog();
+ const elementRef = useRef();
+
+ useEffect(() =>
+ {
+ if(elementRef && elementRef.current) elementRef.current.scrollTop = 0;
+ }, [ currentPage ]);
if(!currentPage) return null;
@@ -38,7 +44,7 @@ export const CatalogItemGridWidgetView: FC = pro
}
return (
-
+
{ currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => ) }
{ children }
diff --git a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx
index 5cca1d29..83190db0 100644
--- a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx
+++ b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx
@@ -103,11 +103,7 @@ export const CatalogPurchaseWidgetView: FC = pro
{
if(!currentOffer) return;
- return () =>
- {
- setPurchaseState(CatalogPurchaseState.NONE);
- setPurchaseOptions({ quantity: 1, extraData: null, extraParamRequired: false, previewStuffData: null });
- }
+ setPurchaseState(CatalogPurchaseState.NONE);
}, [ currentOffer, setPurchaseOptions ]);
useEffect(() =>
diff --git a/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx
index fdc3f5f4..3ab901e6 100644
--- a/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx
+++ b/src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx
@@ -1,4 +1,4 @@
-import { FC, useEffect, useState } from 'react';
+import { FC, useEffect, useRef, useState } from 'react';
import { IPurchasableOffer, LocalizeText, Offer, ProductTypeEnum } from '../../../../../api';
import { AutoGrid, AutoGridProps, Button, ButtonGroup } from '../../../../../common';
import { useCatalog } from '../../../../../hooks';
@@ -18,6 +18,7 @@ export const CatalogSpacesWidgetView: FC = props =
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(-1);
const [ selectedOfferForGroup, setSelectedOfferForGroup ] = useState(null);
const { currentPage = null, currentOffer = null, setCurrentOffer = null, setPurchaseOptions = null } = useCatalog();
+ const elementRef = useRef();
const setSelectedOffer = (offer: IPurchasableOffer) =>
{
@@ -91,6 +92,11 @@ export const CatalogSpacesWidgetView: FC = props =
});
}, [ currentOffer, selectedGroupIndex, selectedOfferForGroup, setPurchaseOptions ]);
+ useEffect(() =>
+ {
+ if(elementRef && elementRef.current) elementRef.current.scrollTop = 0;
+ }, [ selectedGroupIndex ]);
+
if(!groupedOffers || (selectedGroupIndex === -1)) return null;
const offers = groupedOffers[selectedGroupIndex];
@@ -100,7 +106,7 @@ export const CatalogSpacesWidgetView: FC = props =
{ SPACES_GROUP_NAMES.map((name, index) => ) }
-
+
{ offers && (offers.length > 0) && offers.map((offer, index) => setSelectedOffer(offer) } />) }
{ children }
diff --git a/src/components/guide-tool/views/GuideToolOngoingView.tsx b/src/components/guide-tool/views/GuideToolOngoingView.tsx
index 7dd01850..080de332 100644
--- a/src/components/guide-tool/views/GuideToolOngoingView.tsx
+++ b/src/components/guide-tool/views/GuideToolOngoingView.tsx
@@ -1,5 +1,5 @@
import { GuideSessionGetRequesterRoomMessageComposer, GuideSessionInviteRequesterMessageComposer, GuideSessionMessageMessageComposer, GuideSessionRequesterRoomMessageEvent, GuideSessionResolvedMessageComposer } from '@nitrots/nitro-renderer';
-import { FC, KeyboardEvent, useCallback, useState } from 'react';
+import { FC, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
import { GetSessionDataManager, GuideToolMessageGroup, LocalizeText, SendMessageComposer, TryVisitRoom } from '../../../api';
import { Base, Button, ButtonGroup, Column, Flex, LayoutAvatarImageView, Text } from '../../../common';
import { useMessageEvent } from '../../../hooks';
@@ -16,10 +16,18 @@ interface GuideToolOngoingViewProps
export const GuideToolOngoingView: FC = props =>
{
+ const scrollDiv = useRef(null);
+
const { isGuide = false, userId = 0, userName = null, userFigure = null, isTyping = false, messageGroups = [] } = props;
const [ messageText, setMessageText ] = useState('');
+ useEffect(() =>
+ {
+ scrollDiv.current?.scrollIntoView({ block: 'end', behavior: 'smooth' });
+
+ }, [ messageGroups ]);
+
const visit = useCallback(() =>
{
SendMessageComposer(new GuideSessionGetRequesterRoomMessageComposer());
@@ -38,7 +46,7 @@ export const GuideToolOngoingView: FC = props =>
useMessageEvent(GuideSessionRequesterRoomMessageEvent, event =>
{
const parser = event.getParser();
-
+
TryVisitRoom(parser.requesterRoomId);
});
@@ -100,7 +108,8 @@ export const GuideToolOngoingView: FC = props =>
}
);
- }) }
+ }) }
+
diff --git a/src/components/help/HelpView.tsx b/src/components/help/HelpView.tsx
index 7a257be7..e0df417c 100644
--- a/src/components/help/HelpView.tsx
+++ b/src/components/help/HelpView.tsx
@@ -70,8 +70,6 @@ export const HelpView: FC<{}> = props =>
setIsVisible(true);
}, [ activeReport ]);
-
- if(!isVisible && !activeReport) return null;
const CurrentStepView = () =>
{
@@ -97,19 +95,20 @@ export const HelpView: FC<{}> = props =>
return (
<>
-
-
-
-
-
-
-
-
-
-
-
-
-
+ { isVisible &&
+
+
+
+
+
+
+
+
+
+
+
+
+ }
>
diff --git a/src/components/help/views/SelectReportedChatsView.tsx b/src/components/help/views/SelectReportedChatsView.tsx
index d83e6a02..0dfba492 100644
--- a/src/components/help/views/SelectReportedChatsView.tsx
+++ b/src/components/help/views/SelectReportedChatsView.tsx
@@ -21,6 +21,7 @@ export const SelectReportedChatsView: FC<{}> = props =>
return messengerHistory.filter(chat => (chat.entityId === activeReport.reportedUserId) && (chat.type === ChatEntryType.TYPE_IM));
}
+ return [];
}, [ activeReport, chatHistory, messengerHistory ]);
const selectChat = (chatEntry: IChatEntry) =>
@@ -62,7 +63,7 @@ export const SelectReportedChatsView: FC<{}> = props =>
{ LocalizeText('help.emergency.chat_report.description') }
- { !!!userChats.length &&
+ { !userChats || !userChats.length &&
{ LocalizeText('help.cfh.error.no_user_data') } }
{ (userChats.length > 0) &&
diff --git a/src/components/help/views/name-change/NameChangeView.tsx b/src/components/help/views/name-change/NameChangeView.tsx
index d4959efc..5cc76a9a 100644
--- a/src/components/help/views/name-change/NameChangeView.tsx
+++ b/src/components/help/views/name-change/NameChangeView.tsx
@@ -17,13 +17,11 @@ export const NameChangeView:FC<{}> = props =>
const [ layout, setLayout ] = useState(INIT);
const [ newUsername, setNewUsername ] = useState('');
- const onHelpNameChangeEvent = useCallback((event: HelpNameChangeEvent) =>
+ useUiEvent(HelpNameChangeEvent.INIT, event =>
{
setLayout(INIT);
setIsVisible(true);
- }, []);
-
- useUiEvent(HelpNameChangeEvent.INIT, onHelpNameChangeEvent);
+ });
const onAction = useCallback((action: string, value?: string) =>
{
diff --git a/src/components/inventory/views/badge/InventoryBadgeItemView.tsx b/src/components/inventory/views/badge/InventoryBadgeItemView.tsx
index e5bfb71c..bb6c54af 100644
--- a/src/components/inventory/views/badge/InventoryBadgeItemView.tsx
+++ b/src/components/inventory/views/badge/InventoryBadgeItemView.tsx
@@ -6,14 +6,14 @@ import { useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks
export const InventoryBadgeItemView: FC> = props =>
{
const { badgeCode = null, children = null, ...rest } = props;
- const { selectedBadgeCode = null, setSelectedBadgeCode = null, getBadgeId = null } = useInventoryBadges();
+ const { selectedBadgeCode = null, setSelectedBadgeCode = null, toggleBadge = null, getBadgeId = null } = useInventoryBadges();
const { isUnseen = null } = useInventoryUnseenTracker();
const unseen = isUnseen(UnseenItemCategory.BADGE, getBadgeId(badgeCode));
return (
- setSelectedBadgeCode(badgeCode) } { ...rest }>
+ setSelectedBadgeCode(badgeCode) } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } { ...rest }>
{ children }
);
-}
+}
\ No newline at end of file
diff --git a/src/components/inventory/views/bot/InventoryBotItemView.tsx b/src/components/inventory/views/bot/InventoryBotItemView.tsx
index d2a15cf4..d2130693 100644
--- a/src/components/inventory/views/bot/InventoryBotItemView.tsx
+++ b/src/components/inventory/views/bot/InventoryBotItemView.tsx
@@ -26,13 +26,16 @@ export const InventoryBotItemView: FC>
case MouseEventType.ROLL_OUT:
if(!isMouseDown || (selectedBot !== botItem)) return;
+ attemptBotPlacement(botItem);
+ return;
+ case 'dblclick':
attemptBotPlacement(botItem);
return;
}
}
return (
-
+
{ children }
diff --git a/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx b/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx
index f72c6ab5..f5e84814 100644
--- a/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx
+++ b/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx
@@ -24,6 +24,9 @@ export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props =>
case MouseEventType.ROLL_OUT:
if(!isMouseDown || !(groupItem === selectedItem)) return;
+ attemptItemPlacement(groupItem);
+ return;
+ case 'dblclick':
attemptItemPlacement(groupItem);
return;
}
@@ -31,5 +34,5 @@ export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props =>
const count = groupItem.getUnlockedCount();
- return ;
+ return ;
}
diff --git a/src/components/inventory/views/furniture/InventoryTradeView.tsx b/src/components/inventory/views/furniture/InventoryTradeView.tsx
index 3de9eef8..77337fcd 100644
--- a/src/components/inventory/views/furniture/InventoryTradeView.tsx
+++ b/src/components/inventory/views/furniture/InventoryTradeView.tsx
@@ -21,6 +21,7 @@ export const InventoryTradeView: FC = props =>
const [ otherGroupItem, setOtherGroupItem ] = useState(null);
const [ filteredGroupItems, setFilteredGroupItems ] = useState(null);
const [ countdownTick, setCountdownTick ] = useState(3);
+ const [ quantity, setQuantity ] = useState(1);
const { ownUser = null, otherUser = null, groupItems = [], tradeState = TradeState.TRADING_STATE_READY, progressTrade = null, removeItem = null, setTradeState = null } = useInventoryTrade();
const { simpleAlert = null } = useNotification();
@@ -118,6 +119,29 @@ export const InventoryTradeView: FC = props =>
return
}
+ const updateQuantity = (value: number, totalItemCount: number) =>
+ {
+ if(isNaN(Number(value)) || Number(value) < 0 || !value) value = 1;
+
+ value = Math.max(Number(value), 1);
+ value = Math.min(Number(value), totalItemCount);
+
+ if(value === quantity) return;
+
+ setQuantity(value);
+ }
+
+ const changeCount = (totalItemCount: number) =>
+ {
+ updateQuantity(quantity, totalItemCount);
+ attemptItemOffer(quantity);
+ }
+
+ useEffect(() =>
+ {
+ setQuantity(1);
+ }, [ groupItem ]);
+
useEffect(() =>
{
if(tradeState !== TradeState.TRADING_STATE_COUNTDOWN) return;
@@ -159,18 +183,29 @@ export const InventoryTradeView: FC = props =>
const count = item.getUnlockedCount();
return (
- (count && setGroupItem(item)) }>
+ (count && setGroupItem(item)) } onDoubleClick={ event => attemptItemOffer(1) }>
{ ((count > 0) && (groupItem === item)) &&
- }
+
+ }
);
}) }
-
- { groupItem ? groupItem.name : LocalizeText('catalog_selectproduct') }
-
+
+
+
+ setQuantity(event.target.valueAsNumber) } />
+
+
+
+
+
+
+ { groupItem ? groupItem.name : LocalizeText('catalog_selectproduct') }
+
+
@@ -188,11 +223,11 @@ export const InventoryTradeView: FC = props =>
if(!item) return ;
return (
- setOwnGroupItem(item) }>
+ setOwnGroupItem(item) } onDoubleClick={ event => removeItem(item) }>
{ (ownGroupItem === item) &&
- }
+ }
);
}) }
diff --git a/src/components/inventory/views/pet/InventoryPetItemView.tsx b/src/components/inventory/views/pet/InventoryPetItemView.tsx
index 41b0619e..aad45f95 100644
--- a/src/components/inventory/views/pet/InventoryPetItemView.tsx
+++ b/src/components/inventory/views/pet/InventoryPetItemView.tsx
@@ -26,13 +26,16 @@ export const InventoryPetItemView: FC>
case MouseEventType.ROLL_OUT:
if(!isMouseDown || !(petItem === selectedPet)) return;
+ attemptPetPlacement(petItem);
+ return;
+ case 'dblclick':
attemptPetPlacement(petItem);
return;
}
}
-
+
return (
-
+
{ children }
diff --git a/src/components/navigator/NavigatorView.tsx b/src/components/navigator/NavigatorView.tsx
index 9b3ae5eb..dcc2f9eb 100644
--- a/src/components/navigator/NavigatorView.tsx
+++ b/src/components/navigator/NavigatorView.tsx
@@ -24,6 +24,7 @@ export const NavigatorView: FC<{}> = props =>
const [ needsSearch, setNeedsSearch ] = useState(false);
const { searchResult = null, topLevelContext = null, topLevelContexts = null, navigatorData = null } = useNavigator();
const pendingSearch = useRef<{ value: string, code: string }>(null);
+ const elementRef = useRef();
useRoomSessionManagerEvent(RoomSessionEvent.CREATED, event =>
{
@@ -158,6 +159,8 @@ export const NavigatorView: FC<{}> = props =>
if(!searchResult) return;
setIsLoading(false);
+
+ if(elementRef && elementRef.current) elementRef.current.scrollTop = 0;
}, [ searchResult ]);
useEffect(() =>
@@ -214,7 +217,7 @@ export const NavigatorView: FC<{}> = props =>
{ !isCreatorOpen &&
<>
-
+
{ (searchResult && searchResult.results.map((result, index) => )) }
> }
diff --git a/src/components/navigator/views/search/NavigatorSearchResultView.tsx b/src/components/navigator/views/search/NavigatorSearchResultView.tsx
index 143c3435..a85daaca 100644
--- a/src/components/navigator/views/search/NavigatorSearchResultView.tsx
+++ b/src/components/navigator/views/search/NavigatorSearchResultView.tsx
@@ -13,7 +13,6 @@ export interface NavigatorSearchResultViewProps extends AutoGridProps
export const NavigatorSearchResultView: FC = props =>
{
const { searchResult = null, ...rest } = props;
-
const [ isExtended, setIsExtended ] = useState(true);
const [ displayMode, setDisplayMode ] = useState(0);
@@ -44,7 +43,7 @@ export const NavigatorSearchResultView: FC = pro
//setIsExtended(searchResult.closed);
setDisplayMode(searchResult.mode);
- }, [ searchResult,props ]);
+ }, [ searchResult ]);
const gridHasTwoColumns = (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS);
diff --git a/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss b/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss
index 7edd0c7b..558b0297 100644
--- a/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss
+++ b/src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss
@@ -70,6 +70,17 @@
}
}
+ .body-image-plant {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ max-width: 68px;
+ height: 85px;
+ max-height: 90px;
+ border-radius: $border-radius;
+ }
+
.badge-image {
width: 45px;
height: 45px;
diff --git a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetPetView.tsx b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetPetView.tsx
index 7847f439..a590b785 100644
--- a/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetPetView.tsx
+++ b/src/components/room/widgets/avatar-info/infostand/InfoStandWidgetPetView.tsx
@@ -1,7 +1,9 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { FC } from 'react';
-import { AvatarInfoPet, LocalizeText } from '../../../../../api';
-import { Base, Column, Flex, LayoutPetImageView, Text, UserProfileIconView } from '../../../../../common';
+import { PetRespectComposer, PetType } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { AvatarInfoPet, ConvertSeconds, CreateLinkEvent, GetConfiguration, LocalizeText, SendMessageComposer } from '../../../../../api';
+import { Base, Button, Column, Flex, LayoutCounterTimeView, LayoutPetImageView, LayoutRarityLevelView, Text, UserProfileIconView } from '../../../../../common';
+import { useRoom, useSessionInfo } from '../../../../../hooks';
interface InfoStandWidgetPetViewProps
{
@@ -12,72 +14,196 @@ interface InfoStandWidgetPetViewProps
export const InfoStandWidgetPetView: FC = props =>
{
const { avatarInfo = null, onClose = null } = props;
+ const [ remainingGrowTime, setRemainingGrowTime ] = useState(0);
+ const [ remainingTimeToLive, setRemainingTimeToLive ] = useState(0);
+ const { roomSession = null } = useRoom();
+ const { petRespectRemaining = 0, respectPet = null } = useSessionInfo();
+
+ useEffect(() =>
+ {
+ setRemainingGrowTime(avatarInfo.remainingGrowTime);
+ setRemainingTimeToLive(avatarInfo.remainingTimeToLive);
+ }, [ avatarInfo ]);
+
+ useEffect(() =>
+ {
+ if((avatarInfo.petType !== PetType.MONSTERPLANT) || avatarInfo.dead) return;
+
+ const interval = setInterval(() =>
+ {
+ setRemainingGrowTime(prevValue => (prevValue - 1));
+ setRemainingTimeToLive(prevValue => (prevValue - 1));
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, [ avatarInfo ]);
if(!avatarInfo) return null;
+ const processButtonAction = (action: string) =>
+ {
+ let hideMenu = true;
+
+ if (!action || action == '') return;
+
+ switch (action)
+ {
+ case 'respect':
+ respectPet(avatarInfo.id);
+
+ if((petRespectRemaining - 1) >= 1) hideMenu = false;
+ break;
+ case 'buyfood':
+ CreateLinkEvent('catalog/open/' + GetConfiguration('catalog.links')['pets.buy_saddle']);
+ break;
+ case 'train':
+ // not coded
+ break;
+ case 'treat':
+ SendMessageComposer(new PetRespectComposer(avatarInfo.id));
+ break;
+ case 'compost':
+ roomSession?.compostPlant(avatarInfo.id);
+ break;
+ case 'pick_up':
+ roomSession?.pickupPet(avatarInfo.id);
+ break;
+ }
+
+ if(hideMenu) onClose();
+ }
+
return (
-
-
-
-
- { avatarInfo.name }
-
-
- { LocalizeText(`pet.breed.${ avatarInfo.petType }.${ avatarInfo.petBreed }`) }
-
-
-
-
-
-
-
-
- { LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ avatarInfo.level.toString(), avatarInfo.maximumLevel.toString() ]) }
-
- { LocalizeText('infostand.pet.text.happiness') }
-
-
- { avatarInfo.happyness + '/' + avatarInfo.maximumHappyness }
-
-
-
+
+
+
+
+
+ { avatarInfo.name }
+
+
+ { LocalizeText(`pet.breed.${ avatarInfo.petType }.${ avatarInfo.petBreed }`) }
+
+
+ { (avatarInfo.petType === PetType.MONSTERPLANT) &&
+ <>
+
+
+
-
- { LocalizeText('infostand.pet.text.experience') }
-
-
- { avatarInfo.experience + '/' + avatarInfo.levelExperienceGoal }
-
-
-
+
+ { !avatarInfo.dead &&
+
+ { LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ avatarInfo.level.toString(), avatarInfo.maximumLevel.toString() ]) }
+ }
+
+ { LocalizeText('infostand.pet.text.wellbeing') }
+
+
+ { avatarInfo.dead ? '00:00:00' : ConvertSeconds((remainingTimeToLive == 0 ? avatarInfo.remainingTimeToLive : remainingTimeToLive)).split(':')[1] + ':' + ConvertSeconds((remainingTimeToLive == null || remainingTimeToLive == undefined ? 0 : remainingTimeToLive)).split(':')[2] + ':' + ConvertSeconds((remainingTimeToLive == null || remainingTimeToLive == undefined ? 0 : remainingTimeToLive)).split(':')[3] }
+
+
+
+
+ { remainingGrowTime != 0 && remainingGrowTime > 0 &&
+
+ { LocalizeText('infostand.pet.text.growth') }
+
+ }
+
+ { LocalizeText('infostand.pet.text.raritylevel', [ 'level' ], [ LocalizeText(`infostand.pet.raritylevel.${ avatarInfo.rarityLevel }`) ]) }
+
+
+
-
- { LocalizeText('infostand.pet.text.energy') }
-
-
- { avatarInfo.energy + '/' + avatarInfo.maximumEnergy }
-
-
-
+
+ { LocalizeText('pet.age', [ 'age' ], [ avatarInfo.age.toString() ]) }
+
-
-
-
-
-
- { LocalizeText('infostand.text.petrespect', [ 'count' ], [ avatarInfo.respect.toString() ]) }
- { LocalizeText('pet.age', [ 'age' ], [ avatarInfo.age.toString() ]) }
-
-
-
-
-
-
- { LocalizeText('infostand.text.petowner', [ 'name' ], [ avatarInfo.ownerName ]) }
-
-
+ > }
+ { (avatarInfo.petType !== PetType.MONSTERPLANT) &&
+ <>
+
+
+
+
+
+
+ { LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ avatarInfo.level.toString(), avatarInfo.maximumLevel.toString() ]) }
+
+ { LocalizeText('infostand.pet.text.happiness') }
+
+
+ { avatarInfo.happyness + '/' + avatarInfo.maximumHappyness }
+
+
+
+
+
+ { LocalizeText('infostand.pet.text.experience') }
+
+
+ { avatarInfo.experience + '/' + avatarInfo.levelExperienceGoal }
+
+
+
+
+
+ { LocalizeText('infostand.pet.text.energy') }
+
+
+ { avatarInfo.energy + '/' + avatarInfo.maximumEnergy }
+
+
+
+
+
+
+
+
+
+ { (avatarInfo.petType !== PetType.MONSTERPLANT) &&
+ { LocalizeText('infostand.text.petrespect', [ 'count' ], [ avatarInfo.respect.toString() ]) } }
+ { LocalizeText('pet.age', [ 'age' ], [ avatarInfo.age.toString() ]) }
+
+
+ > }
+
+
+
+
+ { LocalizeText('infostand.text.petowner', [ 'name' ], [ avatarInfo.ownerName ]) }
+
+
+
+
+ { (avatarInfo.petType !== PetType.MONSTERPLANT) &&
+ }
+ { avatarInfo.isOwner && (avatarInfo.petType !== PetType.MONSTERPLANT) &&
+ }
+ { !avatarInfo.dead && ((avatarInfo.energy / avatarInfo.maximumEnergy) < 0.98) && (avatarInfo.petType === PetType.MONSTERPLANT) &&
+ }
+ { roomSession?.isRoomOwner && (avatarInfo.petType === PetType.MONSTERPLANT) &&
+ }
+ { avatarInfo.isOwner &&
+ }
+ { (petRespectRemaining > 0) && (avatarInfo.petType !== PetType.MONSTERPLANT) &&
+ }
+
);
}
diff --git a/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetAvatarView.tsx b/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetAvatarView.tsx
index 3a4185aa..9556888e 100644
--- a/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetAvatarView.tsx
+++ b/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetAvatarView.tsx
@@ -3,7 +3,7 @@ import { RoomControllerLevel, RoomObjectCategory, RoomObjectVariable, RoomUnitGi
import { FC, useEffect, useMemo, useState } from 'react';
import { AvatarInfoUser, CreateLinkEvent, DispatchUiEvent, GetOwnRoomObject, GetSessionDataManager, GetUserProfile, LocalizeText, MessengerFriend, ReportType, RoomWidgetUpdateChatInputContentEvent, SendMessageComposer } from '../../../../../api';
import { Base, Flex } from '../../../../../common';
-import { useFriends, useHelp, useRoom } from '../../../../../hooks';
+import { useFriends, useHelp, useRoom, useSessionInfo } from '../../../../../hooks';
import { ContextMenuHeaderView } from '../../context-menu/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../context-menu/ContextMenuListItemView';
import { ContextMenuView } from '../../context-menu/ContextMenuView';
@@ -26,10 +26,10 @@ export const AvatarInfoWidgetAvatarView: FC = p
{
const { avatarInfo = null, onClose = null } = props;
const [ mode, setMode ] = useState(MODE_NORMAL);
- const [ respectsLeft, setRespectsLeft ] = useState(0);
const { canRequestFriend = null } = useFriends();
const { report = null } = useHelp();
const { roomSession = null } = useRoom();
+ const { userRespectRemaining = 0, respectUser = null } = useSessionInfo();
const isShowGiveRights = useMemo(() =>
{
@@ -113,13 +113,9 @@ export const AvatarInfoWidgetAvatarView: FC = p
setMode(MODE_RELATIONSHIP);
break;
case 'respect': {
- let newRespectsLeft = (respectsLeft - 1);
-
- setRespectsLeft(newRespectsLeft);
+ respectUser(avatarInfo.webID);
- GetSessionDataManager().giveRespect(avatarInfo.webID);
-
- if(newRespectsLeft > 0) hideMenu = false;
+ if((userRespectRemaining - 1) >= 1) hideMenu = false;
break;
}
case 'ignore':
@@ -203,7 +199,6 @@ export const AvatarInfoWidgetAvatarView: FC = p
useEffect(() =>
{
setMode(MODE_NORMAL);
- setRespectsLeft(avatarInfo.respectLeft);
}, [ avatarInfo ]);
return (
@@ -223,9 +218,9 @@ export const AvatarInfoWidgetAvatarView: FC = p
processAction('whisper') }>
{ LocalizeText('infostand.button.whisper') }
- { (respectsLeft > 0) &&
+ { (userRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.respect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.respect', [ 'count' ], [ userRespectRemaining.toString() ]) }
}
{ !canRequestFriend(avatarInfo.webID) &&
processAction('relationship') }>
diff --git a/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetOwnPetView.tsx b/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetOwnPetView.tsx
index 107393b5..af889c12 100644
--- a/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetOwnPetView.tsx
+++ b/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetOwnPetView.tsx
@@ -1,7 +1,7 @@
import { PetRespectComposer, PetType, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomUnitGiveHandItemPetComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useState } from 'react';
-import { AvatarInfoPet, CreateLinkEvent, GetConfiguration, GetOwnRoomObject, GetSessionDataManager, LocalizeText, SendMessageComposer } from '../../../../../api';
-import { useRoom } from '../../../../../hooks';
+import { AvatarInfoPet, CreateLinkEvent, GetConfiguration, GetOwnRoomObject, LocalizeText, SendMessageComposer } from '../../../../../api';
+import { useRoom, useSessionInfo } from '../../../../../hooks';
import { ContextMenuHeaderView } from '../../context-menu/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../context-menu/ContextMenuListItemView';
import { ContextMenuView } from '../../context-menu/ContextMenuView';
@@ -21,8 +21,8 @@ export const AvatarInfoWidgetOwnPetView: FC = p
{
const { avatarInfo = null, onClose = null } = props;
const [ mode, setMode ] = useState(MODE_NORMAL);
- const [ respectsLeft, setRespectsLeft ] = useState(0);
const { roomSession = null } = useRoom();
+ const { petRespectRemaining = 0, respectPet = null } = useSessionInfo();
const canGiveHandItem = useMemo(() =>
{
@@ -49,18 +49,9 @@ export const AvatarInfoWidgetOwnPetView: FC = p
switch(name)
{
case 'respect':
- let newRespectsLeft = 0;
+ respectPet(avatarInfo.id);
- setRespectsLeft(prevValue =>
- {
- newRespectsLeft = (prevValue - 1);
-
- return newRespectsLeft;
- });
-
- GetSessionDataManager().givePetRespect(avatarInfo.id);
-
- if(newRespectsLeft > 0) hideMenu = false;
+ if((petRespectRemaining - 1) >= 1) hideMenu = false;
break;
case 'treat':
SendMessageComposer(new PetRespectComposer(avatarInfo.id));
@@ -131,8 +122,6 @@ export const AvatarInfoWidgetOwnPetView: FC = p
return MODE_NORMAL;
});
-
- setRespectsLeft(avatarInfo.respectsPetLeft);
}, [ avatarInfo ]);
return (
@@ -142,9 +131,9 @@ export const AvatarInfoWidgetOwnPetView: FC = p
{ (mode === MODE_NORMAL) &&
<>
- { (respectsLeft > 0) &&
+ { (petRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.petrespect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
}
processAction('train') }>
{ LocalizeText('infostand.button.train') }
@@ -170,9 +159,9 @@ export const AvatarInfoWidgetOwnPetView: FC = p
{ LocalizeText('infostand.button.toggle_riding_permission') }
- { (respectsLeft > 0) &&
+ { (petRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.petrespect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
}
processAction('train') }>
{ LocalizeText('infostand.button.train') }
@@ -189,9 +178,9 @@ export const AvatarInfoWidgetOwnPetView: FC = p
processAction('dismount') }>
{ LocalizeText('infostand.button.dismount') }
- { (respectsLeft > 0) &&
+ { (petRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.petrespect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
}
> }
{ (mode === MODE_MONSTER_PLANT) &&
@@ -209,7 +198,7 @@ export const AvatarInfoWidgetOwnPetView: FC = p
}
{ !avatarInfo.dead && ((avatarInfo.energy / avatarInfo.maximumEnergy) < 0.98) &&
processAction('treat') }>
- { LocalizeText('infostand.button.treat') }
+ { LocalizeText('infostand.button.pettreat') }
}
{ !avatarInfo.dead && (avatarInfo.level === avatarInfo.maximumLevel) && avatarInfo.breedable &&
<>
diff --git a/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetPetView.tsx b/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetPetView.tsx
index a4cc6721..4219ace9 100644
--- a/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetPetView.tsx
+++ b/src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetPetView.tsx
@@ -1,7 +1,7 @@
import { PetRespectComposer, PetType, RoomControllerLevel, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomUnitGiveHandItemPetComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useState } from 'react';
import { AvatarInfoPet, GetOwnRoomObject, GetSessionDataManager, LocalizeText, SendMessageComposer } from '../../../../../api';
-import { useRoom } from '../../../../../hooks';
+import { useRoom, useSessionInfo } from '../../../../../hooks';
import { ContextMenuHeaderView } from '../../context-menu/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../context-menu/ContextMenuListItemView';
import { ContextMenuView } from '../../context-menu/ContextMenuView';
@@ -21,8 +21,8 @@ export const AvatarInfoWidgetPetView: FC = props =
{
const { avatarInfo = null, onClose = null } = props;
const [ mode, setMode ] = useState(MODE_NORMAL);
- const [ respectsLeft, setRespectsLeft ] = useState(0);
const { roomSession = null } = useRoom();
+ const { petRespectRemaining = 0, respectPet = null } = useSessionInfo();
const canPickUp = useMemo(() =>
{
@@ -54,18 +54,9 @@ export const AvatarInfoWidgetPetView: FC = props =
switch(name)
{
case 'respect':
- let newRespectsLeft = 0;
+ respectPet(avatarInfo.id);
- setRespectsLeft(prevValue =>
- {
- newRespectsLeft = (prevValue - 1);
-
- return newRespectsLeft;
- });
-
- GetSessionDataManager().givePetRespect(avatarInfo.id);
-
- if(newRespectsLeft > 0) hideMenu = false;
+ if((petRespectRemaining - 1) >= 1) hideMenu = false;
break;
case 'treat':
SendMessageComposer(new PetRespectComposer(avatarInfo.id));
@@ -98,8 +89,6 @@ export const AvatarInfoWidgetPetView: FC = props =
return MODE_NORMAL;
});
-
- setRespectsLeft(avatarInfo.respectsPetLeft);
}, [ avatarInfo ]);
return (
@@ -107,9 +96,9 @@ export const AvatarInfoWidgetPetView: FC = props =
{ avatarInfo.name }
- { (mode === MODE_NORMAL) && (respectsLeft > 0) &&
+ { (mode === MODE_NORMAL) && (petRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.petrespect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
}
{ (mode === MODE_SADDLED_UP) &&
<>
@@ -117,9 +106,9 @@ export const AvatarInfoWidgetPetView: FC = props =
processAction('mount') }>
{ LocalizeText('infostand.button.mount') }
}
- { (respectsLeft > 0) &&
+ { (petRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.petrespect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
}
> }
{ (mode === MODE_RIDING) &&
@@ -127,14 +116,14 @@ export const AvatarInfoWidgetPetView: FC = props =
processAction('dismount') }>
{ LocalizeText('infostand.button.dismount') }
- { (respectsLeft > 0) &&
+ { (petRespectRemaining > 0) &&
processAction('respect') }>
- { LocalizeText('infostand.button.petrespect', [ 'count' ], [ respectsLeft.toString() ]) }
+ { LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
}
> }
{ (mode === MODE_MONSTER_PLANT) && !avatarInfo.dead && ((avatarInfo.energy / avatarInfo.maximumEnergy) < 0.98) &&
processAction('treat') }>
- { LocalizeText('infostand.button.treat') }
+ { LocalizeText('infostand.button.pettreat') }
}
{ canPickUp &&
processAction('pick_up') }>
diff --git a/src/components/room/widgets/chat-input/ChatInputView.tsx b/src/components/room/widgets/chat-input/ChatInputView.tsx
index dcfb514e..f5db0f43 100644
--- a/src/components/room/widgets/chat-input/ChatInputView.tsx
+++ b/src/components/room/widgets/chat-input/ChatInputView.tsx
@@ -89,8 +89,15 @@ export const ChatInputView: FC<{}> = props =>
if(text.length <= maxChatLength)
{
- setChatValue('');
- sendChat(text, chatType, recipientName, chatStyleId);
+ if(/%CC%/g.test(encodeURIComponent(text)))
+ {
+ setChatValue('');
+ }
+ else
+ {
+ setChatValue('');
+ sendChat(text, chatType, recipientName, chatStyleId);
+ }
}
setChatValue(append);
@@ -141,7 +148,7 @@ export const ChatInputView: FC<{}> = props =>
}
return;
}
-
+
}, [ floodBlocked, inputRef, chatModeIdWhisper, anotherInputHasFocus, setInputFocus, checkSpecialKeywordForInput, sendChatValue ]);
useUiEvent(RoomWidgetUpdateChatInputContentEvent.CHAT_INPUT_CONTENT, event =>
diff --git a/src/components/room/widgets/furniture/FurnitureGiftOpeningView.tsx b/src/components/room/widgets/furniture/FurnitureGiftOpeningView.tsx
index d6a8c001..75ffe8ba 100644
--- a/src/components/room/widgets/furniture/FurnitureGiftOpeningView.tsx
+++ b/src/components/room/widgets/furniture/FurnitureGiftOpeningView.tsx
@@ -1,14 +1,24 @@
import { FC } from 'react';
-import { CreateLinkEvent, LocalizeText } from '../../../../api';
+import { attemptItemPlacement, CreateLinkEvent, LocalizeText } from '../../../../api';
import { Button, Column, Flex, LayoutGiftTagView, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
-import { useFurniturePresentWidget } from '../../../../hooks';
+import { useFurniturePresentWidget, useInventoryFurni } from '../../../../hooks';
export const FurnitureGiftOpeningView: FC<{}> = props =>
{
const { objectId = -1, classId = -1, itemType = null, text = null, isOwnerOfFurniture = false, senderName = null, senderFigure = null, placedItemId = -1, placedItemType = null, placedInRoom = false, imageUrl = null, openPresent = null, onClose = null } = useFurniturePresentWidget();
+ const { groupItems = [] } = useInventoryFurni();
if(objectId === -1) return null;
+ const place = (itemId: number) =>
+ {
+ const groupItem = groupItems.find(group => (group.getItemById(itemId)?.id === itemId));
+
+ if(groupItem) attemptItemPlacement(groupItem);
+
+ onClose();
+ }
+
return (
@@ -45,7 +55,7 @@ export const FurnitureGiftOpeningView: FC<{}> = props =>
}
-