Rebuild: Inventory

This commit is contained in:
DuckieTM 2024-04-01 14:41:56 +02:00
parent 3e94b7d5c4
commit 133eae4328
19 changed files with 434 additions and 895 deletions

View File

@ -1,92 +1,12 @@
.nitro-inventory { .nitro-inventory {
height: $inventory-height;
min-height: $inventory-height;
max-height: $inventory-height + 150px;
width: $inventory-width; width: $inventory-width;
min-width: $inventory-width; height: $inventory-height;
max-width: $inventory-width;
color: #FFF;
input {
min-height: 20px;
max-height: 20px;
}
&.trading {
width: 535px;
min-height: $inventory-height + 315px !important;
max-width: $inventory-width + 15px !important;
min-width: $inventory-width + 15px !important;
.trading-inventory {
max-height: 240px;
}
.nitro-item-count {
top: 0px;
right: -2px;
background-color: white;
padding: 0px;
padding-left: 3px;
padding-right: 3px;
color: #306A83;
border: 1px solid #2F6982;
font-weight: normal !important;
font-family: Goldfish;
}
}
.empty-image { .empty-image {
background: url("@/assets/images/inventory/empty.png"); background: url('@/assets/images/inventory/empty.png') no-repeat;
background-repeat: no-repeat;
width: 129px; width: 129px;
height: 181px; height: 181px;
} }
.empty-petsimage {
background: url("@/assets/images/inventory/petsempty.png");
background-repeat: no-repeat;
width: 220px;
height: 220px;
}
.empty-furniimage {
background: url("@/assets/images/inventory/furniempty.png");
background-repeat: no-repeat;
width: 220px;
height: 220px;
}
.empty-botsimage {
background: url("@/assets/images/inventory/botsempty.png");
background-repeat: no-repeat;
width: 220px;
height: 220px;
}
.bubble-inventory {
position: relative;
font-family: sans-serif;
font-size: 14px;
line-height: 5px;
width: auto;
background: #fff;
border-radius: 15px;
padding: 5px;
text-align: left;
color: #000;
}
.bubble-inventory-bottom-left:after {
content: "";
position: absolute;
bottom: 0;
left: 15%;
width: 0;
height: 0;
border: 20px solid transparent;
border-top-color: #fff;
border-bottom: 0;
border-right: 0;
margin-left: -10px;
margin-bottom: -20px;
}
.trade-button { .trade-button {
min-height: 0; min-height: 0;
@ -94,89 +14,4 @@
padding: 1px 2px; padding: 1px 2px;
z-index: 5; z-index: 5;
} }
.trade-bg {
border-image-source: url('@/assets/images/inventory/trading_bg.png');
border-image-slice: 15 15 15 15 fill;
border-image-width: 15px 15px 15px 15px;
z-index: 1;
}
.credits-align {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 230px;
}
.quantity-input {
width: 49px;
}
.lock-design-left {
margin-left: 21px;
margin-right: 33px;
}
.lock-design-right {
margin-left: 21px;
}
.divisor {
height: 180px;
border-left: 2px solid #B4B4B4;
margin-top: 15px;
}
.inventory-items {
border-image-source: url(@/assets/images/inventory/item.png) !important;
border-image-slice: 6 6 6 6 fill !important;
border-image-width: 6px 6px 6px 6px !important;
&.active {
border-image-source: url(@/assets/images/inventory/selected_item.png) !important;
border-image-slice: 6 6 6 6 fill !important;
border-image-width: 6px 6px 6px 6px !important;
}
&.unseen {
background-color: #9BCA64;
}
.nitro-item-count {
top: 0px;
right: -2px;
background-color: white;
padding: 0px;
padding-left: 3px;
padding-right: 3px;
color: #306A83;
border: 1px solid #2F6982;
font-weight: normal !important;
font-family: Goldfish;
}
}
}
.calc-wrapper {
width: 96%;
height: calc(100% - 100px);
}
.size-list-badges {
width: 335px;
}
.size-badges {
width: 145px;
}
.nitro-inventory-category-filter {
background-color: #C9C9C9;
height: 25px;
}
.text-shadow-around-text {
text-shadow: -1px 0 white, 0 1px white, 1px 0 #ececec, 0 -1px white;
} }

View File

@ -1,10 +1,8 @@
import { BadgePointLimitsEvent, ILinkEventTracker, IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { BadgePointLimitsEvent, ILinkEventTracker, IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { AddEventLinkTracker, GetLocalization, GetRoomEngine, GroupItem, LocalizeText, RemoveLinkEventTracker, isObjectMoverRequested, setObjectMoverRequested } from '../../api'; import { AddEventLinkTracker, GetLocalization, GetRoomEngine, isObjectMoverRequested, LocalizeText, RemoveLinkEventTracker, setObjectMoverRequested, UnseenItemCategory } from '../../api';
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
import { useInventoryBadges, useInventoryFurni, useInventoryTrade, useInventoryUnseenTracker, useMessageEvent, useRoomEngineEvent, useRoomSessionManagerEvent } from '../../hooks'; import { useInventoryTrade, useInventoryUnseenTracker, useMessageEvent, useRoomEngineEvent, useRoomSessionManagerEvent } from '../../hooks';
import { TABS, TAB_BADGES, TAB_BOTS, TAB_FURNITURE, TAB_PETS, UNSEEN_CATEGORIES } from './constants';
import { InventoryCategoryFilterView } from './views/InventoryCategoryFilterView';
import { InventoryBadgeView } from './views/badge/InventoryBadgeView'; import { InventoryBadgeView } from './views/badge/InventoryBadgeView';
import { InventoryBotView } from './views/bot/InventoryBotView'; import { InventoryBotView } from './views/bot/InventoryBotView';
import { InventoryFurnitureView } from './views/furniture/InventoryFurnitureView'; import { InventoryFurnitureView } from './views/furniture/InventoryFurnitureView';
@ -12,6 +10,12 @@ import { InventoryTradeView } from './views/furniture/InventoryTradeView';
import { InventoryPetView } from './views/pet/InventoryPetView'; import { InventoryPetView } from './views/pet/InventoryPetView';
import { InventoryFurnitureDeleteView } from './views/furniture/InventoryFurnitureDeleteView'; import { InventoryFurnitureDeleteView } from './views/furniture/InventoryFurnitureDeleteView';
const TAB_FURNITURE: string = 'inventory.furni';
const TAB_BOTS: string = 'inventory.bots';
const TAB_PETS: string = 'inventory.furni.tab.pets';
const TAB_BADGES: string = 'inventory.badges';
const TABS = [ TAB_FURNITURE, TAB_BOTS, TAB_PETS, TAB_BADGES ];
const UNSEEN_CATEGORIES = [ UnseenItemCategory.FURNI, UnseenItemCategory.BOT, UnseenItemCategory.PET, UnseenItemCategory.BADGE ];
export const InventoryView: FC<{}> = props => export const InventoryView: FC<{}> = props =>
{ {
@ -19,12 +23,8 @@ export const InventoryView: FC<{}> = props =>
const [ currentTab, setCurrentTab ] = useState<string>(TABS[0]); const [ currentTab, setCurrentTab ] = useState<string>(TABS[0]);
const [ roomSession, setRoomSession ] = useState<IRoomSession>(null); const [ roomSession, setRoomSession ] = useState<IRoomSession>(null);
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null); const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
const [ filteredGroupItems, setFilteredGroupItems ] = useState<GroupItem[]>([]);
const [ filteredBadgeCodes, setFilteredBadgeCodes ] = useState<string[]>([]);
const { isTrading = false, stopTrading = null } = useInventoryTrade(); const { isTrading = false, stopTrading = null } = useInventoryTrade();
const { getCount = null } = useInventoryUnseenTracker(); const { getCount = null, resetCategory = null } = useInventoryUnseenTracker();
const { groupItems = [] } = useInventoryFurni();
const { badgeCodes = [] } = useInventoryBadges();
const onClose = () => const onClose = () =>
{ {
@ -119,8 +119,10 @@ export const InventoryView: FC<{}> = props =>
if(!isVisible) return null; if(!isVisible) return null;
return ( return (
<NitroCardView uniqueKey={ 'inventory' } className={ `${ isTrading ? 'nitro-inventory trading no-resize' : 'nitro-inventory' }` } theme={ isTrading ? 'primary' : '' } > <>
<NitroCardView uniqueKey={ 'inventory' } className="nitro-inventory" theme={ isTrading ? 'primary-slim' : '' } >
<NitroCardHeaderView headerText={ LocalizeText('inventory.title') } onCloseClick={ onClose } /> <NitroCardHeaderView headerText={ LocalizeText('inventory.title') } onCloseClick={ onClose } />
{ !isTrading &&
<> <>
<NitroCardTabsView> <NitroCardTabsView>
{ TABS.map((name, index) => { TABS.map((name, index) =>
@ -132,21 +134,23 @@ export const InventoryView: FC<{}> = props =>
); );
}) } }) }
</NitroCardTabsView> </NitroCardTabsView>
<NitroCardContentView overflow="hidden"> <NitroCardContentView>
{ (currentTab !== TAB_PETS && currentTab !== TAB_BOTS) && <InventoryCategoryFilterView currentTab={ currentTab } groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } badgeCodes={ badgeCodes } setBadgeCodes={ setFilteredBadgeCodes } /> }
{ (currentTab === TAB_FURNITURE ) && { (currentTab === TAB_FURNITURE ) &&
<InventoryFurnitureView roomSession={ roomSession } roomPreviewer={ roomPreviewer } isTrading={ isTrading } filteredGroupItems={ filteredGroupItems } /> } <InventoryFurnitureView roomSession={ roomSession } roomPreviewer={ roomPreviewer } /> }
{ (currentTab === TAB_PETS ) &&
<InventoryPetView roomSession={ roomSession } roomPreviewer={ roomPreviewer } isTrading={ isTrading } /> }
{ (currentTab === TAB_BADGES ) &&
<InventoryBadgeView filteredBadgeCodes={ filteredBadgeCodes } /> }
{ (currentTab === TAB_BOTS ) && { (currentTab === TAB_BOTS ) &&
<InventoryBotView roomSession={ roomSession } roomPreviewer={ roomPreviewer } isTrading={ isTrading } /> } <InventoryBotView roomSession={ roomSession } roomPreviewer={ roomPreviewer } /> }
{ (currentTab === TAB_PETS ) &&
{ isTrading && <InventoryTradeView currentTab={ currentTab } setCurrentTab={ (e) => setCurrentTab(e) } cancelTrade={ onClose } /> } <InventoryPetView roomSession={ roomSession } roomPreviewer={ roomPreviewer } /> }
{ (currentTab === TAB_BADGES ) &&
<InventoryBadgeView /> }
</NitroCardContentView> </NitroCardContentView>
</> }
{ isTrading &&
<NitroCardContentView>
<InventoryTradeView cancelTrade={ onClose } />
</NitroCardContentView> }
</NitroCardView>
<InventoryFurnitureDeleteView /> <InventoryFurnitureDeleteView />
</> </>
</NitroCardView>
); );
} }

