add shouldComponentUpdate to vbox components

This commit is contained in:
Mashy 2019-10-27 22:59:59 +10:00
parent 200482dd79
commit c534f973a1
3 changed files with 394 additions and 365 deletions

View File

@ -2,14 +2,13 @@ const preact = require('preact');
const range = require('lodash/range');
const reactStringReplace = require('react-string-replace');
const { Component } = require('preact');
const { INFO } = require('./../constants');
const { convertItem, removeTier } = require('../utils');
const { tutorialStage } = require('../tutorial.utils');
const shapes = require('./shapes');
class InfoComponent extends Component {
class InfoComponent extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.tutorial !== this.props.tutorial) return true;
if (newProps.tutorial) return false; // We don't care about info during tutorial

View File

@ -18,7 +18,6 @@ const addState = connect(
account,
itemInfo,
itemEquip,
activeConstruct,
navInstance,
tutorial,
} = state;
@ -39,7 +38,6 @@ const addState = connect(
itemInfo,
itemEquip,
navInstance,
activeConstruct,
sendUnequip,
tutorial,
};
@ -77,19 +75,21 @@ const addState = connect(
function Construct(props) {
const {
// Changing state variables
construct,
iter,
itemEquip,
construct,
itemInfo,
mobileVisible,
player,
tutorial,
// Function Calls
sendVboxApply,
sendUnequip,
setActiveConstruct,
setItemUnequip,
setItemEquip,
itemInfo,
setInfo,
sendUnequip,
mobileVisible,
tutorial,
} = props;
const { vbox } = player;
@ -242,61 +242,73 @@ function Construct(props) {
);
}
function InstanceConstructs(props) {
const {
activeConstruct,
itemEquip,
player,
instance,
// clearInfo,
setInfo,
setActiveConstruct,
class InstanceConstructs extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.itemEquip !== this.props.itemEquip) return true;
if (newProps.instance.phase !== this.props.instance.phase) return true;
if (newProps.tutorial !== this.props.tutorial) return true;
if (newProps.navInstance !== this.props.navInstance) return true;
// JSON or Array objects
if (JSON.stringify(newProps.player) !== JSON.stringify(this.props.player)) return true;
return false;
}
sendVboxApply,
itemInfo,
setVboxHighlight,
setItemUnequip,
setItemEquip,
sendUnequip,
navInstance,
tutorial,
} = props;
if (!player) return false;
if (instance.phase === 'Lobby') return false;
const constructs = range(0, 3).map(i => {
if (tutorial && tutorial < 6) {
if (tutorial <= 2 || (tutorial > 2 && i > 0)) {
const mobileVisible = navInstance === i + 1;
const classes = `instance-construct ${mobileVisible ? 'visible' : ''}`;
return (<div key={player.constructs[i].id} class={classes}></div>);
}
}
return Construct({
iter: i,
construct: player.constructs[i],
activeConstruct,
render(props) {
const {
// Changing state variables
itemEquip,
setItemUnequip,
setItemEquip,
instance, // we only change phase
navInstance,
player,
sendVboxApply,
tutorial,
// Static data
itemInfo,
// Function calls
setInfo,
setActiveConstruct,
itemInfo,
sendVboxApply,
setVboxHighlight,
setItemUnequip,
setItemEquip,
sendUnequip,
tutorial,
mobileVisible: navInstance === i + 1,
});
});
} = props;
console.log('grep constructs');
return (
<div class='construct-list' onClick={() => setActiveConstruct(null)}>
{constructs}
</div>
);
if (!player) return false;
if (instance.phase === 'Lobby') return false;
const constructs = range(0, 3).map(i => {
if (tutorial && tutorial < 6) {
if (tutorial <= 2 || (tutorial > 2 && i > 0)) {
const mobileVisible = navInstance === i + 1;
const classes = `instance-construct ${mobileVisible ? 'visible' : ''}`;
return (<div key={player.constructs[i].id} class={classes}></div>);
}
}
return Construct({
iter: i,
construct: player.constructs[i],
itemEquip,
setItemUnequip,
setItemEquip,
player,
sendVboxApply,
setInfo,
setActiveConstruct,
itemInfo,
setVboxHighlight,
sendUnequip,
tutorial,
mobileVisible: navInstance === i + 1,
});
});
return (
<div class='construct-list' onClick={() => setActiveConstruct(null)}>
{constructs}
</div>
);
}
}
module.exports = addState(InstanceConstructs);

View File

