Merge branch 'master' into ng-ui-fixes

This commit is contained in:
Nicolas Gotchac
2016-12-11 01:26:55 +01:00
35 changed files with 355 additions and 166 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "parity.js",
"version": "0.2.105",
"version": "0.2.107",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",
@@ -146,6 +146,7 @@
"mobx-react-devtools": "4.2.10",
"moment": "2.17.0",
"phoneformat.js": "1.0.3",
"push.js": "0.0.11",
"qs": "6.3.0",
"react": "15.4.1",
"react-ace": "4.1.0",

View File

@@ -67,7 +67,7 @@ if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) {
const api = new SecureApi(`ws://${parityUrl}`, token);
ContractInstances.create(api);
const store = initStore(api);
const store = initStore(api, hashHistory);
store.dispatch({ type: 'initAll', api });
store.dispatch(setApi(api));

View File

@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import thunk from 'redux-thunk';
import { routerMiddleware } from 'react-router-redux';
import ErrorsMiddleware from '~/ui/Errors/middleware';
import SettingsMiddleware from '~/views/Settings/middleware';
@@ -22,12 +23,13 @@ import SignerMiddleware from './providers/signerMiddleware';
import statusMiddleware from '~/views/Status/middleware';
import CertificationsMiddleware from './providers/certifications/middleware';
export default function (api) {
export default function (api, browserHistory) {
const errors = new ErrorsMiddleware();
const signer = new SignerMiddleware(api);
const settings = new SettingsMiddleware();
const status = statusMiddleware();
const certifications = new CertificationsMiddleware();
const routeMiddleware = routerMiddleware(browserHistory);
const middleware = [
settings.toMiddleware(),
@@ -36,5 +38,5 @@ export default function (api) {
certifications.toMiddleware()
];
return middleware.concat(status, thunk);
return middleware.concat(status, routeMiddleware, thunk);
}

View File

@@ -15,11 +15,14 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { range, uniq, isEqual } from 'lodash';
import BigNumber from 'bignumber.js';
import { push } from 'react-router-redux';
import { hashToImageUrl } from './imagesReducer';
import { setAddressImage } from './imagesActions';
import * as ABIS from '~/contracts/abi';
import { notifyTransaction } from '~/util/notifications';
import imagesEthereum from '../../../assets/images/contracts/ethereum-black-64x64.png';
const ETH = {
@@ -28,7 +31,64 @@ const ETH = {
image: imagesEthereum
};
export function setBalances (balances) {
function setBalances (_balances) {
return (dispatch, getState) => {
const state = getState();
const accounts = state.personal.accounts;
const nextBalances = _balances;
const prevBalances = state.balances.balances;
const balances = { ...prevBalances };
Object.keys(nextBalances).forEach((address) => {
if (!balances[address]) {
balances[address] = Object.assign({}, nextBalances[address]);
return;
}
const balance = Object.assign({}, balances[address]);
const { tokens, txCount = balance.txCount } = nextBalances[address];
const nextTokens = [].concat(balance.tokens);
tokens.forEach((t) => {
const { token, value } = t;
const { tag } = token;
const tokenIndex = nextTokens.findIndex((tok) => tok.token.tag === tag);
if (tokenIndex === -1) {
nextTokens.push({
token,
value
});
} else {
const oldValue = nextTokens[tokenIndex].value;
// If received a token/eth (old value < new value), notify
if (oldValue.lt(value) && accounts[address]) {
const account = accounts[address];
const txValue = value.minus(oldValue);
const redirectToAccount = () => {
const route = `/account/${account.address}`;
dispatch(push(route));
};
notifyTransaction(account, token, txValue, redirectToAccount);
}
nextTokens[tokenIndex] = { token, value };
}
});
balances[address] = { txCount: txCount || new BigNumber(0), tokens: nextTokens };
});
dispatch(_setBalances(balances));
};
}
function _setBalances (balances) {
return {
type: 'setBalances',
balances
@@ -123,14 +183,14 @@ export function fetchBalances (_addresses) {
const fullFetch = addresses.length === 1;
const fetchedAddresses = uniq(addresses.concat(Object.keys(accounts)));
const addressesToFetch = uniq(addresses.concat(Object.keys(accounts)));
return Promise
.all(fetchedAddresses.map((addr) => fetchAccount(addr, api, fullFetch)))
.all(addressesToFetch.map((addr) => fetchAccount(addr, api, fullFetch)))
.then((accountsBalances) => {
const balances = {};
fetchedAddresses.forEach((addr, idx) => {
addressesToFetch.forEach((addr, idx) => {
balances[addr] = accountsBalances[idx];
});
@@ -146,10 +206,12 @@ export function fetchBalances (_addresses) {
export function updateTokensFilter (_addresses, _tokens) {
return (dispatch, getState) => {
const { api, balances, personal } = getState();
const { visibleAccounts } = personal;
const { visibleAccounts, accounts } = personal;
const { tokensFilter } = balances;
const addresses = uniq(_addresses || visibleAccounts || []).sort();
const addressesToFetch = uniq(visibleAccounts.concat(Object.keys(accounts)));
const addresses = uniq(_addresses || addressesToFetch || []).sort();
const tokens = _tokens || Object.values(balances.tokens) || [];
const tokenAddresses = tokens.map((t) => t.address).sort();
@@ -221,8 +283,10 @@ export function updateTokensFilter (_addresses, _tokens) {
export function queryTokensFilter (tokensFilter) {
return (dispatch, getState) => {
const { api, personal, balances } = getState();
const { visibleAccounts } = personal;
const { visibleAccounts, accounts } = personal;
const visibleAddresses = visibleAccounts.map((a) => a.toLowerCase());
const addressesToFetch = uniq(visibleAddresses.concat(Object.keys(accounts)));
Promise
.all([
@@ -237,18 +301,16 @@ export function queryTokensFilter (tokensFilter) {
.concat(logsTo)
.forEach((log) => {
const tokenAddress = log.address;
const fromAddress = '0x' + log.topics[1].slice(-40);
const toAddress = '0x' + log.topics[2].slice(-40);
const fromIdx = visibleAddresses.indexOf(fromAddress);
const toIdx = visibleAddresses.indexOf(toAddress);
if (fromIdx > -1) {
addresses.push(visibleAccounts[fromIdx]);
if (addressesToFetch.includes(fromAddress)) {
addresses.push(fromAddress);
}
if (toIdx > -1) {
addresses.push(visibleAccounts[toIdx]);
if (addressesToFetch.includes(toAddress)) {
addresses.push(toAddress);
}
tokenAddresses.push(tokenAddress);
@@ -269,9 +331,10 @@ export function queryTokensFilter (tokensFilter) {
export function fetchTokensBalances (_addresses = null, _tokens = null) {
return (dispatch, getState) => {
const { api, personal, balances } = getState();
const { visibleAccounts } = personal;
const { visibleAccounts, accounts } = personal;
const addresses = _addresses || visibleAccounts;
const addressesToFetch = uniq(visibleAccounts.concat(Object.keys(accounts)));
const addresses = _addresses || addressesToFetch;
const tokens = _tokens || Object.values(balances.tokens);
if (addresses.length === 0) {

View File

@@ -15,7 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { handleActions } from 'redux-actions';
import BigNumber from 'bignumber.js';
const initialState = {
balances: {},
@@ -26,39 +25,7 @@ const initialState = {
export default handleActions({
setBalances (state, action) {
const nextBalances = action.balances;
const prevBalances = state.balances;
const balances = { ...prevBalances };
Object.keys(nextBalances).forEach((address) => {
if (!balances[address]) {
balances[address] = Object.assign({}, nextBalances[address]);
return;
}
const balance = Object.assign({}, balances[address]);
const { tokens, txCount = balance.txCount } = nextBalances[address];
const nextTokens = [].concat(balance.tokens);
tokens.forEach((t) => {
const { token, value } = t;
const { tag } = token;
const tokenIndex = nextTokens.findIndex((tok) => tok.token && tok.token.tag === tag);
if (tokenIndex === -1) {
nextTokens.push({
token,
value
});
} else {
nextTokens[tokenIndex] = { token, value };
}
});
balances[address] = Object.assign({}, { txCount: txCount || new BigNumber(0), tokens: nextTokens });
});
const { balances } = action;
return Object.assign({}, state, { balances });
},

View File

@@ -21,11 +21,11 @@ export Status from './status';
export apiReducer from './apiReducer';
export balancesReducer from './balancesReducer';
export blockchainReducer from './blockchainReducer';
export compilerReducer from './compilerReducer';
export imagesReducer from './imagesReducer';
export personalReducer from './personalReducer';
export signerReducer from './signerReducer';
export statusReducer from './statusReducer';
export blockchainReducer from './blockchainReducer';
export compilerReducer from './compilerReducer';
export snackbarReducer from './snackbarReducer';
export statusReducer from './statusReducer';
export walletReducer from './walletReducer';

View File

@@ -54,7 +54,10 @@ export default class Status {
this._api.eth
.getBlockByNumber(blockNumber)
.then((block) => {
this._store.dispatch(statusCollection({ gasLimit: block.gasLimit }));
this._store.dispatch(statusCollection({
blockTimestamp: block.timestamp,
gasLimit: block.gasLimit
}));
})
.catch((error) => {
console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error);

View File

@@ -19,6 +19,7 @@ import { handleActions } from 'redux-actions';
const initialState = {
blockNumber: new BigNumber(0),
blockTimestamp: new Date(),
devLogs: [],
devLogsLevels: null,
devLogsEnabled: false,

View File

@@ -17,7 +17,12 @@
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { apiReducer, balancesReducer, blockchainReducer, compilerReducer, imagesReducer, personalReducer, signerReducer, statusReducer as nodeStatusReducer, snackbarReducer, walletReducer } from './providers';
import {
apiReducer, balancesReducer, blockchainReducer,
compilerReducer, imagesReducer, personalReducer,
signerReducer, statusReducer as nodeStatusReducer,
snackbarReducer, walletReducer
} from './providers';
import certificationsReducer from './providers/certifications/reducer';
import errorReducer from '~/ui/Errors/reducers';

View File

@@ -32,9 +32,9 @@ const storeCreation = window.devToolsExtension
? window.devToolsExtension()(createStore)
: createStore;
export default function (api) {
export default function (api, browserHistory) {
const reducers = initReducers();
const middleware = initMiddleware(api);
const middleware = initMiddleware(api, browserHistory);
const store = applyMiddleware(...middleware)(storeCreation)(reducers);
new BalancesProvider(store, api).start();

View File

@@ -17,11 +17,13 @@
import React, { Component, PropTypes } from 'react';
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
import { nodeOrStringProptype } from '~/util/proptypes';
import styles from './actionbar.css';
export default class Actionbar extends Component {
static propTypes = {
title: PropTypes.string,
title: nodeOrStringProptype(),
buttons: PropTypes.array,
children: PropTypes.node,
className: PropTypes.string

View File

@@ -18,7 +18,7 @@
.layout {
padding: 0.25em;
> * {
&>div {
margin-bottom: 0.75em;
}
}

View File

@@ -16,21 +16,38 @@
import React, { Component, PropTypes } from 'react';
import Actionbar from '../Actionbar';
import { nodeOrStringProptype } from '~/util/proptypes';
import styles from './page.css';
export default class Page extends Component {
static propTypes = {
buttons: PropTypes.array,
className: PropTypes.string,
children: PropTypes.node
children: PropTypes.node,
title: nodeOrStringProptype()
};
render () {
const { className, children } = this.props;
const { buttons, className, children, title } = this.props;
const classes = `${styles.layout} ${className}`;
let actionbar = null;
if (title || buttons) {
actionbar = (
<Actionbar
buttons={ buttons }
title={ title } />
);
}
return (
<div className={ classes }>
{ children }
<div>
{ actionbar }
<div className={ classes }>
{ children }
</div>
</div>
);
}

View File

@@ -0,0 +1,45 @@
// 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 <http://www.gnu.org/licenses/>.
import Push from 'push.js';
import BigNumber from 'bignumber.js';
import { noop } from 'lodash';
import { fromWei } from '~/api/util/wei';
import ethereumIcon from '~/../assets/images/contracts/ethereum-black-64x64.png';
import unkownIcon from '~/../assets/images/contracts/unknown-64x64.png';
export function notifyTransaction (account, token, _value, onClick) {
const name = account.name || account.address;
const value = token.tag.toLowerCase() === 'eth'
? fromWei(_value)
: _value.div(new BigNumber(token.format || 1));
const icon = token.tag.toLowerCase() === 'eth'
? ethereumIcon
: (token.image || unkownIcon);
Push.create(`${name}`, {
body: `You just received ${value.toFormat()} ${token.tag.toUpperCase()}`,
icon: {
x16: icon,
x32: icon
},
timeout: 20000,
onClick: onClick || noop
});
}

View File

@@ -51,9 +51,16 @@ export default class Dapp extends Component {
src = `${dappsUrl}/${app.contentHash}/`;
break;
default:
const dapphost = process.env.NODE_ENV === 'production' && !app.secure
? `${dappsUrl}/ui`
: '';
let dapphost = process.env.DAPPS_URL || (
process.env.NODE_ENV === 'production' && !app.secure
? `${dappsUrl}/ui`
: ''
);
if (dapphost === '/') {
dapphost = '';
}
src = `${dapphost}/${app.url}.html`;
break;
}

View File

@@ -23,8 +23,7 @@ export default class Signer extends Component {
render () {
return (
<div>
<Actionbar
title='Trusted Signer' />
<Actionbar title='Trusted Signer' />
<RequestsPage />
</div>
);

View File

@@ -30,12 +30,6 @@ export default class Store {
}
}
@action unsubscribe () {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
}
@action setBalance = (address, balance) => {
this.setBalances({ [address]: balance });
}
@@ -50,6 +44,12 @@ export default class Store {
}
}
@action unsubscribe () {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
}
fetchBalance (address) {
this._api.eth
.getBalance(address)

View File

@@ -22,7 +22,7 @@ import ReorderIcon from 'material-ui/svg-icons/action/reorder';
import { Container } from '~/ui';
import styles from './Debug.css';
import styles from './debug.css';
export default class Debug extends Component {
static propTypes = {

View File

@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './Debug';
export default from './debug';

View File

@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './MiningSettings';
export default from './miningSettings';

View File

@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './Status';
export default from './status';

View File

@@ -28,10 +28,19 @@
content: '';
}
.blockinfo {
font-size: 24px;
.blockInfo {
color: #aaa;
line-height: 24px;
font-size: 1.5em;
line-height: 1.5em;
}
.blockByline {
color: #aaa;
font-size: 0.75em;
}
.padBottom {
padding-bottom: 1.25em !important;
}
.col,

View File

@@ -14,14 +14,16 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import bytes from 'bytes';
import moment from 'moment';
import React, { Component, PropTypes } from 'react';
import { Container, ContainerTitle, Input } from '~/ui';
import styles from './Status.css';
import MiningSettings from '../MiningSettings';
import styles from './status.css';
export default class Status extends Component {
static propTypes = {
nodeStatus: PropTypes.object.isRequired,
@@ -44,23 +46,26 @@ export default class Status extends Component {
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.col3 }>
<div className={ styles.col12 }>
<div className={ `${styles.col12} ${styles.padBottom}` }>
<ContainerTitle title='best block' />
<h2 { ...this._test('best-block') } className={ styles.blockinfo }>
<div { ...this._test('best-block') } className={ styles.blockInfo }>
#{ nodeStatus.blockNumber.toFormat() }
</h2>
</div>
<div className={ styles.blockByline }>
{ moment().calendar(nodeStatus.blockTimestamp) }
</div>
</div>
<div className={ styles.col12 }>
<div className={ `${styles.col12} ${styles.padBottom}` }>
<ContainerTitle title='peers' />
<h2 { ...this._test('peers') } className={ styles.blockinfo }>
<div { ...this._test('peers') } className={ styles.blockInfo }>
{ peers }
</h2>
</div>
</div>
<div className={ styles.col12 }>
<div className={ `${styles.col12} ${styles.padBottom}` }>
<ContainerTitle title='hash rate' />
<h2 { ...this._test('hashrate') } className={ styles.blockinfo }>
<div { ...this._test('hashrate') } className={ styles.blockInfo }>
{ `${hashrate} H/s` }
</h2>
</div>
</div>
</div>
<div className={ styles.col5 }>

View File

@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './StatusPage';
export default from './statusPage';

View File

@@ -14,5 +14,9 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.container {
.body {
&>div {
margin-bottom: 0.25em;
}
}

View File

@@ -23,6 +23,8 @@ import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from '~/redux/
import Debug from '../../components/Debug';
import Status from '../../components/Status';
import styles from './statusPage.css';
class StatusPage extends Component {
static propTypes = {
nodeStatus: PropTypes.object.isRequired,
@@ -39,7 +41,7 @@ class StatusPage extends Component {
render () {
return (
<div>
<div className={ styles.body }>
<Status { ...this.props } />
<Debug { ...this.props } />
</div>

View File

@@ -16,22 +16,16 @@
import React, { Component } from 'react';
import { Actionbar, Page } from '~/ui';
import { Page } from '~/ui';
import StatusPage from './containers/StatusPage';
import styles from './status.css';
export default class Status extends Component {
render () {
return (
<div className={ styles.container }>
<Actionbar
title='status' />
<Page>
<StatusPage />
</Page>
</div>
<Page title='status'>
<StatusPage />
</Page>
);
}
}

View File

@@ -20,6 +20,7 @@ const path = require('path');
const WebpackErrorNotificationPlugin = require('webpack-error-notification');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const Shared = require('./shared');
const DAPPS = require('../src/dapps');
@@ -41,7 +42,7 @@ module.exports = {
output: {
publicPath: '/',
path: path.join(__dirname, '../', DEST),
filename: '[name].[hash].js'
filename: '[name].[hash:10].js'
},
module: {
@@ -85,13 +86,20 @@ module.exports = {
{
test: /\.css$/,
include: [ /src/ ],
// exclude: [ /src\/dapps/ ],
loader: isProd ? ExtractTextPlugin.extract([
// 'style-loader',
'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
'postcss-loader'
]) : undefined,
// use: [ 'happypack/loader?id=css' ]
use: [
use: isProd ? undefined : [
'style-loader',
'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
'postcss-loader'
]
},
{
test: /\.css$/,
exclude: [ /src/ ],
@@ -99,11 +107,15 @@ module.exports = {
},
{
test: /\.(png|jpg)$/,
use: [ 'file-loader?name=[name].[hash].[ext]' ]
use: [ 'file-loader?&name=assets/[name].[hash:10].[ext]' ]
},
{
test: /\.(woff(2)|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [ 'file-loader' ]
test: /\.(woff(2)|ttf|eot|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [ 'file-loader?name=fonts/[name][hash:10].[ext]' ]
},
{
test: /\.svg(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [ 'file-loader?name=assets/[name].[hash:10].[ext]' ]
}
],
noParse: [
@@ -153,13 +165,20 @@ module.exports = {
if (!isProd) {
plugins.push(
new webpack.optimize.CommonsChunkPlugin({
filename: 'commons.[hash].js',
filename: 'commons.[hash:10].js',
name: 'commons',
minChunks: Infinity
})
);
}
if (isProd) {
plugins.push(new ExtractTextPlugin({
filename: 'styles/[name].[hash:10].css',
allChunks: true
}));
}
return plugins;
}())
};