View File

@ -1,9 +0,0 @@
export class InventoryFilterType
{
public static EVERYTHING: string = 'inventory.filter.option.everything';
public static FLOOR: string = 'inventory.furni.tab.floor';
public static WALL: string = 'inventory.furni.tab.wall';
public static ANYWHERE: string = 'inventory.placement.option.anywhere';
public static IN_ROOM: string = 'inventory.placement.option.inroom';
public static IN_INVENTORY: string = 'inventory.placement.option.notinroom';
}

View File

@ -1,9 +0,0 @@
import { UnseenItemCategory } from '../../../api/inventory/UnseenItemCategory';
export const TAB_FURNITURE: string = 'inventory.furni';
export const TAB_BOTS: string = 'inventory.bots';
export const TAB_PETS: string = 'inventory.furni.tab.pets';
export const TAB_BADGES: string = 'inventory.badges';
export const TABS = [ TAB_FURNITURE, TAB_PETS, TAB_BADGES, TAB_BOTS ];
export const UNSEEN_CATEGORIES = [ UnseenItemCategory.FURNI, UnseenItemCategory.PET, UnseenItemCategory.BADGE, UnseenItemCategory.BOT ];
export const MAX_ITEMS_TO_TRADE: number = 9;

View File

@ -1,2 +0,0 @@
export * from './InventoryFilterType';
export * from './InventoryTabConstants';

View File

@ -1,41 +1,26 @@
import { FC } from 'react'; import { FC } from 'react';
import { CreateLinkEvent, LocalizeText } from '../../../api'; import { Column, Grid, GridProps, Text } from '../../../common';
import { Button, Column, Flex, Grid, GridProps, Text } from '../../../common';
export interface InventoryCategoryEmptyViewProps extends GridProps export interface InventoryCategoryEmptyViewProps extends GridProps
{ {
title: string; title: string;
desc: string; desc: string;
isTrading?: boolean;
} }
export const InventoryCategoryEmptyView: FC<InventoryCategoryEmptyViewProps> = props => export const InventoryCategoryEmptyView: FC<InventoryCategoryEmptyViewProps> = props =>
{ {
const { title = '', desc = '', isTrading = false, children = null, ...rest } = props; const { title = '', desc = '', children = null, ...rest } = props;
return ( return (
<Grid {...rest}> <Grid { ...rest }>
<Column justifyContent="start" center size={6} overflow="hidden"> <Column center size={ 5 } overflow="hidden">
<div className="empty-furniimage" />
</Column>
<Column justifyContent="center" size={6} overflow="hidden">
<div className="bubble-inventory bubble-inventory-bottom-left">
<Text fontSize={6} overflow="unset" truncate>
{title}
</Text>
<Text overflow="auto" fontSize={6}>
{" "}
{desc}
</Text>
</div>
<div className="empty-image" /> <div className="empty-image" />
{ !isTrading &&
<Flex gap={ 2 } position="absolute" className="bottom-2">
<Button className="py-1" onClick={ () => CreateLinkEvent('catalog/open') }>{ LocalizeText('inventory.open.catalog') }</Button>
</Flex>
}
</Column> </Column>
{children} <Column justifyContent="center" size={ 7 } overflow="hidden">
<Text fontWeight="bold" fontSize={ 5 } overflow="unset" truncate>{ title }</Text>
<Text overflow="auto">{ desc }</Text>
</Column>
{ children }
</Grid> </Grid>
); );
}; }

View File

@ -1,40 +0,0 @@
import { FC } from 'react';
import { CreateLinkEvent, LocalizeText } from '../../../api';
import { Button, Column, Flex, Grid, GridProps, Text } from '../../../common';
export interface InventoryCategoryEmptyViewBots extends GridProps
{
title: string;
desc: string;
isTrading?: boolean;
}
export const InventoryCategoryEmptyViewBots: FC< InventoryCategoryEmptyViewBotsProps> = (props) => {
const { title = '', desc = '', isTrading = false, children = null, ...rest } = props;
return (
<Grid {...rest}>
<Column justifyContent="start" center size={6} overflow="hidden">
<div className="empty-botsimage" />
</Column>
<Column justifyContent="center" size={6} overflow="hidden">
<div className="bubble-inventory bubble-inventory-bottom-left">
<Text fontSize={6} overflow="unset" truncate>
{title}
</Text>
<Text overflow="auto" fontSize={6}>
{" "}
{desc}
</Text>
</div>
<div className="empty-image" />
{ !isTrading &&
<Flex gap={ 2 } position="absolute" className="bottom-2">
<Button className="py-1" onClick={ () => CreateLinkEvent('catalog/open') }>{ LocalizeText('inventory.open.catalog') }</Button>
</Flex>
}
</Column>
{children}
</Grid>
);
};

View File

@ -1,40 +0,0 @@
import { FC } from 'react';
import { CreateLinkEvent, LocalizeText } from '../../../api';
import { Button, Column, Flex, Grid, GridProps, Text } from '../../../common';
export interface InventoryCategoryEmptyViewPets extends GridProps
{
title: string;
desc: string;
isTrading?: boolean;
}
export const InventoryCategoryEmptyViewPets: FC< InventoryCategoryEmptyViewBotsProps> = (props) => {
const { title = '', desc = '', isTrading = false, children = null, ...rest } = props;
return (
<Grid {...rest}>
<Column justifyContent="start" center size={6} overflow="hidden">
<div className="empty-petsimage" />
</Column>
<Column justifyContent="center" size={6} overflow="hidden">
<div className="bubble-inventory bubble-inventory-bottom-left">
<Text fontSize={6} overflow="unset" truncate>
{title}
</Text>
<Text overflow="auto" fontSize={6}>
{" "}
{desc}
</Text>
</div>
<div className="empty-image" />
{ !isTrading &&
<Flex gap={ 2 } position="absolute" className="bottom-2">
<Button className="py-1" onClick={ () => CreateLinkEvent('catalog/open') }>{ LocalizeText('inventory.open.catalog') }</Button>
</Flex>
}
</Column>
{children}
</Grid>
);
};

View File

