diff --git a/src/components/inventory/InventoryView.scss b/src/components/inventory/InventoryView.scss index ef1466e..62815ad 100644 --- a/src/components/inventory/InventoryView.scss +++ b/src/components/inventory/InventoryView.scss @@ -1,7 +1,10 @@ .nitro-inventory { - width: $inventory-width; height: $inventory-height; - min-width: 326px; + min-height: $inventory-height; + max-height: $inventory-height + 150px; + width: $inventory-width; + min-width: $inventory-width; + max-width: $inventory-width; color: #FFF; input { @@ -11,29 +14,169 @@ &.trading { width: 535px; - min-height: 505px; + min-height: $inventory-height + 315px !important; + max-width: $inventory-width + 15px !important; + min-width: $inventory-width + 15px !important; .trading-inventory { - height: 200px; + 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 { - background: url('@/assets/images/inventory/empty.png') no-repeat; - width: 129px; - height: 181px; - } + .empty-image { + background: url("@/assets/images/inventory/empty.png"); + background-repeat: no-repeat; + width: 129px; + 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; + } - .trade-button { - min-height: 0; - font-size: 8px; - padding: 1px 2px; - z-index: 5; - } + .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-bg { - border-image-source: url(@/assets/images/inventory/trading_bg.png); + .trade-button { + min-height: 0; + font-size: 8px; + padding: 1px 2px; + 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; +} \ No newline at end of file diff --git a/src/components/inventory/InventoryView.tsx b/src/components/inventory/InventoryView.tsx index 7cf0969..1fac18f 100644 --- a/src/components/inventory/InventoryView.tsx +++ b/src/components/inventory/InventoryView.tsx @@ -1,8 +1,10 @@ import { BadgePointLimitsEvent, ILinkEventTracker, IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { AddEventLinkTracker, GetLocalization, GetRoomEngine, isObjectMoverRequested, LocalizeText, RemoveLinkEventTracker, setObjectMoverRequested, UnseenItemCategory } from '../../api'; +import { AddEventLinkTracker, GetLocalization, GetRoomEngine, GroupItem, LocalizeText, RemoveLinkEventTracker, isObjectMoverRequested, setObjectMoverRequested } from '../../api'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; -import { useInventoryTrade, useInventoryUnseenTracker, useMessageEvent, useRoomEngineEvent, useRoomSessionManagerEvent } from '../../hooks'; +import { useInventoryBadges, useInventoryFurni, 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 { InventoryBotView } from './views/bot/InventoryBotView'; import { InventoryFurnitureView } from './views/furniture/InventoryFurnitureView'; @@ -10,12 +12,6 @@ import { InventoryTradeView } from './views/furniture/InventoryTradeView'; import { InventoryPetView } from './views/pet/InventoryPetView'; 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 => { @@ -23,8 +19,12 @@ export const InventoryView: FC<{}> = props => const [ currentTab, setCurrentTab ] = useState(TABS[0]); const [ roomSession, setRoomSession ] = useState(null); const [ roomPreviewer, setRoomPreviewer ] = useState(null); + const [ filteredGroupItems, setFilteredGroupItems ] = useState([]); + const [ filteredBadgeCodes, setFilteredBadgeCodes ] = useState([]); const { isTrading = false, stopTrading = null } = useInventoryTrade(); - const { getCount = null, resetCategory = null } = useInventoryUnseenTracker(); + const { getCount = null } = useInventoryUnseenTracker(); + const { groupItems = [] } = useInventoryFurni(); + const { badgeCodes = [] } = useInventoryBadges(); const onClose = () => { @@ -119,36 +119,34 @@ export const InventoryView: FC<{}> = props => if(!isVisible) return null; return ( - + - { !isTrading && - <> - - { TABS.map((name, index) => - { - return ( - setCurrentTab(name) } count={ getCount(UNSEEN_CATEGORIES[index]) }> - { LocalizeText(name) } - - ); - }) } - - - { (currentTab === TAB_FURNITURE ) && - } - { (currentTab === TAB_BOTS ) && - } - { (currentTab === TAB_PETS ) && - } - { (currentTab === TAB_BADGES ) && - } - - } - { isTrading && - - - } + <> + + { TABS.map((name, index) => + { + return ( + setCurrentTab(name) } count={ getCount(UNSEEN_CATEGORIES[index]) }> + { LocalizeText(name) } + + ); + }) } + + + { (currentTab !== TAB_PETS && currentTab !== TAB_BOTS) && } + { (currentTab === TAB_FURNITURE ) && + } + { (currentTab === TAB_PETS ) && + } + { (currentTab === TAB_BADGES ) && + } + { (currentTab === TAB_BOTS ) && + } + + { isTrading && setCurrentTab(e) } cancelTrade={ onClose } /> } + - - ); -} + + + ); +} \ No newline at end of file diff --git a/src/components/inventory/views/InventoryCategoryEmptyView.tsx b/src/components/inventory/views/InventoryCategoryEmptyView.tsx index f250364..b3b92ee 100644 --- a/src/components/inventory/views/InventoryCategoryEmptyView.tsx +++ b/src/components/inventory/views/InventoryCategoryEmptyView.tsx @@ -1,26 +1,41 @@ import { FC } from 'react'; -import { Column, Grid, GridProps, Text } from '../../../common'; +import { CreateLinkEvent, LocalizeText } from '../../../api'; +import { Button, Column, Flex, Grid, GridProps, Text } from '../../../common'; export interface InventoryCategoryEmptyViewProps extends GridProps { title: string; desc: string; + isTrading?: boolean; } export const InventoryCategoryEmptyView: FC = props => { - const { title = '', desc = '', children = null, ...rest } = props; + const { title = '', desc = '', isTrading = false, children = null, ...rest } = props; return ( - - -
- - - { title } - { desc } - - { children } - - ); -} + + +
+ + +
+ + {title} + + + {" "} + {desc} + +
+
+ { !isTrading && + + + + } + + {children} + + ); +}; \ No newline at end of file diff --git a/src/components/inventory/views/InventoryCategoryEmptyViewBots.tsx b/src/components/inventory/views/InventoryCategoryEmptyViewBots.tsx new file mode 100644 index 0000000..bbb89bf --- /dev/null +++ b/src/components/inventory/views/InventoryCategoryEmptyViewBots.tsx @@ -0,0 +1,40 @@ +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 ( + + +
+ + +
+ + {title} + + + {" "} + {desc} + +
+
+ { !isTrading && + + + + } + + {children} + + ); +}; diff --git a/src/components/inventory/views/InventoryCategoryEmptyViewPets.tsx b/src/components/inventory/views/InventoryCategoryEmptyViewPets.tsx new file mode 100644 index 0000000..ba9221e --- /dev/null +++ b/src/components/inventory/views/InventoryCategoryEmptyViewPets.tsx @@ -0,0 +1,40 @@ +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 ( + + +
+ + +
+ + {title} + + + {" "} + {desc} + +
+
+ { !isTrading && + + + + } + + {children} + + ); +}; \ No newline at end of file diff --git a/src/components/inventory/views/InventoryCategoryFilterView.tsx b/src/components/inventory/views/InventoryCategoryFilterView.tsx new file mode 100644 index 0000000..6f9552d --- /dev/null +++ b/src/components/inventory/views/InventoryCategoryFilterView.tsx @@ -0,0 +1,107 @@ +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>; + setBadgeCodes: Dispatch>; +} + +export const InventoryCategoryFilterView: FC = props => +{ + const { currentTab = null, groupItems = [], badgeCodes = [], setGroupItems = null, setBadgeCodes = null } = props; + const [ filterType, setFilterType ] = useState(InventoryFilterType.EVERYTHING); + const [ filterPlace, setFilterPlace ] = useState(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 ( + + + + setSearchValue(event.target.value) } /> + + { (searchValue && !!searchValue.length) && setSearchValue('') } /> } + + { (currentTab !== TAB_BADGES) && + <> + + + + + + + + } + + ); +} diff --git a/src/components/inventory/views/badge/InventoryBadgeItemView.tsx b/src/components/inventory/views/badge/InventoryBadgeItemView.tsx index bb6c54a..198c13d 100644 --- a/src/components/inventory/views/badge/InventoryBadgeItemView.tsx +++ b/src/components/inventory/views/badge/InventoryBadgeItemView.tsx @@ -11,7 +11,7 @@ export const InventoryBadgeItemView: FC const unseen = isUnseen(UnseenItemCategory.BADGE, getBadgeId(badgeCode)); return ( - setSelectedBadgeCode(badgeCode) } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } { ...rest }> + setSelectedBadgeCode(badgeCode) } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } { ...rest }> { children } diff --git a/src/components/inventory/views/badge/InventoryBadgeView.tsx b/src/components/inventory/views/badge/InventoryBadgeView.tsx index 96da219..0ffc885 100644 --- a/src/components/inventory/views/badge/InventoryBadgeView.tsx +++ b/src/components/inventory/views/badge/InventoryBadgeView.tsx @@ -1,14 +1,21 @@ import { FC, useEffect, useState } from 'react'; import { LocalizeBadgeName, LocalizeText, UnseenItemCategory } from '../../../../api'; import { AutoGrid, Button, Column, Flex, Grid, LayoutBadgeImageView, Text } from '../../../../common'; -import { useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks'; +import { useAchievements, useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks'; import { InventoryBadgeItemView } from './InventoryBadgeItemView'; -export const InventoryBadgeView: FC<{}> = props => +interface InventoryBadgeViewProps { + filteredBadgeCodes: string[]; +} + +export const InventoryBadgeView: FC = props => +{ + const { filteredBadgeCodes = [] } = props; const [ isVisible, setIsVisible ] = useState(false); - const { badgeCodes = [], activeBadgeCodes = [], selectedBadgeCode = null, isWearingBadge = null, canWearBadges = null, toggleBadge = null, getBadgeId = null, activate = null, deactivate = null } = useInventoryBadges(); + const { activeBadgeCodes = [], selectedBadgeCode = null, isWearingBadge = null, canWearBadges = null, toggleBadge = null, getBadgeId = null, activate = null, deactivate = null } = useInventoryBadges(); const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker(); + const { achievementScore = 0 } = useAchievements(); useEffect(() => { @@ -34,33 +41,41 @@ export const InventoryBadgeView: FC<{}> = props => }, []); return ( - - - - { badgeCodes && (badgeCodes.length > 0) && badgeCodes.map((badgeCode, index) => - { - if(isWearingBadge(badgeCode)) return null; +
+ + + + { filteredBadgeCodes && filteredBadgeCodes.length > 0 && filteredBadgeCodes.map((badgeCode, index) => + { + if (isWearingBadge(badgeCode)) return null; - return - }) } - - - - - { LocalizeText('inventory.badges.activebadges') } - - { activeBadgeCodes && (activeBadgeCodes.length > 0) && activeBadgeCodes.map((badgeCode, index) => ) } + return ; + }) } + + + { LocalizeText('inventory.badges.activebadges') } + + { activeBadgeCodes && (activeBadgeCodes.length > 0) && activeBadgeCodes.map((badgeCode, index) => ) } + + + + + { !!selectedBadgeCode && - - + + - { LocalizeBadgeName(selectedBadgeCode) } + { LocalizeBadgeName(selectedBadgeCode) } - - } + + + } +
+ { LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) } +
- +
); } diff --git a/src/components/inventory/views/bot/InventoryBotItemView.tsx b/src/components/inventory/views/bot/InventoryBotItemView.tsx index d213069..9bc89a2 100644 --- a/src/components/inventory/views/bot/InventoryBotItemView.tsx +++ b/src/components/inventory/views/bot/InventoryBotItemView.tsx @@ -1,43 +1,58 @@ -import { MouseEventType } from '@nitrots/nitro-renderer'; -import { FC, MouseEvent, PropsWithChildren, useState } from 'react'; -import { attemptBotPlacement, IBotItem, UnseenItemCategory } from '../../../../api'; -import { LayoutAvatarImageView, LayoutGridItem } from '../../../../common'; -import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks'; +import { MouseEventType } from "@nitrots/nitro-renderer"; +import { FC, MouseEvent, PropsWithChildren, useState } from "react"; +import { + attemptBotPlacement, + IBotItem, + UnseenItemCategory, +} from "../../../../api"; +import { LayoutAvatarImageView, LayoutGridItem } from "../../../../common"; +import { useInventoryBots, useInventoryUnseenTracker } from "../../../../hooks"; -export const InventoryBotItemView: FC> = props => -{ - const { botItem = null, children = null, ...rest } = props; - const [ isMouseDown, setMouseDown ] = useState(false); - const { selectedBot = null, setSelectedBot = null } = useInventoryBots(); - const { isUnseen = null } = useInventoryUnseenTracker(); - const unseen = isUnseen(UnseenItemCategory.BOT, botItem.botData.id); +export const InventoryBotItemView: FC< + PropsWithChildren<{ botItem: IBotItem }> +> = (props) => { + const { botItem = null, children = null, ...rest } = props; + const [isMouseDown, setMouseDown] = useState(false); + const { selectedBot = null, setSelectedBot = null } = useInventoryBots(); + const { isUnseen = null } = useInventoryUnseenTracker(); + const unseen = isUnseen(UnseenItemCategory.BOT, botItem.botData.id); - const onMouseEvent = (event: MouseEvent) => - { - switch(event.type) - { - case MouseEventType.MOUSE_DOWN: - setSelectedBot(botItem); - setMouseDown(true); - return; - case MouseEventType.MOUSE_UP: - setMouseDown(false); - return; - case MouseEventType.ROLL_OUT: - if(!isMouseDown || (selectedBot !== botItem)) return; + const onMouseEvent = (event: MouseEvent) => { + switch (event.type) { + case MouseEventType.MOUSE_DOWN: + setSelectedBot(botItem); + setMouseDown(true); + return; + case MouseEventType.MOUSE_UP: + setMouseDown(false); + return; + case MouseEventType.ROLL_OUT: + if (!isMouseDown || selectedBot !== botItem) return; - attemptBotPlacement(botItem); - return; - case 'dblclick': - attemptBotPlacement(botItem); - return; - } + attemptBotPlacement(botItem); + return; + case "dblclick": + attemptBotPlacement(botItem); + return; } + }; - return ( - - - { children } - - ); -} + return ( + + + {children} + + ); +}; diff --git a/src/components/inventory/views/bot/InventoryBotView.tsx b/src/components/inventory/views/bot/InventoryBotView.tsx index 8ea90d3..7977190 100644 --- a/src/components/inventory/views/bot/InventoryBotView.tsx +++ b/src/components/inventory/views/bot/InventoryBotView.tsx @@ -1,20 +1,21 @@ import { IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { attemptBotPlacement, GetRoomEngine, LocalizeText, UnseenItemCategory } from '../../../../api'; +import { GetRoomEngine, LocalizeText, UnseenItemCategory, attemptBotPlacement } from '../../../../api'; import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common'; import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks'; -import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView'; +import { InventoryCategoryEmptyViewBots } from '../InventoryCategoryEmptyViewBots'; import { InventoryBotItemView } from './InventoryBotItemView'; interface InventoryBotViewProps { roomSession: IRoomSession; roomPreviewer: RoomPreviewer; + isTrading: boolean; } export const InventoryBotView: FC = props => { - const { roomSession = null, roomPreviewer = null } = props; + const { roomSession = null, roomPreviewer = null, isTrading = false } = props; const [ isVisible, setIsVisible ] = useState(false); const { botItems = [], selectedBot = null, activate = null, deactivate = null } = useInventoryBots(); const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker(); @@ -64,7 +65,7 @@ export const InventoryBotView: FC = props => return () => setIsVisible(false); }, []); - if(!botItems || !botItems.length) return ; + if(!botItems || !botItems.length) return ; return ( @@ -81,11 +82,11 @@ export const InventoryBotView: FC = props => { selectedBot.botData.name } { !!roomSession && - } }
); -} +} \ No newline at end of file diff --git a/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx b/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx index f5e8481..cb91302 100644 --- a/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx +++ b/src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx @@ -1,12 +1,12 @@ import { MouseEventType } from '@nitrots/nitro-renderer'; import { FC, MouseEvent, useState } from 'react'; -import { attemptItemPlacement, GroupItem } from '../../../../api'; +import { GroupItem, attemptItemPlacement } from '../../../../api'; import { LayoutGridItem } from '../../../../common'; import { useInventoryFurni } from '../../../../hooks'; -export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props => +export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem, isTrading: boolean, attemptItemOffer: (count: number) => void, setGroupItem: (item: GroupItem) => void }> = props => { - const { groupItem = null, ...rest } = props; + const { groupItem = null, isTrading = null, attemptItemOffer = null, setGroupItem = null, ...rest } = props; const [ isMouseDown, setMouseDown ] = useState(false); const { selectedItem = null, setSelectedItem = null } = useInventoryFurni(); @@ -24,15 +24,16 @@ export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props => case MouseEventType.ROLL_OUT: if(!isMouseDown || !(groupItem === selectedItem)) return; - attemptItemPlacement(groupItem); + if (!isTrading) attemptItemPlacement(groupItem); return; case 'dblclick': - attemptItemPlacement(groupItem); + if (!isTrading) attemptItemPlacement(groupItem); + if (isTrading) (setGroupItem(groupItem), attemptItemOffer(1)) return; } } const count = groupItem.getUnlockedCount(); - - return ; + + return (count && setGroupItem(groupItem)) } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } onDoubleClick={ onMouseEvent } { ...rest } />; } diff --git a/src/components/inventory/views/furniture/InventoryTradeView.tsx b/src/components/inventory/views/furniture/InventoryTradeView.tsx index 27382cf..85bf28d 100644 --- a/src/components/inventory/views/furniture/InventoryTradeView.tsx +++ b/src/components/inventory/views/furniture/InventoryTradeView.tsx @@ -1,158 +1,49 @@ -import { IObjectData, TradingListAddItemComposer, TradingListAddItemsComposer } from '@nitrots/nitro-renderer'; +import { AdvancedMap } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { FaChevronLeft, FaChevronRight, FaLock, FaUnlock } from 'react-icons/fa'; -import { FurniCategory, getGuildFurniType, GroupItem, IFurnitureItem, LocalizeText, NotificationAlertType, SendMessageComposer, TradeState } from '../../../../api'; +import { GroupItem, LocalizeText, TradeState } from '../../../../api'; import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, Text } from '../../../../common'; -import { useInventoryTrade, useNotification } from '../../../../hooks'; -import { InventoryFurnitureSearchView } from './InventoryFurnitureSearchView'; +import { useInventoryTrade } from '../../../../hooks'; +import { MAX_ITEMS_TO_TRADE, TABS, TAB_FURNITURE } from '../../constants'; interface InventoryTradeViewProps { + currentTab: string; + setCurrentTab: (value: string) => void; cancelTrade: () => void; } -const MAX_ITEMS_TO_TRADE: number = 9; - export const InventoryTradeView: FC = props => { - const { cancelTrade = null } = props; - const [ groupItem, setGroupItem ] = useState(null); + const { currentTab = null, setCurrentTab = null, cancelTrade = null } = props; const [ ownGroupItem, setOwnGroupItem ] = useState(null); 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(); - - 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 { ownUser = null, otherUser = null, tradeState = TradeState.TRADING_STATE_READY, progressTrade = null, removeItem = null, setTradeState = null } = useInventoryTrade(); const getLockIcon = (accepts: boolean) => { if(accepts) { - return + return } else { - return + return } } - const updateQuantity = (value: number, totalItemCount: number) => + const getTotalCredits = (items: AdvancedMap): 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); + 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); } - const changeCount = (totalItemCount: number) => - { - updateQuantity(quantity, totalItemCount); - attemptItemOffer(quantity); - } - - useEffect(() => - { - setQuantity(1); - }, [ groupItem ]); - useEffect(() => { if(tradeState !== TradeState.TRADING_STATE_COUNTDOWN) return; setCountdownTick(3); - const interval = setInterval(() => + const interval = window.setInterval(() => { setCountdownTick(prevValue => { @@ -178,102 +69,93 @@ export const InventoryTradeView: FC = props => return ( - - - - - - { filteredGroupItems && (filteredGroupItems.length > 0) && filteredGroupItems.map((item, index) => - { - const count = item.getUnlockedCount(); + + { currentTab === TAB_FURNITURE && + <> + { LocalizeText('inventory.trading.info.add') } + + + + { (ownUser.accepts) && } + { LocalizeText('inventory.trading.you') } { LocalizeText('inventory.trading.areoffering') } + + + { Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) => + { + const item = (ownUser.userItems.getWithIndex(i) || null); - return ( - (count && setGroupItem(item)) } onDoubleClick={ event => attemptItemOffer(1) }> - { ((count > 0) && (groupItem === item)) && - - } - - ); - }) } - - - - - setQuantity(event.target.valueAsNumber) } /> + if(!item) return ; + + return ( + setOwnGroupItem(item) } onDoubleClick={ event => removeItem(item) }> + { (ownGroupItem === item) && + removeItem(item) } /> } + + ); + }) } + + + { LocalizeText('inventory.trading.info.itemcount', [ 'value' ], [ ownUser.itemCount.toString() ]) } + { LocalizeText('inventory.trading.info.creditvalue.own', [ 'value' ], [ getTotalCredits(ownUser.userItems).toString() ]) } + - - + + { getLockIcon(ownUser.accepts) } + + + + + { (otherUser.accepts) && } + { otherUser.userName } { LocalizeText('inventory.trading.isoffering') } + + + { Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) => + { + const item = (otherUser.userItems.getWithIndex(i) || null); + + if(!item) return ; + + return setOtherGroupItem(item) } />; + }) } + + + { LocalizeText('inventory.trading.info.itemcount', [ 'value' ], [ otherUser.itemCount.toString() ]) } + { LocalizeText('inventory.trading.info.creditvalue', [ 'value' ], [ getTotalCredits(otherUser.userItems).toString() ]) } + + + { getLockIcon(otherUser.accepts) } + - - { groupItem ? groupItem.name : LocalizeText('catalog_selectproduct') } - - - - - - - - - - { LocalizeText('inventory.trading.you') } { LocalizeText('inventory.trading.areoffering') }: + + } + { currentTab !== TAB_FURNITURE && + <> + + + { LocalizeText('inventory.trading.minimized.trade_in_progress') } - - { Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) => - { - const item = (ownUser.userItems.getWithIndex(i) || null); - - if(!item) return ; - - return ( - setOwnGroupItem(item) } onDoubleClick={ event => removeItem(item) }> - { (ownGroupItem === item) && - } - - ); - }) } - - - { ownGroupItem ? ownGroupItem.name : LocalizeText('catalog_selectproduct') } - - - { getLockIcon(ownUser.accepts) } - - - { otherUser.userName } { LocalizeText('inventory.trading.isoffering') }: - - - { Array.from(Array(MAX_ITEMS_TO_TRADE), (e, i) => - { - const item = (otherUser.userItems.getWithIndex(i) || null); - - if(!item) return ; - - return setOtherGroupItem(item) } />; - }) } - - - { otherGroupItem ? otherGroupItem.name : LocalizeText('catalog_selectproduct') } - - - { getLockIcon(otherUser.accepts) } - + + } + { (currentTab === TAB_FURNITURE) && + <> + { (tradeState === TradeState.TRADING_STATE_READY) && + } + { (tradeState === TradeState.TRADING_STATE_RUNNING) && + } + { (tradeState === TradeState.TRADING_STATE_COUNTDOWN) && + } + { (tradeState === TradeState.TRADING_STATE_CONFIRMING) && + } + { (tradeState === TradeState.TRADING_STATE_CONFIRMED) && + } + + } + { (currentTab !== TAB_FURNITURE) && + + } - { (tradeState === TradeState.TRADING_STATE_READY) && - } - { (tradeState === TradeState.TRADING_STATE_RUNNING) && - } - { (tradeState === TradeState.TRADING_STATE_COUNTDOWN) && - } - { (tradeState === TradeState.TRADING_STATE_CONFIRMING) && - } - { (tradeState === TradeState.TRADING_STATE_CONFIRMED) && - } diff --git a/src/components/inventory/views/pet/InventoryPetItemView.tsx b/src/components/inventory/views/pet/InventoryPetItemView.tsx index aad45f9..e0088f9 100644 --- a/src/components/inventory/views/pet/InventoryPetItemView.tsx +++ b/src/components/inventory/views/pet/InventoryPetItemView.tsx @@ -1,43 +1,58 @@ -import { MouseEventType } from '@nitrots/nitro-renderer'; -import { FC, MouseEvent, PropsWithChildren, useState } from 'react'; -import { attemptPetPlacement, IPetItem, UnseenItemCategory } from '../../../../api'; -import { LayoutGridItem, LayoutPetImageView } from '../../../../common'; -import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks'; +import { MouseEventType } from "@nitrots/nitro-renderer"; +import { FC, MouseEvent, PropsWithChildren, useState } from "react"; +import { + attemptPetPlacement, + IPetItem, + UnseenItemCategory, +} from "../../../../api"; +import { LayoutGridItem, LayoutPetImageView } from "../../../../common"; +import { useInventoryPets, useInventoryUnseenTracker } from "../../../../hooks"; -export const InventoryPetItemView: FC> = props => -{ - const { petItem = null, children = null, ...rest } = props; - const [ isMouseDown, setMouseDown ] = useState(false); - const { selectedPet = null, setSelectedPet = null } = useInventoryPets(); - const { isUnseen } = useInventoryUnseenTracker(); - const unseen = isUnseen(UnseenItemCategory.PET, petItem.petData.id); +export const InventoryPetItemView: FC< + PropsWithChildren<{ petItem: IPetItem }> +> = (props) => { + const { petItem = null, children = null, ...rest } = props; + const [isMouseDown, setMouseDown] = useState(false); + const { selectedPet = null, setSelectedPet = null } = useInventoryPets(); + const { isUnseen } = useInventoryUnseenTracker(); + const unseen = isUnseen(UnseenItemCategory.PET, petItem.petData.id); - const onMouseEvent = (event: MouseEvent) => - { - switch(event.type) - { - case MouseEventType.MOUSE_DOWN: - setSelectedPet(petItem); - setMouseDown(true); - return; - case MouseEventType.MOUSE_UP: - setMouseDown(false); - return; - case MouseEventType.ROLL_OUT: - if(!isMouseDown || !(petItem === selectedPet)) return; + const onMouseEvent = (event: MouseEvent) => { + switch (event.type) { + case MouseEventType.MOUSE_DOWN: + setSelectedPet(petItem); + setMouseDown(true); + return; + case MouseEventType.MOUSE_UP: + setMouseDown(false); + return; + case MouseEventType.ROLL_OUT: + if (!isMouseDown || !(petItem === selectedPet)) return; - attemptPetPlacement(petItem); - return; - case 'dblclick': - attemptPetPlacement(petItem); - return; - } + attemptPetPlacement(petItem); + return; + case "dblclick": + attemptPetPlacement(petItem); + return; } + }; - return ( - - - { children } - - ); -} + return ( + + + {children} + + ); +}; diff --git a/src/components/inventory/views/pet/InventoryPetView.tsx b/src/components/inventory/views/pet/InventoryPetView.tsx index 0855c98..c90276b 100644 --- a/src/components/inventory/views/pet/InventoryPetView.tsx +++ b/src/components/inventory/views/pet/InventoryPetView.tsx @@ -1,20 +1,21 @@ import { IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { attemptPetPlacement, GetRoomEngine, LocalizeText, UnseenItemCategory } from '../../../../api'; +import { GetRoomEngine, LocalizeText, UnseenItemCategory, attemptPetPlacement } from '../../../../api'; import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common'; import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks'; -import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView'; +import { InventoryCategoryEmptyViewPets } from '../InventoryCategoryEmptyViewPets'; import { InventoryPetItemView } from './InventoryPetItemView'; interface InventoryPetViewProps { roomSession: IRoomSession; roomPreviewer: RoomPreviewer; + isTrading: boolean; } export const InventoryPetView: FC = props => { - const { roomSession = null, roomPreviewer = null } = props; + const { roomSession = null, roomPreviewer = null, isTrading = false } = props; const [ isVisible, setIsVisible ] = useState(false); const { petItems = null, selectedPet = null, activate = null, deactivate = null } = useInventoryPets(); const { isUnseen = null, removeUnseen = null } = useInventoryUnseenTracker(); @@ -63,7 +64,7 @@ export const InventoryPetView: FC = props => return () => setIsVisible(false); }, []); - if(!petItems || !petItems.length) return ; + if(!petItems || !petItems.length) return ; return ( @@ -80,11 +81,11 @@ export const InventoryPetView: FC = props => { selectedPet.petData.name } { !!roomSession && - } } ); -} +} \ No newline at end of file