@ -1,8 +1,9 @@
const preact = require('preact');
const { connect } = require('preact-redux');
const range = require('lodash/range');
const countBy = require('lodash/countBy');
const without = require('lodash/without');
const { connect } = require('preact-redux');
const { removeTier } = require('../utils');
const shapes = require('./shapes');
const actions = require('../actions');
@ -95,333 +96,350 @@ const addState = connect(
);
function Vbox(args) {
const {
combiner,
navInstance,
itemInfo,
player,
reclaiming,
sendVboxAccept,
sendVboxCombine,
sendVboxDiscard,
sendVboxReclaim,
setCombiner,
setInfo,
vboxSelected,
setVboxSelected,
setItemEquip,
tutorial,
itemUnequip,
sendItemUnequip,
setReclaiming,
info,
} = args;
if (!player) return false;
const { vbox } = player;
const vboxSelecting = vboxSelected.length;
function combinerChange(newCombiner) {
setCombiner(newCombiner);
if (newCombiner.length === 1) {
setItemEquip(newCombiner[0]);
} else {
setItemEquip(null);
}
return true;
class Vbox extends preact.Component {
shouldComponentUpdate(newProps) {
// Single variable props
if (newProps.combiner !== this.props.combiner) return true;
if (newProps.itemUnequip !== this.props.itemUnequip) return true;
if (newProps.reclaiming !== this.props.reclaiming) return true;
if (newProps.navInstance !== this.props.navInstance) return true;
if (newProps.tutorial !== this.props.tutorial) return true;
// Don't bother if info changes during tutorial
if (!newProps.tutorial && newProps.info !== this.props.info) return true;
// JSON or Array objects
if (JSON.stringify(newProps.vboxSelected) !== JSON.stringify(this.props.vboxSelected)) return true;
if (JSON.stringify(newProps.player) !== JSON.stringify(this.props.player)) return true;
return false;
}
//
// VBOX
//
function vboxHover(e, v) {
if (v) {
if (info !== v) setInfo(v);
e.stopPropagation();
}
return true;
}
render(args) {
const {
// Changing state variables
combiner,
itemUnequip,
player,
reclaiming,
tutorial,
navInstance,
info,
vboxSelected,
function clearVboxSelected() {
setVboxSelected([]);
}
// Static
itemInfo,
// Function Calls
sendItemUnequip,
sendVboxAccept,
sendVboxCombine,
sendVboxDiscard,
sendVboxReclaim,
setVboxSelected,
setItemEquip,
setInfo,
setCombiner,
setReclaiming,
} = args;
function vboxBuySelected() {
if (!vboxSelecting) return false;
document.activeElement.blur();
clearVboxSelected();
sendVboxAccept(vboxSelected[0], vboxSelected[1]);
return true;
}
if (!player) return false;
const { vbox } = player;
const vboxSelecting = vboxSelected.length;
function availableBtn(v, group, index) {
if (!v) return <button disabled class='empty' >&nbsp;</button>;
const selected = vboxSelected[0] === group && vboxSelected[1] === index;
function combinerChange(newCombiner) {
setCombiner(newCombiner);
// state not yet set in double click handler
function onDblClick(e) {
clearVboxSelected();
sendVboxAccept(group, index);
e.stopPropagation();
}
function onClick(e) {
e.stopPropagation();
setItemEquip(null);
setCombiner([]);
if (selected) return clearVboxSelected();
return setVboxSelected([group, index]);
}
const combinerItems = combiner.map(j => vbox.bound[j]);
const combinerCount = countBy(combinerItems, co => co);
const comboHighlight = combinerItems.length > 0 && itemInfo.combos.some(combo => {
if (combo.components.includes(v)) {
return combinerItems.every(c => {
if (!combo.components.includes(c)) return false;
const comboCount = countBy(combo.components, co => co);
if (combinerCount[c] > comboCount[c]) return false;
if (c === v && combinerCount[c] + 1 > comboCount[c]) return false;
return true;
});
} return false;
}) ? 'combo-border' : '';
const classes = `${v.toLowerCase()} ${selected ? 'highlight' : ''} ${comboHighlight}`;
if (shapes[v]) {
return (
<button
class={classes}
onMouseOver={e => vboxHover(e, v)}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onDblClick={onDblClick} >
{shapes[v]()}
</button>
);
}
return (
<button
class={classes}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onDblClick={onDblClick}
onMouseOver={e => vboxHover(e, v)}>
{v}
</button>
);
}
function vboxElement() {
return (
<div class='vbox-vbox'
onMouseDown={() => setReclaiming(false)}
onClick={e => e.stopPropagation()}
onMouseOver={e => hoverInfo(e, 'vbox')}>
<div class="vbox-hdr">
<h3 onTouchStart={e => e.target.scrollIntoView(true)}>VBOX</h3>
<div class="bits" onMouseOver={e => hoverInfo(e, 'bits')} >{vbox.bits}b</div>
</div>
<div class="vbox-colours">
{range(0, 6).map(i => availableBtn(vbox.free[0][i], 0, i))}
</div>
<div class="vbox-items">
{range(0, 3).map(i => availableBtn(vbox.free[1][i], 1, i))}
{range(0, 3).map(i => availableBtn(vbox.free[2][i], 2, i))}
</div>
<button
class='vbox-btn'
onMouseOver={e => hoverInfo(e, 'refill')}
disabled={tutorial && tutorial < 7}
onClick={e => e.stopPropagation()}
onMouseDown={() => sendVboxDiscard()}>
refill - 2b
</button>
</div>
);
}
//
// INVENTORY
//
function reclaimClick(e) {
e.stopPropagation();
return setReclaiming(!reclaiming);
}
const inventoryClass = `vbox-section ${reclaiming ? 'reclaiming' : ''}`;
function inventoryBtn(v, i) {
const inventoryHighlight = vboxSelecting || itemUnequip.length;
if (!v && v !== 0) {
return <button disabled={!inventoryHighlight} class={inventoryHighlight ? 'receiving' : 'empty'} >&nbsp;</button>;
}
const combinerItems = combiner.map(j => vbox.bound[j]);
const combinerCount = countBy(combinerItems, co => co);
const comboHighlight = combinerItems.length > 0 && itemInfo.combos.some(combo => {
if (combo.components.includes(v)) {
return combinerItems.every(c => {
if (!combo.components.includes(c)) return false;
const comboCount = countBy(combo.components, co => co);
if (combinerCount[c] > comboCount[c]) return false;
if (c === v && combinerCount[c] + 1 > comboCount[c]) return false;
return true;
});
} return false;
}) ? 'combo-border' : '';
function onClick(e) {
if (vboxSelecting) clearVboxSelected();
if (reclaiming) return sendVboxReclaim(i);
// 4 things selected
if (combiner.length > 2) return combinerChange([i]);
// removing
const combinerIndex = combiner.indexOf(i);
if (combinerIndex > -1) {
return combinerChange(without(combiner, i));
if (newCombiner.length === 1) {
setItemEquip(newCombiner[0]);
} else {
setItemEquip(null);
}
combiner.push(i);
if (!comboHighlight) {
return combinerChange([i]);
}
return combinerChange(combiner);
}
const highlighted = combiner.indexOf(i) > -1;
const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : '';
const classes = `${highlighted ? 'highlight' : border} ${comboHighlight}`;
if (shapes[v]) {
return (
<button
class={classes}
onMouseOver={e => vboxHover(e, v)}
onClick={e => e.stopPropagation()}
onMouseDown={onClick}>
{shapes[v]()}
</button>
);
}
return (
<button
class={classes}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onMouseOver={e => vboxHover(e, v)}>
{v}
</button>
);
}
function combinerBtn() {
let text = '';
let comboItem = '';
if (combiner.length < 3) {
for (let i = 0; i < 3; i++) {
if (combiner.length > i) {
text += '■ ';
} else {
text += '▫ ';
}
}
} else {
// Since theres 3 items in combiner and you can't have invalid combos we can preview it
const combinerItems = combiner.map(j => vbox.bound[j]);
const combinerCount = countBy(combinerItems, co => co);
const comboItemObj = itemInfo.combos.find(combo => combinerItems.every(c => {
if (!combo.components.includes(c)) return false;
const comboCount = countBy(combo.components, co => co);
if (combinerCount[c] > comboCount[c]) return false;
return true;
}));
comboItem = comboItemObj ? comboItemObj.item : 'refine';
comboItem = comboItem.replace('Plus', '+');
text = `Combine - ${comboItem}`;
}
return (
<button
class='vbox-btn'
disabled={combiner.length !== 3}
onMouseOver={e => hoverInfo(e, comboItem)}
onClick={e => e.stopPropagation()}
onMouseDown={() => sendVboxCombine()}>
{text}
</button>
);
}
function inventoryElement() {
function inventoryClick(e) {
e.stopPropagation();
setReclaiming(false);
if (vboxSelecting) return vboxBuySelected();
if (itemUnequip.length) return sendItemUnequip(itemUnequip);
return true;
}
return (
<div class={inventoryClass}
onMouseDown={inventoryClick}
onClick={e => e.stopPropagation()}
style={vboxSelecting || (itemUnequip.length) ? { cursor: 'pointer' } : null}
onMouseOver={e => hoverInfo(e, 'inventory')}>
<div class="vbox-hdr">
<h3 onTouchStart={e => e.target.scrollIntoView(true)}>INVENTORY</h3>
//
// VBOX
//
function vboxHover(e, v) {
if (v) {
if (info !== v) setInfo(v);
e.stopPropagation();
}
return true;
}
function clearVboxSelected() {
setVboxSelected([]);
}
function vboxBuySelected() {
if (!vboxSelecting) return false;
document.activeElement.blur();
clearVboxSelected();
sendVboxAccept(vboxSelected[0], vboxSelected[1]);
return true;
}
function availableBtn(v, group, index) {
if (!v) return <button disabled class='empty' >&nbsp;</button>;
const selected = vboxSelected[0] === group && vboxSelected[1] === index;
// state not yet set in double click handler
function onDblClick(e) {
clearVboxSelected();
sendVboxAccept(group, index);
e.stopPropagation();
}
function onClick(e) {
e.stopPropagation();
setItemEquip(null);
setCombiner([]);
if (selected) return clearVboxSelected();
return setVboxSelected([group, index]);
}
const combinerItems = combiner.map(j => vbox.bound[j]);
const combinerCount = countBy(combinerItems, co => co);
const comboHighlight = combinerItems.length > 0 && itemInfo.combos.some(combo => {
if (combo.components.includes(v)) {
return combinerItems.every(c => {
if (!combo.components.includes(c)) return false;
const comboCount = countBy(combo.components, co => co);
if (combinerCount[c] > comboCount[c]) return false;
if (c === v && combinerCount[c] + 1 > comboCount[c]) return false;
return true;
});
} return false;
}) ? 'combo-border' : '';
const classes = `${v.toLowerCase()} ${selected ? 'highlight' : ''} ${comboHighlight}`;
if (shapes[v]) {
return (
<button
disabled={tutorial && tutorial < 8}
class='vbox-btn reclaim'
onMouseOver={e => hoverInfo(e, 'reclaim')}
class={classes}
onMouseOver={e => vboxHover(e, v)}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onMouseDown={reclaimClick}>
reclaim
onDblClick={onDblClick} >
{shapes[v]()}
</button>
);
}
return (
<button
class={classes}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onDblClick={onDblClick}
onMouseOver={e => vboxHover(e, v)}>
{v}
</button>
);
}
function vboxElement() {
return (
<div class='vbox-vbox'
onMouseDown={() => setReclaiming(false)}
onClick={e => e.stopPropagation()}
onMouseOver={e => hoverInfo(e, 'vbox')}>
<div class="vbox-hdr">
<h3 onTouchStart={e => e.target.scrollIntoView(true)}>VBOX</h3>
<div class="bits" onMouseOver={e => hoverInfo(e, 'bits')} >{vbox.bits}b</div>
</div>
<div class="vbox-colours">
{range(0, 6).map(i => availableBtn(vbox.free[0][i], 0, i))}
</div>
<div class="vbox-items">
{range(0, 3).map(i => availableBtn(vbox.free[1][i], 1, i))}
{range(0, 3).map(i => availableBtn(vbox.free[2][i], 2, i))}
</div>
<button
class='vbox-btn'
onMouseOver={e => hoverInfo(e, 'refill')}
disabled={tutorial && tutorial < 7}
onClick={e => e.stopPropagation()}
onMouseDown={() => sendVboxDiscard()}>
refill - 2b
</button>
</div>
<div class='vbox-items'>
{range(0, 9).map(i => inventoryBtn(vbox.bound[i], i))}
);
}
//
// INVENTORY
//
function reclaimClick(e) {
e.stopPropagation();
return setReclaiming(!reclaiming);
}
const inventoryClass = `vbox-section ${reclaiming ? 'reclaiming' : ''}`;
function inventoryBtn(v, i) {
const inventoryHighlight = vboxSelecting || itemUnequip.length;
if (!v && v !== 0) {
return <button disabled={!inventoryHighlight} class={inventoryHighlight ? 'receiving' : 'empty'} >&nbsp;</button>;
}
const combinerItems = combiner.map(j => vbox.bound[j]);
const combinerCount = countBy(combinerItems, co => co);
const comboHighlight = combinerItems.length > 0 && itemInfo.combos.some(combo => {
if (combo.components.includes(v)) {
return combinerItems.every(c => {
if (!combo.components.includes(c)) return false;
const comboCount = countBy(combo.components, co => co);
if (combinerCount[c] > comboCount[c]) return false;
if (c === v && combinerCount[c] + 1 > comboCount[c]) return false;
return true;
});
} return false;
}) ? 'combo-border' : '';
function onClick(e) {
if (vboxSelecting) clearVboxSelected();
if (reclaiming) return sendVboxReclaim(i);
// 4 things selected
if (combiner.length > 2) return combinerChange([i]);
// removing
const combinerIndex = combiner.indexOf(i);
if (combinerIndex > -1) {
return combinerChange(without(combiner, i));
}
combiner.push(i);
if (!comboHighlight) {
return combinerChange([i]);
}
return combinerChange(combiner);
}
const highlighted = combiner.indexOf(i) > -1;
const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : '';
const classes = `${highlighted ? 'highlight' : border} ${comboHighlight}`;
if (shapes[v]) {
return (
<button
class={classes}
onMouseOver={e => vboxHover(e, v)}
onClick={e => e.stopPropagation()}
onMouseDown={onClick}>
{shapes[v]()}
</button>
);
}
return (
<button
class={classes}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onMouseOver={e => vboxHover(e, v)}>
{v}
</button>
);
}
function combinerBtn() {
let text = '';
let comboItem = '';
if (combiner.length < 3) {
for (let i = 0; i < 3; i++) {
if (combiner.length > i) {
text += '■ ';
} else {
text += '▫ ';
}
}
} else {
// Since theres 3 items in combiner and you can't have invalid combos we can preview it
const combinerItems = combiner.map(j => vbox.bound[j]);
const combinerCount = countBy(combinerItems, co => co);
const comboItemObj = itemInfo.combos.find(combo => combinerItems.every(c => {
if (!combo.components.includes(c)) return false;
const comboCount = countBy(combo.components, co => co);
if (combinerCount[c] > comboCount[c]) return false;
return true;
}));
comboItem = comboItemObj ? comboItemObj.item : 'refine';
comboItem = comboItem.replace('Plus', '+');
text = `Combine - ${comboItem}`;
}
return (
<button
class='vbox-btn'
disabled={combiner.length !== 3}
onMouseOver={e => hoverInfo(e, comboItem)}
onClick={e => e.stopPropagation()}
onMouseDown={() => sendVboxCombine()}>
{text}
</button>
);
}
function inventoryElement() {
function inventoryClick(e) {
e.stopPropagation();
setReclaiming(false);
if (vboxSelecting) return vboxBuySelected();
if (itemUnequip.length) return sendItemUnequip(itemUnequip);
return true;
}
return (
<div class={inventoryClass}
onMouseDown={inventoryClick}
onClick={e => e.stopPropagation()}
style={vboxSelecting || (itemUnequip.length) ? { cursor: 'pointer' } : null}
onMouseOver={e => hoverInfo(e, 'inventory')}>
<div class="vbox-hdr">
<h3 onTouchStart={e => e.target.scrollIntoView(true)}>INVENTORY</h3>
<button
disabled={tutorial && tutorial < 8}
class='vbox-btn reclaim'
onMouseOver={e => hoverInfo(e, 'reclaim')}
onClick={e => e.stopPropagation()}
onMouseDown={reclaimClick}>
reclaim
</button>
</div>
<div class='vbox-items'>
{range(0, 9).map(i => inventoryBtn(vbox.bound[i], i))}
</div>
{combinerBtn()}
</div>
{combinerBtn()}
);
}
//
// EVERYTHING
//
function hoverInfo(e, newInfo) {
e.stopPropagation();
if (info === newInfo) return true;
return setInfo(newInfo);
}
const classes = `vbox ${navInstance === 0 ? 'visible' : ''}`;
return (
<div class={classes}>
{vboxElement()}
<div class="vbox-arrow"></div>
{inventoryElement()}
</div>
);
}
//
// EVERYTHING
//
function hoverInfo(e, newInfo) {
e.stopPropagation();
if (info === newInfo) return true;
return setInfo(newInfo);
}
const classes = `vbox ${navInstance === 0 ? 'visible' : ''}`;
return (
<div class={classes}>
{vboxElement()}
<div class="vbox-arrow"></div>
{inventoryElement()}
</div>
);
}
module.exports = addState(Vbox);