From 5e3f028d2dbdb491101ede0f181c211d9f5e52eb Mon Sep 17 00:00:00 2001 From: duckietm Date: Fri, 3 May 2024 09:02:47 +0200 Subject: [PATCH] Fix: Inventory --- src/components/inventory/InventoryView.scss | 192 +++++++++-- src/components/inventory/InventoryView.tsx | 80 ++--- .../constants/InventoryFilterType.ts | 9 + .../constants/InventoryTabConstants.ts | 9 + src/components/inventory/constants/index.ts | 2 + .../views/InventoryCategoryFilterView.tsx | 107 ++++++ .../views/badge/InventoryBadgeItemView.tsx | 2 +- .../views/badge/InventoryBadgeView.tsx | 65 ++-- .../views/bot/InventoryBotItemView.tsx | 91 +++--- .../inventory/views/bot/InventoryBotView.tsx | 11 +- .../furniture/InventoryFurnitureItemView.tsx | 15 +- .../furniture/InventoryFurnitureView.tsx | 230 +++++++++---- .../views/furniture/InventoryTradeView.tsx | 306 ++++++------------ .../views/pet/InventoryPetItemView.tsx | 91 +++--- .../inventory/views/pet/InventoryPetView.tsx | 9 +- 15 files changed, 764 insertions(+), 455 deletions(-) create mode 100644 src/components/inventory/constants/InventoryFilterType.ts create mode 100644 src/components/inventory/constants/InventoryTabConstants.ts create mode 100644 src/components/inventory/constants/index.ts create mode 100644 src/components/inventory/views/InventoryCategoryFilterView.tsx diff --git a/src/components/inventory/InventoryView.scss b/src/components/inventory/InventoryView.scss index 8243e1a..62815ad 100644 --- a/src/components/inventory/InventoryView.scss +++ b/src/components/inventory/InventoryView.scss @@ -1,40 +1,182 @@ .nitro-inventory { - width: $inventory-width; height: $inventory-height; + min-height: $inventory-height; + max-height: $inventory-height + 150px; + width: $inventory-width; + min-width: $inventory-width; + max-width: $inventory-width; + color: #FFF; - .empty-image { - background: url('@/assets/images/inventory/empty.png') no-repeat; - width: 129px; - height: 181px; + input { + min-height: 20px; + max-height: 20px; } - .trade-button { - min-height: 0; - font-size: 8px; - padding: 1px 2px; - z-index: 5; + &.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 { + 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; + } + + .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 { + 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; } - .tab-icon { - width:18px; - height:18px; - background-position: center; - background-repeat: no-repeat; + .inventory-items { - &.tab-inventory-furni { - background-image: url(@/assets/images/inventory/furni.png); + 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; } - &.tab-inventory-bots { - background-image: url(@/assets/images/inventory/bots.png); + &.unseen { + background-color: #9BCA64; } - &.tab-inventory-furni-tab-pets { - background-image: url(@/assets/images/inventory/pets.png); - } - - &.tab-inventory-badges { - background-image: url(@/assets/images/inventory/ach.png); + .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 12c8dff..1fac18f 100644 --- a/src/components/inventory/InventoryView.tsx +++ b/src/components/inventory/InventoryView.tsx @@ -1,20 +1,17 @@ import { BadgePointLimitsEvent, ILinkEventTracker, IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; -import { AddEventLinkTracker, GetLocalization, GetRoomEngine, LocalizeText, RemoveLinkEventTracker, UnseenItemCategory, isObjectMoverRequested, setObjectMoverRequested } from '../../api'; -import { Base, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; -import { useInventoryTrade, useInventoryUnseenTracker, useMessageEvent, useRoomEngineEvent, useRoomSessionManagerEvent } from '../../hooks'; +import { AddEventLinkTracker, GetLocalization, GetRoomEngine, GroupItem, LocalizeText, RemoveLinkEventTracker, isObjectMoverRequested, setObjectMoverRequested } from '../../api'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; +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'; 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 => { @@ -22,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 = () => { @@ -118,35 +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]) }> - - - ); - }) } - - - { (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/constants/InventoryFilterType.ts b/src/components/inventory/constants/InventoryFilterType.ts new file mode 100644 index 0000000..26e3241 --- /dev/null +++ b/src/components/inventory/constants/InventoryFilterType.ts @@ -0,0 +1,9 @@ +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'; +} \ No newline at end of file diff --git a/src/components/inventory/constants/InventoryTabConstants.ts b/src/components/inventory/constants/InventoryTabConstants.ts new file mode 100644 index 0000000..db46e7c --- /dev/null +++ b/src/components/inventory/constants/InventoryTabConstants.ts @@ -0,0 +1,9 @@ +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; \ No newline at end of file diff --git a/src/components/inventory/constants/index.ts b/src/components/inventory/constants/index.ts new file mode 100644 index 0000000..a88e32f --- /dev/null +++ b/src/components/inventory/constants/index.ts @@ -0,0 +1,2 @@ +export * from './InventoryFilterType'; +export * from './InventoryTabConstants'; \ 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 ab2bbbb..aba3f86 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() ]) } +
- +
); -} +} \ No newline at end of file diff --git a/src/components/inventory/views/bot/InventoryBotItemView.tsx b/src/components/inventory/views/bot/InventoryBotItemView.tsx index 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 4d792e9..4c112c2 100644 --- a/src/components/inventory/views/bot/InventoryBotView.tsx +++ b/src/components/inventory/views/bot/InventoryBotView.tsx @@ -1,6 +1,6 @@ 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'; @@ -10,11 +10,12 @@ 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 ( @@ -79,7 +80,7 @@ export const InventoryBotView: FC = props =>
{ selectedBot && - { selectedBot.botData.name } + { selectedBot.botData.name } { !!roomSession && + } + { (selectedItem && (selectedItem.items[0].isTradable || !selectedItem.items[0].isTradable)) && + + 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) && { totalItems } } + + } + { (selectedItem && (selectedItem.items[0].recyclable || !selectedItem.items[0].recyclable)) && + + 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) && { totalItems } } + + } { selectedItem && selectedItem.stuffData.isUnique && } { (selectedItem && selectedItem.stuffData.rarityLevel > -1) && } + { selectedItem && - { selectedItem.name } - - { !!roomSession && - } - { (selectedItem && selectedItem.isSellable) && - } + + { selectedItem.name } + { (selectedItem.description) && { selectedItem.description } } + { (!isTrading) && + <> + { !!roomSession && + } + { (selectedItem && selectedItem.isSellable) && + } + + } + { (isTrading) && + + setQuantity(event.target.valueAsNumber) } /> + + + } + + } diff --git a/src/components/inventory/views/furniture/InventoryTradeView.tsx b/src/components/inventory/views/furniture/InventoryTradeView.tsx index 5ceca06..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 => { @@ -177,103 +68,96 @@ export const InventoryTradeView: FC = props => if((tradeState === TradeState.TRADING_STATE_READY) || !ownUser || !otherUser) return null; 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') }: - { getLockIcon(ownUser.accepts) } + + } + { 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') } - - - - - { otherUser.userName } { LocalizeText('inventory.trading.isoffering') }: - { getLockIcon(otherUser.accepts) } - - - { 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') } - - - + + } + { (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 2879b23..fb402ad 100644 --- a/src/components/inventory/views/pet/InventoryPetView.tsx +++ b/src/components/inventory/views/pet/InventoryPetView.tsx @@ -1,6 +1,6 @@ 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'; @@ -10,11 +10,12 @@ 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(); @@ -78,7 +79,7 @@ export const InventoryPetView: FC = props => { selectedPet && selectedPet.petData && - { selectedPet.petData.name } + { selectedPet.petData.name } { !!roomSession &&