- { recordTypeSelect(type, this.onTypeChange, styles.spacing) }
+
+
+
+
+
+
}
@@ -86,14 +107,30 @@ export default class Lookup extends Component {
);
}
- onNameChange = (e) => {
- this.setState({ name: e.target.value });
+ onInputChange = (e) => {
+ this.setState({ input: e.target.value });
};
+
onTypeChange = (e, i, type) => {
this.setState({ type });
- this.props.actions.clear();
+ this.props.clear();
};
+
onLookupClick = () => {
- this.props.actions.lookup(this.state.name, this.state.type);
+ const { input, type } = this.state;
+
+ if (type === 'reverse') {
+ this.props.reverseLookup(input);
+ } else {
+ this.props.lookup(input, type);
+ }
};
}
+
+const mapStateToProps = (state) => state.lookup;
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators({
+ clear, lookup, reverseLookup
+ }, dispatch);
+
+export default connect(mapStateToProps, mapDispatchToProps)(Lookup);
diff --git a/js/src/dapps/registry/Lookup/reducers.js b/js/src/dapps/registry/Lookup/reducers.js
index d6807c713..b675fc702 100644
--- a/js/src/dapps/registry/Lookup/reducers.js
+++ b/js/src/dapps/registry/Lookup/reducers.js
@@ -14,39 +14,34 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
+import { isStage } from '../util/actions';
+
const initialState = {
pending: false,
- name: '', type: '',
result: null
};
export default (state = initialState, action) => {
- if (action.type === 'lookup clear') {
- return { ...state, result: null };
+ const { type } = action;
+
+ if (type.slice(0, 7) !== 'lookup ' && type.slice(0, 14) !== 'reverseLookup ') {
+ return state;
}
- if (action.type === 'lookup start') {
- return {
- pending: true,
- name: action.name, type: action.entry,
- result: null
- };
+ if (isStage('clear', action)) {
+ return { pending: state.pending, result: null };
}
- if (action.type === 'lookup error') {
- return {
- pending: false,
- name: initialState.name, type: initialState.type,
- result: null
- };
+ if (isStage('start', action)) {
+ return { pending: true, result: null };
}
- if (action.type === 'lookup success') {
- return {
- pending: false,
- name: initialState.name, type: initialState.type,
- result: action.result
- };
+ if (isStage('error', action)) {
+ return { pending: false, result: null };
+ }
+
+ if (isStage('success', action)) {
+ return { pending: false, result: action.result };
}
return state;
diff --git a/js/src/dapps/registry/Names/actions.js b/js/src/dapps/registry/Names/actions.js
index 448f710b1..67867ca8e 100644
--- a/js/src/dapps/registry/Names/actions.js
+++ b/js/src/dapps/registry/Names/actions.js
@@ -15,6 +15,7 @@
// along with Parity. If not, see
.
import { sha3, api } from '../parity.js';
+import postTx from '../util/post-tx';
const alreadyQueued = (queue, action, name) =>
!!queue.find((entry) => entry.action === action && entry.name === name);
@@ -30,42 +31,32 @@ export const reserve = (name) => (dispatch, getState) => {
const account = state.accounts.selected;
const contract = state.contract;
const fee = state.fee;
-
if (!contract || !account) {
return;
}
+ name = name.toLowerCase();
+
if (alreadyQueued(state.names.queue, 'reserve', name)) {
return;
}
-
const reserve = contract.functions.find((f) => f.name === 'reserve');
- name = name.toLowerCase();
+ dispatch(reserveStart(name));
+
const options = {
from: account.address,
value: fee
};
- const values = [ sha3(name) ];
+ const values = [
+ sha3(name)
+ ];
- dispatch(reserveStart(name));
-
- reserve.estimateGas(options, values)
- .then((gas) => {
- options.gas = gas.mul(1.2).toFixed(0);
- return reserve.postTransaction(options, values);
- })
- .then((requestId) => {
- return api.pollMethod('parity_checkRequest', requestId);
- })
- .then((txhash) => {
+ postTx(api, reserve, options, values)
+ .then((txHash) => {
dispatch(reserveSuccess(name));
})
.catch((err) => {
- if (err && err.type === 'REQUEST_REJECTED') {
- return dispatch(reserveFail(name));
- }
-
console.error(`could not reserve ${name}`);
if (err) {
@@ -86,38 +77,31 @@ export const drop = (name) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const contract = state.contract;
-
if (!contract || !account) {
return;
}
+ name = name.toLowerCase();
if (alreadyQueued(state.names.queue, 'drop', name)) {
return;
}
const drop = contract.functions.find((f) => f.name === 'drop');
- name = name.toLowerCase();
- const options = { from: account.address };
- const values = [ sha3(name) ];
-
dispatch(dropStart(name));
- drop.estimateGas(options, values)
- .then((gas) => {
- options.gas = gas.mul(1.2).toFixed(0);
- return drop.postTransaction(options, values);
- })
- .then((requestId) => {
- return api.pollMethod('parity_checkRequest', requestId);
- })
+
+ const options = {
+ from: account.address
+ };
+ const values = [
+ sha3(name)
+ ];
+
+ postTx(api, drop, options, values)
.then((txhash) => {
dispatch(dropSuccess(name));
})
.catch((err) => {
- if (err && err.type === 'REQUEST_REJECTED') {
- dispatch(reserveFail(name));
- }
-
console.error(`could not drop ${name}`);
if (err) {
diff --git a/js/src/dapps/registry/Names/names.css b/js/src/dapps/registry/Names/names.css
index 4507e38d4..b56387909 100644
--- a/js/src/dapps/registry/Names/names.css
+++ b/js/src/dapps/registry/Names/names.css
@@ -20,7 +20,8 @@
}
.box {
- margin: 0 1em 1em 1em;
+ display: flex;
+ align-items: baseline;
}
.spacing {
diff --git a/js/src/dapps/registry/Names/names.js b/js/src/dapps/registry/Names/names.js
index c0c4badf0..c3f0e79f6 100644
--- a/js/src/dapps/registry/Names/names.js
+++ b/js/src/dapps/registry/Names/names.js
@@ -15,6 +15,8 @@
// along with Parity. If not, see
.
import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
import { Card, CardHeader, CardText } from 'material-ui/Card';
import TextField from 'material-ui/TextField';
import DropDownMenu from 'material-ui/DropDownMenu';
@@ -24,6 +26,7 @@ import CheckIcon from 'material-ui/svg-icons/navigation/check';
import { fromWei } from '../parity.js';
+import { reserve, drop } from './actions';
import styles from './names.css';
const useSignerText = (
Use the Signer to authenticate the following changes.
);
@@ -72,13 +75,15 @@ const renderQueue = (queue) => {
);
};
-export default class Names extends Component {
+class Names extends Component {
static propTypes = {
- actions: PropTypes.object.isRequired,
fee: PropTypes.object.isRequired,
pending: PropTypes.bool.isRequired,
- queue: PropTypes.array.isRequired
+ queue: PropTypes.array.isRequired,
+
+ reserve: PropTypes.func.isRequired,
+ drop: PropTypes.func.isRequired
}
state = {
@@ -117,27 +122,29 @@ export default class Names extends Component {
: (
To drop a name, you have to be the owner.
)
)
}
-
-
-
-
-
-
}
- onTouchTap={ this.onSubmitClick }
- />
+
+
+
+
+
+
+ }
+ onTouchTap={ this.onSubmitClick }
+ />
+
{ queue.length > 0
? (
{ useSignerText }{ renderQueue(queue) }
)
: null
@@ -156,9 +163,14 @@ export default class Names extends Component {
onSubmitClick = () => {
const { action, name } = this.state;
if (action === 'reserve') {
- this.props.actions.reserve(name);
+ this.props.reserve(name);
} else if (action === 'drop') {
- this.props.actions.drop(name);
+ this.props.drop(name);
}
};
}
+
+const mapStateToProps = (state) => ({ ...state.names, fee: state.fee });
+const mapDispatchToProps = (dispatch) => bindActionCreators({ reserve, drop }, dispatch);
+
+export default connect(mapStateToProps, mapDispatchToProps)(Names);
diff --git a/js/src/dapps/registry/Names/reducers.js b/js/src/dapps/registry/Names/reducers.js
index 2b43c8b3e..17230ad40 100644
--- a/js/src/dapps/registry/Names/reducers.js
+++ b/js/src/dapps/registry/Names/reducers.js
@@ -14,36 +14,38 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
+import { isAction, isStage, addToQueue, removeFromQueue } from '../util/actions';
+
const initialState = {
pending: false,
queue: []
};
export default (state = initialState, action) => {
- if (action.type === 'names reserve start') {
- return { ...state, pending: true };
- }
- if (action.type === 'names reserve success') {
- return {
- ...state, pending: false,
- queue: state.queue.concat({ action: 'reserve', name: action.name })
- };
- }
- if (action.type === 'names reserve fail') {
- return { ...state, pending: false };
- }
-
- if (action.type === 'names drop start') {
- return { ...state, pending: true };
- }
- if (action.type === 'names drop success') {
- return {
- ...state, pending: false,
- queue: state.queue.concat({ action: 'drop', name: action.name })
- };
- }
- if (action.type === 'names drop fail') {
- return { ...state, pending: false };
+ if (isAction('names', 'reserve', action)) {
+ if (isStage('start', action)) {
+ return {
+ ...state, pending: true,
+ queue: addToQueue(state.queue, 'reserve', action.name)
+ };
+ } else if (isStage('success', action) || isStage('fail', action)) {
+ return {
+ ...state, pending: false,
+ queue: removeFromQueue(state.queue, 'reserve', action.name)
+ };
+ }
+ } else if (isAction('names', 'drop', action)) {
+ if (isStage('start', action)) {
+ return {
+ ...state, pending: true,
+ queue: addToQueue(state.queue, 'drop', action.name)
+ };
+ } else if (isStage('success', action) || isStage('fail', action)) {
+ return {
+ ...state, pending: false,
+ queue: removeFromQueue(state.queue, 'drop', action.name)
+ };
+ }
}
return state;
diff --git a/js/src/dapps/registry/Records/actions.js b/js/src/dapps/registry/Records/actions.js
index cff54d94d..9afcb172c 100644
--- a/js/src/dapps/registry/Records/actions.js
+++ b/js/src/dapps/registry/Records/actions.js
@@ -1,4 +1,21 @@
-import { sha3 } from '../parity.js';
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+import { sha3, api } from '../parity.js';
+import postTx from '../util/post-tx';
export const start = (name, key, value) => ({ type: 'records update start', name, key, value });
@@ -10,25 +27,28 @@ export const update = (name, key, value) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const contract = state.contract;
-
if (!contract || !account) {
return;
}
- const fnName = key === 'A' ? 'setAddress' : 'set';
- const fn = contract.functions.find((f) => f.name === fnName);
-
name = name.toLowerCase();
- const options = { from: account.address };
- const values = [ sha3(name), key, value ];
+
+ const fnName = key === 'A' ? 'setAddress' : 'set';
+ const setAddress = contract.functions.find((f) => f.name === fnName);
dispatch(start(name, key, value));
- fn.estimateGas(options, values)
- .then((gas) => {
- options.gas = gas.mul(1.2).toFixed(0);
- return fn.postTransaction(options, values);
- })
- .then((data) => {
+
+ const options = {
+ from: account.address
+ };
+ const values = [
+ sha3(name),
+ key,
+ value
+ ];
+
+ postTx(api, setAddress, options, values)
+ .then((txHash) => {
dispatch(success());
}).catch((err) => {
console.error(`could not update ${key} record of ${name}`);
diff --git a/js/src/dapps/registry/Records/records.css b/js/src/dapps/registry/Records/records.css
index a2224125a..e16ea4a15 100644
--- a/js/src/dapps/registry/Records/records.css
+++ b/js/src/dapps/registry/Records/records.css
@@ -23,6 +23,16 @@
margin-top: 0;
}
+.box {
+ display: flex;
+ align-items: baseline;
+}
+
.spacing {
margin-left: 1em;
}
+
+.button {
+ flex-grow: 0;
+ flex-shrink: 0;
+}
diff --git a/js/src/dapps/registry/Records/records.js b/js/src/dapps/registry/Records/records.js
index 085a70152..f1d92cac8 100644
--- a/js/src/dapps/registry/Records/records.js
+++ b/js/src/dapps/registry/Records/records.js
@@ -15,22 +15,27 @@
// along with Parity. If not, see
.
import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
import { Card, CardHeader, CardText } from 'material-ui/Card';
import TextField from 'material-ui/TextField';
+import DropDownMenu from 'material-ui/DropDownMenu';
+import MenuItem from 'material-ui/MenuItem';
import RaisedButton from 'material-ui/RaisedButton';
import SaveIcon from 'material-ui/svg-icons/content/save';
-import recordTypeSelect from '../ui/record-type-select.js';
+import { update } from './actions';
import styles from './records.css';
-export default class Records extends Component {
+class Records extends Component {
static propTypes = {
- actions: PropTypes.object.isRequired,
pending: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
- value: PropTypes.string.isRequired
+ value: PropTypes.string.isRequired,
+
+ update: PropTypes.func.isRequired
}
state = { name: '', type: 'A', value: '' };
@@ -48,28 +53,36 @@ export default class Records extends Component {
You can only modify entries of names that you previously registered.
-
-
- { recordTypeSelect(type, this.onTypeChange, styles.spacing) }
-
-
}
- onTouchTap={ this.onSaveClick }
- />
+
+
+
+
+
+
+
+
+
+ }
+ onTouchTap={ this.onSaveClick }
+ />
+
+
);
@@ -86,6 +99,11 @@ export default class Records extends Component {
};
onSaveClick = () => {
const { name, type, value } = this.state;
- this.props.actions.update(name, type, value);
+ this.props.update(name, type, value);
};
}
+
+const mapStateToProps = (state) => state.records;
+const mapDispatchToProps = (dispatch) => bindActionCreators({ update }, dispatch);
+
+export default connect(mapStateToProps, mapDispatchToProps)(Records);
diff --git a/js/src/dapps/registry/Records/reducers.js b/js/src/dapps/registry/Records/reducers.js
index 7e8f06755..9629e8149 100644
--- a/js/src/dapps/registry/Records/reducers.js
+++ b/js/src/dapps/registry/Records/reducers.js
@@ -1,21 +1,39 @@
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+import { isAction, isStage } from '../util/actions';
+
const initialState = {
pending: false,
name: '', type: '', value: ''
};
export default (state = initialState, action) => {
- if (action.type === 'records update start') {
- return {
- ...state,
- pending: true,
- name: action.name, type: action.entry, value: action.value
- };
+ if (!isAction('records', 'update', action)) {
+ return state;
}
- if (action.type === 'records update error' || action.type === 'records update success') {
+ if (isStage('start', action)) {
return {
- ...state,
- pending: false,
+ ...state, pending: true,
+ name: action.name, type: action.entry, value: action.value
+ };
+ } else if (isStage('success', action) || isStage('fail', action)) {
+ return {
+ ...state, pending: false,
name: initialState.name, type: initialState.type, value: initialState.value
};
}
diff --git a/js/src/dapps/registry/Reverse/actions.js b/js/src/dapps/registry/Reverse/actions.js
new file mode 100644
index 000000000..07a1afade
--- /dev/null
+++ b/js/src/dapps/registry/Reverse/actions.js
@@ -0,0 +1,92 @@
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+import { api } from '../parity.js';
+import postTx from '../util/post-tx';
+
+export const start = (action, name, address) => ({ type: `reverse ${action} start`, name, address });
+
+export const success = (action) => ({ type: `reverse ${action} success` });
+
+export const fail = (action) => ({ type: `reverse ${action} error` });
+
+export const propose = (name, address) => (dispatch, getState) => {
+ const state = getState();
+ const account = state.accounts.selected;
+ const contract = state.contract;
+ if (!contract || !account) {
+ return;
+ }
+
+ name = name.toLowerCase();
+
+ const proposeReverse = contract.functions.find((f) => f.name === 'proposeReverse');
+
+ dispatch(start('propose', name, address));
+
+ const options = {
+ from: account.address
+ };
+ const values = [
+ name,
+ address
+ ];
+
+ postTx(api, proposeReverse, options, values)
+ .then((txHash) => {
+ dispatch(success('propose'));
+ })
+ .catch((err) => {
+ console.error(`could not propose reverse ${name} for address ${address}`);
+ if (err) {
+ console.error(err.stack);
+ }
+ dispatch(fail('propose'));
+ });
+};
+
+export const confirm = (name) => (dispatch, getState) => {
+ const state = getState();
+ const account = state.accounts.selected;
+ const contract = state.contract;
+ if (!contract || !account) {
+ return;
+ }
+ name = name.toLowerCase();
+
+ const confirmReverse = contract.functions.find((f) => f.name === 'confirmReverse');
+
+ dispatch(start('confirm', name));
+
+ const options = {
+ from: account.address
+ };
+ const values = [
+ name
+ ];
+
+ postTx(api, confirmReverse, options, values)
+ .then((txHash) => {
+ dispatch(success('confirm'));
+ })
+ .catch((err) => {
+ console.error(`could not confirm reverse ${name}`);
+ if (err) {
+ console.error(err.stack);
+ }
+ dispatch(fail('confirm'));
+ });
+};
diff --git a/js/src/dapps/registry/Reverse/index.js b/js/src/dapps/registry/Reverse/index.js
new file mode 100644
index 000000000..ecaf9c3db
--- /dev/null
+++ b/js/src/dapps/registry/Reverse/index.js
@@ -0,0 +1 @@
+export default from './reverse';
diff --git a/js/src/dapps/registry/Reverse/reducers.js b/js/src/dapps/registry/Reverse/reducers.js
new file mode 100644
index 000000000..53a242c3b
--- /dev/null
+++ b/js/src/dapps/registry/Reverse/reducers.js
@@ -0,0 +1,68 @@
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+import { isAction, isStage } from '../util/actions';
+
+const initialState = {
+ pending: false,
+ queue: []
+};
+
+export default (state = initialState, action) => {
+ if (isAction('reverse', 'propose', action)) {
+ if (isStage('start', action)) {
+ return {
+ ...state, pending: true,
+ queue: state.queue.concat({
+ action: 'propose',
+ name: action.name,
+ address: action.address
+ })
+ };
+ } else if (isStage('success', action) || isStage('fail', action)) {
+ return {
+ ...state, pending: false,
+ queue: state.queue.filter((e) =>
+ e.action === 'propose' &&
+ e.name === action.name &&
+ e.address === action.address
+ )
+ };
+ }
+ }
+
+ if (isAction('reverse', 'confirm', action)) {
+ if (isStage('start', action)) {
+ return {
+ ...state, pending: true,
+ queue: state.queue.concat({
+ action: 'confirm',
+ name: action.name
+ })
+ };
+ } else if (isStage('success', action) || isStage('fail', action)) {
+ return {
+ ...state, pending: false,
+ queue: state.queue.filter((e) =>
+ e.action === 'confirm' &&
+ e.name === action.name
+ )
+ };
+ }
+ }
+
+ return state;
+};
diff --git a/js/src/dapps/registry/Reverse/reverse.css b/js/src/dapps/registry/Reverse/reverse.css
new file mode 100644
index 000000000..0b75bfaf4
--- /dev/null
+++ b/js/src/dapps/registry/Reverse/reverse.css
@@ -0,0 +1,39 @@
+/* Copyright 2015, 2016 Ethcore (UK) Ltd.
+/* This file is part of Parity.
+/*
+/* Parity is free software: you can redistribute it and/or modify
+/* it under the terms of the GNU General Public License as published by
+/* the Free Software Foundation, either version 3 of the License, or
+/* (at your option) any later version.
+/*
+/* Parity is distributed in the hope that it will be useful,
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/* GNU General Public License for more details.
+/*
+/* You should have received a copy of the GNU General Public License
+/* along with Parity. If not, see
.
+*/
+
+.reverse {
+ margin: 1em;
+}
+
+.noSpacing {
+ margin-top: 0;
+}
+
+.box {
+ display: flex;
+ align-items: baseline;
+}
+
+.spacing {
+ margin-right: 1em;
+}
+
+.button {
+ flex-grow: 0;
+ flex-shrink: 0;
+}
+
diff --git a/js/src/dapps/registry/Reverse/reverse.js b/js/src/dapps/registry/Reverse/reverse.js
new file mode 100644
index 000000000..79bf2b07f
--- /dev/null
+++ b/js/src/dapps/registry/Reverse/reverse.js
@@ -0,0 +1,136 @@
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import {
+ Card, CardHeader, CardText, TextField, DropDownMenu, MenuItem, RaisedButton, AddIcon, CheckIcon
+} from 'material-ui';
+
+import { propose, confirm } from './actions';
+import styles from './reverse.css';
+
+class Reverse extends Component {
+ static propTypes = {
+ pending: PropTypes.bool.isRequired,
+ queue: PropTypes.array.isRequired,
+
+ propose: PropTypes.func.isRequired,
+ confirm: PropTypes.func.isRequired
+ }
+
+ state = {
+ action: 'propose',
+ name: '',
+ address: ''
+ };
+
+ render () {
+ const { pending } = this.props;
+ const { action, address, name } = this.state;
+
+ const explanation = action === 'propose'
+ ? (
+
+ To propose a reverse entry for foo
, you have to be the owner of it.
+
+ ) : (
+
+ To confirm a proposal, send the transaction from the account that the name has been proposed for.
+
+ );
+
+ let addressInput = null;
+ if (action === 'propose') {
+ addressInput = (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ To make others to find the name of an address using the registry, you can propose & confirm reverse entries.
+
+
+ { explanation }
+
+
+
+
+
+ { addressInput }
+
+
+ : }
+ onTouchTap={ this.onSubmitClick }
+ />
+
+
+
+
+ );
+ }
+
+ onNameChange = (e) => {
+ this.setState({ name: e.target.value });
+ };
+
+ onAddressChange = (e) => {
+ this.setState({ address: e.target.value });
+ };
+
+ onActionChange = (e, i, action) => {
+ this.setState({ action });
+ };
+
+ onSubmitClick = () => {
+ const { action, name, address } = this.state;
+
+ if (action === 'propose') {
+ this.props.propose(name, address);
+ } else if (action === 'confirm') {
+ this.props.confirm(name);
+ }
+ };
+}
+
+const mapStateToProps = (state) => state.reverse;
+const mapDispatchToProps = (dispatch) => bindActionCreators({ propose, confirm }, dispatch);
+
+export default connect(mapStateToProps, mapDispatchToProps)(Reverse);
diff --git a/js/src/dapps/registry/actions.js b/js/src/dapps/registry/actions.js
index 391953c22..6f4cef8e9 100644
--- a/js/src/dapps/registry/actions.js
+++ b/js/src/dapps/registry/actions.js
@@ -23,63 +23,76 @@ import * as lookup from './Lookup/actions.js';
import * as events from './Events/actions.js';
import * as names from './Names/actions.js';
import * as records from './Records/actions.js';
+import * as reverse from './Reverse/actions.js';
-export { addresses, accounts, lookup, events, names, records };
+export { addresses, accounts, lookup, events, names, records, reverse };
+
+export const setIsTestnet = (isTestnet) => ({ type: 'set isTestnet', isTestnet });
+
+export const fetchIsTestnet = () => (dispatch) =>
+ api.net.version()
+ .then((netVersion) => {
+ dispatch(setIsTestnet(
+ netVersion === '2' || // morden
+ netVersion === '3' // ropsten
+ ));
+ })
+ .catch((err) => {
+ console.error('could not check if testnet');
+ if (err) {
+ console.error(err.stack);
+ }
+ });
export const setContract = (contract) => ({ type: 'set contract', contract });
export const fetchContract = () => (dispatch) =>
api.parity.registryAddress()
- .then((address) => {
- const contract = api.newContract(registryAbi, address);
- dispatch(setContract(contract));
- dispatch(fetchFee());
- dispatch(fetchOwner());
- })
- .catch((err) => {
- console.error('could not fetch contract');
-
- if (err) {
- console.error(err.stack);
- }
- });
+ .then((address) => {
+ const contract = api.newContract(registryAbi, address);
+ dispatch(setContract(contract));
+ dispatch(fetchFee());
+ dispatch(fetchOwner());
+ })
+ .catch((err) => {
+ console.error('could not fetch contract');
+ if (err) {
+ console.error(err.stack);
+ }
+ });
export const setFee = (fee) => ({ type: 'set fee', fee });
const fetchFee = () => (dispatch, getState) => {
const { contract } = getState();
-
if (!contract) {
return;
}
contract.instance.fee.call()
- .then((fee) => dispatch(setFee(fee)))
- .catch((err) => {
- console.error('could not fetch fee');
-
- if (err) {
- console.error(err.stack);
- }
- });
+ .then((fee) => dispatch(setFee(fee)))
+ .catch((err) => {
+ console.error('could not fetch fee');
+ if (err) {
+ console.error(err.stack);
+ }
+ });
};
export const setOwner = (owner) => ({ type: 'set owner', owner });
export const fetchOwner = () => (dispatch, getState) => {
const { contract } = getState();
-
if (!contract) {
return;
}
contract.instance.owner.call()
- .then((owner) => dispatch(setOwner(owner)))
- .catch((err) => {
- console.error('could not fetch owner');
-
- if (err) {
- console.error(err.stack);
- }
- });
+ .then((owner) => dispatch(setOwner(owner)))
+ .catch((err) => {
+ console.error('could not fetch owner');
+ if (err) {
+ console.error(err.stack);
+ }
+ });
};
diff --git a/js/src/dapps/registry/reducers.js b/js/src/dapps/registry/reducers.js
index 06b8f024b..45ca9642e 100644
--- a/js/src/dapps/registry/reducers.js
+++ b/js/src/dapps/registry/reducers.js
@@ -20,6 +20,10 @@ import lookupReducer from './Lookup/reducers.js';
import eventsReducer from './Events/reducers.js';
import namesReducer from './Names/reducers.js';
import recordsReducer from './Records/reducers.js';
+import reverseReducer from './Reverse/reducers.js';
+
+const isTestnetReducer = (state = null, action) =>
+ action.type === 'set isTestnet' ? action.isTestnet : state;
const contractReducer = (state = null, action) =>
action.type === 'set contract' ? action.contract : state;
@@ -31,6 +35,7 @@ const ownerReducer = (state = null, action) =>
action.type === 'set owner' ? action.owner : state;
const initialState = {
+ isTestnet: isTestnetReducer(undefined, { type: '' }),
accounts: accountsReducer(undefined, { type: '' }),
contacts: contactsReducer(undefined, { type: '' }),
contract: contractReducer(undefined, { type: '' }),
@@ -39,10 +44,12 @@ const initialState = {
lookup: lookupReducer(undefined, { type: '' }),
events: eventsReducer(undefined, { type: '' }),
names: namesReducer(undefined, { type: '' }),
- records: recordsReducer(undefined, { type: '' })
+ records: recordsReducer(undefined, { type: '' }),
+ reverse: reverseReducer(undefined, { type: '' })
};
export default (state = initialState, action) => ({
+ isTestnet: isTestnetReducer(state.isTestnet, action),
accounts: accountsReducer(state.accounts, action),
contacts: contactsReducer(state.contacts, action),
contract: contractReducer(state.contract, action),
@@ -51,5 +58,6 @@ export default (state = initialState, action) => ({
lookup: lookupReducer(state.lookup, action),
events: eventsReducer(state.events, action),
names: namesReducer(state.names, action),
- records: recordsReducer(state.records, action)
+ records: recordsReducer(state.records, action),
+ reverse: reverseReducer(state.reverse, action)
});
diff --git a/js/src/dapps/registry/ui/address.css b/js/src/dapps/registry/ui/address.css
new file mode 100644
index 000000000..69cd27b9e
--- /dev/null
+++ b/js/src/dapps/registry/ui/address.css
@@ -0,0 +1,41 @@
+/* Copyright 2015, 2016 Ethcore (UK) Ltd.
+/* This file is part of Parity.
+/*
+/* Parity is free software: you can redistribute it and/or modify
+/* it under the terms of the GNU General Public License as published by
+/* the Free Software Foundation, either version 3 of the License, or
+/* (at your option) any later version.
+/*
+/* Parity is distributed in the hope that it will be useful,
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/* GNU General Public License for more details.
+/*
+/* You should have received a copy of the GNU General Public License
+/* along with Parity. If not, see
.
+*/
+
+.container {
+ display: inline-block;
+ vertical-align: middle;
+ line-height: 24px;
+}
+
+.align {
+ display: inline-block;
+ vertical-align: top;
+ line-height: 24px;
+}
+
+.link {
+ text-decoration: none;
+ color: inherit;
+
+ &:hover {
+ text-decoration: underline;
+ }
+
+ & abbr {
+ text-decoration: inherit;
+ }
+}
diff --git a/js/src/dapps/registry/ui/address.js b/js/src/dapps/registry/ui/address.js
index f19894b48..e3eac2c97 100644
--- a/js/src/dapps/registry/ui/address.js
+++ b/js/src/dapps/registry/ui/address.js
@@ -14,34 +14,85 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
-import React from 'react';
-import renderHash from './hash';
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+
+import Hash from './hash';
+import etherscanUrl from '../util/etherscan-url';
import IdentityIcon from '../IdentityIcon';
-const container = {
- display: 'inline-block',
- verticalAlign: 'middle',
- height: '24px'
-};
-const align = {
- display: 'inline-block',
- verticalAlign: 'top',
- lineHeight: '24px'
-};
+import styles from './address.css';
-export default (address, accounts, contacts, shortenHash = true) => {
- let caption;
- if (accounts[address]) {
- caption = (
{ accounts[address].name || address });
- } else if (contacts[address]) {
- caption = (
{ contacts[address].name || address });
- } else {
- caption = (
{ shortenHash ? renderHash(address) : address }
);
+class Address extends Component {
+ static propTypes = {
+ address: PropTypes.string.isRequired,
+ accounts: PropTypes.object.isRequired,
+ contacts: PropTypes.object.isRequired,
+ isTestnet: PropTypes.bool.isRequired,
+ key: PropTypes.string,
+ shortenHash: PropTypes.bool
}
- return (
-
-
- { caption }
-
- );
-};
+
+ static defaultProps = {
+ key: 'address',
+ shortenHash: true
+ }
+
+ render () {
+ const { address, accounts, contacts, isTestnet, key, shortenHash } = this.props;
+
+ let caption;
+ if (accounts[address] || contacts[address]) {
+ const name = (accounts[address] || contacts[address] || {}).name;
+ caption = (
+
+
+ { name || address }
+
+
+ );
+ } else {
+ caption = (
+
+ { shortenHash ? (
+
+ ) : address }
+
+ );
+ }
+
+ return (
+
+
+ { caption }
+
+ );
+ }
+}
+
+export default connect(
+ // mapStateToProps
+ (state) => ({
+ accounts: state.accounts.all,
+ contacts: state.contacts,
+ isTestnet: state.isTestnet
+ }),
+ // mapDispatchToProps
+ null
+)(Address);
diff --git a/js/src/dapps/registry/ui/hash.css b/js/src/dapps/registry/ui/hash.css
new file mode 100644
index 000000000..a16d340a1
--- /dev/null
+++ b/js/src/dapps/registry/ui/hash.css
@@ -0,0 +1,25 @@
+/* Copyright 2015, 2016 Ethcore (UK) Ltd.
+/* This file is part of Parity.
+/*
+/* Parity is free software: you can redistribute it and/or modify
+/* it under the terms of the GNU General Public License as published by
+/* the Free Software Foundation, either version 3 of the License, or
+/* (at your option) any later version.
+/*
+/* Parity is distributed in the hope that it will be useful,
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/* GNU General Public License for more details.
+/*
+/* You should have received a copy of the GNU General Public License
+/* along with Parity. If not, see
.
+*/
+
+.link {
+ text-decoration: none;
+ color: inherit;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
diff --git a/js/src/dapps/registry/ui/hash.js b/js/src/dapps/registry/ui/hash.js
index 7a586b256..6eeaab7b2 100644
--- a/js/src/dapps/registry/ui/hash.js
+++ b/js/src/dapps/registry/ui/hash.js
@@ -14,11 +14,53 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
-export default (hash) => {
- const shortened = hash.length > (2 + 9 + 9)
- ? hash.substr(2, 9) + '...' + hash.slice(-9)
- : hash.slice(2);
- return (
{ shortened });
-};
+import etherscanUrl from '../util/etherscan-url';
+
+import styles from './hash.css';
+
+const leading0x = /^0x/;
+
+class Hash extends Component {
+ static propTypes = {
+ hash: PropTypes.string.isRequired,
+ isTestnet: PropTypes.bool.isRequired,
+ linked: PropTypes.bool
+ }
+
+ static defaultProps = {
+ linked: false
+ }
+
+ render () {
+ const { hash, isTestnet, linked } = this.props;
+
+ let shortened = hash.toLowerCase().replace(leading0x, '');
+ shortened = shortened.length > (6 + 6)
+ ? shortened.substr(0, 6) + '...' + shortened.slice(-6)
+ : shortened;
+
+ if (linked) {
+ return (
+
+ { shortened }
+
+ );
+ }
+
+ return (
{ shortened });
+ }
+}
+
+export default connect(
+ (state) => ({ // mapStateToProps
+ isTestnet: state.isTestnet
+ }),
+ null // mapDispatchToProps
+)(Hash);
diff --git a/js/src/dapps/registry/ui/record-type-select.js b/js/src/dapps/registry/util/actions.js
similarity index 57%
rename from js/src/dapps/registry/ui/record-type-select.js
rename to js/src/dapps/registry/util/actions.js
index e146dbf7d..0f4f350fc 100644
--- a/js/src/dapps/registry/ui/record-type-select.js
+++ b/js/src/dapps/registry/util/actions.js
@@ -14,14 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
-import React from 'react';
-import DropDownMenu from 'material-ui/DropDownMenu';
-import MenuItem from 'material-ui/MenuItem';
+export const isAction = (ns, type, action) => {
+ return action.type.slice(0, ns.length + 1 + type.length) === `${ns} ${type}`;
+};
-export default (value, onSelect, className = '') => (
-
-
-
-
-
-);
+export const isStage = (stage, action) => {
+ return action.type.slice(-1 - stage.length) === ` ${stage}`;
+};
+
+export const addToQueue = (queue, action, name) => {
+ return queue.concat({ action, name });
+};
+
+export const removeFromQueue = (queue, action, name) => {
+ return queue.filter((e) => e.action === action && e.name === name);
+};
diff --git a/js/src/dapps/registry/util/etherscan-url.js b/js/src/dapps/registry/util/etherscan-url.js
new file mode 100644
index 000000000..26fb9a84f
--- /dev/null
+++ b/js/src/dapps/registry/util/etherscan-url.js
@@ -0,0 +1,26 @@
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+const leading0x = /^0x/;
+
+const etherscanUrl = (hash, isTestnet) => {
+ hash = hash.toLowerCase().replace(leading0x, '');
+ const type = hash.length === 40 ? 'address' : 'tx';
+
+ return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/${type}/0x${hash}`;
+};
+
+export default etherscanUrl;
diff --git a/js/src/dapps/registry/util/post-tx.js b/js/src/dapps/registry/util/post-tx.js
new file mode 100644
index 000000000..84326dcab
--- /dev/null
+++ b/js/src/dapps/registry/util/post-tx.js
@@ -0,0 +1,36 @@
+// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see
.
+
+const postTx = (api, method, opt = {}, values = []) => {
+ opt = Object.assign({}, opt);
+
+ return method.estimateGas(opt, values)
+ .then((gas) => {
+ opt.gas = gas.mul(1.2).toFixed(0);
+ return method.postTransaction(opt, values);
+ })
+ .then((reqId) => {
+ return api.pollMethod('parity_checkRequest', reqId);
+ })
+ .catch((err) => {
+ if (err && err.type === 'REQUEST_REJECTED') {
+ throw new Error('The request has been rejected.');
+ }
+ throw err;
+ });
+};
+
+export default postTx;