Several fixes to the Wallet in general (#4504)
* Fix address non-ellipsis on Wallet view * Add warning if daily limit has been reached * Add OOG warnings to TxHash * Fixes to the Warning * Added stringified version of the changes * Add wallet link for notifications * Fix tests * Use Formatted Messages * React Intl * s/ui.walletSettings/walletSettings in React Intl
This commit is contained in:
committed by
Jaco Greeff
parent
da2e28dad1
commit
ace5c27211
@@ -61,3 +61,8 @@
|
||||
margin-left: 0.125em;
|
||||
}
|
||||
|
||||
.modifications {
|
||||
color: white;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { observer } from 'mobx-react';
|
||||
import { pick } from 'lodash';
|
||||
@@ -23,7 +24,7 @@ import ActionDone from 'material-ui/svg-icons/action/done';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||
|
||||
import { Button, Modal, TxHash, BusyStep, Form, TypedInput, InputAddress, AddressSelect } from '~/ui';
|
||||
import { Button, Modal, TxHash, BusyStep, Form, TypedInput, Input, InputAddress, AddressSelect } from '~/ui';
|
||||
import { fromWei } from '~/api/util/wei';
|
||||
|
||||
import WalletSettingsStore from './walletSettingsStore.js';
|
||||
@@ -51,12 +52,27 @@ class WalletSettings extends Component {
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
title='rejected'
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='walletSettings.rejected.title'
|
||||
defaultMessage='rejected'
|
||||
/>
|
||||
}
|
||||
actions={ this.renderDialogActions() }
|
||||
>
|
||||
<BusyStep
|
||||
title='The modifications have been rejected'
|
||||
state='The wallet settings will not be modified. You can safely close this window.'
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='walletSettings.rejected.busyStep.title'
|
||||
defaultMessage='The modifications have been rejected'
|
||||
/>
|
||||
}
|
||||
state={
|
||||
<FormattedMessage
|
||||
id='walletSettings.rejected.busyStep.state'
|
||||
defaultMessage='The wallet settings will not be modified. You can safely close this window.'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
@@ -112,57 +128,124 @@ class WalletSettings extends Component {
|
||||
|
||||
default:
|
||||
case 'EDIT':
|
||||
const { wallet, errors } = this.store;
|
||||
const { errors, fromString, wallet } = this.store;
|
||||
const { accountsInfo, senders } = this.props;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<p>
|
||||
In order to edit this contract's settings, at
|
||||
least { this.store.initialWallet.require.toNumber() } owners have to
|
||||
send the very same modifications.
|
||||
Otherwise, no modification will be taken into account...
|
||||
<FormattedMessage
|
||||
id='walletSettings.edit.message'
|
||||
defaultMessage={
|
||||
`In order to edit this contract's settings, at
|
||||
least { owners, number } { owners, plural,
|
||||
one { owner }
|
||||
other { owners }
|
||||
} have to
|
||||
send the very same modifications. You can paste a stringified version
|
||||
of the modifications here.`
|
||||
}
|
||||
values={ {
|
||||
owners: this.store.initialWallet.require.toNumber()
|
||||
} }
|
||||
/>
|
||||
</p>
|
||||
|
||||
<Input
|
||||
hint='[ ... ]'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.fromString.label'
|
||||
defaultMessage='modifications'
|
||||
/>
|
||||
}
|
||||
onChange={ this.store.onModificationsStringChange }
|
||||
/>
|
||||
|
||||
<AddressSelect
|
||||
label='from account (wallet owner)'
|
||||
hint='send modifications as this owner'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.sender.label'
|
||||
defaultMessage='from account (wallet owner)'
|
||||
/>
|
||||
}
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.sender.hint'
|
||||
defaultMessage='send modifications as this owner'
|
||||
/>
|
||||
}
|
||||
value={ wallet.sender }
|
||||
error={ errors.sender }
|
||||
onChange={ this.store.onSenderChange }
|
||||
accounts={ senders }
|
||||
/>
|
||||
|
||||
<TypedInput
|
||||
label='other wallet owners'
|
||||
value={ wallet.owners.slice() }
|
||||
onChange={ this.store.onOwnersChange }
|
||||
accounts={ accountsInfo }
|
||||
param='address[]'
|
||||
/>
|
||||
<br />
|
||||
|
||||
<div className={ styles.splitInput }>
|
||||
<TypedInput
|
||||
label='required owners'
|
||||
hint='number of required owners to accept a transaction'
|
||||
error={ errors.require }
|
||||
min={ 1 }
|
||||
onChange={ this.store.onRequireChange }
|
||||
max={ wallet.owners.length }
|
||||
param='uint'
|
||||
value={ wallet.require }
|
||||
/>
|
||||
{
|
||||
fromString
|
||||
? null
|
||||
: (
|
||||
<div>
|
||||
<TypedInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.owners.label'
|
||||
defaultMessage='other wallet owners'
|
||||
/>
|
||||
}
|
||||
value={ wallet.owners.slice() }
|
||||
onChange={ this.store.onOwnersChange }
|
||||
accounts={ accountsInfo }
|
||||
param='address[]'
|
||||
/>
|
||||
|
||||
<TypedInput
|
||||
label='wallet day limit'
|
||||
hint='amount of ETH spendable without confirmations'
|
||||
value={ wallet.dailylimit }
|
||||
error={ errors.dailylimit }
|
||||
onChange={ this.store.onDailylimitChange }
|
||||
param='uint'
|
||||
isEth
|
||||
/>
|
||||
</div>
|
||||
<div className={ styles.splitInput }>
|
||||
<TypedInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.required.label'
|
||||
defaultMessage='required owners'
|
||||
/>
|
||||
}
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.required.hint'
|
||||
defaultMessage='number of required owners to accept a transaction'
|
||||
/>
|
||||
}
|
||||
error={ errors.require }
|
||||
min={ 1 }
|
||||
onChange={ this.store.onRequireChange }
|
||||
max={ wallet.owners.length }
|
||||
param='uint'
|
||||
value={ wallet.require }
|
||||
/>
|
||||
|
||||
<TypedInput
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.daylimit.label'
|
||||
defaultMessage='wallet day limit'
|
||||
/>
|
||||
}
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='walletSettings.modifications.daylimit.hint'
|
||||
defaultMessage='amount of ETH spendable without confirmations'
|
||||
/>
|
||||
}
|
||||
value={ wallet.dailylimit }
|
||||
error={ errors.dailylimit }
|
||||
onChange={ this.store.onDailylimitChange }
|
||||
param='uint'
|
||||
isEth
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -171,7 +254,12 @@ class WalletSettings extends Component {
|
||||
renderChanges (changes) {
|
||||
if (changes.length === 0) {
|
||||
return (
|
||||
<p>No modifications have been made to the Wallet settings.</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='walletSettings.changes.none'
|
||||
defaultMessage='No modifications have been made to the Wallet settings.'
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,7 +271,31 @@ class WalletSettings extends Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>You are about to make the following modifications</p>
|
||||
<p className={ styles.modifications }>
|
||||
<FormattedMessage
|
||||
id='walletSettings.changes.modificationString'
|
||||
defaultMessage={
|
||||
`For your modifications to be taken into account,
|
||||
other owners have to send the same modifications. They can paste
|
||||
this string to make it easier:`
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<Input
|
||||
allowCopy
|
||||
label='modifications'
|
||||
readOnly
|
||||
value={ this.store.changesToString() }
|
||||
/>
|
||||
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='walletSettings.changes.overview'
|
||||
defaultMessage={
|
||||
`You are about to make the following modifications`
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
{ modifications }
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,10 +30,11 @@ const STEPS = {
|
||||
export default class WalletSettingsStore {
|
||||
accounts = {};
|
||||
|
||||
@observable step = null;
|
||||
@observable requests = [];
|
||||
@observable deployState = '';
|
||||
@observable done = false;
|
||||
@observable fromString = false;
|
||||
@observable requests = [];
|
||||
@observable step = null;
|
||||
|
||||
@observable wallet = {
|
||||
owners: null,
|
||||
@@ -79,6 +80,51 @@ export default class WalletSettingsStore {
|
||||
.map((s) => s.idx);
|
||||
}
|
||||
|
||||
@action
|
||||
changesFromString (json) {
|
||||
try {
|
||||
const data = JSON.parse(json);
|
||||
const changes = data.map((datum) => {
|
||||
const [ type, valueStr ] = datum.split(';');
|
||||
|
||||
let value = valueStr;
|
||||
|
||||
// Only addresses start with `0x`, the others
|
||||
// are BigNumbers
|
||||
if (!/^0x/.test(valueStr)) {
|
||||
value = new BigNumber(valueStr, 16);
|
||||
}
|
||||
|
||||
return { type, value };
|
||||
});
|
||||
|
||||
this.changes = changes;
|
||||
} catch (error) {
|
||||
if (!(error instanceof SyntaxError)) {
|
||||
console.error('changes from string', error);
|
||||
}
|
||||
|
||||
this.changes = [];
|
||||
}
|
||||
}
|
||||
|
||||
changesToString () {
|
||||
const changes = this.changes.map((change) => {
|
||||
const { type, value } = change;
|
||||
|
||||
const valueStr = (value && typeof value.plus === 'function')
|
||||
? value.toString(16)
|
||||
: value;
|
||||
|
||||
return [
|
||||
type,
|
||||
valueStr
|
||||
].join(';');
|
||||
});
|
||||
|
||||
return JSON.stringify(changes);
|
||||
}
|
||||
|
||||
get changes () {
|
||||
const changes = [];
|
||||
|
||||
@@ -127,6 +173,36 @@ export default class WalletSettingsStore {
|
||||
return changes;
|
||||
}
|
||||
|
||||
set changes (changes) {
|
||||
transaction(() => {
|
||||
this.wallet.dailylimit = this.initialWallet.dailylimit;
|
||||
this.wallet.require = this.initialWallet.require;
|
||||
this.wallet.owners = this.initialWallet.owners.slice();
|
||||
|
||||
changes.forEach((change) => {
|
||||
const { type, value } = change;
|
||||
|
||||
switch (type) {
|
||||
case 'dailylimit':
|
||||
this.wallet.dailylimit = value;
|
||||
break;
|
||||
|
||||
case 'require':
|
||||
this.wallet.require = value;
|
||||
break;
|
||||
|
||||
case 'remove_owner':
|
||||
this.wallet.owners = this.wallet.owners.filter((owner) => owner !== value);
|
||||
break;
|
||||
|
||||
case 'add_owner':
|
||||
this.wallet.owners.push(value);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
constructor (api, wallet) {
|
||||
this.api = api;
|
||||
this.step = this.stepsKeys[0];
|
||||
@@ -177,6 +253,16 @@ export default class WalletSettingsStore {
|
||||
this.onChange({ dailylimit });
|
||||
}
|
||||
|
||||
@action onModificationsStringChange = (event, value) => {
|
||||
this.changesFromString(value);
|
||||
|
||||
if (this.changes && this.changes.length > 0) {
|
||||
this.fromString = true;
|
||||
} else {
|
||||
this.fromString = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action send = () => {
|
||||
const changes = this.changes;
|
||||
const walletInstance = this.walletInstance;
|
||||
|
||||
Reference in New Issue
Block a user