Fixes to the Registry dapp (#4984)

* Don't show fee warning when there is none

* Hide Warning in Registry onclick

* Use the default account in the Registry

* Fix Etherscan links in Regsitry
This commit is contained in:
Nicolas Gotchac 2017-03-22 11:56:52 +01:00 committed by Jaco Greeff
parent b725829bfd
commit a028e445fe
11 changed files with 118 additions and 102 deletions

View File

@ -42,7 +42,7 @@ export default class Personal {
// FIXME: Because of the different API instances, the "wait for valid changes" approach
// doesn't work. Since the defaultAccount is critical to operation, we poll in exactly
// same way we do in ../eth (ala same as eth_blockNumber) and update. This should be moved
// same way we do in ../eth (ala eth_blockNumber) and update. This should be moved
// to pub-sub as it becomes available
_defaultAccount = (timerDisabled = false) => {
const nextTimeout = (timeout = 1000) => {

View File

@ -15,24 +15,10 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.button {
/* TODO remove !important once material design lite is used */
padding: 0 !important;
}
.icon {
/* TODO remove !important once material design lite is used */
margin: 0 !important;
width: 30px !important;
height: 30px !important;
}
.menuIcon {
display: inline-block;
vertical-align: middle;
}
.menuText {
display: inline-block;
line-height: 24px;
vertical-align: top;
margin: 0 !important;
padding: 0 !important;
width: 30px !important;
}

View File

@ -17,83 +17,50 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import IconMenu from 'material-ui/IconMenu';
import IconButton from 'material-ui/IconButton/IconButton';
import AccountIcon from 'material-ui/svg-icons/action/account-circle';
import MenuItem from 'material-ui/MenuItem';
import { init } from './actions';
import IdentityIcon from '../IdentityIcon';
import Address from '../ui/address';
import { select } from './actions';
import styles from './accounts.css';
class Accounts extends Component {
static propTypes = {
all: PropTypes.object.isRequired,
selected: PropTypes.object,
selected: PropTypes.oneOfType([
PropTypes.oneOf([ null ]),
PropTypes.string
]),
onInit: PropTypes.func.isRequired
};
select: PropTypes.func.isRequired
componentWillMount () {
this.props.onInit();
}
render () {
const { all, selected } = this.props;
const { selected } = this.props;
const origin = { horizontal: 'right', vertical: 'top' };
const accountsButton = (
<IconButton className={ styles.button }>
{ selected
? (
<IdentityIcon
className={ styles.icon }
address={ selected.address }
/>
) : (
<AccountIcon
className={ styles.icon }
color='white'
/>
)
}
</IconButton>);
if (!selected) {
return (
<AccountIcon
className={ styles.icon }
color='white'
/>
);
}
return (
<IconMenu
value={ selected ? this.renderAccount(selected) : null }
onChange={ this.onAccountSelect }
iconButtonElement={ accountsButton }
anchorOrigin={ origin }
targetOrigin={ origin }
>
{ Object.values(all).map(this.renderAccount) }
</IconMenu>
<IdentityIcon
className={ styles.icon }
address={ selected }
/>
);
}
renderAccount = (account) => {
const { selected } = this.props;
const isSelected = selected && selected.address === account.address;
return (
<MenuItem
key={ account.address }
value={ account.address }
checked={ isSelected }
insetChildren={ !isSelected }
>
<Address address={ account.address } />
</MenuItem>
);
};
onAccountSelect = (e, address) => {
this.props.select(address);
};
}
const mapStateToProps = (state) => state.accounts;
const mapDispatchToProps = (dispatch) => bindActionCreators({ select }, dispatch);
const mapDispatchToProps = (dispatch) => bindActionCreators({
onInit: init
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(Accounts);

View File

@ -14,4 +14,27 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { api } from '../parity';
export const select = (address) => ({ type: 'accounts select', address });
export const init = () => (dispatch) => {
api.subscribe('parity_defaultAccount', (error, accountAddress) => {
if (error) {
return console.error(error);
}
if (accountAddress) {
dispatch(select(accountAddress));
}
});
return api.parity
.defaultAccount()
.then((accountAddress) => {
dispatch(select(accountAddress));
})
.catch((error) => {
console.error(error);
});
};

View File

@ -16,9 +16,11 @@
*/
.header {
align-items: center;
display: flex;
justify-content: space-between;
margin: 0; padding: .3em 1em;
margin: 0;
padding: 0.3em 1em;
color: #fff;
background-color: #333;
}
@ -54,6 +56,7 @@
background: #f80;
bottom: 0;
color: #fff;
cursor: pointer;
left: 0;
opacity: 1;
padding: 1.5em;

View File

@ -49,6 +49,10 @@ export default class Application extends Component {
fee: nullableProptype(PropTypes.object.isRequired)
};
state = {
showWarning: true
};
render () {
const { contract, fee } = this.props;
let warning = null;
@ -65,9 +69,7 @@ export default class Application extends Component {
<Lookup />
{ this.renderActions() }
<Events />
<div className={ styles.warning }>
WARNING: The name registry is experimental. Please ensure that you understand the risks, benefits & consequences of registering a name before doing so. A non-refundable fee of { api.util.fromWei(fee).toFormat(3) }<small>ETH</small> is required for all registrations.
</div>
{ this.renderWarning() }
</div>
) : (
<CircularProgress size={ 60 } />
@ -98,4 +100,39 @@ export default class Application extends Component {
</div>
);
}
renderWarning () {
const { showWarning } = this.state;
const { fee } = this.props;
if (!showWarning) {
return null;
}
return (
<div
className={ styles.warning }
onClick={ this.handleHideWarning }
>
<span>
WARNING: The name registry is experimental. Please ensure that you understand the risks,
benefits & consequences of registering a name before doing so.
</span>
{
fee && api.util.fromWei(fee).gt(0)
? (
<span>
&nbsp;A non-refundable fee of { api.util.fromWei(fee).toFormat(3) } <small>ETH</small>
&nbsp;is required for all registrations.
</span>
)
: null
}
</div>
);
}
handleHideWarning = () => {
this.setState({ showWarning: false });
}
}

View File

@ -33,11 +33,11 @@ export const reserveFail = (name, error) => ({ type: 'names reserve fail', name,
export const reserve = (name) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const accountAddress = state.accounts.selected;
const contract = state.contract;
const fee = state.fee;
if (!contract || !account) {
if (!contract || !accountAddress) {
return;
}
@ -58,7 +58,7 @@ export const reserve = (name) => (dispatch, getState) => {
const { reserve } = contract.instance;
const options = {
from: account.address,
from: accountAddress,
value: fee
};
const values = [
@ -88,10 +88,10 @@ export const dropFail = (name, error) => ({ type: 'names drop fail', name, error
export const drop = (name) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const accountAddress = state.accounts.selected;
const contract = state.contract;
if (!contract || !account) {
if (!contract || !accountAddress) {
return;
}
@ -105,14 +105,14 @@ export const drop = (name) => (dispatch, getState) => {
return getOwner(contract, name)
.then((owner) => {
if (owner.toLowerCase() !== account.address.toLowerCase()) {
if (owner.toLowerCase() !== accountAddress.toLowerCase()) {
throw new Error(`you are not the owner of "${name}"`);
}
const { drop } = contract.instance;
const options = {
from: account.address
from: accountAddress
};
const values = [

View File

@ -30,10 +30,10 @@ export const fail = (error) => ({ type: 'records update fail', error });
export const update = (name, key, value) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const accountAddress = state.accounts.selected;
const contract = state.contract;
if (!contract || !account) {
if (!contract || !accountAddress) {
return;
}
@ -42,7 +42,7 @@ export const update = (name, key, value) => (dispatch, getState) => {
return getOwner(contract, name)
.then((owner) => {
if (owner.toLowerCase() !== account.address.toLowerCase()) {
if (owner.toLowerCase() !== accountAddress.toLowerCase()) {
throw new Error(`you are not the owner of "${name}"`);
}
@ -51,7 +51,7 @@ export const update = (name, key, value) => (dispatch, getState) => {
: contract.instance.setData || contract.instance.set;
const options = {
from: account.address
from: accountAddress
};
const values = [

View File

@ -30,10 +30,10 @@ export const fail = (action, error) => ({ type: `reverse ${action} fail`, error
export const propose = (name, address) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const accountAddress = state.accounts.selected;
const contract = state.contract;
if (!contract || !account) {
if (!contract || !accountAddress) {
return;
}
@ -42,14 +42,14 @@ export const propose = (name, address) => (dispatch, getState) => {
return getOwner(contract, name)
.then((owner) => {
if (owner.toLowerCase() !== account.address.toLowerCase()) {
if (owner.toLowerCase() !== accountAddress.toLowerCase()) {
throw new Error(`you are not the owner of "${name}"`);
}
const { proposeReverse } = contract.instance;
const options = {
from: account.address
from: accountAddress
};
const values = [
@ -74,10 +74,10 @@ export const propose = (name, address) => (dispatch, getState) => {
export const confirm = (name) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const accountAddress = state.accounts.selected;
const contract = state.contract;
if (!contract || !account) {
if (!contract || !accountAddress) {
return;
}
@ -86,14 +86,14 @@ export const confirm = (name) => (dispatch, getState) => {
return getOwner(contract, name)
.then((owner) => {
if (owner.toLowerCase() !== account.address.toLowerCase()) {
if (owner.toLowerCase() !== accountAddress.toLowerCase()) {
throw new Error(`you are not the owner of "${name}"`);
}
const { confirmReverse } = contract.instance;
const options = {
from: account.address
from: accountAddress
};
const values = [

View File

@ -31,8 +31,8 @@ export default (state = initialState, action) => {
return { ...state, all: accounts };
}
if (action.type === 'accounts select' && state.all[action.address]) {
return { ...state, selected: state.all[action.address] };
if (action.type === 'accounts select') {
return { ...state, selected: action.address };
}
return state;

View File

@ -22,7 +22,7 @@ const etherscanUrl = (hash, isTestnet, netVersion) => {
hash = hash.toLowerCase().replace(leading0x, '');
const type = hash.length === 40 ? 'address' : 'tx';
return `https://${externalUrl(isTestnet, netVersion)}/${type}/0x${hash}`;
return `${externalUrl(isTestnet, netVersion)}/${type}/0x${hash}`;
};
export default etherscanUrl;