-
} />
-
+ { this.renderName(address) }
+
+
+
{ uuidText }
{ meta.description }
{ this.renderTxCount() }
+
@@ -89,6 +94,18 @@ export default class Header extends Component {
);
}
+ renderName (address) {
+ const { hideName } = this.props;
+
+ if (hideName) {
+ return null;
+ }
+
+ return (
+
} />
+ );
+ }
+
renderTxCount () {
const { balance, isContract } = this.props;
diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js
index cbacd5280..840de05b9 100644
--- a/js/src/views/Account/account.js
+++ b/js/src/views/Account/account.js
@@ -26,7 +26,7 @@ import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
import { EditMeta, DeleteAccount, Shapeshift, SMSVerification, Transfer, PasswordManager } from '~/modals';
import { Actionbar, Button, Page } from '~/ui';
-import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png';
+import shapeshiftBtn from '~/../assets/images/shapeshift-btn.png';
import Header from './Header';
import Transactions from './Transactions';
diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js
index 3183a2903..a19b9a9de 100644
--- a/js/src/views/Accounts/Summary/summary.js
+++ b/js/src/views/Accounts/Summary/summary.js
@@ -153,7 +153,7 @@ export default class Summary extends Component {
const { link, noLink, account, name } = this.props;
const { address } = account;
- const viewLink = `/${link || 'account'}/${address}`;
+ const viewLink = `/${link || 'accounts'}/${address}`;
const content = (
diff --git a/js/src/views/Address/address.js b/js/src/views/Address/address.js
index c1427b2be..9c39203ba 100644
--- a/js/src/views/Address/address.js
+++ b/js/src/views/Address/address.js
@@ -19,8 +19,9 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ActionDelete from 'material-ui/svg-icons/action/delete';
import ContentCreate from 'material-ui/svg-icons/content/create';
+import ContentAdd from 'material-ui/svg-icons/content/add';
-import { EditMeta } from '~/modals';
+import { EditMeta, AddAddress } from '~/modals';
import { Actionbar, Button, Page } from '~/ui';
import Header from '../Account/Header';
@@ -32,7 +33,7 @@ class Address extends Component {
static contextTypes = {
api: PropTypes.object.isRequired,
router: PropTypes.object.isRequired
- }
+ };
static propTypes = {
setVisibleAccounts: PropTypes.func.isRequired,
@@ -40,12 +41,13 @@ class Address extends Component {
contacts: PropTypes.object,
balances: PropTypes.object,
params: PropTypes.object
- }
+ };
state = {
showDeleteDialog: false,
- showEditDialog: false
- }
+ showEditDialog: false,
+ showAdd: false
+ };
componentDidMount () {
this.setVisibleAccounts();
@@ -73,32 +75,69 @@ class Address extends Component {
render () {
const { contacts, balances } = this.props;
const { address } = this.props.params;
- const { showDeleteDialog } = this.state;
+
+ if (Object.keys(contacts).length === 0) {
+ return null;
+ }
const contact = (contacts || {})[address];
const balance = (balances || {})[address];
- if (!contact) {
+ return (
+
+ { this.renderAddAddress(contact, address) }
+ { this.renderEditDialog(contact) }
+ { this.renderActionbar(contact) }
+ { this.renderDelete(contact) }
+
+
+
+
+
+ );
+ }
+
+ renderAddAddress (contact, address) {
+ if (contact) {
+ return null;
+ }
+
+ const { contacts } = this.props;
+ const { showAdd } = this.state;
+
+ if (!showAdd) {
return null;
}
return (
-
- { this.renderEditDialog(contact) }
- { this.renderActionbar(contact) }
-
-
-
-
-
-
+
+ );
+ }
+
+ renderDelete (contact) {
+ if (!contact) {
+ return null;
+ }
+
+ const { showDeleteDialog } = this.state;
+
+ return (
+
);
}
@@ -116,17 +155,27 @@ class Address extends Component {
onClick={ this.showDeleteDialog } />
];
+ const addToBook = (
+
}
+ label='save address'
+ onClick={ this.onOpenAdd }
+ />
+ );
+
return (
+ buttons={ !contact ? [ addToBook ] : buttons }
+ />
);
}
renderEditDialog (contact) {
const { showEditDialog } = this.state;
- if (!showEditDialog) {
+ if (!contact || !showEditDialog) {
return null;
}
@@ -151,6 +200,16 @@ class Address extends Component {
showDeleteDialog = () => {
this.setState({ showDeleteDialog: true });
}
+
+ onOpenAdd = () => {
+ this.setState({
+ showAdd: true
+ });
+ }
+
+ onCloseAdd = () => {
+ this.setState({ showAdd: false });
+ }
}
function mapStateToProps (state) {
diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js
index 5e0ed4e18..609f029c7 100644
--- a/js/src/views/Addresses/addresses.js
+++ b/js/src/views/Addresses/addresses.js
@@ -23,7 +23,7 @@ import { uniq, isEqual } from 'lodash';
import List from '../Accounts/List';
import Summary from '../Accounts/Summary';
import { AddAddress } from '~/modals';
-import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page } from '~/ui';
+import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page, Loading } from '~/ui';
import { setVisibleAccounts } from '~/redux/providers/personalActions';
import styles from './addresses.css';
@@ -72,27 +72,40 @@ class Addresses extends Component {
}
render () {
- const { balances, contacts, hasContacts } = this.props;
- const { searchValues, sortOrder } = this.state;
-
return (
{ this.renderActionbar() }
{ this.renderAddAddress() }
-
+ { this.renderAccountsList() }
);
}
+ renderAccountsList () {
+ const { balances, contacts, hasContacts } = this.props;
+ const { searchValues, sortOrder } = this.state;
+
+ if (hasContacts && Object.keys(balances).length === 0) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ }
+
renderSortButton () {
const onChange = (sortOrder) => {
this.setState({ sortOrder });
diff --git a/js/src/views/Application/TabBar/tabBar.css b/js/src/views/Application/TabBar/tabBar.css
index b11df4e40..172750c8f 100644
--- a/js/src/views/Application/TabBar/tabBar.css
+++ b/js/src/views/Application/TabBar/tabBar.css
@@ -30,24 +30,34 @@
}
}
-.tabs button,
+.tabLink {
+ display: flex;
+
+ > * {
+ flex: 1;
+ }
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.4) !important;
+ }
+
+ &.tabactive, &.tabactive:hover {
+ background: rgba(0, 0, 0, 0.25) !important;
+ border-radius: 4px 4px 0 0;
+
+ * {
+ color: white !important;
+ }
+ }
+}
+
+.tabLink,
.settings,
.logo,
.last {
background: rgba(0, 0, 0, 0.5) !important; /* rgba(0, 0, 0, 0.25) !important; */
}
-.tabs button:hover {
- background: rgba(0, 0, 0, 0.4) !important;
-}
-
-button.tabactive,
-button.tabactive:hover {
- color: white !important;
- background: rgba(0, 0, 0, 0.25) !important;
- border-radius: 4px 4px 0 0;
-}
-
.tabbarTooltip {
left: 3.3em;
top: 0.5em;
diff --git a/js/src/views/Application/TabBar/tabBar.js b/js/src/views/Application/TabBar/tabBar.js
index 9d9f55874..63f569f1f 100644
--- a/js/src/views/Application/TabBar/tabBar.js
+++ b/js/src/views/Application/TabBar/tabBar.js
@@ -16,7 +16,7 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
-import { bindActionCreators } from 'redux';
+import { Link } from 'react-router';
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
import { Tab as MUITab } from 'material-ui/Tabs';
import { isEqual } from 'lodash';
@@ -26,41 +26,26 @@ import { Badge, Tooltip } from '~/ui';
import styles from './tabBar.css';
import imagesEthcoreBlock from '../../../../assets/images/parity-logo-white-no-text.svg';
-const TABMAP = {
- accounts: 'account',
- wallet: 'account',
- addresses: 'address',
- apps: 'app',
- contracts: 'contract',
- deploy: 'contract'
-};
-
class Tab extends Component {
static propTypes = {
- active: PropTypes.bool,
view: PropTypes.object,
children: PropTypes.node,
- pendings: PropTypes.number,
- onChange: PropTypes.func
+ pendings: PropTypes.number
};
shouldComponentUpdate (nextProps) {
- return nextProps.active !== this.props.active ||
- (nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
+ return (nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
}
render () {
- const { active, view, children } = this.props;
+ const { view, children } = this.props;
const label = this.getLabel(view);
return (
{ children }
@@ -118,11 +103,6 @@ class Tab extends Component {
return this.renderLabel(label, null);
}
-
- handleClick = () => {
- const { onChange, view } = this.props;
- onChange(view);
- }
}
class TabBar extends Component {
@@ -132,7 +112,6 @@ class TabBar extends Component {
static propTypes = {
views: PropTypes.array.isRequired,
- hash: PropTypes.string.isRequired,
pending: PropTypes.array,
isTest: PropTypes.bool,
netChain: PropTypes.string
@@ -142,34 +121,11 @@ class TabBar extends Component {
pending: []
};
- state = {
- activeViewId: ''
- };
-
- setActiveView (props = this.props) {
- const { hash, views } = props;
- const view = views.find((view) => view.value === hash);
-
- this.setState({ activeViewId: view.id });
- }
-
- componentWillMount () {
- this.setActiveView();
- }
-
- componentWillReceiveProps (nextProps) {
- if (nextProps.hash !== this.props.hash) {
- this.setActiveView(nextProps);
- }
- }
-
shouldComponentUpdate (nextProps, nextState) {
const prevViews = this.props.views.map((v) => v.id).sort();
const nextViews = nextProps.views.map((v) => v.id).sort();
- return (nextProps.hash !== this.props.hash) ||
- (nextProps.pending.length !== this.props.pending.length) ||
- (nextState.activeViewId !== this.state.activeViewId) ||
+ return (nextProps.pending.length !== this.props.pending.length) ||
(!isEqual(prevViews, nextViews));
}
@@ -206,7 +162,6 @@ class TabBar extends Component {
renderTabs () {
const { views, pending } = this.props;
- const { activeViewId } = this.state;
const items = views
.map((view, index) => {
@@ -216,60 +171,66 @@ class TabBar extends Component {
)
: null;
- const active = activeViewId === view.id;
-
return (
-
- { body }
-
+
+ { body }
+
+
);
});
return (
-
+
{ items }
);
}
-
- onChange = (view) => {
- const { router } = this.context;
-
- router.push(view.route);
- this.setState({ activeViewId: view.id });
- }
}
-function mapStateToProps (state) {
- const { views } = state.settings;
+function mapStateToProps (initState) {
+ const { views } = initState.settings;
- const filteredViews = Object
+ let filteredViewIds = Object
.keys(views)
- .filter((id) => views[id].fixed || views[id].active)
+ .filter((id) => views[id].fixed || views[id].active);
+
+ let filteredViews = filteredViewIds
.map((id) => ({
...views[id],
id
}));
- const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
- const hash = TABMAP[windowHash] || windowHash;
+ return (state) => {
+ const { views } = state.settings;
- return { views: filteredViews, hash };
-}
+ const viewIds = Object
+ .keys(views)
+ .filter((id) => views[id].fixed || views[id].active);
-function mapDispatchToProps (dispatch) {
- return bindActionCreators({}, dispatch);
+ if (isEqual(viewIds, filteredViewIds)) {
+ return { views: filteredViews };
+ }
+
+ filteredViewIds = viewIds;
+ filteredViews = viewIds
+ .map((id) => ({
+ ...views[id],
+ id
+ }));
+
+ return { views: filteredViews };
+ };
}
export default connect(
- mapStateToProps,
- mapDispatchToProps
+ mapStateToProps
)(TabBar);
diff --git a/js/src/views/Application/application.css b/js/src/views/Application/application.css
index 1b26ed071..4ce748a30 100644
--- a/js/src/views/Application/application.css
+++ b/js/src/views/Application/application.css
@@ -19,4 +19,5 @@
display: flex;
flex-direction: column;
min-height: 100vh;
+ padding-bottom: 1em;
}
diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js
index b84705b32..1e15c3947 100644
--- a/js/src/views/Contracts/contracts.js
+++ b/js/src/views/Contracts/contracts.js
@@ -85,7 +85,7 @@ class Contracts extends Component {
{ this.renderDeployContract() }
,
}
- label='write contract'
+ label='develop contract'
/>
,
diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js
index cf847202a..b0b731b93 100644
--- a/js/src/views/Dapps/dapps.js
+++ b/js/src/views/Dapps/dapps.js
@@ -72,9 +72,17 @@ export default class Dapps extends Component {
] }
/>
- { this.renderList(this.store.visibleLocal) }
- { this.renderList(this.store.visibleBuiltin) }
- { this.renderList(this.store.visibleNetwork, externalOverlay) }
+
+ { this.renderList(this.store.visibleLocal) }
+
+
+
+ { this.renderList(this.store.visibleBuiltin) }
+
+
+
+ { this.renderList(this.store.visibleNetwork, externalOverlay) }
+
);
diff --git a/js/test/npmLibrary.js b/js/test/npmParity.js
similarity index 92%
rename from js/test/npmLibrary.js
rename to js/test/npmParity.js
index 63d8f9515..6e125e9e2 100644
--- a/js/test/npmLibrary.js
+++ b/js/test/npmParity.js
@@ -15,8 +15,8 @@
// along with Parity. If not, see
.
try {
- var Api = require('../.npmjs/library.js').Api;
- var Abi = require('../.npmjs/library.js').Abi;
+ var Api = require('../.npmjs/parity/library.js').Api;
+ var Abi = require('../.npmjs/parity/library.js').Abi;
if (typeof Api !== 'function') {
throw new Error('No Api');
diff --git a/js/webpack/npm.js b/js/webpack/npm.js
index 7353efe55..a1bbaeda9 100644
--- a/js/webpack/npm.js
+++ b/js/webpack/npm.js
@@ -23,14 +23,27 @@ const Shared = require('./shared');
const ENV = process.env.NODE_ENV || 'development';
const isProd = ENV === 'production';
+const LIBRARY = process.env.LIBRARY;
+if (!LIBRARY) {
+ process.exit(-1);
+}
+const SRC = LIBRARY.toLowerCase();
+const OUTPUT_PATH = path.join(__dirname, '../.npmjs', SRC);
+
+const TEST_CONTEXT = SRC === 'parity'
+ ? '../npm/parity/test/'
+ : `../src/3rdparty/${SRC}/`;
+
+console.log(`Building ${LIBRARY} from library.${SRC}.js to .npmjs/${SRC}`);
+
module.exports = {
context: path.join(__dirname, '../src'),
target: 'node',
- entry: 'library.js',
+ entry: `library.${SRC}.js`,
output: {
- path: path.join(__dirname, '../.npmjs'),
+ path: OUTPUT_PATH,
filename: 'library.js',
- library: 'Parity',
+ library: LIBRARY,
libraryTarget: 'umd',
umdNamedDefine: true
},
@@ -66,19 +79,52 @@ module.exports = {
plugins: Shared.getPlugins().concat([
new CopyWebpackPlugin([
{
- from: '../parity.package.json',
+ from: `../npm/${SRC}/package.json`,
to: 'package.json',
transform: function (content, path) {
const json = JSON.parse(content.toString());
json.version = packageJson.version;
+
+ // Add tests dependencies to Dev Deps
+ json.devDependencies.chai = packageJson.devDependencies.chai;
+ json.devDependencies.mocha = packageJson.devDependencies.mocha;
+ json.devDependencies.nock = packageJson.devDependencies.nock;
+
+ // Add test script
+ json.scripts.test = 'mocha \'test/*.spec.js\'';
+
return new Buffer(JSON.stringify(json, null, ' '), 'utf-8');
}
},
{
from: '../LICENSE'
},
+
+ // Copy the base test config
{
- from: '../parity.md',
+ from: '../npm/test',
+ to: 'test'
+ },
+
+ // Copy the actual tests
+ {
+ context: TEST_CONTEXT,
+ from: '**/*.spec.js',
+ to: 'test',
+ transform: function (content, path) {
+ let output = content.toString();
+
+ // Don't skip tests
+ output = output.replace(/describe\.skip/, 'describe');
+
+ // Require parent library
+ output = output.replace('require(\'./\')', 'require(\'../\')');
+
+ return new Buffer(output, 'utf-8');
+ }
+ },
+ {
+ from: `../npm/${SRC}/README.md`,
to: 'README.md'
}
], { copyUnmodified: true })
diff --git a/js/webpack/shared.js b/js/webpack/shared.js
index 8b6807b2a..3c593fd87 100644
--- a/js/webpack/shared.js
+++ b/js/webpack/shared.js
@@ -36,6 +36,29 @@ function getBabelrc () {
// [ "es2015", { "modules": false } ]
babelrc.presets[es2015Index] = [ 'es2015', { modules: false } ];
babelrc['babelrc'] = false;
+
+ const BABEL_PRESET_ENV = process.env.BABEL_PRESET_ENV;
+ const npmStart = process.env.npm_lifecycle_event === 'start';
+ const npmStartApp = process.env.npm_lifecycle_event === 'start:app';
+
+ if (BABEL_PRESET_ENV && (npmStart || npmStartApp)) {
+ console.log('using babel-preset-env');
+
+ babelrc.presets = [
+ // 'es2017',
+ 'stage-0', 'react',
+ [
+ 'env',
+ {
+ targets: { browsers: ['last 2 Chrome versions'] },
+ modules: false,
+ loose: true,
+ useBuiltIns: true
+ }
+ ]
+ ];
+ }
+
return babelrc;
}
diff --git a/parity/user_defaults.rs b/parity/user_defaults.rs
index a1078b634..652abfea1 100644
--- a/parity/user_defaults.rs
+++ b/parity/user_defaults.rs
@@ -128,7 +128,13 @@ impl Default for UserDefaults {
impl UserDefaults {
pub fn load
(path: P) -> Result where P: AsRef {
match File::open(path) {
- Ok(file) => from_reader(file).map_err(|e| e.to_string()),
+ Ok(file) => match from_reader(file) {
+ Ok(defaults) => Ok(defaults),
+ Err(e) => {
+ warn!("Error loading user defaults file: {:?}", e);
+ Ok(UserDefaults::default())
+ },
+ },
_ => Ok(UserDefaults::default()),
}
}