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