@ -1,107 +0,0 @@
import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react';
import { GroupItem, LocalizeBadgeName, LocalizeText } from '../../../api';
import { Flex } from '../../../common';
import { InventoryFilterType, TAB_BADGES, TAB_FURNITURE } from '../constants';
export interface InventoryCategoryFilterViewProps
{
currentTab: string;
groupItems: GroupItem[];
badgeCodes: string[];
setGroupItems: Dispatch<SetStateAction<GroupItem[]>>;
setBadgeCodes: Dispatch<SetStateAction<string[]>>;
}
export const InventoryCategoryFilterView: FC<InventoryCategoryFilterViewProps> = props =>
{
const { currentTab = null, groupItems = [], badgeCodes = [], setGroupItems = null, setBadgeCodes = null } = props;
const [ filterType, setFilterType ] = useState<string>(InventoryFilterType.EVERYTHING);
const [ filterPlace, setFilterPlace ] = useState<string>(InventoryFilterType.IN_INVENTORY);
const [ searchValue, setSearchValue ] = useState('');
useEffect(() =>
{
if (currentTab !== TAB_BADGES) return;
let filteredBadgeCodes = [ ...badgeCodes ];
const filteredBadges = badgeCodes.filter( badge => badge.startsWith('ACH_') );
const numberMap = {};
filteredBadges.forEach(badge =>
{
const name = badge.split(/[\d]+/)[0];
const number = Number(badge.replace(name, ''));
if (numberMap[name] === undefined || number > numberMap[name])
{
numberMap[name] = number;
}
});
const allBadges = Object.keys(numberMap).map( name => `${ name }${ numberMap[name] }` ).concat( badgeCodes.filter( badge => !badge.startsWith('ACH_') ) );
filteredBadgeCodes = allBadges.filter(badgeCode =>
{
return LocalizeBadgeName(badgeCode).toLocaleLowerCase().includes(searchValue?.toLocaleLowerCase().replace(' ', ''));
});
setBadgeCodes(filteredBadgeCodes);
}, [ badgeCodes, currentTab, searchValue, setBadgeCodes ]);
useEffect(() =>
{
if (currentTab !== TAB_FURNITURE) return;
let filteredGroupItems = [ ...groupItems ];
const comparison = searchValue.toLocaleLowerCase();
if (filterType === InventoryFilterType.EVERYTHING) return setGroupItems(groupItems.filter( item => item.name.toLocaleLowerCase().includes(comparison) ));
filteredGroupItems = groupItems.filter(item =>
{
const isWallFilter = (filterType === InventoryFilterType.WALL) ? item.isWallItem : false;
const isFloorFilter = (filterType === InventoryFilterType.FLOOR) ? !item.isWallItem : false;
const isSearchFilter = (item.name.toLocaleLowerCase().includes(comparison)) ? true : false;
return comparison && comparison.length ? (isSearchFilter && (isWallFilter || isFloorFilter)) : isWallFilter || isFloorFilter;
});
setGroupItems(filteredGroupItems);
}, [ groupItems, setGroupItems, searchValue, filterType, currentTab ]);
useEffect(() =>
{
setFilterType(InventoryFilterType.EVERYTHING);
setFilterPlace(InventoryFilterType.IN_INVENTORY);
setSearchValue('');
}, [ currentTab ]);
return (
<Flex className="nitro-inventory-category-filter rounded p-1 mt-n1" style={ { width: currentTab === TAB_BADGES ? '320px' : '100%' } }>
<Flex className="position-relative">
<Flex fullWidth alignItems="center" position="relative">
<input type="text" className="form-control form-control-sm" value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
</Flex>
{ (searchValue && !!searchValue.length) && <i className="icon icon-clear position-absolute cursor-pointer end-1 top-1" onClick={ event => setSearchValue('') } /> }
</Flex>
{ (currentTab !== TAB_BADGES) &&
<>
<Flex alignItems="center" position="relative" className="ms-2">
<select className="form-select form-select-sm" value={ filterType } onChange={ event => setFilterType(event.target.value) }>
{ [ InventoryFilterType.EVERYTHING, InventoryFilterType.FLOOR, InventoryFilterType.WALL ].map((type, index) => <option key={ index } value={ type }>{ LocalizeText(type) }</option>) }
</select>
</Flex>
<Flex alignItems="center" position="relative" className="ms-2">
<select className="form-select form-select-sm" value={ filterPlace } onChange={ event => setFilterPlace(event.target.value) } disabled={ currentTab === TAB_FURNITURE }>
{ [ InventoryFilterType.ANYWHERE, InventoryFilterType.IN_ROOM, InventoryFilterType.IN_INVENTORY ].map((type, index) => <option key={ index } value={ type }>{ LocalizeText(type) }</option>) }
</select>
</Flex>
</>
}
</Flex>
);
}

View File

@ -11,7 +11,7 @@ export const InventoryBadgeItemView: FC<PropsWithChildren<{ badgeCode: string }>
const unseen = isUnseen(UnseenItemCategory.BADGE, getBadgeId(badgeCode)); const unseen = isUnseen(UnseenItemCategory.BADGE, getBadgeId(badgeCode));
return ( return (
<LayoutGridItem className="badge-container" itemActive={ (selectedBadgeCode === badgeCode) } itemUnseen={ unseen } onMouseDown={ event => setSelectedBadgeCode(badgeCode) } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } { ...rest }> <LayoutGridItem itemActive={ (selectedBadgeCode === badgeCode) } itemUnseen={ unseen } onMouseDown={ event => setSelectedBadgeCode(badgeCode) } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } { ...rest }>
<LayoutBadgeImageView badgeCode={ badgeCode } /> <LayoutBadgeImageView badgeCode={ badgeCode } />
{ children } { children }
</LayoutGridItem> </LayoutGridItem>

View File

@ -1,21 +1,14 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { LocalizeBadgeName, LocalizeText, UnseenItemCategory } from '../../../../api'; import { LocalizeBadgeName, LocalizeText, UnseenItemCategory } from '../../../../api';
import { AutoGrid, Button, Column, Flex, Grid, LayoutBadgeImageView, Text } from '../../../../common'; import { AutoGrid, Button, Column, Flex, Grid, LayoutBadgeImageView, Text } from '../../../../common';
import { useAchievements, useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks'; import { useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks';
import { InventoryBadgeItemView } from './InventoryBadgeItemView'; import { InventoryBadgeItemView } from './InventoryBadgeItemView';
interface InventoryBadgeViewProps export const InventoryBadgeView: FC<{}> = props =>
{ {
filteredBadgeCodes: string[];
}
export const InventoryBadgeView: FC<InventoryBadgeViewProps> = props =>
{
const { filteredBadgeCodes = [] } = props;
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const { activeBadgeCodes = [], selectedBadgeCode = null, isWearingBadge = null, canWearBadges = null, toggleBadge = null, getBadgeId = null, activate = null, deactivate = null } = useInventoryBadges(); const { badgeCodes = [], activeBadgeCodes = [], selectedBadgeCode = null, isWearingBadge = null, canWearBadges = null, toggleBadge = null, getBadgeId = null, activate = null, deactivate = null } = useInventoryBadges();
const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker(); const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker();
const { achievementScore = 0 } = useAchievements();
useEffect(() => useEffect(() =>
{ {
@ -41,41 +34,33 @@ export const InventoryBadgeView: FC<InventoryBadgeViewProps> = props =>
}, []); }, []);
return ( return (
<div className="badge-wrapper calc-wrapper mt-n2"> <Grid>
<Grid gap={ 1 }> <Column size={ 7 } overflow="hidden">
<Column overflow="hidden" className="size-list-badges mt-1"> <AutoGrid columnCount={ 4 }>
<AutoGrid gap={ 1 } columnCount={ 5 }> { badgeCodes && (badgeCodes.length > 0) && badgeCodes.map((badgeCode, index) =>
{ filteredBadgeCodes && filteredBadgeCodes.length > 0 && filteredBadgeCodes.map((badgeCode, index) =>
{ {
if (isWearingBadge(badgeCode)) return null; if(isWearingBadge(badgeCode)) return null;
return <InventoryBadgeItemView key={ index } badgeCode={ badgeCode } />; return <InventoryBadgeItemView key={ index } badgeCode={ badgeCode } />
}) } }) }
</AutoGrid> </AutoGrid>
</Column> </Column>
<Column className="justify-content-between size-badges position-absolute end-2 mt-n4" overflow="auto"> <Column className="justify-content-between" size={ 5 } overflow="auto">
<Column overflow="hidden"> <Column overflow="hidden" gap={ 2 }>
<Text center bold>{ LocalizeText('inventory.badges.activebadges') }</Text> <Text>{ LocalizeText('inventory.badges.activebadges') }</Text>
<AutoGrid gap={ 1 } columnCount={ 5 }> <AutoGrid columnCount={ 3 }>
{ activeBadgeCodes && (activeBadgeCodes.length > 0) && activeBadgeCodes.map((badgeCode, index) => <InventoryBadgeItemView key={ index } badgeCode={ badgeCode } />) } { activeBadgeCodes && (activeBadgeCodes.length > 0) && activeBadgeCodes.map((badgeCode, index) => <InventoryBadgeItemView key={ index } badgeCode={ badgeCode } />) }
</AutoGrid> </AutoGrid>
</Column> </Column>
{ !!selectedBadgeCode &&
<Column grow justifyContent="end" gap={ 2 }>
<Flex alignItems="center" gap={ 2 }>
<LayoutBadgeImageView shrink badgeCode={ selectedBadgeCode } />
<Text>{ LocalizeBadgeName(selectedBadgeCode) }</Text>
</Flex>
<Button variant={ (isWearingBadge(selectedBadgeCode) ? 'danger' : 'success') } disabled={ !isWearingBadge(selectedBadgeCode) && !canWearBadges() } onClick={ event => toggleBadge(selectedBadgeCode) }>{ LocalizeText(isWearingBadge(selectedBadgeCode) ? 'inventory.badges.clearbadge' : 'inventory.badges.wearbadge') }</Button>
</Column> }
</Column> </Column>
</Grid> </Grid>
<Column gap={ 0 } position="absolute" className="bottom-2" style={ { width: '96%' } }>
{ !!selectedBadgeCode &&
<Flex className="bg-white pb-1 px-2 rounded mt-2" style={ { height: '50px' } } justifyContent={ 'between' } alignItems={ 'end' } gap={ 2 }>
<Flex alignItems="start" gap={ 2 }>
<LayoutBadgeImageView shrink badgeCode={ selectedBadgeCode } />
<Text className="font-bold mt-2">{ LocalizeBadgeName(selectedBadgeCode) }</Text>
</Flex>
<Button className="btn btn-primary mb-1" style={ { fontSize: '12px' } } disabled={ !isWearingBadge(selectedBadgeCode) && !canWearBadges() } onClick={ event => toggleBadge(selectedBadgeCode) }>{ LocalizeText(isWearingBadge(selectedBadgeCode) ? 'inventory.badges.clearbadge' : 'inventory.badges.wearbadge') }</Button>
</Flex>
}
<div className="nitro-progress-bar text-white mt-1">
<Text small center style={ { marginTop: '-1px' } }>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
</div>
</Column>
</div>
); );
} }

View File

@ -1,24 +1,21 @@
import { MouseEventType } from "@nitrots/nitro-renderer"; import { MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, PropsWithChildren, useState } from "react"; import { FC, MouseEvent, PropsWithChildren, useState } from 'react';
import { import { attemptBotPlacement, IBotItem, UnseenItemCategory } from '../../../../api';
attemptBotPlacement, import { LayoutAvatarImageView, LayoutGridItem } from '../../../../common';
IBotItem, import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks';
UnseenItemCategory,
} from "../../../../api";
import { LayoutAvatarImageView, LayoutGridItem } from "../../../../common";
import { useInventoryBots, useInventoryUnseenTracker } from "../../../../hooks";
export const InventoryBotItemView: FC< export const InventoryBotItemView: FC<PropsWithChildren<{ botItem: IBotItem }>> = props =>
PropsWithChildren<{ botItem: IBotItem }> {
> = (props) => {
const { botItem = null, children = null, ...rest } = props; const { botItem = null, children = null, ...rest } = props;
const [isMouseDown, setMouseDown] = useState(false); const [ isMouseDown, setMouseDown ] = useState(false);
const { selectedBot = null, setSelectedBot = null } = useInventoryBots(); const { selectedBot = null, setSelectedBot = null } = useInventoryBots();
const { isUnseen = null } = useInventoryUnseenTracker(); const { isUnseen = null } = useInventoryUnseenTracker();
const unseen = isUnseen(UnseenItemCategory.BOT, botItem.botData.id); const unseen = isUnseen(UnseenItemCategory.BOT, botItem.botData.id);
const onMouseEvent = (event: MouseEvent) => { const onMouseEvent = (event: MouseEvent) =>
switch (event.type) { {
switch(event.type)
{
case MouseEventType.MOUSE_DOWN: case MouseEventType.MOUSE_DOWN:
setSelectedBot(botItem); setSelectedBot(botItem);
setMouseDown(true); setMouseDown(true);
@ -27,32 +24,20 @@ export const InventoryBotItemView: FC<
setMouseDown(false); setMouseDown(false);
return; return;
case MouseEventType.ROLL_OUT: case MouseEventType.ROLL_OUT:
if (!isMouseDown || selectedBot !== botItem) return; if(!isMouseDown || (selectedBot !== botItem)) return;
attemptBotPlacement(botItem); attemptBotPlacement(botItem);
return; return;
case "dblclick": case 'dblclick':
attemptBotPlacement(botItem); attemptBotPlacement(botItem);
return; return;
} }
}; }
return ( return (
<LayoutGridItem <LayoutGridItem itemActive={ (selectedBot === botItem) } itemUnseen={ unseen } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } onDoubleClick={ onMouseEvent } { ...rest }>
itemActive={selectedBot === botItem} <LayoutAvatarImageView figure={ botItem.botData.figure } direction={ 3 } headOnly={ true } />
itemUnseen={unseen} { children }
onMouseDown={onMouseEvent}
onMouseUp={onMouseEvent}
onMouseOut={onMouseEvent}
onDoubleClick={onMouseEvent}
{...rest}
>
<LayoutAvatarImageView
figure={botItem.botData.figure}
direction={3}
headOnly={true}
/>
{children}
</LayoutGridItem> </LayoutGridItem>
); );
}; }

