refactor: much better item UI handling

This commit is contained in:
cha0s 2019-10-15 04:28:42 -05:00
parent ae19826040
commit 20837ea2f0
5 changed files with 54 additions and 47 deletions

View File

@ -0,0 +1,22 @@
// 3rd party.
import * as I from 'immutable';
import React, {useState} from 'react';
// 1st party.
import {useEvent} from './use-event';
export function useInventorySlice(selfEntity, start, end) {
const [items, setItems] = useState(I.List());
useEvent(selfEntity, 'inventoryChanged', () => {
setItems(items.withMutations((items) => {
for (let i = start; i < end; ++i) {
const item = selfEntity.itemInSlot(i);
items.set(i, item ? item : undefined);
}
}));
});
const slice = [];
for (let i = start; i < end; ++i) {
slice.push(items.get(i));
}
return slice;
}

View File

@ -1,51 +1,32 @@
// 3rd party. // 3rd party.
import classnames from 'classnames'; import classnames from 'classnames';
import * as I from 'immutable'; import React from 'react';
import React, {useEffect, useState} from 'react';
// 2nd party. // 2nd party.
import {compose} from '@avocado/core'; import {compose} from '@avocado/core';
import contempo from 'contempo'; import contempo from 'contempo';
// 1st party. // 1st party.
import {useEvent} from '../hooks/use-event';
import {usePropertyChange} from '../hooks/use-property-change'; import {usePropertyChange} from '../hooks/use-property-change';
import {useInventorySlice} from '../hooks/use-inventory-slice';
import ItemSlot from './item-slot'; import ItemSlot from './item-slot';
const decorate = compose( const decorate = compose(
contempo(require('./hotbar.raw.scss').default), contempo(require('./hotbar.raw.scss').default),
); );
const HotbarComponent = ({app}) => { const HotbarComponent = ({selfEntity}) => {
const selfEntity = usePropertyChange(app, 'selfEntity');
const activeSlotIndex = usePropertyChange(selfEntity, 'activeSlotIndex');
const [items, setItems] = useState(I.List());
useEvent(selfEntity, 'inventoryChanged', () => {
setItems(items.withMutations((items) => {
for (let i = 0; i < 10; ++i) {
const item = selfEntity.itemInSlot(i);
if (!item) {
continue;
}
if (!items.has(i)) {
items.set(i, I.Map());
}
items.set(i, items.get(i).withMutations((listItem) => {
listItem.set('backgroundImage', item.imageForSlot());
listItem.set('qty', item.qty);
}));
}
}));
});
const hotkeyForSlot = (index) => { const hotkeyForSlot = (index) => {
return (index + 1) % 10; return (index + 1) % 10;
} }
const slots = []; const activeSlotIndex = usePropertyChange(selfEntity, 'activeSlotIndex');
for (let i = 0; i < 10; ++i) { const itemSlice = useInventorySlice(selfEntity, 0, 10);
const slot = <ItemSlot const itemSlots = itemSlice.map((item, i) => {
return <ItemSlot
key={i}
item={item}
className={classnames( className={classnames(
activeSlotIndex === i ? 'active' : '', activeSlotIndex === i ? 'active' : '',
)} )}
item={items.get(i)}
key={i}
onClick={(event) => { onClick={(event) => {
if (selfEntity) { if (selfEntity) {
selfEntity.activeSlotIndex = i; selfEntity.activeSlotIndex = i;
@ -57,13 +38,12 @@ const HotbarComponent = ({app}) => {
> >
<div className="hotbar-key">{hotkeyForSlot(i)}</div> <div className="hotbar-key">{hotkeyForSlot(i)}</div>
</ItemSlot>; </ItemSlot>;
slots.push(slot); });
}
return <div return <div
className="hotbar unselectable" className="hotbar unselectable"
> >
<div className="hotbar-inner"> <div className="hotbar-inner">
{slots} {itemSlots}
</div> </div>
</div>; </div>;
} }

View File

@ -6,6 +6,7 @@ import {compose} from '@avocado/core';
import contempo from 'contempo'; import contempo from 'contempo';
// 1st party. // 1st party.
import {useEvent} from '../hooks/use-event'; import {useEvent} from '../hooks/use-event';
import {usePropertyChange} from '../hooks/use-property-change';
import QuickStatus from './quick-status'; import QuickStatus from './quick-status';
import Hotbar from './hotbar'; import Hotbar from './hotbar';
import Inventory from './inventory'; import Inventory from './inventory';
@ -17,6 +18,7 @@ const decorate = compose(
const MenuComponent = ({app}) => { const MenuComponent = ({app}) => {
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const selfEntity = usePropertyChange(app, 'selfEntity');
useEvent(app, 'isMenuOpenedChanged', (_, isOpened) => { useEvent(app, 'isMenuOpenedChanged', (_, isOpened) => {
setOpened(isOpened); setOpened(isOpened);
}); });
@ -25,10 +27,10 @@ const MenuComponent = ({app}) => {
'menu-inner', 'menu-inner',
opened ? 'open' : '', opened ? 'open' : '',
)}> )}>
<Inventory app={app} /> <Inventory selfEntity={selfEntity} />
<QuickStatus app={app} /> <QuickStatus app={app} />
<WorldTime worldTime={app.worldTime} /> <WorldTime worldTime={app.worldTime} />
<Hotbar app={app} /> <Hotbar selfEntity={selfEntity} />
</div> </div>
</div>; </div>;
} }

View File

@ -1,24 +1,25 @@
// 3rd party. // 3rd party.
import React, {useEffect, useState} from 'react'; import React from 'react';
// 2nd party. // 2nd party.
import {compose} from '@avocado/core'; import {compose} from '@avocado/core';
import contempo from 'contempo'; import contempo from 'contempo';
// 1st party. // 1st party.
import {useInventorySlice} from '../hooks/use-inventory-slice';
import ItemSlot from './item-slot'; import ItemSlot from './item-slot';
const decorate = compose( const decorate = compose(
contempo(require('./inventory.raw.scss').default), contempo(require('./inventory.raw.scss').default),
); );
const InventoryComponent = () => { const InventoryComponent = ({selfEntity}) => {
const bagSlots = []; const bagSlice = useInventorySlice(selfEntity, 10, 50);
for (let i = 0; i < 40; ++i) { const bagSlots = bagSlice.map((item, i) => {
bagSlots.push(<ItemSlot key={i} />); return <ItemSlot key={i} item={item} />;
} });
const equipmentSlots = []; const equipmentSlice = useInventorySlice(selfEntity, 50, 54);
for (let i = 0; i < 4; ++i) { const equipmentSlots = equipmentSlice.map((item, i) => {
equipmentSlots.push(<ItemSlot key={i} />); return <ItemSlot key={i} item={item} />;
} });
return <div className="inventory unselectable"> return <div className="inventory unselectable">
<div className="inventory-inner"> <div className="inventory-inner">
<div className="bag">{bagSlots}</div> <div className="bag">{bagSlots}</div>

View File

@ -4,6 +4,8 @@ import React, {useEffect, useState} from 'react';
// 2nd party. // 2nd party.
import {compose} from '@avocado/core'; import {compose} from '@avocado/core';
import contempo from 'contempo'; import contempo from 'contempo';
// 1st party.
import {useEvent} from '../hooks/use-event';
const decorate = compose( const decorate = compose(
contempo(require('./item-slot.raw.scss').default), contempo(require('./item-slot.raw.scss').default),
@ -11,15 +13,15 @@ const decorate = compose(
const ItemSlotComponent = (props) => { const ItemSlotComponent = (props) => {
const {children, className, item, ...rest} = props; const {children, className, item, ...rest} = props;
const [qty, setQty] = useState(undefined);
useEvent(item, 'qtyChanged', (_, qty) => setQty(item.qty));
let backgroundImageUri; let backgroundImageUri;
let qty;
let qtyClass; let qtyClass;
if (item) { if (item) {
const backgroundImage = item.get('backgroundImage'); const backgroundImage = item.imageForSlot();
if (backgroundImage) { if (backgroundImage) {
backgroundImageUri = backgroundImage.uri; backgroundImageUri = backgroundImage.uri;
} }
qty = item.get('qty');
if (qty > 9999) { if (qty > 9999) {
qtyClass = 'e4'; qtyClass = 'e4';
} }