Merge pull request #3799 from ethcore/ng-ui-fixes
Several Fixes to the UI
This commit is contained in:
commit
078feaadd5
@ -62,6 +62,10 @@ export default class Contracts {
|
||||
}
|
||||
|
||||
static create (api) {
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
return new Contracts(api);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Redirect, Router, Route } from 'react-router';
|
||||
import { Redirect, Router, Route, IndexRoute } from 'react-router';
|
||||
|
||||
import { Accounts, Account, Addresses, Address, Application, Contract, Contracts, WriteContract, Wallet, Dapp, Dapps, Settings, SettingsBackground, SettingsParity, SettingsProxy, SettingsViews, Signer, Status } from '~/views';
|
||||
|
||||
@ -26,6 +26,23 @@ export default class MainApplication extends Component {
|
||||
routerHistory: PropTypes.any.isRequired
|
||||
};
|
||||
|
||||
handleDeprecatedRoute = (nextState, replace) => {
|
||||
const { address } = nextState.params;
|
||||
const redirectMap = {
|
||||
account: 'accounts',
|
||||
address: 'addresses',
|
||||
contract: 'contracts'
|
||||
};
|
||||
|
||||
const oldRoute = nextState.routes[0].path;
|
||||
const newRoute = Object.keys(redirectMap).reduce((newRoute, key) => {
|
||||
return newRoute.replace(new RegExp(`^/${key}`), '/' + redirectMap[key]);
|
||||
}, oldRoute);
|
||||
|
||||
console.warn(`Route "${oldRoute}" is deprecated. Please use "${newRoute}"`);
|
||||
replace(newRoute.replace(':address', address));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { routerHistory } = this.props;
|
||||
|
||||
@ -34,26 +51,46 @@ export default class MainApplication extends Component {
|
||||
<Redirect from='/' to='/accounts' />
|
||||
<Redirect from='/auth' to='/accounts' query={ {} } />
|
||||
<Redirect from='/settings' to='/settings/views' />
|
||||
|
||||
{ /** Backward Compatible links */ }
|
||||
<Route path='/account/:address' onEnter={ this.handleDeprecatedRoute } />
|
||||
<Route path='/address/:address' onEnter={ this.handleDeprecatedRoute } />
|
||||
<Route path='/contract/:address' onEnter={ this.handleDeprecatedRoute } />
|
||||
|
||||
<Route path='/' component={ Application }>
|
||||
<Route path='accounts' component={ Accounts } />
|
||||
<Route path='account/:address' component={ Account } />
|
||||
<Route path='wallet/:address' component={ Wallet } />
|
||||
<Route path='addresses' component={ Addresses } />
|
||||
<Route path='address/:address' component={ Address } />
|
||||
<Route path='accounts'>
|
||||
<IndexRoute component={ Accounts } />
|
||||
<Route path=':address' component={ Account } />
|
||||
<Route path='/wallet/:address' component={ Wallet } />
|
||||
</Route>
|
||||
|
||||
<Route path='addresses'>
|
||||
<IndexRoute component={ Addresses } />
|
||||
<Route path=':address' component={ Address } />
|
||||
</Route>
|
||||
|
||||
<Route path='apps' component={ Dapps } />
|
||||
<Route path='app/:id' component={ Dapp } />
|
||||
<Route path='contracts' component={ Contracts } />
|
||||
<Route path='contracts/write' component={ WriteContract } />
|
||||
<Route path='contract/:address' component={ Contract } />
|
||||
|
||||
<Route path='contracts'>
|
||||
<IndexRoute component={ Contracts } />
|
||||
<Route path='develop' component={ WriteContract } />
|
||||
<Route path=':address' component={ Contract } />
|
||||
</Route>
|
||||
|
||||
<Route path='settings' component={ Settings }>
|
||||
<Route path='background' component={ SettingsBackground } />
|
||||
<Route path='proxy' component={ SettingsProxy } />
|
||||
<Route path='views' component={ SettingsViews } />
|
||||
<Route path='parity' component={ SettingsParity } />
|
||||
</Route>
|
||||
|
||||
<Route path='signer' component={ Signer } />
|
||||
<Route path='status' component={ Status } />
|
||||
<Route path='status/:subpage' component={ Status } />
|
||||
|
||||
<Route path='status'>
|
||||
<IndexRoute component={ Status } />
|
||||
<Route path=':subpage' component={ Status } />
|
||||
</Route>
|
||||
</Route>
|
||||
</Router>
|
||||
);
|
||||
|
@ -28,6 +28,7 @@ export default class AddAddress extends Component {
|
||||
|
||||
static propTypes = {
|
||||
contacts: PropTypes.object.isRequired,
|
||||
address: PropTypes.string,
|
||||
onClose: PropTypes.func
|
||||
};
|
||||
|
||||
@ -39,6 +40,12 @@ export default class AddAddress extends Component {
|
||||
description: ''
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
if (this.props.address) {
|
||||
this.onEditAddress(null, this.props.address);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Modal
|
||||
@ -77,6 +84,8 @@ export default class AddAddress extends Component {
|
||||
hint='the network address for the entry'
|
||||
error={ addressError }
|
||||
value={ address }
|
||||
disabled={ !!this.props.address }
|
||||
allowCopy={ false }
|
||||
onChange={ this.onEditAddress } />
|
||||
<Input
|
||||
label='address name'
|
||||
|
@ -30,10 +30,12 @@ export default class WalletInfo extends Component {
|
||||
owners: PropTypes.array.isRequired,
|
||||
required: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object,
|
||||
PropTypes.number
|
||||
]).isRequired,
|
||||
daylimit: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object,
|
||||
PropTypes.number
|
||||
]).isRequired,
|
||||
|
||||
|
@ -28,26 +28,25 @@ export default class DetailsStep extends Component {
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
|
||||
onFromAddressChange: PropTypes.func.isRequired,
|
||||
onNameChange: PropTypes.func.isRequired,
|
||||
onDescriptionChange: PropTypes.func.isRequired,
|
||||
onAbiChange: PropTypes.func.isRequired,
|
||||
onCodeChange: PropTypes.func.isRequired,
|
||||
onParamsChange: PropTypes.func.isRequired,
|
||||
onDescriptionChange: PropTypes.func.isRequired,
|
||||
onFromAddressChange: PropTypes.func.isRequired,
|
||||
onInputsChange: PropTypes.func.isRequired,
|
||||
onNameChange: PropTypes.func.isRequired,
|
||||
onParamsChange: PropTypes.func.isRequired,
|
||||
|
||||
abi: PropTypes.string,
|
||||
abiError: PropTypes.string,
|
||||
balances: PropTypes.object,
|
||||
code: PropTypes.string,
|
||||
codeError: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
descriptionError: PropTypes.string,
|
||||
fromAddress: PropTypes.string,
|
||||
fromAddressError: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
nameError: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
descriptionError: PropTypes.string,
|
||||
abi: PropTypes.string,
|
||||
abiError: PropTypes.string,
|
||||
code: PropTypes.string,
|
||||
codeError: PropTypes.string,
|
||||
|
||||
readOnly: PropTypes.bool
|
||||
};
|
||||
|
||||
@ -77,6 +76,7 @@ export default class DetailsStep extends Component {
|
||||
render () {
|
||||
const {
|
||||
accounts,
|
||||
balances,
|
||||
readOnly,
|
||||
|
||||
fromAddress, fromAddressError,
|
||||
@ -94,24 +94,28 @@ export default class DetailsStep extends Component {
|
||||
<AddressSelect
|
||||
label='from account (contract owner)'
|
||||
hint='the owner account for this contract'
|
||||
value={ fromAddress }
|
||||
error={ fromAddressError }
|
||||
accounts={ accounts }
|
||||
onChange={ this.onFromAddressChange } />
|
||||
balances={ balances }
|
||||
error={ fromAddressError }
|
||||
onChange={ this.onFromAddressChange }
|
||||
value={ fromAddress }
|
||||
/>
|
||||
|
||||
<Input
|
||||
label='contract name'
|
||||
hint='a name for the deployed contract'
|
||||
error={ nameError }
|
||||
onChange={ this.onNameChange }
|
||||
value={ name || '' }
|
||||
onChange={ this.onNameChange } />
|
||||
/>
|
||||
|
||||
<Input
|
||||
label='contract description (optional)'
|
||||
hint='a description for the contract'
|
||||
error={ descriptionError }
|
||||
onChange={ this.onDescriptionChange }
|
||||
value={ description }
|
||||
onChange={ this.onDescriptionChange } />
|
||||
/>
|
||||
|
||||
{ this.renderContractSelect() }
|
||||
|
||||
@ -119,17 +123,19 @@ export default class DetailsStep extends Component {
|
||||
label='abi / solc combined-output'
|
||||
hint='the abi of the contract to deploy or solc combined-output'
|
||||
error={ abiError }
|
||||
value={ solcOutput }
|
||||
onChange={ this.onSolcChange }
|
||||
onSubmit={ this.onSolcSubmit }
|
||||
readOnly={ readOnly } />
|
||||
readOnly={ readOnly }
|
||||
value={ solcOutput }
|
||||
/>
|
||||
<Input
|
||||
label='code'
|
||||
hint='the compiled code of the contract to deploy'
|
||||
error={ codeError }
|
||||
value={ code }
|
||||
onSubmit={ this.onCodeChange }
|
||||
readOnly={ readOnly || solc } />
|
||||
readOnly={ readOnly || solc }
|
||||
value={ code }
|
||||
/>
|
||||
|
||||
</Form>
|
||||
);
|
||||
|
@ -15,8 +15,10 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, TxHash } from '~/ui';
|
||||
import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation';
|
||||
@ -36,7 +38,7 @@ const STEPS = {
|
||||
COMPLETED: { title: 'completed' }
|
||||
};
|
||||
|
||||
export default class DeployContract extends Component {
|
||||
class DeployContract extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
@ -45,6 +47,7 @@ export default class DeployContract extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
balances: PropTypes.object,
|
||||
abi: PropTypes.string,
|
||||
code: PropTypes.string,
|
||||
readOnly: PropTypes.bool,
|
||||
@ -192,7 +195,7 @@ export default class DeployContract extends Component {
|
||||
}
|
||||
|
||||
renderStep () {
|
||||
const { accounts, readOnly } = this.props;
|
||||
const { accounts, readOnly, balances } = this.props;
|
||||
const { address, deployError, step, deployState, txhash, rejected } = this.state;
|
||||
|
||||
if (deployError) {
|
||||
@ -216,6 +219,7 @@ export default class DeployContract extends Component {
|
||||
<DetailsStep
|
||||
{ ...this.state }
|
||||
accounts={ accounts }
|
||||
balances={ balances }
|
||||
readOnly={ readOnly }
|
||||
onFromAddressChange={ this.onFromAddressChange }
|
||||
onDescriptionChange={ this.onDescriptionChange }
|
||||
@ -394,3 +398,17 @@ export default class DeployContract extends Component {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (initState, initProps) {
|
||||
const fromAddresses = Object.keys(initProps.accounts);
|
||||
|
||||
return (state) => {
|
||||
const balances = pick(state.balances.balances, fromAddresses);
|
||||
return { balances };
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(DeployContract);
|
||||
|
||||
|
@ -32,25 +32,27 @@ export default class DetailsStep extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
contract: PropTypes.object.isRequired,
|
||||
amount: PropTypes.string,
|
||||
amountError: PropTypes.string,
|
||||
onAmountChange: PropTypes.func.isRequired,
|
||||
fromAddress: PropTypes.string,
|
||||
fromAddressError: PropTypes.string,
|
||||
gasEdit: PropTypes.bool,
|
||||
onFromAddressChange: PropTypes.func.isRequired,
|
||||
func: PropTypes.object,
|
||||
funcError: PropTypes.string,
|
||||
onFuncChange: PropTypes.func,
|
||||
onGasEditClick: PropTypes.func,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
values: PropTypes.array.isRequired,
|
||||
valuesError: PropTypes.array.isRequired,
|
||||
warning: PropTypes.string,
|
||||
onValueChange: PropTypes.func.isRequired
|
||||
|
||||
amount: PropTypes.string,
|
||||
amountError: PropTypes.string,
|
||||
balances: PropTypes.object,
|
||||
fromAddress: PropTypes.string,
|
||||
fromAddressError: PropTypes.string,
|
||||
func: PropTypes.object,
|
||||
funcError: PropTypes.string,
|
||||
gasEdit: PropTypes.bool,
|
||||
onFuncChange: PropTypes.func,
|
||||
onGasEditClick: PropTypes.func,
|
||||
warning: PropTypes.string
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, amount, amountError, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props;
|
||||
const { accounts, amount, amountError, balances, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
@ -61,6 +63,7 @@ export default class DetailsStep extends Component {
|
||||
value={ fromAddress }
|
||||
error={ fromAddressError }
|
||||
accounts={ accounts }
|
||||
balances={ balances }
|
||||
onChange={ onFromAddressChange } />
|
||||
{ this.renderFunctionSelect() }
|
||||
{ this.renderParameters() }
|
||||
|
@ -18,6 +18,8 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { observer } from 'mobx-react';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
@ -57,6 +59,7 @@ class ExecuteContract extends Component {
|
||||
isTest: PropTypes.bool,
|
||||
fromAddress: PropTypes.string,
|
||||
accounts: PropTypes.object,
|
||||
balances: PropTypes.object,
|
||||
contract: PropTypes.object,
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
@ -362,10 +365,15 @@ class ExecuteContract extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { gasLimit } = state.nodeStatus;
|
||||
function mapStateToProps (initState, initProps) {
|
||||
const fromAddresses = Object.keys(initProps.accounts);
|
||||
|
||||
return { gasLimit };
|
||||
return (state) => {
|
||||
const balances = pick(state.balances.balances, fromAddresses);
|
||||
const { gasLimit } = state.nodeStatus;
|
||||
|
||||
return { gasLimit, balances };
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
|
@ -134,6 +134,7 @@ export default class Details extends Component {
|
||||
images: PropTypes.object.isRequired,
|
||||
sender: PropTypes.string,
|
||||
senderError: PropTypes.string,
|
||||
sendersBalances: PropTypes.object,
|
||||
recipient: PropTypes.string,
|
||||
recipientError: PropTypes.string,
|
||||
tag: PropTypes.string,
|
||||
@ -203,7 +204,7 @@ export default class Details extends Component {
|
||||
}
|
||||
|
||||
renderFromAddress () {
|
||||
const { sender, senderError, senders } = this.props;
|
||||
const { sender, senderError, senders, sendersBalances } = this.props;
|
||||
|
||||
if (!senders) {
|
||||
return null;
|
||||
@ -218,6 +219,7 @@ export default class Details extends Component {
|
||||
hint='the sender address'
|
||||
value={ sender }
|
||||
onChange={ this.onEditSender }
|
||||
balances={ sendersBalances }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -54,6 +54,7 @@ export default class TransferStore {
|
||||
|
||||
@observable sender = '';
|
||||
@observable senderError = null;
|
||||
@observable sendersBalances = {};
|
||||
|
||||
@observable total = '0.0';
|
||||
@observable totalError = null;
|
||||
@ -66,8 +67,6 @@ export default class TransferStore {
|
||||
onClose = null;
|
||||
|
||||
senders = null;
|
||||
sendersBalances = null;
|
||||
|
||||
isWallet = false;
|
||||
wallet = null;
|
||||
|
||||
|
@ -155,8 +155,8 @@ class Transfer extends Component {
|
||||
|
||||
renderDetailsPage () {
|
||||
const { account, balance, images, senders } = this.props;
|
||||
const { valueAll, extras, recipient, recipientError, sender, senderError } = this.store;
|
||||
const { tag, total, totalError, value, valueError } = this.store;
|
||||
const { recipient, recipientError, sender, senderError, sendersBalances } = this.store;
|
||||
const { valueAll, extras, tag, total, totalError, value, valueError } = this.store;
|
||||
|
||||
return (
|
||||
<Details
|
||||
@ -170,6 +170,7 @@ class Transfer extends Component {
|
||||
recipientError={ recipientError }
|
||||
sender={ sender }
|
||||
senderError={ senderError }
|
||||
sendersBalances={ sendersBalances }
|
||||
tag={ tag }
|
||||
total={ total }
|
||||
totalError={ totalError }
|
||||
|
@ -15,7 +15,9 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.account {
|
||||
padding: 4px 0 0 0;
|
||||
padding: 0.25em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
@ -27,6 +29,11 @@
|
||||
padding: 0 0 0 1em;
|
||||
}
|
||||
|
||||
.balance {
|
||||
color: #aaa;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.image {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
|
@ -21,6 +21,8 @@ import AutoComplete from '../AutoComplete';
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
import IdentityName from '../../IdentityName';
|
||||
|
||||
import { fromWei } from '~/api/util/wei';
|
||||
|
||||
import styles from './addressSelect.css';
|
||||
|
||||
export default class AddressSelect extends Component {
|
||||
@ -40,27 +42,46 @@ export default class AddressSelect extends Component {
|
||||
value: PropTypes.string,
|
||||
tokens: PropTypes.object,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
allowInput: PropTypes.bool
|
||||
allowInput: PropTypes.bool,
|
||||
balances: PropTypes.object
|
||||
}
|
||||
|
||||
state = {
|
||||
autocompleteEntries: [],
|
||||
entries: {},
|
||||
addresses: [],
|
||||
value: ''
|
||||
}
|
||||
|
||||
entriesFromProps (props = this.props) {
|
||||
const { accounts, contacts, contracts, wallets } = props;
|
||||
const entries = Object.assign({}, accounts || {}, wallets || {}, contacts || {}, contracts || {});
|
||||
return entries;
|
||||
const { accounts = {}, contacts = {}, contracts = {}, wallets = {} } = props;
|
||||
|
||||
const autocompleteEntries = [].concat(
|
||||
Object.values(wallets),
|
||||
'divider',
|
||||
Object.values(accounts),
|
||||
'divider',
|
||||
Object.values(contacts),
|
||||
'divider',
|
||||
Object.values(contracts)
|
||||
);
|
||||
|
||||
const entries = {
|
||||
...wallets,
|
||||
...accounts,
|
||||
...contacts,
|
||||
...contracts
|
||||
};
|
||||
|
||||
return { autocompleteEntries, entries };
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { value } = this.props;
|
||||
const entries = this.entriesFromProps();
|
||||
const { entries, autocompleteEntries } = this.entriesFromProps();
|
||||
const addresses = Object.keys(entries).sort();
|
||||
|
||||
this.setState({ entries, addresses, value });
|
||||
this.setState({ autocompleteEntries, entries, addresses, value });
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
@ -71,7 +92,7 @@ export default class AddressSelect extends Component {
|
||||
|
||||
render () {
|
||||
const { allowInput, disabled, error, hint, label } = this.props;
|
||||
const { entries, value } = this.state;
|
||||
const { autocompleteEntries, value } = this.state;
|
||||
|
||||
const searchText = this.getSearchText();
|
||||
const icon = this.renderIdentityIcon(value);
|
||||
@ -89,7 +110,7 @@ export default class AddressSelect extends Component {
|
||||
onUpdateInput={ allowInput && this.onUpdateInput }
|
||||
value={ searchText }
|
||||
filter={ this.handleFilter }
|
||||
entries={ entries }
|
||||
entries={ autocompleteEntries }
|
||||
entry={ this.getEntry() || {} }
|
||||
renderItem={ this.renderItem }
|
||||
/>
|
||||
@ -129,7 +150,34 @@ export default class AddressSelect extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
renderBalance (address) {
|
||||
const { balances = {} } = this.props;
|
||||
const balance = balances[address];
|
||||
|
||||
if (!balance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ethToken = balance.tokens.find((tok) => tok.token && tok.token.tag && tok.token.tag.toLowerCase() === 'eth');
|
||||
|
||||
if (!ethToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const value = fromWei(ethToken.value);
|
||||
|
||||
return (
|
||||
<div className={ styles.balance }>
|
||||
{ value.toFormat(3) }<small> { 'ETH' }</small>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderMenuItem (address) {
|
||||
const balance = this.props.balances
|
||||
? this.renderBalance(address)
|
||||
: null;
|
||||
|
||||
const item = (
|
||||
<div className={ styles.account }>
|
||||
<IdentityIcon
|
||||
@ -139,6 +187,7 @@ export default class AddressSelect extends Component {
|
||||
<IdentityName
|
||||
className={ styles.name }
|
||||
address={ address } />
|
||||
{ balance }
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -155,11 +204,10 @@ export default class AddressSelect extends Component {
|
||||
|
||||
getSearchText () {
|
||||
const entry = this.getEntry();
|
||||
const { value } = this.state;
|
||||
|
||||
return entry && entry.name
|
||||
? entry.name.toUpperCase()
|
||||
: value;
|
||||
: this.state.value;
|
||||
}
|
||||
|
||||
getEntry () {
|
||||
|
@ -16,11 +16,24 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import keycode from 'keycode';
|
||||
import { MenuItem, AutoComplete as MUIAutoComplete } from 'material-ui';
|
||||
import { MenuItem, AutoComplete as MUIAutoComplete, Divider as MUIDivider } from 'material-ui';
|
||||
import { PopoverAnimationVertical } from 'material-ui/Popover';
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
// Hack to prevent "Unknown prop `disableFocusRipple` on <hr> tag" error
|
||||
class Divider extends Component {
|
||||
static muiName = MUIDivider.muiName;
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div style={ { margin: '0.25em 0' } }>
|
||||
<MUIDivider style={ { height: 2 } } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class AutoComplete extends Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
@ -38,15 +51,17 @@ export default class AutoComplete extends Component {
|
||||
PropTypes.array,
|
||||
PropTypes.object
|
||||
])
|
||||
}
|
||||
};
|
||||
|
||||
state = {
|
||||
lastChangedValue: undefined,
|
||||
entry: null,
|
||||
open: false,
|
||||
fakeBlur: false,
|
||||
dataSource: []
|
||||
}
|
||||
dataSource: [],
|
||||
dividerBreaks: []
|
||||
};
|
||||
|
||||
dividersVisibility = {};
|
||||
|
||||
componentWillMount () {
|
||||
const dataSource = this.getDataSource();
|
||||
@ -64,7 +79,7 @@ export default class AutoComplete extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { disabled, error, hint, label, value, className, filter, onUpdateInput } = this.props;
|
||||
const { disabled, error, hint, label, value, className, onUpdateInput } = this.props;
|
||||
const { open, dataSource } = this.state;
|
||||
|
||||
return (
|
||||
@ -78,9 +93,9 @@ export default class AutoComplete extends Component {
|
||||
onUpdateInput={ onUpdateInput }
|
||||
searchText={ value }
|
||||
onFocus={ this.onFocus }
|
||||
onBlur={ this.onBlur }
|
||||
onClose={ this.onClose }
|
||||
animation={ PopoverAnimationVertical }
|
||||
filter={ filter }
|
||||
filter={ this.handleFilter }
|
||||
popoverProps={ { open } }
|
||||
openOnFocus
|
||||
menuCloseDelay={ 0 }
|
||||
@ -100,18 +115,76 @@ export default class AutoComplete extends Component {
|
||||
? entries
|
||||
: Object.values(entries);
|
||||
|
||||
if (renderItem && typeof renderItem === 'function') {
|
||||
return entriesArray.map(entry => renderItem(entry));
|
||||
let currentDivider = 0;
|
||||
let firstSet = false;
|
||||
|
||||
const dataSource = entriesArray.map((entry, index) => {
|
||||
// Render divider
|
||||
if (typeof entry === 'string' && entry.toLowerCase() === 'divider') {
|
||||
// Don't add divider if nothing before
|
||||
if (!firstSet) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const item = {
|
||||
text: '',
|
||||
divider: currentDivider,
|
||||
isDivider: true,
|
||||
value: (
|
||||
<Divider />
|
||||
)
|
||||
};
|
||||
|
||||
currentDivider++;
|
||||
return item;
|
||||
}
|
||||
|
||||
let item;
|
||||
|
||||
if (renderItem && typeof renderItem === 'function') {
|
||||
item = renderItem(entry);
|
||||
} else {
|
||||
item = {
|
||||
text: entry,
|
||||
value: (
|
||||
<MenuItem
|
||||
primaryText={ entry }
|
||||
/>
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
if (!firstSet) {
|
||||
item.first = true;
|
||||
firstSet = true;
|
||||
}
|
||||
|
||||
item.divider = currentDivider;
|
||||
|
||||
return item;
|
||||
}).filter((item) => item !== undefined);
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
handleFilter = (searchText, name, item) => {
|
||||
if (item.isDivider) {
|
||||
return this.dividersVisibility[item.divider];
|
||||
}
|
||||
|
||||
return entriesArray.map(entry => ({
|
||||
text: entry,
|
||||
value: (
|
||||
<MenuItem
|
||||
primaryText={ entry }
|
||||
/>
|
||||
)
|
||||
}));
|
||||
if (item.first) {
|
||||
this.dividersVisibility = {};
|
||||
}
|
||||
|
||||
const { filter } = this.props;
|
||||
const show = filter(searchText, name, item);
|
||||
|
||||
// Show the related divider
|
||||
if (show) {
|
||||
this.dividersVisibility[item.divider] = true;
|
||||
}
|
||||
|
||||
return show;
|
||||
}
|
||||
|
||||
onKeyDown = (event) => {
|
||||
@ -121,7 +194,6 @@ export default class AutoComplete extends Component {
|
||||
case 'down':
|
||||
const { menu } = muiAutocomplete.refs;
|
||||
menu && menu.handleKeyDown(event);
|
||||
this.setState({ fakeBlur: true });
|
||||
break;
|
||||
|
||||
case 'enter':
|
||||
@ -155,22 +227,12 @@ export default class AutoComplete extends Component {
|
||||
this.setState({ entry, open: false });
|
||||
}
|
||||
|
||||
onBlur = (event) => {
|
||||
onClose = (event) => {
|
||||
const { onUpdateInput } = this.props;
|
||||
|
||||
// TODO: Handle blur gracefully where we use onUpdateInput (currently replaces
|
||||
// input where text is allowed with the last selected value from the dropdown)
|
||||
if (!onUpdateInput) {
|
||||
window.setTimeout(() => {
|
||||
const { entry, fakeBlur } = this.state;
|
||||
|
||||
if (fakeBlur) {
|
||||
this.setState({ fakeBlur: false });
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleOnChange(entry);
|
||||
}, 200);
|
||||
const { entry } = this.state;
|
||||
this.handleOnChange(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
.layout {
|
||||
padding: 0.25em 0.25em 1em 0.25em;
|
||||
padding: 0.25em;
|
||||
|
||||
&>div {
|
||||
margin-bottom: 0.75em;
|
||||
|
@ -31,6 +31,10 @@
|
||||
.infoline,
|
||||
.uuidline {
|
||||
line-height: 1.618em;
|
||||
|
||||
&.bigaddress {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.infoline,
|
||||
|
@ -32,18 +32,20 @@ export default class Header extends Component {
|
||||
balance: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
isContract: PropTypes.bool
|
||||
isContract: PropTypes.bool,
|
||||
hideName: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
children: null,
|
||||
isContract: false
|
||||
isContract: false,
|
||||
hideName: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { api } = this.context;
|
||||
const { account, balance, className, children } = this.props;
|
||||
const { account, balance, className, children, hideName } = this.props;
|
||||
const { address, meta, uuid } = account;
|
||||
|
||||
if (!account) {
|
||||
@ -60,17 +62,20 @@ export default class Header extends Component {
|
||||
<IdentityIcon
|
||||
address={ address } />
|
||||
<div className={ styles.floatleft }>
|
||||
<ContainerTitle title={ <IdentityName address={ address } unknown /> } />
|
||||
<div className={ styles.addressline }>
|
||||
{ this.renderName(address) }
|
||||
|
||||
<div className={ [ hideName ? styles.bigaddress : '', styles.addressline ].join(' ') }>
|
||||
<CopyToClipboard data={ address } />
|
||||
<div className={ styles.address }>{ address }</div>
|
||||
</div>
|
||||
|
||||
{ uuidText }
|
||||
<div className={ styles.infoline }>
|
||||
{ meta.description }
|
||||
</div>
|
||||
{ this.renderTxCount() }
|
||||
</div>
|
||||
|
||||
<div className={ styles.tags }>
|
||||
<Tags tags={ meta.tags } />
|
||||
</div>
|
||||
@ -89,6 +94,18 @@ export default class Header extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderName (address) {
|
||||
const { hideName } = this.props;
|
||||
|
||||
if (hideName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ContainerTitle title={ <IdentityName address={ address } unknown /> } />
|
||||
);
|
||||
}
|
||||
|
||||
renderTxCount () {
|
||||
const { balance, isContract } = this.props;
|
||||
|
||||
|
@ -26,7 +26,7 @@ import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
||||
import { EditMeta, DeleteAccount, Shapeshift, SMSVerification, Transfer, PasswordManager } from '~/modals';
|
||||
import { Actionbar, Button, Page } from '~/ui';
|
||||
|
||||
import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png';
|
||||
import shapeshiftBtn from '~/../assets/images/shapeshift-btn.png';
|
||||
|
||||
import Header from './Header';
|
||||
import Transactions from './Transactions';
|
||||
|
@ -153,7 +153,7 @@ export default class Summary extends Component {
|
||||
const { link, noLink, account, name } = this.props;
|
||||
|
||||
const { address } = account;
|
||||
const viewLink = `/${link || 'account'}/${address}`;
|
||||
const viewLink = `/${link || 'accounts'}/${address}`;
|
||||
|
||||
const content = (
|
||||
<IdentityName address={ address } name={ name } unknown />
|
||||
|
@ -19,8 +19,9 @@ import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import ActionDelete from 'material-ui/svg-icons/action/delete';
|
||||
import ContentCreate from 'material-ui/svg-icons/content/create';
|
||||
import ContentAdd from 'material-ui/svg-icons/content/add';
|
||||
|
||||
import { EditMeta } from '~/modals';
|
||||
import { EditMeta, AddAddress } from '~/modals';
|
||||
import { Actionbar, Button, Page } from '~/ui';
|
||||
|
||||
import Header from '../Account/Header';
|
||||
@ -32,7 +33,7 @@ class Address extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired,
|
||||
router: PropTypes.object.isRequired
|
||||
}
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
setVisibleAccounts: PropTypes.func.isRequired,
|
||||
@ -40,12 +41,13 @@ class Address extends Component {
|
||||
contacts: PropTypes.object,
|
||||
balances: PropTypes.object,
|
||||
params: PropTypes.object
|
||||
}
|
||||
};
|
||||
|
||||
state = {
|
||||
showDeleteDialog: false,
|
||||
showEditDialog: false
|
||||
}
|
||||
showEditDialog: false,
|
||||
showAdd: false
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.setVisibleAccounts();
|
||||
@ -73,32 +75,69 @@ class Address extends Component {
|
||||
render () {
|
||||
const { contacts, balances } = this.props;
|
||||
const { address } = this.props.params;
|
||||
const { showDeleteDialog } = this.state;
|
||||
|
||||
if (Object.keys(contacts).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contact = (contacts || {})[address];
|
||||
const balance = (balances || {})[address];
|
||||
|
||||
if (!contact) {
|
||||
return (
|
||||
<div>
|
||||
{ this.renderAddAddress(contact, address) }
|
||||
{ this.renderEditDialog(contact) }
|
||||
{ this.renderActionbar(contact) }
|
||||
{ this.renderDelete(contact) }
|
||||
<Page>
|
||||
<Header
|
||||
account={ contact || { address, meta: {} } }
|
||||
balance={ balance }
|
||||
hideName={ !contact }
|
||||
/>
|
||||
<Transactions
|
||||
address={ address }
|
||||
/>
|
||||
</Page>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddAddress (contact, address) {
|
||||
if (contact) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { contacts } = this.props;
|
||||
const { showAdd } = this.state;
|
||||
|
||||
if (!showAdd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ this.renderEditDialog(contact) }
|
||||
{ this.renderActionbar(contact) }
|
||||
<Delete
|
||||
account={ contact }
|
||||
visible={ showDeleteDialog }
|
||||
route='/addresses'
|
||||
onClose={ this.closeDeleteDialog } />
|
||||
<Page>
|
||||
<Header
|
||||
account={ contact }
|
||||
balance={ balance } />
|
||||
<Transactions
|
||||
address={ address } />
|
||||
</Page>
|
||||
</div>
|
||||
<AddAddress
|
||||
contacts={ contacts }
|
||||
onClose={ this.onCloseAdd }
|
||||
address={ address }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderDelete (contact) {
|
||||
if (!contact) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { showDeleteDialog } = this.state;
|
||||
|
||||
return (
|
||||
<Delete
|
||||
account={ contact }
|
||||
visible={ showDeleteDialog }
|
||||
route='/addresses'
|
||||
onClose={ this.closeDeleteDialog }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -116,17 +155,27 @@ class Address extends Component {
|
||||
onClick={ this.showDeleteDialog } />
|
||||
];
|
||||
|
||||
const addToBook = (
|
||||
<Button
|
||||
key='newAddress'
|
||||
icon={ <ContentAdd /> }
|
||||
label='save address'
|
||||
onClick={ this.onOpenAdd }
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Actionbar
|
||||
title='Address Information'
|
||||
buttons={ !contact ? [] : buttons } />
|
||||
buttons={ !contact ? [ addToBook ] : buttons }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderEditDialog (contact) {
|
||||
const { showEditDialog } = this.state;
|
||||
|
||||
if (!showEditDialog) {
|
||||
if (!contact || !showEditDialog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -151,6 +200,16 @@ class Address extends Component {
|
||||
showDeleteDialog = () => {
|
||||
this.setState({ showDeleteDialog: true });
|
||||
}
|
||||
|
||||
onOpenAdd = () => {
|
||||
this.setState({
|
||||
showAdd: true
|
||||
});
|
||||
}
|
||||
|
||||
onCloseAdd = () => {
|
||||
this.setState({ showAdd: false });
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
|
@ -23,7 +23,7 @@ import { uniq, isEqual } from 'lodash';
|
||||
import List from '../Accounts/List';
|
||||
import Summary from '../Accounts/Summary';
|
||||
import { AddAddress } from '~/modals';
|
||||
import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page } from '~/ui';
|
||||
import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page, Loading } from '~/ui';
|
||||
import { setVisibleAccounts } from '~/redux/providers/personalActions';
|
||||
|
||||
import styles from './addresses.css';
|
||||
@ -72,27 +72,40 @@ class Addresses extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { balances, contacts, hasContacts } = this.props;
|
||||
const { searchValues, sortOrder } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ this.renderActionbar() }
|
||||
{ this.renderAddAddress() }
|
||||
<Page>
|
||||
<List
|
||||
link='address'
|
||||
search={ searchValues }
|
||||
accounts={ contacts }
|
||||
balances={ balances }
|
||||
empty={ !hasContacts }
|
||||
order={ sortOrder }
|
||||
handleAddSearchToken={ this.onAddSearchToken } />
|
||||
{ this.renderAccountsList() }
|
||||
</Page>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccountsList () {
|
||||
const { balances, contacts, hasContacts } = this.props;
|
||||
const { searchValues, sortOrder } = this.state;
|
||||
|
||||
if (hasContacts && Object.keys(balances).length === 0) {
|
||||
return (
|
||||
<Loading />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<List
|
||||
link='addresses'
|
||||
search={ searchValues }
|
||||
accounts={ contacts }
|
||||
balances={ balances }
|
||||
empty={ !hasContacts }
|
||||
order={ sortOrder }
|
||||
handleAddSearchToken={ this.onAddSearchToken }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderSortButton () {
|
||||
const onChange = (sortOrder) => {
|
||||
this.setState({ sortOrder });
|
||||
|
@ -30,24 +30,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tabs button,
|
||||
.tabLink {
|
||||
display: flex;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
&.tabactive, &.tabactive:hover {
|
||||
background: rgba(0, 0, 0, 0.25) !important;
|
||||
border-radius: 4px 4px 0 0;
|
||||
|
||||
* {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabLink,
|
||||
.settings,
|
||||
.logo,
|
||||
.last {
|
||||
background: rgba(0, 0, 0, 0.5) !important; /* rgba(0, 0, 0, 0.25) !important; */
|
||||
}
|
||||
|
||||
.tabs button:hover {
|
||||
background: rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
button.tabactive,
|
||||
button.tabactive:hover {
|
||||
color: white !important;
|
||||
background: rgba(0, 0, 0, 0.25) !important;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.tabbarTooltip {
|
||||
left: 3.3em;
|
||||
top: 0.5em;
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Link } from 'react-router';
|
||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||
import { Tab as MUITab } from 'material-ui/Tabs';
|
||||
import { isEqual } from 'lodash';
|
||||
@ -26,41 +26,26 @@ import { Badge, Tooltip } from '~/ui';
|
||||
import styles from './tabBar.css';
|
||||
import imagesEthcoreBlock from '../../../../assets/images/parity-logo-white-no-text.svg';
|
||||
|
||||
const TABMAP = {
|
||||
accounts: 'account',
|
||||
wallet: 'account',
|
||||
addresses: 'address',
|
||||
apps: 'app',
|
||||
contracts: 'contract',
|
||||
deploy: 'contract'
|
||||
};
|
||||
|
||||
class Tab extends Component {
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
view: PropTypes.object,
|
||||
children: PropTypes.node,
|
||||
pendings: PropTypes.number,
|
||||
onChange: PropTypes.func
|
||||
pendings: PropTypes.number
|
||||
};
|
||||
|
||||
shouldComponentUpdate (nextProps) {
|
||||
return nextProps.active !== this.props.active ||
|
||||
(nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
|
||||
return (nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { active, view, children } = this.props;
|
||||
const { view, children } = this.props;
|
||||
|
||||
const label = this.getLabel(view);
|
||||
|
||||
return (
|
||||
<MUITab
|
||||
className={ active ? styles.tabactive : '' }
|
||||
selected={ active }
|
||||
icon={ view.icon }
|
||||
label={ label }
|
||||
onTouchTap={ this.handleClick }
|
||||
>
|
||||
{ children }
|
||||
</MUITab>
|
||||
@ -118,11 +103,6 @@ class Tab extends Component {
|
||||
|
||||
return this.renderLabel(label, null);
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const { onChange, view } = this.props;
|
||||
onChange(view);
|
||||
}
|
||||
}
|
||||
|
||||
class TabBar extends Component {
|
||||
@ -132,7 +112,6 @@ class TabBar extends Component {
|
||||
|
||||
static propTypes = {
|
||||
views: PropTypes.array.isRequired,
|
||||
hash: PropTypes.string.isRequired,
|
||||
pending: PropTypes.array,
|
||||
isTest: PropTypes.bool,
|
||||
netChain: PropTypes.string
|
||||
@ -142,34 +121,11 @@ class TabBar extends Component {
|
||||
pending: []
|
||||
};
|
||||
|
||||
state = {
|
||||
activeViewId: ''
|
||||
};
|
||||
|
||||
setActiveView (props = this.props) {
|
||||
const { hash, views } = props;
|
||||
const view = views.find((view) => view.value === hash);
|
||||
|
||||
this.setState({ activeViewId: view.id });
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.setActiveView();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.hash !== this.props.hash) {
|
||||
this.setActiveView(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
const prevViews = this.props.views.map((v) => v.id).sort();
|
||||
const nextViews = nextProps.views.map((v) => v.id).sort();
|
||||
|
||||
return (nextProps.hash !== this.props.hash) ||
|
||||
(nextProps.pending.length !== this.props.pending.length) ||
|
||||
(nextState.activeViewId !== this.state.activeViewId) ||
|
||||
return (nextProps.pending.length !== this.props.pending.length) ||
|
||||
(!isEqual(prevViews, nextViews));
|
||||
}
|
||||
|
||||
@ -206,7 +162,6 @@ class TabBar extends Component {
|
||||
|
||||
renderTabs () {
|
||||
const { views, pending } = this.props;
|
||||
const { activeViewId } = this.state;
|
||||
|
||||
const items = views
|
||||
.map((view, index) => {
|
||||
@ -216,60 +171,66 @@ class TabBar extends Component {
|
||||
)
|
||||
: null;
|
||||
|
||||
const active = activeViewId === view.id;
|
||||
|
||||
return (
|
||||
<Tab
|
||||
active={ active }
|
||||
view={ view }
|
||||
onChange={ this.onChange }
|
||||
<Link
|
||||
key={ view.id }
|
||||
pendings={ pending.length }
|
||||
to={ view.route }
|
||||
activeClassName={ styles.tabactive }
|
||||
className={ styles.tabLink }
|
||||
>
|
||||
{ body }
|
||||
</Tab>
|
||||
<Tab
|
||||
view={ view }
|
||||
pendings={ pending.length }
|
||||
>
|
||||
{ body }
|
||||
</Tab>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ styles.tabs }
|
||||
onChange={ this.onChange }>
|
||||
<div className={ styles.tabs }>
|
||||
{ items }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (view) => {
|
||||
const { router } = this.context;
|
||||
|
||||
router.push(view.route);
|
||||
this.setState({ activeViewId: view.id });
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { views } = state.settings;
|
||||
function mapStateToProps (initState) {
|
||||
const { views } = initState.settings;
|
||||
|
||||
const filteredViews = Object
|
||||
let filteredViewIds = Object
|
||||
.keys(views)
|
||||
.filter((id) => views[id].fixed || views[id].active)
|
||||
.filter((id) => views[id].fixed || views[id].active);
|
||||
|
||||
let filteredViews = filteredViewIds
|
||||
.map((id) => ({
|
||||
...views[id],
|
||||
id
|
||||
}));
|
||||
|
||||
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
|
||||
const hash = TABMAP[windowHash] || windowHash;
|
||||
return (state) => {
|
||||
const { views } = state.settings;
|
||||
|
||||
return { views: filteredViews, hash };
|
||||
}
|
||||
const viewIds = Object
|
||||
.keys(views)
|
||||
.filter((id) => views[id].fixed || views[id].active);
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({}, dispatch);
|
||||
if (isEqual(viewIds, filteredViewIds)) {
|
||||
return { views: filteredViews };
|
||||
}
|
||||
|
||||
filteredViewIds = viewIds;
|
||||
filteredViews = viewIds
|
||||
.map((id) => ({
|
||||
...views[id],
|
||||
id
|
||||
}));
|
||||
|
||||
return { views: filteredViews };
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps
|
||||
)(TabBar);
|
||||
|
@ -19,4 +19,5 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class Contracts extends Component {
|
||||
{ this.renderDeployContract() }
|
||||
<Page>
|
||||
<List
|
||||
link='contract'
|
||||
link='contracts'
|
||||
search={ searchValues }
|
||||
accounts={ contracts }
|
||||
balances={ balances }
|
||||
@ -142,12 +142,12 @@ class Contracts extends Component {
|
||||
label='deploy contract'
|
||||
onClick={ this.onDeployContract } />,
|
||||
<Link
|
||||
to='/contracts/write'
|
||||
to='/contracts/develop'
|
||||
key='writeContract'
|
||||
>
|
||||
<Button
|
||||
icon={ <FileIcon /> }
|
||||
label='write contract'
|
||||
label='develop contract'
|
||||
/>
|
||||
</Link>,
|
||||
|
||||
|
@ -72,9 +72,17 @@ export default class Dapps extends Component {
|
||||
] }
|
||||
/>
|
||||
<Page>
|
||||
{ this.renderList(this.store.visibleLocal) }
|
||||
{ this.renderList(this.store.visibleBuiltin) }
|
||||
{ this.renderList(this.store.visibleNetwork, externalOverlay) }
|
||||
<div>
|
||||
{ this.renderList(this.store.visibleLocal) }
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{ this.renderList(this.store.visibleBuiltin) }
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{ this.renderList(this.store.visibleNetwork, externalOverlay) }
|
||||
</div>
|
||||
</Page>
|
||||
</div>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user