View File

@ -1,21 +1,20 @@
import { IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer'; import { IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { GetRoomEngine, LocalizeText, UnseenItemCategory, attemptBotPlacement } from '../../../../api'; import { attemptBotPlacement, GetRoomEngine, LocalizeText, UnseenItemCategory } from '../../../../api';
import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common'; import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common';
import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks'; import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks';
import { InventoryCategoryEmptyViewBots } from '../InventoryCategoryEmptyViewBots'; import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryBotItemView } from './InventoryBotItemView'; import { InventoryBotItemView } from './InventoryBotItemView';
interface InventoryBotViewProps interface InventoryBotViewProps
{ {
roomSession: IRoomSession; roomSession: IRoomSession;
roomPreviewer: RoomPreviewer; roomPreviewer: RoomPreviewer;
isTrading: boolean;
} }
export const InventoryBotView: FC<InventoryBotViewProps> = props => export const InventoryBotView: FC<InventoryBotViewProps> = props =>
{ {
const { roomSession = null, roomPreviewer = null, isTrading = false } = props; const { roomSession = null, roomPreviewer = null } = props;
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const { botItems = [], selectedBot = null, activate = null, deactivate = null } = useInventoryBots(); const { botItems = [], selectedBot = null, activate = null, deactivate = null } = useInventoryBots();
const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker(); const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker();
@ -65,7 +64,7 @@ export const InventoryBotView: FC<InventoryBotViewProps> = props =>
return () => setIsVisible(false); return () => setIsVisible(false);
}, []); }, []);
if(!botItems || !botItems.length) return <InventoryCategoryEmptyViewBots title={ LocalizeText('inventory.empty.bots.title') } desc={ LocalizeText('inventory.empty.bots.desc') } isTrading={ isTrading } />; if(!botItems || !botItems.length) return <InventoryCategoryEmptyView title={ LocalizeText('inventory.empty.bots.title') } desc={ LocalizeText('inventory.empty.bots.desc') } />;
return ( return (
<Grid> <Grid>
@ -80,7 +79,7 @@ export const InventoryBotView: FC<InventoryBotViewProps> = props =>
</Column> </Column>
{ selectedBot && { selectedBot &&
<Column grow justifyContent="between" gap={ 2 }> <Column grow justifyContent="between" gap={ 2 }>
<Text grow truncate>{ selectedBot.botData.name }</Text> <Text grow>{ selectedBot.botData.name }</Text>
{ !!roomSession && { !!roomSession &&
<Button variant="success" onClick={ event => attemptBotPlacement(selectedBot) }> <Button variant="success" onClick={ event => attemptBotPlacement(selectedBot) }>
{ LocalizeText('inventory.furni.placetoroom') } { LocalizeText('inventory.furni.placetoroom') }

View File

@ -1,12 +1,12 @@
import { MouseEventType } from '@nitrots/nitro-renderer'; import { MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, useState } from 'react'; import { FC, MouseEvent, useState } from 'react';
import { GroupItem, attemptItemPlacement } from '../../../../api'; import { attemptItemPlacement, GroupItem } from '../../../../api';
import { LayoutGridItem } from '../../../../common'; import { LayoutGridItem } from '../../../../common';
import { useInventoryFurni } from '../../../../hooks'; import { useInventoryFurni } from '../../../../hooks';
export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem, isTrading: boolean, attemptItemOffer: (count: number) => void, setGroupItem: (item: GroupItem) => void }> = props => export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props =>
{ {
const { groupItem = null, isTrading = null, attemptItemOffer = null, setGroupItem = null, ...rest } = props; const { groupItem = null, ...rest } = props;
const [ isMouseDown, setMouseDown ] = useState(false); const [ isMouseDown, setMouseDown ] = useState(false);
const { selectedItem = null, setSelectedItem = null } = useInventoryFurni(); const { selectedItem = null, setSelectedItem = null } = useInventoryFurni();
@ -24,16 +24,15 @@ export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem, isTrading: b
case MouseEventType.ROLL_OUT: case MouseEventType.ROLL_OUT:
if(!isMouseDown || !(groupItem === selectedItem)) return; if(!isMouseDown || !(groupItem === selectedItem)) return;
if (!isTrading) attemptItemPlacement(groupItem); attemptItemPlacement(groupItem);
return; return;
case 'dblclick': case 'dblclick':
if (!isTrading) attemptItemPlacement(groupItem); attemptItemPlacement(groupItem);
if (isTrading) (setGroupItem(groupItem), attemptItemOffer(1))
return; return;
} }
} }
const count = groupItem.getUnlockedCount(); const count = groupItem.getUnlockedCount();
return <LayoutGridItem className={ !count ? 'inventory-items opacity-0-5 ' : 'inventory-items' } itemImage={ groupItem.iconUrl } itemCount={ groupItem.getUnlockedCount() } itemActive={ (groupItem === selectedItem) } itemUniqueNumber={ groupItem.stuffData.uniqueNumber } itemUnseen={ groupItem.hasUnseenItems } onClick={ event => (count && setGroupItem(groupItem)) } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } onDoubleClick={ onMouseEvent } { ...rest } />; return <LayoutGridItem className={ !count ? 'opacity-0-5 ' : '' } itemImage={ groupItem.iconUrl } itemCount={ groupItem.getUnlockedCount() } itemActive={ (groupItem === selectedItem) } itemUniqueNumber={ groupItem.stuffData.uniqueNumber } itemUnseen={ groupItem.hasUnseenItems } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } onDoubleClick={ onMouseEvent } { ...rest } />;
} }

View File

@ -1,20 +1,18 @@
import { DeleteFurniTypeInventoryComposer, IObjectData, IRoomSession, RoomObjectVariable, RoomPreviewer, TradingListAddItemComposer, TradingListAddItemsComposer, Vector3d } from '@nitrots/nitro-renderer'; import { IRoomSession, RoomObjectVariable, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { DispatchUiEvent, FurniCategory, GetRoomEngine, GetSessionDataManager, GroupItem, IFurnitureItem, LocalizeText, NotificationAlertType, SendMessageComposer, UnseenItemCategory, attemptItemPlacement, getGuildFurniType } from '../../../../api'; import { attemptItemPlacement, DispatchUiEvent, FurniCategory, GetRoomEngine, GetSessionDataManager, GroupItem, LocalizeText, UnseenItemCategory } from '../../../../api';
import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutLimitedEditionCompactPlateView, LayoutRarityLevelView, LayoutRoomPreviewerView, Text } from '../../../../common'; import { AutoGrid, Button, Column, Grid, LayoutLimitedEditionCompactPlateView, LayoutRarityLevelView, LayoutRoomPreviewerView, Text } from '../../../../common';
import { CatalogPostMarketplaceOfferEvent, DeleteItemConfirmEvent } from '../../../../events'; import { CatalogPostMarketplaceOfferEvent } from '../../../../events';
import { FaTrashAlt } from 'react-icons/fa'; import { DeleteItemConfirmEvent } from '../../../../events';
import { useInventoryFurni, useInventoryTrade, useInventoryUnseenTracker, useNotification } from '../../../../hooks'; import { useInventoryFurni, useInventoryUnseenTracker } from '../../../../hooks';
import { MAX_ITEMS_TO_TRADE } from '../../constants';
import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView'; import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryFurnitureItemView } from './InventoryFurnitureItemView'; import { InventoryFurnitureItemView } from './InventoryFurnitureItemView';
import { InventoryFurnitureSearchView } from './InventoryFurnitureSearchView';
interface InventoryFurnitureViewProps interface InventoryFurnitureViewProps
{ {
roomSession: IRoomSession; roomSession: IRoomSession;
roomPreviewer: RoomPreviewer; roomPreviewer: RoomPreviewer;
isTrading: boolean;
filteredGroupItems: GroupItem[];
} }
const attemptPlaceMarketplaceOffer = (groupItem: GroupItem) => const attemptPlaceMarketplaceOffer = (groupItem: GroupItem) =>
@ -39,120 +37,11 @@ const attemptDeleteItem = (groupItem: GroupItem) =>
export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props => export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
{ {
const { roomSession = null, roomPreviewer = null, isTrading = null, filteredGroupItems = [] } = props; const { roomSession = null, roomPreviewer = null } = props;
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ groupItem, setGroupItem ] = useState<GroupItem>(null); const [ filteredGroupItems, setFilteredGroupItems ] = useState<GroupItem[]>([]);
const [ quantity, setQuantity ] = useState<number>(1);
const { groupItems = [], selectedItem = null, activate = null, deactivate = null } = useInventoryFurni(); const { groupItems = [], selectedItem = null, activate = null, deactivate = null } = useInventoryFurni();
const { ownUser = null } = useInventoryTrade();
const { resetItems = null } = useInventoryUnseenTracker(); const { resetItems = null } = useInventoryUnseenTracker();
const { simpleAlert = null } = useNotification();
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);
}
const canTradeItem = (isWallItem: boolean, spriteId: number, category: number, groupable: boolean, stuffData: IObjectData) =>
{
if(!ownUser || ownUser.accepts || !ownUser.userItems) return false;
if(ownUser.userItems.length < MAX_ITEMS_TO_TRADE) return true;
if(!groupable) return false;
let type = spriteId.toString();
if(category === FurniCategory.POSTER)
{
type = ((type + 'poster') + stuffData.getLegacyString());
}
else
{
if(category === FurniCategory.GUILD_FURNI)
{
type = getGuildFurniType(spriteId, stuffData);
}
else
{
type = (((isWallItem) ? 'I' : 'S') + type);
}
}
return !!ownUser.userItems.getValue(type);
}
const attemptItemOffer = (count: number) =>
{
if(!groupItem) return;
const tradeItems = groupItem.getTradeItems(count);
if(!tradeItems || !tradeItems.length) return;
let coreItem: IFurnitureItem = null;
const itemIds: number[] = [];
for(const item of tradeItems)
{
itemIds.push(item.id);
if(!coreItem) coreItem = item;
}
const ownItemCount = ownUser.userItems.length;
if((ownItemCount + itemIds.length) <= 1500)
{
if(!coreItem.isGroupable && (itemIds.length))
{
SendMessageComposer(new TradingListAddItemComposer(itemIds.pop()));
}
else
{
const tradeIds: number[] = [];
for(const itemId of itemIds)
{
if(canTradeItem(coreItem.isWallItem, coreItem.type, coreItem.category, coreItem.isGroupable, coreItem.stuffData))
{
tradeIds.push(itemId);
}
}
if(tradeIds.length)
{
if(tradeIds.length === 1)
{
SendMessageComposer(new TradingListAddItemComposer(tradeIds.pop()));
}
else
{
SendMessageComposer(new TradingListAddItemsComposer(...tradeIds));
}
}
}
}
else
{
simpleAlert(LocalizeText('trading.items.too_many_items.desc'), NotificationAlertType.DEFAULT, null, null, LocalizeText('trading.items.too_many_items.title'));
}
setGroupItem(selectedItem);
}
useEffect(() => useEffect(() =>
{ {
@ -202,8 +91,6 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
roomPreviewer.addFurnitureIntoRoom(selectedItem.type, new Vector3d(90), selectedItem.stuffData, (furnitureItem.extra.toString())); roomPreviewer.addFurnitureIntoRoom(selectedItem.type, new Vector3d(90), selectedItem.stuffData, (furnitureItem.extra.toString()));
} }
} }
setGroupItem(selectedItem);
}, [ roomPreviewer, selectedItem ]); }, [ roomPreviewer, selectedItem ]);
useEffect(() => useEffect(() =>
@ -231,73 +118,40 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
return () => setIsVisible(false); return () => setIsVisible(false);
}, []); }, []);
useEffect(() => if(!groupItems || !groupItems.length) return <InventoryCategoryEmptyView title={ LocalizeText('inventory.empty.title') } desc={ LocalizeText('inventory.empty.desc') } />;
{
setQuantity(1);
}, [ filteredGroupItems ]);
if(!groupItems || !groupItems.length) return <InventoryCategoryEmptyView title={ LocalizeText('inventory.empty.title') } desc={ LocalizeText('inventory.empty.desc') } isTrading={ isTrading } />;
const totalItems = !isTrading ? selectedItem.items.length : selectedItem.getUnlockedCount();
return ( return (
<Grid className="mt-n1"> <Grid>
<Column size={ 7 } overflow="hidden" style={ { height: `calc(100% - ${ !isTrading ? '34px' : '5px' })` } }> <Column size={ 7 } overflow="hidden">
<AutoGrid gap={ 1 } columnCount={ 5 } className={ isTrading ? 'trading-inventory' : '' }> <InventoryFurnitureSearchView groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } />
{ filteredGroupItems && (filteredGroupItems.length > 0) && filteredGroupItems.map((item, index) => <InventoryFurnitureItemView key={ index } groupItem={ item } isTrading={ isTrading } attemptItemOffer={ (e) => attemptItemOffer(e) } setGroupItem={ (e) => setGroupItem(e) } />) } <AutoGrid columnCount={ 5 }>
{ filteredGroupItems && (filteredGroupItems.length > 0) && filteredGroupItems.map((item, index) => <InventoryFurnitureItemView key={ index } groupItem={ item } />) }
</AutoGrid> </AutoGrid>
</Column> </Column>
<Column size={ 5 } overflow="auto"> <Column size={ 5 } overflow="auto">
<Column overflow="hidden" position="relative" className="cursor-pointer"> <Column overflow="hidden" position="relative">
<LayoutRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> <LayoutRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
{ selectedItem &&
<Button variant="danger" className="bottom-2 end-2" position="absolute" onClick={ event => attemptDeleteItem(selectedItem) }>
<FaTrashAlt className="fa-icon" />
</Button>
}
{ (selectedItem && (selectedItem.items[0].isTradable || !selectedItem.items[0].isTradable)) &&
<Flex gap={ 2 } position="absolute" className="top-2 start-2">
<Base className={ `icon ${ (selectedItem.items[0].isTradable && totalItems > 0) ? 'icon-tradeable' : 'icon-not-tradeable' }` } title={ LocalizeText((selectedItem.items[0].isTradable && totalItems > 0) ? 'inventory.furni.preview.tradeable_amount' : 'inventory.furni.preview.not_tradeable') } />
{ (selectedItem.items[0].isTradable && totalItems > 0) && <Text variant="black" className="text-shadow-around-text mt-n1">{ totalItems }</Text> }
</Flex>
}
{ (selectedItem && (selectedItem.items[0].recyclable || !selectedItem.items[0].recyclable)) &&
<Flex gap={ 2 } position="absolute" className="top-4 start-2">
<Base className={ `icon ${ (selectedItem.items[0].recyclable && totalItems > 0) ? 'icon-recyclable' : 'icon-not-recyclable' }` } title={ LocalizeText((selectedItem.items[0].recyclable && totalItems > 0) ? 'inventory.furni.preview.recyclable_amount' : 'inventory.furni.preview.not_recyclable') } />
{ (selectedItem.items[0].recyclable && totalItems > 0) && <Text variant="black" className="text-shadow-around-text">{ totalItems }</Text> }
</Flex>
}
{ selectedItem && selectedItem.stuffData.isUnique && { selectedItem && selectedItem.stuffData.isUnique &&
<LayoutLimitedEditionCompactPlateView className="top-2 end-2" position="absolute" uniqueNumber={ selectedItem.stuffData.uniqueNumber } uniqueSeries={ selectedItem.stuffData.uniqueSeries } /> } <LayoutLimitedEditionCompactPlateView className="top-2 end-2" position="absolute" uniqueNumber={ selectedItem.stuffData.uniqueNumber } uniqueSeries={ selectedItem.stuffData.uniqueSeries } /> }
{ (selectedItem && selectedItem.stuffData.rarityLevel > -1) && { (selectedItem && selectedItem.stuffData.rarityLevel > -1) &&
<LayoutRarityLevelView className="top-2 end-2" position="absolute" level={ selectedItem.stuffData.rarityLevel } /> } <LayoutRarityLevelView className="top-2 end-2" position="absolute" level={ selectedItem.stuffData.rarityLevel } /> }
</Column> </Column>
{ selectedItem && { selectedItem &&
<Column grow justifyContent="between" gap={ 2 }> <Column grow justifyContent="between" gap={ 2 }>
<Column gap={ 1 } position={ !isTrading ? 'absolute' : 'relative' } className="bottom-1" style={ { width: !isTrading ? '39%' : '' } }> <Text grow>{ selectedItem.name }</Text>
<Text grow truncate bold>{ selectedItem.name }</Text> <Column gap={ 1 }>
{ (selectedItem.description) && <Text grow truncate small>{ selectedItem.description }</Text> }
{ (!isTrading) &&
<>
{ !!roomSession && { !!roomSession &&
<Button className="p-0 px-2" onClick={ event => attemptItemPlacement(selectedItem) }> <Button variant="success" onClick={ event => attemptItemPlacement(selectedItem) }>
{ LocalizeText('inventory.furni.placetoroom') } { LocalizeText('inventory.furni.placetoroom') }
</Button> } </Button> }
{ (selectedItem && selectedItem.isSellable) && { (selectedItem && selectedItem.isSellable) &&
<Button className="p-0 px-2" onClick={ event => attemptPlaceMarketplaceOffer(selectedItem) }> <Button onClick={ event => attemptPlaceMarketplaceOffer(selectedItem) }>
{ LocalizeText('inventory.marketplace.sell') } { LocalizeText('inventory.marketplace.sell') }
</Button> } </Button> }
</> { selectedItem &&
} <Button variant="danger" onClick={ event => attemptDeleteItem(selectedItem) }>
{ (isTrading) && { LocalizeText('inventory.furni.delete') }
<Column gap={ 1 } alignItems="start"> </Button> }
<input type="number" className="quantity-input remove-outline form-control" placeholder={ LocalizeText('catalog.bundlewidget.spinner.select.amount') } disabled={ selectedItem.getUnlockedCount() === 0 } value={ !quantity ? '' : quantity } onChange={ event => setQuantity(event.target.valueAsNumber) } />
<Button variant="secondary" disabled={ !quantity || selectedItem.getUnlockedCount() === 0 } onClick={ event => !quantity ? null : changeCount(selectedItem.getUnlockedCount()) }>{ LocalizeText('inventory.trading.areoffering') }</Button>
</Column>
}
</Column> </Column>
</Column> } </Column> }
</Column> </Column>

View File

@ -1,49 +1,158 @@
import { AdvancedMap } from '@nitrots/nitro-renderer'; import { IObjectData, TradingListAddItemComposer, TradingListAddItemsComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { GroupItem, LocalizeText, TradeState } from '../../../../api'; import { FaChevronLeft, FaChevronRight, FaLock, FaUnlock } from 'react-icons/fa';
import { FurniCategory, getGuildFurniType, GroupItem, IFurnitureItem, LocalizeText, NotificationAlertType, SendMessageComposer, TradeState } from '../../../../api';
import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, Text } from '../../../../common'; import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, Text } from '../../../../common';
import { useInventoryTrade } from '../../../../hooks'; import { useInventoryTrade, useNotification } from '../../../../hooks';
import { MAX_ITEMS_TO_TRADE, TABS, TAB_FURNITURE } from '../../constants'; import { InventoryFurnitureSearchView } from './InventoryFurnitureSearchView';
interface InventoryTradeViewProps interface InventoryTradeViewProps
{ {
currentTab: string;
setCurrentTab: (value: string) => void;
cancelTrade: () => void; cancelTrade: () => void;
} }
const MAX_ITEMS_TO_TRADE: number = 9;
export const InventoryTradeView: FC<InventoryTradeViewProps> = props => export const InventoryTradeView: FC<InventoryTradeViewProps> = props =>
{ {
const { currentTab = null, setCurrentTab = null, cancelTrade = null } = props; const { cancelTrade = null } = props;
const [ groupItem, setGroupItem ] = useState<GroupItem>(null);
const [ ownGroupItem, setOwnGroupItem ] = useState<GroupItem>(null); const [ ownGroupItem, setOwnGroupItem ] = useState<GroupItem>(null);
const [ otherGroupItem, setOtherGroupItem ] = useState<GroupItem>(null); const [ otherGroupItem, setOtherGroupItem ] = useState<GroupItem>(null);
const [ filteredGroupItems, setFilteredGroupItems ] = useState<GroupItem[]>(null);
const [ countdownTick, setCountdownTick ] = useState(3); const [ countdownTick, setCountdownTick ] = useState(3);
const { ownUser = null, otherUser = null, tradeState = TradeState.TRADING_STATE_READY, progressTrade = null, removeItem = null, setTradeState = null } = useInventoryTrade(); const [ quantity, setQuantity ] = useState<number>(1);
const { ownUser = null, otherUser = null, groupItems = [], tradeState = TradeState.TRADING_STATE_READY, progressTrade = null, removeItem = null, setTradeState = null } = useInventoryTrade();
const { simpleAlert = null } = useNotification();
const canTradeItem = (isWallItem: boolean, spriteId: number, category: number, groupable: boolean, stuffData: IObjectData) =>
{
if(!ownUser || ownUser.accepts || !ownUser.userItems) return false;
if(ownUser.userItems.length < MAX_ITEMS_TO_TRADE) return true;
if(!groupable) return false;
let type = spriteId.toString();
if(category === FurniCategory.POSTER)
{
type = ((type + 'poster') + stuffData.getLegacyString());
}
else
{
if(category === FurniCategory.GUILD_FURNI)
{
type = getGuildFurniType(spriteId, stuffData);
}
else
{
type = (((isWallItem) ? 'I' : 'S') + type);
}
}
return !!ownUser.userItems.getValue(type);
}
const attemptItemOffer = (count: number) =>
{
if(!groupItem) return;
const tradeItems = groupItem.getTradeItems(count);
if(!tradeItems || !tradeItems.length) return;
let coreItem: IFurnitureItem = null;
const itemIds: number[] = [];
for(const item of tradeItems)
{
itemIds.push(item.id);
if(!coreItem) coreItem = item;
}
const ownItemCount = ownUser.userItems.length;
if((ownItemCount + itemIds.length) <= 1500)
{
if(!coreItem.isGroupable && (itemIds.length))
{
SendMessageComposer(new TradingListAddItemComposer(itemIds.pop()));
}
else
{
const tradeIds: number[] = [];
for(const itemId of itemIds)
{
if(canTradeItem(coreItem.isWallItem, coreItem.type, coreItem.category, coreItem.isGroupable, coreItem.stuffData))
{
tradeIds.push(itemId);
}
}
if(tradeIds.length)
{
if(tradeIds.length === 1)
{
SendMessageComposer(new TradingListAddItemComposer(tradeIds.pop()));
}
else
{
SendMessageComposer(new TradingListAddItemsComposer(...tradeIds));
}
}
}
}
else
{
simpleAlert(LocalizeText('trading.items.too_many_items.desc'), NotificationAlertType.DEFAULT, null, null, LocalizeText('trading.items.too_many_items.title'));
}
}
const getLockIcon = (accepts: boolean) => const getLockIcon = (accepts: boolean) =>
{ {
if(accepts) if(accepts)
{ {
return <Base className="mt-auto mb-5 pb-5 icon icon-lock-locked" /> return <FaLock className="text-success fa-icon" />
} }
else else
{ {
return <Base className="mt-auto mb-5 pb-5 icon icon-lock-open" /> return <FaUnlock className="text-danger fa-icon" />
} }
} }
const getTotalCredits = (items: AdvancedMap<string, GroupItem>): number => const updateQuantity = (value: number, totalItemCount: number) =>
{ {
return items.getValues().map( item => Number(item.iconUrl.split('/')[item.iconUrl.split('/').length - 1]?.split('_')[1]) * item.items.length ).reduce((acc, cur) => acc + (isNaN(cur) ? 0 : cur), 0); 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(() => useEffect(() =>
{ {
if(tradeState !== TradeState.TRADING_STATE_COUNTDOWN) return; if(tradeState !== TradeState.TRADING_STATE_COUNTDOWN) return;
setCountdownTick(3); setCountdownTick(3);
const interval = window.setInterval(() => const interval = setInterval(() =>
{ {
setCountdownTick(prevValue => setCountdownTick(prevValue =>
{ {
@ -68,18 +177,49 @@ export const InventoryTradeView: FC<InventoryTradeViewProps> = props =>
if((tradeState === TradeState.TRADING_STATE_READY) || !ownUser || !otherUser) return null; if((tradeState === TradeState.TRADING_STATE_READY) || !ownUser || !otherUser) return null;
return ( return (
<Column> <Grid>
<Column fullWidth size={ 12 } overflow="hidden" className="trade-bg p-2 text-black">
{ currentTab === TAB_FURNITURE &&
<>
<Text small>{ LocalizeText('inventory.trading.info.add') }</Text>
<Grid gap={ 0 } overflow="hidden" className="px-2">
<Column size={ 4 } overflow="hidden"> <Column size={ 4 } overflow="hidden">
<Flex> <InventoryFurnitureSearchView groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } />
{ (ownUser.accepts) && <Base className="icon icon-confirmed" /> } <Flex column fullHeight justifyContent="between" overflow="hidden" gap={ 2 }>
<small className={ ownUser.accepts ? 'px-2' : '' }><b>{ LocalizeText('inventory.trading.you') }</b> { LocalizeText('inventory.trading.areoffering') }</small> <AutoGrid columnCount={ 3 }>
{ filteredGroupItems && (filteredGroupItems.length > 0) && filteredGroupItems.map((item, index) =>
{
const count = item.getUnlockedCount();
return (
<LayoutGridItem key={ index } className={ !count ? 'opacity-0-5 ' : '' } itemImage={ item.iconUrl } itemCount={ count } itemActive={ (groupItem === item) } itemUniqueNumber={ item.stuffData.uniqueNumber } onClick={ event => (count && setGroupItem(item)) } onDoubleClick={ event => attemptItemOffer(1) }>
{ ((count > 0) && (groupItem === item)) &&
<Button position="absolute" variant="success" className="trade-button bottom-1 end-1" onClick={ event => attemptItemOffer(1) }>
<FaChevronRight className="fa-icon" />
</Button>
}
</LayoutGridItem>
);
}) }
</AutoGrid>
<Column gap={ 1 } alignItems="end">
<Grid overflow="hidden">
<Column size={ 6 } overflow="hidden">
<input type="number" className="form-control form-control-sm quantity-input" placeholder={ LocalizeText('catalog.bundlewidget.spinner.select.amount') } disabled={ !groupItem } value={ quantity } onChange={ event => setQuantity(event.target.valueAsNumber) } />
</Column>
<Column size={ 6 } overflow="hidden">
<Button variant="secondary" disabled={ !groupItem } onClick={ event => changeCount(groupItem.getUnlockedCount()) }>{ LocalizeText('inventory.trading.areoffering') }</Button>
</Column>
</Grid>
<Base fullWidth className="badge bg-muted text-black">
{ groupItem ? groupItem.name : LocalizeText('catalog_selectproduct') }
</Base>
</Column>
</Flex> </Flex>
<AutoGrid columnCount={ 3 } columnMinWidth={ 35 } columnMinHeight={ 35 }> </Column>
<Column size={ 8 } overflow="hidden">
<Grid overflow="hidden">
<Column size={ 6 } overflow="hidden">
<Flex justifyContent="between" alignItems="center">
<Text>{ LocalizeText('inventory.trading.you') } { LocalizeText('inventory.trading.areoffering') }:</Text>
{ getLockIcon(ownUser.accepts) }
</Flex>
<AutoGrid columnCount={ 3 }>
{ Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) => { Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) =>
{ {
const item = (ownUser.userItems.getWithIndex(i) || null); const item = (ownUser.userItems.getWithIndex(i) || null);
@ -89,26 +229,23 @@ export const InventoryTradeView: FC<InventoryTradeViewProps> = props =>
return ( return (
<LayoutGridItem key={ i } itemActive={ (ownGroupItem === item) } itemImage={ item.iconUrl } itemCount={ item.getTotalCount() } itemUniqueNumber={ item.stuffData.uniqueNumber } onClick={ event => setOwnGroupItem(item) } onDoubleClick={ event => removeItem(item) }> <LayoutGridItem key={ i } itemActive={ (ownGroupItem === item) } itemImage={ item.iconUrl } itemCount={ item.getTotalCount() } itemUniqueNumber={ item.stuffData.uniqueNumber } onClick={ event => setOwnGroupItem(item) } onDoubleClick={ event => removeItem(item) }>
{ (ownGroupItem === item) && { (ownGroupItem === item) &&
<Base position="absolute" onClick={ event => removeItem(item) } /> } <Button position="absolute" variant="danger" className="trade-button bottom-1 start-1" onClick={ event => removeItem(item) }>
<FaChevronLeft className="fa-icon" />
</Button> }
</LayoutGridItem> </LayoutGridItem>
); );
}) } }) }
</AutoGrid> </AutoGrid>
<Column gap={ 0 } fullWidth> <Base fullWidth className="badge bg-muted text-black">
<small>{ LocalizeText('inventory.trading.info.itemcount', [ 'value' ], [ ownUser.itemCount.toString() ]) }</small> { ownGroupItem ? ownGroupItem.name : LocalizeText('catalog_selectproduct') }
<small className="credits-align">{ LocalizeText('inventory.trading.info.creditvalue.own', [ 'value' ], [ getTotalCredits(ownUser.userItems).toString() ]) }</small> </Base>
</Column> </Column>
</Column> <Column size={ 6 } overflow="hidden">
<Flex className="lock-design-left"> <Flex justifyContent="between" alignItems="center">
{ getLockIcon(ownUser.accepts) } <Text>{ otherUser.userName } { LocalizeText('inventory.trading.isoffering') }:</Text>
{ getLockIcon(otherUser.accepts) }
</Flex> </Flex>
<Flex className="divisor"></Flex> <AutoGrid columnCount={ 3 }>
<Column size={ 4 } overflow="hidden">
<Flex>
{ (otherUser.accepts) && <Base className="icon icon-confirmed" /> }
<small className={ otherUser.accepts ? 'px-2' : '' }><b>{ otherUser.userName }</b> { LocalizeText('inventory.trading.isoffering') }</small>
</Flex>
<AutoGrid columnCount={ 3 } columnMinWidth={ 35 } columnMinHeight={ 35 }>
{ Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) => { Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) =>
{ {
const item = (otherUser.userItems.getWithIndex(i) || null); const item = (otherUser.userItems.getWithIndex(i) || null);
@ -118,30 +255,15 @@ export const InventoryTradeView: FC<InventoryTradeViewProps> = props =>
return <LayoutGridItem key={ i } itemActive={ (otherGroupItem === item) } itemImage={ item.iconUrl } itemCount={ item.getTotalCount() } itemUniqueNumber={ item.stuffData.uniqueNumber } onClick={ event => setOtherGroupItem(item) } />; return <LayoutGridItem key={ i } itemActive={ (otherGroupItem === item) } itemImage={ item.iconUrl } itemCount={ item.getTotalCount() } itemUniqueNumber={ item.stuffData.uniqueNumber } onClick={ event => setOtherGroupItem(item) } />;
}) } }) }
</AutoGrid> </AutoGrid>
<Column gap={ 0 } fullWidth> <Base fullWidth className="badge bg-muted w-100 text-black">
<small>{ LocalizeText('inventory.trading.info.itemcount', [ 'value' ], [ otherUser.itemCount.toString() ]) }</small> { otherGroupItem ? otherGroupItem.name : LocalizeText('catalog_selectproduct') }
<small>{ LocalizeText('inventory.trading.info.creditvalue', [ 'value' ], [ getTotalCredits(otherUser.userItems).toString() ]) }</small> </Base>
</Column> </Column>
</Column>
<Flex className="lock-design-right">
{ getLockIcon(otherUser.accepts) }
</Flex>
</Grid> </Grid>
</>
}
{ currentTab !== TAB_FURNITURE &&
<>
<Flex className="px-2">
<Base className="flex-shrink-0 icon icon-report-room" />
<Text bold>{ LocalizeText('inventory.trading.minimized.trade_in_progress') }</Text>
</Flex>
</>
}
<Flex grow justifyContent="between"> <Flex grow justifyContent="between">
{ (currentTab === TAB_FURNITURE) && <Button variant="danger" onClick={ cancelTrade }>{ LocalizeText('generic.cancel') }</Button>
<>
{ (tradeState === TradeState.TRADING_STATE_READY) && { (tradeState === TradeState.TRADING_STATE_READY) &&
<Button variant="secondary" disabled={ (!ownUser.itemCount && !otherUser.itemCount) } onClick={ progressTrade }>{ LocalizeText('inventory.trading.accept') }</Button> } <Button variant="secondary" disabled={ (!ownUser.itemCount && !otherUser.itemCount) } className="text-black" onClick={ progressTrade }>{ LocalizeText('inventory.trading.accept') }</Button> }
{ (tradeState === TradeState.TRADING_STATE_RUNNING) && { (tradeState === TradeState.TRADING_STATE_RUNNING) &&
<Button variant="secondary" disabled={ (!ownUser.itemCount && !otherUser.itemCount) } onClick={ progressTrade }>{ LocalizeText(ownUser.accepts ? 'inventory.trading.modify' : 'inventory.trading.accept') }</Button> } <Button variant="secondary" disabled={ (!ownUser.itemCount && !otherUser.itemCount) } onClick={ progressTrade }>{ LocalizeText(ownUser.accepts ? 'inventory.trading.modify' : 'inventory.trading.accept') }</Button> }
{ (tradeState === TradeState.TRADING_STATE_COUNTDOWN) && { (tradeState === TradeState.TRADING_STATE_COUNTDOWN) &&
@ -150,14 +272,8 @@ export const InventoryTradeView: FC<InventoryTradeViewProps> = props =>
<Button variant="secondary" onClick={ progressTrade }>{ LocalizeText('inventory.trading.button.restore') }</Button> } <Button variant="secondary" onClick={ progressTrade }>{ LocalizeText('inventory.trading.button.restore') }</Button> }
{ (tradeState === TradeState.TRADING_STATE_CONFIRMED) && { (tradeState === TradeState.TRADING_STATE_CONFIRMED) &&
<Button variant="secondary">{ LocalizeText('inventory.trading.info.waiting') }</Button> } <Button variant="secondary">{ LocalizeText('inventory.trading.info.waiting') }</Button> }
</>
}
{ (currentTab !== TAB_FURNITURE) &&
<Button variant="secondary" onClick={ () => setCurrentTab(TABS[0]) }>{ LocalizeText('inventory.trading.minimized.continue_trade') }</Button>
}
<Button variant="danger" onClick={ cancelTrade }>{ LocalizeText('generic.cancel') }</Button>
</Flex> </Flex>
</Column> </Column>
</Column> </Grid>
); );
} }

View File

@ -1,24 +1,21 @@
import { MouseEventType } from "@nitrots/nitro-renderer"; import { MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, PropsWithChildren, useState } from "react"; import { FC, MouseEvent, PropsWithChildren, useState } from 'react';
import { import { attemptPetPlacement, IPetItem, UnseenItemCategory } from '../../../../api';
attemptPetPlacement, import { LayoutGridItem, LayoutPetImageView } from '../../../../common';
IPetItem, import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks';
UnseenItemCategory,
} from "../../../../api";
import { LayoutGridItem, LayoutPetImageView } from "../../../../common";
import { useInventoryPets, useInventoryUnseenTracker } from "../../../../hooks";
export const InventoryPetItemView: FC< export const InventoryPetItemView: FC<PropsWithChildren<{ petItem: IPetItem }>> = props =>
PropsWithChildren<{ petItem: IPetItem }> {
> = (props) => {
const { petItem = null, children = null, ...rest } = props; const { petItem = null, children = null, ...rest } = props;
const [isMouseDown, setMouseDown] = useState(false); const [ isMouseDown, setMouseDown ] = useState(false);
const { selectedPet = null, setSelectedPet = null } = useInventoryPets(); const { selectedPet = null, setSelectedPet = null } = useInventoryPets();
const { isUnseen } = useInventoryUnseenTracker(); const { isUnseen } = useInventoryUnseenTracker();
const unseen = isUnseen(UnseenItemCategory.PET, petItem.petData.id); const unseen = isUnseen(UnseenItemCategory.PET, petItem.petData.id);
const onMouseEvent = (event: MouseEvent) => { const onMouseEvent = (event: MouseEvent) =>
switch (event.type) { {
switch(event.type)
{
case MouseEventType.MOUSE_DOWN: case MouseEventType.MOUSE_DOWN:
setSelectedPet(petItem); setSelectedPet(petItem);
setMouseDown(true); setMouseDown(true);
@ -27,32 +24,20 @@ export const InventoryPetItemView: FC<
setMouseDown(false); setMouseDown(false);
return; return;
case MouseEventType.ROLL_OUT: case MouseEventType.ROLL_OUT:
if (!isMouseDown || !(petItem === selectedPet)) return; if(!isMouseDown || !(petItem === selectedPet)) return;
attemptPetPlacement(petItem); attemptPetPlacement(petItem);
return; return;
case "dblclick": case 'dblclick':
attemptPetPlacement(petItem); attemptPetPlacement(petItem);
return; return;
} }
}; }
return ( return (
<LayoutGridItem <LayoutGridItem itemActive={ (petItem === selectedPet) } itemUnseen={ unseen } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } onDoubleClick={ onMouseEvent } { ...rest }>
itemActive={petItem === selectedPet} <LayoutPetImageView figure={ petItem.petData.figureData.figuredata } direction={ 3 } headOnly={ true } />
itemUnseen={unseen} { children }
onMouseDown={onMouseEvent}
onMouseUp={onMouseEvent}
onMouseOut={onMouseEvent}
onDoubleClick={onMouseEvent}
{...rest}
>
<LayoutPetImageView
figure={petItem.petData.figureData.figuredata}
direction={3}
headOnly={true}
/>
{children}
</LayoutGridItem> </LayoutGridItem>
); );
}; }

View File

@ -1,21 +1,20 @@
import { IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer'; import { IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { GetRoomEngine, LocalizeText, UnseenItemCategory, attemptPetPlacement } from '../../../../api'; import { attemptPetPlacement, GetRoomEngine, LocalizeText, UnseenItemCategory } from '../../../../api';
import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common'; import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common';
import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks'; import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks';
import { InventoryCategoryEmptyViewPets } from '../InventoryCategoryEmptyViewPets'; import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryPetItemView } from './InventoryPetItemView'; import { InventoryPetItemView } from './InventoryPetItemView';
interface InventoryPetViewProps interface InventoryPetViewProps
{ {
roomSession: IRoomSession; roomSession: IRoomSession;
roomPreviewer: RoomPreviewer; roomPreviewer: RoomPreviewer;
isTrading: boolean;
} }
export const InventoryPetView: FC<InventoryPetViewProps> = props => export const InventoryPetView: FC<InventoryPetViewProps> = props =>
{ {
const { roomSession = null, roomPreviewer = null, isTrading = false } = props; const { roomSession = null, roomPreviewer = null } = props;
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const { petItems = null, selectedPet = null, activate = null, deactivate = null } = useInventoryPets(); const { petItems = null, selectedPet = null, activate = null, deactivate = null } = useInventoryPets();
const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker(); const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker();
@ -64,7 +63,7 @@ export const InventoryPetView: FC<InventoryPetViewProps> = props =>
return () => setIsVisible(false); return () => setIsVisible(false);
}, []); }, []);
if(!petItems || !petItems.length) return <InventoryCategoryEmptyViewPets title={ LocalizeText('inventory.empty.pets.title') } desc={ LocalizeText('inventory.empty.pets.desc') } isTrading={ isTrading } />; if(!petItems || !petItems.length) return <InventoryCategoryEmptyView title={ LocalizeText('inventory.empty.pets.title') } desc={ LocalizeText('inventory.empty.pets.desc') } />;
return ( return (
<Grid> <Grid>
@ -79,7 +78,7 @@ export const InventoryPetView: FC<InventoryPetViewProps> = props =>
</Column> </Column>
{ selectedPet && selectedPet.petData && { selectedPet && selectedPet.petData &&
<Column grow justifyContent="between" gap={ 2 }> <Column grow justifyContent="between" gap={ 2 }>
<Text grow truncate>{ selectedPet.petData.name }</Text> <Text grow>{ selectedPet.petData.name }</Text>
{ !!roomSession && { !!roomSession &&
<Button variant="success" onClick={ event => attemptPetPlacement(selectedPet) }> <Button variant="success" onClick={ event => attemptPetPlacement(selectedPet) }>
{ LocalizeText('inventory.furni.placetoroom') } { LocalizeText('inventory.furni.placetoroom') }

File diff suppressed because one or more lines are too long