Convert all remaining Modals to use Portal (UI consistency) (#4625)

* FirstRun dialog -> Portal

* CreateAccount Modal -> Portal

* CreateWallet dialog -> Portal

* Transfer dialog -> Portal

* Fix failing tests

* ShapeShift dialog -> Portal

* Verification dialog -> Portal

* EditMeta dialog -> Portal

* PasswordManager dialog -> Portal

* WalletSettings dialog -> Portal

* AddAddress dialog -> Portal

* s/delete address/forget address/

* AddContract dialog -> Portal

* DeployContract dialog -> Portal

* ExceuteContract dialog -> Portal

* LoadContract dialog -> Portal

* SaveContract dialog -> Portal

* UpgradeParity dialog -> Portal

* Convert inline modals (tsk, tsk)

* Remove ui/Modal

* Import dialog i18n

* Button array returns (thanks @derhuerst)

* Unneeded debug

* Typo

* Readability formatting
This commit is contained in:
Jaco Greeff 2017-02-22 15:26:58 +01:00 committed by GitHub
parent 49675483c3
commit 6938a7a202
45 changed files with 1009 additions and 666 deletions

View File

@ -14,13 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import ContentAdd from 'material-ui/svg-icons/content/add';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button, Form, Input, InputAddress, Modal } from '~/ui';
import { Button, Form, Input, InputAddress, Portal } from '~/ui';
import { AddIcon, CancelIcon } from '~/ui/Icons';
import Store from './store';
@ -46,8 +45,10 @@ export default class AddAddress extends Component {
render () {
return (
<Modal
actions={ this.renderDialogActions() }
<Portal
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title={
<FormattedMessage
id='addAddress.label'
@ -57,7 +58,7 @@ export default class AddAddress extends Component {
visible
>
{ this.renderFields() }
</Modal>
</Portal>
);
}
@ -66,7 +67,7 @@ export default class AddAddress extends Component {
return ([
<Button
icon={ <ContentClear /> }
icon={ <CancelIcon /> }
label={
<FormattedMessage
id='addAddress.button.close'
@ -78,7 +79,7 @@ export default class AddAddress extends Component {
/>,
<Button
disabled={ hasError }
icon={ <ContentAdd /> }
icon={ <AddIcon /> }
label={
<FormattedMessage
id='addAddress.button.add'
@ -169,7 +170,7 @@ export default class AddAddress extends Component {
onAdd = () => {
this.store.add();
this.props.onClose();
this.onClose();
}
onClose = () => {

View File

@ -21,7 +21,7 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { newError } from '~/redux/actions';
import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '~/ui';
import { Button, Form, Input, InputAddress, Portal, RadioButtons } from '~/ui';
import { AddIcon, CancelIcon, NextIcon, PrevIcon } from '~/ui/Icons';
import Store from './store';
@ -44,9 +44,11 @@ class AddContract extends Component {
const { step } = this.store;
return (
<Modal
actions={ this.renderDialogActions() }
current={ step }
<Portal
activeStep={ step }
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
steps={ [
<FormattedMessage
id='addContract.title.type'
@ -59,10 +61,9 @@ class AddContract extends Component {
key='details'
/>
] }
visible
>
{ this.renderStep() }
</Modal>
</Portal>
);
}

View File

@ -18,7 +18,9 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Form, Input, InputAddress } from '~/ui';
import { Form, Input, InputAddress, QrCode } from '~/ui';
import styles from '../createAccount.css';
@observer
export default class AccountDetails extends Component {
@ -30,42 +32,54 @@ export default class AccountDetails extends Component {
const { address, name } = this.props.store;
return (
<Form>
<Input
allowCopy
hint={
<FormattedMessage
id='createAccount.accountDetails.name.hint'
defaultMessage='a descriptive name for the account'
/>
}
label={
<FormattedMessage
id='createAccount.accountDetails.name.label'
defaultMessage='account name'
/>
}
readOnly
value={ name }
/>
<InputAddress
disabled
hint={
<FormattedMessage
id='createAccount.accountDetails.address.hint'
defaultMessage='the network address for the account'
/>
}
label={
<FormattedMessage
id='createAccount.accountDetails.address.label'
defaultMessage='address'
/>
}
<div className={ styles.details }>
<p>
<FormattedMessage
id='createAccount.accountDetails.intro'
defaultMessage='Your account has been created with the following details:'
/>
</p>
<Form className={ styles.infoForm }>
<Input
allowCopy
hint={
<FormattedMessage
id='createAccount.accountDetails.name.hint'
defaultMessage='a descriptive name for the account'
/>
}
label={
<FormattedMessage
id='createAccount.accountDetails.name.label'
defaultMessage='account name'
/>
}
readOnly
value={ name }
/>
<InputAddress
disabled
hint={
<FormattedMessage
id='createAccount.accountDetails.address.hint'
defaultMessage='the network address for the account'
/>
}
label={
<FormattedMessage
id='createAccount.accountDetails.address.label'
defaultMessage='address'
/>
}
value={ address }
/>
{ this.renderPhrase() }
</Form>
<QrCode
className={ styles.qr }
value={ address }
/>
{ this.renderPhrase() }
</Form>
</div>
);
}

View File

@ -17,10 +17,104 @@
import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton';
import { RadioButtons } from '~/ui';
import styles from '../createAccount.css';
const TYPES = [
{
description: (
<FormattedMessage
id='createAccount.creationType.fromNew.description'
defaultMessage='Create an account by selecting your identity icon and specifying the password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromNew.label'
defaultMessage='Create new account manually'
/>
),
key: 'fromNew'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromPhrase.description'
defaultMessage='Recover an account by entering a previously stored recovery phrase and new password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromPhrase.label'
defaultMessage='Recover account from recovery phrase'
/>
),
key: 'fromPhrase'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromGeth.description'
defaultMessage='Import an accounts from the Geth keystore with the original password'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromGeth.label'
defaultMessage='Import accounts from Geth keystore'
/>
),
key: 'fromGeth'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromJSON.description'
defaultMessage='Create an account by importing an industry-standard JSON keyfile'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromJSON.label'
defaultMessage='Import account from a backup JSON file'
/>
),
key: 'fromJSON'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromPresale.description'
defaultMessage='Create an account by importing an Ethereum presale wallet file'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromPresale.label'
defaultMessage='Import account from an Ethereum pre-sale wallet'
/>
),
key: 'fromPresale'
},
{
description: (
<FormattedMessage
id='createAccount.creationType.fromRaw.description'
defaultMessage='Create an account by entering a previously backed-up raw private key'
/>
),
label: (
<FormattedMessage
id='createAccount.creationType.fromRaw.label'
defaultMessage='Import raw private key'
/>
),
key: 'fromRaw'
}
];
@observer
export default class CreationType extends Component {
static propTypes = {
@ -32,66 +126,12 @@ export default class CreationType extends Component {
return (
<div className={ styles.spaced }>
<RadioButtonGroup
defaultSelected={ createType }
<RadioButtons
name='creationType'
onChange={ this.onChange }
>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromNew.label'
defaultMessage='Create new account manually'
/>
}
value='fromNew'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromPhrase.label'
defaultMessage='Recover account from recovery phrase'
/>
}
value='fromPhrase'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromGeth.label'
defaultMessage='Import accounts from Geth keystore'
/>
}
value='fromGeth'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromJSON.label'
defaultMessage='Import account from a backup JSON file'
/>
}
value='fromJSON'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromPresale.label'
defaultMessage='Import account from an Ethereum pre-sale wallet'
/>
}
value='fromPresale'
/>
<RadioButton
label={
<FormattedMessage
id='createAccount.creationType.fromRaw.label'
defaultMessage='Import raw private key'
/>
}
value='fromRaw'
/>
</RadioButtonGroup>
value={ createType }
values={ TYPES }
/>
</div>
);
}

View File

@ -50,7 +50,7 @@ describe('modals/CreateAccount/CreationType', () => {
beforeEach(() => {
store.setCreateType(SELECT_TYPE);
selector = component.find('RadioButtonGroup');
selector = component.find('RadioButtons');
});
it('renders the selector', () => {
@ -58,7 +58,7 @@ describe('modals/CreateAccount/CreationType', () => {
});
it('passes the store type to defaultSelected', () => {
expect(selector.props().defaultSelected).to.equal(SELECT_TYPE);
expect(selector.props().value).to.equal(SELECT_TYPE);
});
});

View File

@ -59,7 +59,7 @@ describe('modals/CreateAccount/NewAccount', () => {
});
it('creates initial accounts', () => {
expect(Object.keys(instance.state.accounts).length).to.equal(5);
expect(Object.keys(instance.state.accounts).length).to.equal(7);
});
it('sets the initial selected value', () => {

View File

@ -15,6 +15,19 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.details {
text-align: center;
.infoForm {
background: rgba(0, 0, 0, 0.1);
padding: 1.5em;
}
.qr {
margin: 1.5em auto 0 auto;
}
}
.spaced {
line-height: 1.618em;
}
@ -39,7 +52,8 @@
}
}
.identities, .selector {
.identities,
.selector {
display: flex;
}
@ -47,9 +61,10 @@
margin-top: 1.5em;
}
.identities .identity, .selector .button {
flex: 0 1 18%;
width: 18% !important;
.identities .identity,
.selector .button {
flex: 0 1 12.5%;
width: 12.5% !important;
text-align: center;
cursor: pointer;
}

View File

@ -22,7 +22,7 @@ import { bindActionCreators } from 'redux';
import { createIdentityImg } from '~/api/util/identity';
import { newError } from '~/redux/actions';
import { Button, Modal } from '~/ui';
import { Button, Portal } from '~/ui';
import { CancelIcon, CheckIcon, DoneIcon, NextIcon, PrevIcon, PrintIcon } from '~/ui/Icons';
import ParityLogo from '~/../assets/images/parity-logo-black-no-text.svg';
@ -86,10 +86,11 @@ class CreateAccount extends Component {
const { createType, stage } = this.store;
return (
<Modal
visible
actions={ this.renderDialogActions() }
current={ stage }
<Portal
buttons={ this.renderDialogActions() }
activeStep={ stage }
onClose={ this.onClose }
open
steps={
createType === 'fromNew'
? STAGE_NAMES
@ -97,7 +98,7 @@ class CreateAccount extends Component {
}
>
{ this.renderPage() }
</Modal>
</Portal>
);
}

View File

@ -325,6 +325,8 @@ export default class Store {
this._api.parity.generateSecretPhrase(),
this._api.parity.generateSecretPhrase(),
this._api.parity.generateSecretPhrase(),
this._api.parity.generateSecretPhrase(),
this._api.parity.generateSecretPhrase(),
this._api.parity.generateSecretPhrase()
])
.then((phrases) => {

View File

@ -580,9 +580,9 @@ describe('modals/CreateAccount/Store', () => {
});
});
it('returns a map of 5 accounts', () => {
it('returns a map of 7 accounts', () => {
return store.createIdentities().then((accounts) => {
expect(Object.keys(accounts).length).to.equal(5);
expect(Object.keys(accounts).length).to.equal(7);
});
});

View File

@ -18,7 +18,7 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button, Modal, TxHash, BusyStep } from '~/ui';
import { BusyStep, Button, Portal, TxHash } from '~/ui';
import { CancelIcon, DoneIcon, NextIcon } from '~/ui/Icons';
import WalletType from './WalletType';
@ -44,15 +44,16 @@ export default class CreateWallet extends Component {
if (rejected) {
return (
<Modal
visible
<Portal
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title={
<FormattedMessage
id='createWallet.rejected.title'
defaultMessage='rejected'
/>
}
actions={ this.renderDialogActions() }
>
<BusyStep
title={
@ -68,20 +69,21 @@ export default class CreateWallet extends Component {
/>
}
/>
</Modal>
</Portal>
);
}
return (
<Modal
visible
actions={ this.renderDialogActions() }
current={ stage }
<Portal
activeStep={ stage }
busySteps={ waiting }
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
steps={ steps.map((step) => step.title) }
waiting={ waiting }
>
{ this.renderPage() }
</Modal>
</Portal>
);
}
@ -151,6 +153,7 @@ export default class CreateWallet extends Component {
const cancelBtn = (
<Button
icon={ <CancelIcon /> }
key='cancel'
label={
<FormattedMessage
id='createWallet.button.cancel'
@ -164,6 +167,7 @@ export default class CreateWallet extends Component {
const closeBtn = (
<Button
icon={ <CancelIcon /> }
key='close'
label={
<FormattedMessage
id='createWallet.button.close'
@ -177,6 +181,7 @@ export default class CreateWallet extends Component {
const doneBtn = (
<Button
icon={ <DoneIcon /> }
key='done'
label={
<FormattedMessage
id='createWallet.button.done'
@ -190,6 +195,7 @@ export default class CreateWallet extends Component {
const sendingBtn = (
<Button
icon={ <DoneIcon /> }
key='sending'
label={
<FormattedMessage
id='createWallet.button.sending'
@ -203,6 +209,7 @@ export default class CreateWallet extends Component {
const nextBtn = (
<Button
icon={ <NextIcon /> }
key='next'
label={
<FormattedMessage
id='createWallet.button.next'
@ -230,6 +237,7 @@ export default class CreateWallet extends Component {
<Button
disabled={ hasErrors }
icon={ <NextIcon /> }
key='add'
label={
<FormattedMessage
id='createWallet.button.add'
@ -245,6 +253,7 @@ export default class CreateWallet extends Component {
<Button
disabled={ hasErrors }
icon={ <NextIcon /> }
key='create'
label={
<FormattedMessage
id='createWallet.button.create'

View File

@ -41,7 +41,7 @@ function create () {
store = new Store(api);
}
describe.only('modals/DappPermissions/store', () => {
describe('modals/DappPermissions/store', () => {
beforeEach(() => {
create();
});

View File

@ -20,7 +20,7 @@ import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { BusyStep, Button, CompletedStep, CopyToClipboard, GasPriceEditor, IdentityIcon, Modal, TxHash, Warning } from '~/ui';
import { BusyStep, Button, CompletedStep, CopyToClipboard, GasPriceEditor, IdentityIcon, Portal, TxHash, Warning } from '~/ui';
import { CancelIcon, DoneIcon } from '~/ui/Icons';
import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation';
@ -166,21 +166,22 @@ class DeployContract extends Component {
: null;
return (
<Modal
actions={ this.renderDialogActions() }
current={ realStep }
<Portal
buttons={ this.renderDialogActions() }
activeStep={ realStep }
busySteps={ waiting }
onClose={ this.onClose }
open
steps={
realSteps
? realSteps.map((s) => s.title)
: null
}
title={ title }
visible
waiting={ waiting }
>
{ this.renderExceptionWarning() }
{ this.renderStep() }
</Modal>
</Portal>
);
}
@ -207,6 +208,7 @@ class DeployContract extends Component {
const cancelBtn = (
<Button
icon={ <CancelIcon /> }
key='cancel'
label={
<FormattedMessage
id='deployContract.button.cancel'
@ -220,6 +222,7 @@ class DeployContract extends Component {
const closeBtn = (
<Button
icon={ <CancelIcon /> }
key='close'
label={
<FormattedMessage
id='deployContract.button.close'
@ -233,6 +236,7 @@ class DeployContract extends Component {
const closeBtnOk = (
<Button
icon={ <DoneIcon /> }
key='done'
label={
<FormattedMessage
id='deployContract.button.done'
@ -253,6 +257,7 @@ class DeployContract extends Component {
cancelBtn,
<Button
disabled={ !isValid }
key='next'
icon={
<IdentityIcon
address={ fromAddress }
@ -279,6 +284,7 @@ class DeployContract extends Component {
button
/>
}
key='create'
label={
<FormattedMessage
id='deployContract.button.create'

View File

@ -21,7 +21,7 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { newError } from '~/redux/actions';
import { Button, Form, Input, InputChip, Modal } from '~/ui';
import { Button, Form, Input, InputChip, Portal } from '~/ui';
import { CancelIcon, SaveIcon } from '~/ui/Icons';
import Store from './store';
@ -44,15 +44,16 @@ class EditMeta extends Component {
const { description, name, nameError, tags } = this.store;
return (
<Modal
actions={ this.renderActions() }
<Portal
buttons={ this.renderActions() }
onClose={ this.onClose }
open
title={
<FormattedMessage
id='editMeta.title'
defaultMessage='edit metadata'
/>
}
visible
>
<Form>
<Input
@ -101,7 +102,7 @@ class EditMeta extends Component {
tokens={ tags.slice() }
/>
</Form>
</Modal>
</Portal>
);
}
@ -112,12 +113,14 @@ class EditMeta extends Component {
<Button
label='Cancel'
icon={ <CancelIcon /> }
onClick={ this.props.onClose }
key='cancel'
onClick={ this.onClose }
/>,
<Button
disabled={ hasError }
label='Save'
icon={ <SaveIcon /> }
key='save'
onClick={ this.onSave }
/>
];
@ -150,6 +153,10 @@ class EditMeta extends Component {
);
}
onClose = () => {
this.props.onClose();
}
onSave = () => {
if (this.store.hasError) {
return;
@ -157,7 +164,7 @@ class EditMeta extends Component {
return this.store
.save()
.then(() => this.props.onClose())
.then(this.onClose)
.catch((error) => {
this.props.newError(error);
});

View File

@ -21,7 +21,7 @@ import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { toWei } from '~/api/util/wei';
import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Modal, TxHash, Warning } from '~/ui';
import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Portal, TxHash, Warning } from '~/ui';
import { CancelIcon, DoneIcon, NextIcon, PrevIcon } from '~/ui/Icons';
import { MAX_GAS_ESTIMATION } from '~/util/constants';
import { validateAddress, validateUint } from '~/util/validation';
@ -131,21 +131,22 @@ class ExecuteContract extends Component {
}
return (
<Modal
actions={ this.renderDialogActions() }
busy={ sending }
current={ step }
steps={ steps }
visible
waiting={
<Portal
activeStep={ step }
buttons={ this.renderDialogActions() }
busySteps={
advancedOptions
? [STEP_BUSY]
: [STEP_BUSY_OR_ADVANCED]
}
busy={ sending }
onClose={ this.onClose }
open
steps={ steps }
>
{ this.renderExceptionWarning() }
{ this.renderStep() }
</Modal>
</Portal>
);
}
@ -163,7 +164,7 @@ class ExecuteContract extends Component {
}
renderDialogActions () {
const { onClose, fromAddress } = this.props;
const { fromAddress } = this.props;
const { advancedOptions, sending, step, fromAddressError, valuesError } = this.state;
const hasError = fromAddressError || valuesError.find((error) => error);
@ -177,7 +178,7 @@ class ExecuteContract extends Component {
/>
}
icon={ <CancelIcon /> }
onClick={ onClose }
onClick={ this.onClose }
/>
);
const postBtn = (
@ -248,7 +249,7 @@ class ExecuteContract extends Component {
/>
}
icon={ <DoneIcon /> }
onClick={ onClose }
onClick={ this.onClose }
/>
];
}
@ -462,6 +463,10 @@ class ExecuteContract extends Component {
step: this.state.step - 1
});
}
onClose = () => {
this.props.onClose();
}
}
function mapStateToProps (initState, initProps) {

View File

@ -15,12 +15,26 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import styles from '../firstRun.css';
export default class Completed extends Component {
render () {
return (
<div>
<p>Your node setup has been completed successfully and you are ready to use the application. Next you will receive a walk-through of the available functions and the general application interface to get you up and running in record time.</p>
<div className={ styles.completed }>
<p>
<FormattedMessage
id='firstRun.completed.congrats'
defaultMessage='Congratulations! Your node setup has been completed successfully and you are ready to use the application.'
/>
</p>
<p>
<FormattedMessage
id='firstRun.completed.next'
defaultMessage='Next you will receive a walk-through of the available functions and the general application interface to get you up and running in record time.'
/>
</p>
</div>
);
}

View File

@ -15,9 +15,10 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Checkbox } from 'material-ui';
import styles from './tnc.css';
import styles from '../firstRun.css';
export default class TnC extends Component {
static propTypes = {
@ -29,7 +30,7 @@ export default class TnC extends Component {
const { hasAccepted, onAccept } = this.props;
return (
<div className={ styles.body }>
<div className={ styles.tnc }>
<h1>SECURITY WARNINGS</h1>
<ul>
<li>You are responsible for your own computer security. If your machine is compromised you will lose your ether, access to any contracts and maybe more.</li>
@ -165,7 +166,12 @@ export default class TnC extends Component {
<Checkbox
className={ styles.accept }
label='I accept these terms and conditions'
label={
<FormattedMessage
id='firstRun.tnc.accept'
defaultMessage='I accept these terms and conditions'
/>
}
checked={ hasAccepted }
onCheck={ onAccept }
/>

View File

@ -15,12 +15,15 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import imagesEthcore from '../../../../assets/images/parity-logo-white.svg';
import imagesEthcore from '~/../assets/images/parity-logo-white.svg';
import styles from '../firstRun.css';
const LOGO_STYLE = {
float: 'right',
width: '25%',
maxWidth: '10em',
height: 'auto',
margin: '0 1.5em'
};
@ -28,15 +31,52 @@ const LOGO_STYLE = {
export default class FirstRun extends Component {
render () {
return (
<div>
<div className={ styles.welcome }>
<img
src={ imagesEthcore }
alt='Ethcore Ltd.'
style={ LOGO_STYLE }
/>
<p>Welcome to <strong>Parity</strong>, the fastest and simplest way to run your node.</p>
<p>The next few steps will guide you through the process of setting up you Parity instance and the associated account.</p>
<p>Click <strong>Next</strong> to continue your journey.</p>
<p>
<FormattedMessage
id='firstRun.welcome.greeting'
defaultMessage='Welcome to Parity, the fastest and simplest way to run your node.'
/>
</p>
<p>
<FormattedMessage
id='firstRun.welcome.description'
defaultMessage='As part of a new installation, the next few steps will guide you through the process of setting up you Parity instance and your associated accounts. Our aim is to make it as simple as possible and to get you up and running in record-time, so please bear with us. Once completed you will have -'
/>
</p>
<p>
<ul>
<li>
<FormattedMessage
id='firstRun.welcome.step.privacy'
defaultMessage='Understood our privacy policy & terms of operation'
/>
</li>
<li>
<FormattedMessage
id='firstRun.welcome.step.account'
defaultMessage='Created your first Parity account'
/>
</li>
<li>
<FormattedMessage
id='firstRun.welcome.step.recovery'
defaultMessage='Have the ability to recover your account'
/>
</li>
</ul>
</p>
<p>
<FormattedMessage
id='firstRun.welcome.next'
defaultMessage='Click Next to continue your journey.'
/>
</p>
</div>
);
}

View File

@ -15,15 +15,33 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.body {
.completed,
.welcome {
p {
line-height: 1.5em;
}
ul {
background: rgba(0, 0, 0, 0.1);
margin-right: 12em;
padding: 1em 3em;
li {
line-height: 2em;
}
}
}
.tnc {
overflow-y: auto;
}
.body p, .body li {
opacity: 0.8;
line-height: 1.5em;
}
p,
li {
opacity: 0.8;
line-height: 1.5em;
}
.accept {
margin: 1.5em 0;
.accept {
margin: 1.5em 0;
}
}

View File

@ -23,7 +23,7 @@ import { bindActionCreators } from 'redux';
import ParityLogo from '~/../assets/images/parity-logo-black-no-text.svg';
import { createIdentityImg } from '~/api/util/identity';
import { newError } from '~/redux/actions';
import { Button, Modal } from '~/ui';
import { Button, Portal } from '~/ui';
import { CheckIcon, DoneIcon, NextIcon, PrintIcon } from '~/ui/Icons';
import { NewAccount, AccountDetails } from '../CreateAccount';
@ -93,14 +93,15 @@ class FirstRun extends Component {
}
return (
<Modal
actions={ this.renderDialogActions() }
current={ stage }
<Portal
buttons={ this.renderDialogActions() }
activeStep={ stage }
hideClose
steps={ STAGE_NAMES }
visible
open
>
{ this.renderStage() }
</Modal>
</Portal>
);
}

View File

@ -20,7 +20,7 @@ import moment from 'moment';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button, Modal } from '~/ui';
import { Button, Portal } from '~/ui';
import Editor from '~/ui/Editor';
import { CancelIcon, CheckIcon, DeleteIcon } from '~/ui/Icons';
@ -55,8 +55,10 @@ export default class LoadContract extends Component {
const { deleteRequest } = this.state;
return (
<Modal
actions={ this.renderDialogActions() }
<Portal
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title={
deleteRequest
? (
@ -72,10 +74,9 @@ export default class LoadContract extends Component {
/>
)
}
visible
>
{ this.renderBody() }
</Modal>
</Portal>
);
}

View File

@ -78,6 +78,6 @@
.form {
margin-top: 0;
padding: 0 0.5rem 1rem;
padding: 0.75rem 1.5rem 1.5rem 1.5rem;
background-color: rgba(255, 255, 255, 0.05);
}

View File

@ -23,7 +23,7 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { newError, openSnackbar } from '~/redux/actions';
import { Button, Modal, IdentityName, IdentityIcon } from '~/ui';
import { Button, IdentityName, IdentityIcon, Portal } from '~/ui';
import PasswordStrength from '~/ui/Form/PasswordStrength';
import Form, { Input } from '~/ui/Form';
import { CancelIcon, CheckIcon, SendIcon } from '~/ui/Icons';
@ -38,7 +38,7 @@ const MSG_FAILURE_STYLE = {
backgroundColor: 'rgba(229, 115, 115, 0.75)'
};
const TABS_INKBAR_STYLE = {
backgroundColor: 'rgba(255, 255, 255, 0.55)'
backgroundColor: 'rgb(0, 151, 167)' // 'rgba(255, 255, 255, 0.55)'
};
const TABS_ITEM_STYLE = {
backgroundColor: 'rgba(255, 255, 255, 0.05)'
@ -61,20 +61,21 @@ class PasswordManager extends Component {
render () {
return (
<Modal
actions={ this.renderDialogActions() }
<Portal
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title={
<FormattedMessage
id='passwordChange.title'
defaultMessage='Password Manager'
/>
}
visible
>
{ this.renderAccount() }
{ this.renderPage() }
{ this.renderMessage() }
</Modal>
</Portal>
);
}
@ -273,7 +274,6 @@ class PasswordManager extends Component {
renderDialogActions () {
const { actionTab, busy, isRepeatValid } = this.store;
const { onClose } = this.props;
const cancelBtn = (
<Button
@ -285,7 +285,7 @@ class PasswordManager extends Component {
defaultMessage='Cancel'
/>
}
onClick={ onClose }
onClick={ this.onClose }
/>
);
@ -367,6 +367,10 @@ class PasswordManager extends Component {
this.store.setValidatePassword(password);
}
onClose = () => {
this.props.onClose();
}
changePassword = () => {
return this.store
.changePassword()
@ -380,7 +384,7 @@ class PasswordManager extends Component {
/>
</div>
);
this.props.onClose();
this.onClose();
}
})
.catch((error) => {

View File

@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react';
import SaveIcon from 'material-ui/svg-icons/content/save';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { Button, Modal, Form, Input } from '~/ui';
import { Button, Form, Input, Portal } from '~/ui';
import Editor from '~/ui/Editor';
import { ERRORS, validateName } from '~/util/validation';
@ -42,10 +42,11 @@ export default class SaveContract extends Component {
const { name, nameError } = this.state;
return (
<Modal
<Portal
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
title='save contract'
actions={ this.renderDialogActions() }
visible
>
<div>
<Form>
@ -60,11 +61,11 @@ export default class SaveContract extends Component {
<Editor
className={ styles.source }
value={ sourcecode }
maxLines={ 20 }
maxLines={ 25 }
readOnly
/>
</div>
</Modal>
</Portal>
);
}
@ -72,6 +73,7 @@ export default class SaveContract extends Component {
const cancelBtn = (
<Button
icon={ <ContentClear /> }
key='cancel'
label='Cancel'
onClick={ this.onClose }
/>
@ -80,6 +82,7 @@ export default class SaveContract extends Component {
const confirmBtn = (
<Button
icon={ <SaveIcon /> }
key='save'
label='Save'
disabled={ !!this.state.nameError }
onClick={ this.onSave }

View File

@ -37,25 +37,23 @@
}
.shapeshift {
position: absolute;
bottom: 0.5em;
left: 1em;
cursor: pointer;
left: 1.5em;
outline: none;
}
position: absolute;
.shapeshift img {
height: 28px;
img {
height: 28px;
}
}
.info, .busy {
}
.center {
}
.center div {
text-align: center;
div {
text-align: center;
}
}
.error {
@ -74,9 +72,11 @@
color: #aaa;
}
.price div {
text-align: center;
font-size: small;
.price {
div {
text-align: center;
font-size: small;
}
}
.empty {

View File

@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import shapeshiftLogo from '~/../assets/images/shapeshift-logo.png';
import { Button, IdentityIcon, Modal } from '~/ui';
import { Button, IdentityIcon, Portal } from '~/ui';
import { CancelIcon, DoneIcon } from '~/ui/Icons';
import AwaitingDepositStep from './AwaitingDepositStep';
@ -81,9 +81,15 @@ export default class Shapeshift extends Component {
const { error, stage } = this.store;
return (
<Modal
actions={ this.renderDialogActions() }
current={ stage }
<Portal
activeStep={ stage }
busySteps={ [
STAGE_WAIT_DEPOSIT,
STAGE_WAIT_EXCHANGE
] }
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
steps={
error
? null
@ -94,14 +100,9 @@ export default class Shapeshift extends Component {
? ERROR_TITLE
: null
}
visible
waiting={ [
STAGE_WAIT_DEPOSIT,
STAGE_WAIT_EXCHANGE
] }
>
{ this.renderPage() }
</Modal>
</Portal>
);
}
@ -110,13 +111,19 @@ export default class Shapeshift extends Component {
const { coins, error, hasAcceptedTerms, stage } = this.store;
const logo = (
<a href='http://shapeshift.io' target='_blank' className={ styles.shapeshift }>
<a
className={ styles.shapeshift }
href='http://shapeshift.io'
key='logo'
target='_blank'
>
<img src={ shapeshiftLogo } />
</a>
);
const cancelBtn = (
<Button
icon={ <CancelIcon /> }
key='cancel'
label={
<FormattedMessage
id='shapeshift.button.cancel'
@ -147,6 +154,7 @@ export default class Shapeshift extends Component {
button
/>
}
key='shift'
label={
<FormattedMessage
id='shapeshift.button.shift'
@ -169,6 +177,7 @@ export default class Shapeshift extends Component {
logo,
<Button
icon={ <DoneIcon /> }
key='done'
label={
<FormattedMessage
id='shapeshift.button.done'

View File

@ -21,7 +21,7 @@ import { bindActionCreators } from 'redux';
import { observer } from 'mobx-react';
import { pick } from 'lodash';
import { BusyStep, CompletedStep, Button, IdentityIcon, Input, Modal, TxHash, Warning } from '~/ui';
import { BusyStep, CompletedStep, Button, IdentityIcon, Input, Portal, TxHash, Warning } from '~/ui';
import { newError } from '~/ui/Errors/actions';
import { CancelIcon, DoneIcon, NextIcon, PrevIcon } from '~/ui/Icons';
import { nullableProptype } from '~/util/proptypes';
@ -60,21 +60,22 @@ class Transfer extends Component {
const { stage, extras, steps } = this.store;
return (
<Modal
actions={ this.renderDialogActions() }
current={ stage }
steps={ steps }
waiting={
<Portal
activeStep={ stage }
busySteps={
extras
? [STEP_BUSY]
: [STEP_ADVANCED_OR_BUSY]
}
visible
buttons={ this.renderDialogActions() }
onClose={ this.handleClose }
open
steps={ steps }
>
{ this.renderExceptionWarning() }
{ this.renderWalletWarning() }
{ this.renderPage() }
</Modal>
</Portal>
);
}
@ -252,6 +253,7 @@ class Transfer extends Component {
const cancelBtn = (
<Button
icon={ <CancelIcon /> }
key='cancel'
label='Cancel'
onClick={ this.handleClose }
/>
@ -260,6 +262,7 @@ class Transfer extends Component {
<Button
disabled={ !this.store.isValid }
icon={ <NextIcon /> }
key='next'
label='Next'
onClick={ this.store.onNext }
/>
@ -267,6 +270,7 @@ class Transfer extends Component {
const prevBtn = (
<Button
icon={ <PrevIcon /> }
key='back'
label='Back'
onClick={ this.store.onPrev }
/>
@ -275,6 +279,7 @@ class Transfer extends Component {
<Button
disabled={ !this.store.isValid || sending }
icon={ <IdentityIcon address={ account.address } button /> }
key='send'
label='Send'
onClick={ this.store.onSend }
/>
@ -282,6 +287,7 @@ class Transfer extends Component {
const doneBtn = (
<Button
icon={ <DoneIcon /> }
key='close'
label='Close'
onClick={ this.handleClose }
/>

View File

@ -19,9 +19,23 @@
padding-top: 1.25em;
}
.infoStep {
div+div {
padding-top: 1.25em;
.step {
display: flex;
flex-direction: row;
justify-content: flex-start;
.icon {
height: 10em !important;
opacity: 0.5;
width: 10em !important;
}
.text {
background: rgba(0, 0, 0, 0.25);
line-height: 1.5em;
margin-left: 3em;
padding: 2em;
width: 100%;
}
}

View File

@ -18,9 +18,8 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from '~/ui';
import { CancelIcon, DoneIcon, NextIcon } from '~/ui/Icons';
import Modal, { Busy, Completed } from '~/ui/Modal';
import { Button, Portal } from '~/ui';
import { CancelIcon, DoneIcon, ErrorIcon, NextIcon, UpdateIcon, UpdateWaitIcon } from '~/ui/Icons';
import { STEP_COMPLETED, STEP_ERROR, STEP_INFO, STEP_UPDATING } from './store';
import styles from './upgradeParity.css';
@ -43,9 +42,12 @@ export default class UpgradeParity extends Component {
}
return (
<Modal
actions={ this.renderActions() }
current={ store.step }
<Portal
activeStep={ store.step }
busySteps={ [ 1 ] }
buttons={ this.renderActions() }
onClose={ this.onClose }
open
steps={ [
<FormattedMessage
id='upgradeParity.step.info'
@ -57,7 +59,7 @@ export default class UpgradeParity extends Component {
id='upgradeParity.step.updating'
defaultMessage='upgrading parity'
/>,
store.step === STEP_ERROR
store.error
? <FormattedMessage
id='upgradeParity.step.error'
key='error'
@ -69,10 +71,9 @@ export default class UpgradeParity extends Component {
defaultMessage='upgrade completed'
/>
] }
visible
>
{ this.renderStep() }
</Modal>
</Portal>
);
}
@ -89,7 +90,7 @@ export default class UpgradeParity extends Component {
defaultMessage='close'
/>
}
onClick={ store.closeModal }
onClick={ this.onClose }
/>;
const doneButton =
<Button
@ -101,7 +102,7 @@ export default class UpgradeParity extends Component {
defaultMessage='done'
/>
}
onClick={ store.closeModal }
onClick={ this.onDone }
/>;
switch (store.step) {
@ -116,7 +117,7 @@ export default class UpgradeParity extends Component {
defaultMessage='upgrade now'
/>
}
onClick={ store.upgradeNow }
onClick={ this.onUpgrade }
/>,
closeButton
];
@ -136,7 +137,6 @@ export default class UpgradeParity extends Component {
renderStep () {
const { store } = this.props;
const currentversion = this.formatVersion(store);
const newversion = store.upgrading
? this.formatVersion(store.upgrading)
@ -144,70 +144,121 @@ export default class UpgradeParity extends Component {
switch (store.step) {
case STEP_INFO:
return (
<div className={ styles.infoStep }>
<div>
<FormattedMessage
id='upgradeParity.info.upgrade'
defaultMessage='A new version of Parity, version {newversion} is available as an upgrade from your current version {currentversion}'
values={ {
currentversion: <div className={ styles.version }>{ currentversion }</div>,
newversion: <div className={ styles.version }>{ newversion }</div>
} }
/>
</div>
{ this.renderConsensusInfo() }
</div>
);
return this.renderStepInfo(newversion, currentversion);
case STEP_UPDATING:
return (
<Busy
title={
<FormattedMessage
id='upgradeParity.busy'
defaultMessage='Your upgrade to Parity {newversion} is currently in progress'
values={ {
newversion: <div className={ styles.version }>{ newversion }</div>
} }
/>
}
/>
);
return this.renderStepBusy(newversion);
case STEP_COMPLETED:
case STEP_ERROR:
if (store.error) {
return (
<Completed>
<div>
return store.error
? this.renderStepError(newversion)
: this.renderStepCompleted(newversion);
}
}
renderStepBusy (newversion) {
return (
<div className={ styles.step }>
<UpdateWaitIcon className={ styles.icon } />
<div className={ styles.text }>
<FormattedMessage
id='upgradeParity.busy'
defaultMessage='Your upgrade to Parity {newversion} is currently in progress. Please wait until the process completes.'
values={ {
newversion: <div className={ styles.version }>{ newversion }</div>
} }
/>
</div>
</div>
);
}
renderStepCompleted (newversion) {
return (
<div className={ styles.step }>
<DoneIcon className={ styles.icon } />
<div className={ styles.text }>
<FormattedMessage
id='upgradeParity.completed'
defaultMessage='Your upgrade to Parity {newversion} has been successfully completed. Click "done" to automatically reload the application.'
values={ {
newversion: <div className={ styles.version }>{ newversion }</div>
} }
/>
</div>
</div>
);
}
renderStepError (newversion) {
const { store } = this.props;
return (
<div className={ styles.step }>
<ErrorIcon className={ styles.icon } />
<div className={ styles.text }>
<FormattedMessage
id='upgradeParity.failed'
defaultMessage='Your upgrade to Parity {newversion} has failed with an error.'
values={ {
newversion: <div className={ styles.version }>{ newversion }</div>
} }
/>
<div className={ styles.error }>
{ store.error.message }
</div>
</div>
</div>
);
}
renderStepInfo (newversion, currentversion) {
return (
<div className={ styles.step }>
<UpdateIcon className={ styles.icon } />
<div className={ styles.text }>
<div>
<FormattedMessage
id='upgradeParity.info.welcome'
defaultMessage='Welcome to the Parity upgrade wizard, allowing you a completely seamless upgrade experience to the next version of Parity.'
/>
</div>
<div>
<ul>
<li>
<FormattedMessage
id='upgradeParity.failed'
defaultMessage='Your upgrade to Parity {newversion} has failed with an error.'
id='upgradeParity.info.currentVersion'
defaultMessage='You are currently running {currentversion}'
values={ {
currentversion: <div className={ styles.version }>{ currentversion }</div>
} }
/>
</li>
<li>
<FormattedMessage
id='upgradeParity.info.upgrade'
defaultMessage='An upgrade to version {newversion} is available'
values={ {
currentversion: <div className={ styles.version }>{ currentversion }</div>,
newversion: <div className={ styles.version }>{ newversion }</div>
} }
/>
</div>
<div className={ styles.error }>
{ store.error.message }
</div>
</Completed>
);
}
return (
<Completed>
</li>
<li>
{ this.renderConsensusInfo() }
</li>
</ul>
</div>
<div>
<FormattedMessage
id='upgradeParity.completed'
defaultMessage='Your upgrade to Parity {newversion} has been successfully completed.'
values={ {
newversion: <div className={ styles.version }>{ newversion }</div>
} }
id='upgradeParity.info.next'
defaultMessage='Proceed with "upgrade now" to start your Parity upgrade.'
/>
</Completed>
);
}
</div>
</div>
</div>
);
}
renderConsensusInfo () {
@ -217,47 +268,39 @@ export default class UpgradeParity extends Component {
if (consensusCapability) {
if (consensusCapability === 'capable') {
return (
<div>
<FormattedMessage
id='upgradeParity.consensus.capable'
defaultMessage='Your current Parity version is capable of handling the network requirements.'
/>
</div>
<FormattedMessage
id='upgradeParity.consensus.capable'
defaultMessage='Your current Parity version is capable of handling the network requirements.'
/>
);
} else if (consensusCapability.capableUntil) {
return (
<div>
<FormattedMessage
id='upgradeParity.consensus.capableUntil'
defaultMessage='Your current Parity version is capable of handling the network requirements until block {blockNumber}'
values={ {
blockNumber: consensusCapability.capableUntil
} }
/>
</div>
<FormattedMessage
id='upgradeParity.consensus.capableUntil'
defaultMessage='Your current Parity version is capable of handling the network requirements until block {blockNumber}'
values={ {
blockNumber: consensusCapability.capableUntil
} }
/>
);
} else if (consensusCapability.incapableSince) {
return (
<div>
<FormattedMessage
id='upgradeParity.consensus.incapableSince'
defaultMessage='Your current Parity version is incapable of handling the network requirements since block {blockNumber}'
values={ {
blockNumber: consensusCapability.incapableSince
} }
/>
</div>
<FormattedMessage
id='upgradeParity.consensus.incapableSince'
defaultMessage='Your current Parity version is incapable of handling the network requirements since block {blockNumber}'
values={ {
blockNumber: consensusCapability.incapableSince
} }
/>
);
}
}
return (
<div>
<FormattedMessage
id='upgradeParity.consensus.unknown'
defaultMessage='Your current Parity version is capable of handling the network requirements.'
/>
</div>
<FormattedMessage
id='upgradeParity.consensus.unknown'
defaultMessage='Your current Parity version is capable of handling the network requirements.'
/>
);
}
@ -275,4 +318,20 @@ export default class UpgradeParity extends Component {
return `${version.major}.${version.minor}.${version.patch}-${track}`;
}
onClose = () => {
this.props.store.closeModal();
}
onDone = () => {
if (this.props.store.error) {
this.onClose();
} else {
window.location.reload();
}
}
onUpgrade = () => {
this.props.store.upgradeNow();
}
}

View File

@ -15,19 +15,26 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Form, Input } from '~/ui';
import { nodeOrStringProptype } from '~/util/proptypes';
export default class QueryCode extends Component {
static propTypes = {
receiver: PropTypes.string.isRequired,
hint: PropTypes.string,
hint: nodeOrStringProptype(),
isCodeValid: PropTypes.bool.isRequired,
setCode: PropTypes.func.isRequired
}
static defaultProps = {
hint: 'Enter the code you received.'
hint: (
<FormattedMessage
id='verification.code.hint'
defaultMessage='Enter the code you received.'
/>
)
}
render () {
@ -37,9 +44,23 @@ export default class QueryCode extends Component {
<Form>
<p>The verification code has been sent to { receiver }.</p>
<Input
label={ 'verification code' }
label={
<FormattedMessage
id='verification.code.label'
defaultMessage='verification code'
/>
}
hint={ hint }
error={ isCodeValid ? null : 'invalid code' }
error={
isCodeValid
? null
: (
<FormattedMessage
id='verification.code.error'
defaultMessage='invalid code'
/>
)
}
onChange={ this.onChange }
onSubmit={ this.onSubmit }
/>

View File

@ -22,7 +22,7 @@ import { observable } from 'mobx';
import DoneIcon from 'material-ui/svg-icons/action/done-all';
import CancelIcon from 'material-ui/svg-icons/content/clear';
import { Button, IdentityIcon, Modal } from '~/ui';
import { Button, IdentityIcon, Portal } from '~/ui';
import RadioButtons from '~/ui/Form/RadioButtons';
import SMSVerificationStore from './sms-store';
@ -30,17 +30,72 @@ import EmailVerificationStore from './email-store';
import styles from './verification.css';
const methods = {
const METHODS = {
sms: {
label: 'SMS Verification', key: 0, value: 'sms',
description: (<p className={ styles.noSpacing }>It will be stored on the blockchain that you control a phone number (not <em>which</em>).</p>)
label: (
<FormattedMessage
id='verification.types.sms.label'
defaultMessage='SMS Verification'
/>
),
key: 0,
value: 'sms',
description: (
<p className={ styles.noSpacing }>
<FormattedMessage
id='verification.types.sms.description'
defaultMessage='It will be stored on the blockchain that you control a phone number (not <em>which</em>).'
/>
</p>
)
},
email: {
label: 'E-mail Verification', key: 1, value: 'email',
description: (<p className={ styles.noSpacing }>The hash of the e-mail address you prove control over will be stored on the blockchain.</p>)
label: (
<FormattedMessage
id='verification.types.email.label'
defaultMessage='E-mail Verification'
/>
),
key: 1,
value: 'email',
description: (
<p className={ styles.noSpacing }>
<FormattedMessage
id='verification.types.email.description'
defaultMessage='The hash of the e-mail address you prove control over will be stored on the blockchain.'
/>
</p>
)
}
};
const STEPS = [
<FormattedMessage
id='verification.steps.method'
defaultMessage='Method'
/>,
<FormattedMessage
id='verification.steps.data'
defaultMessage='Enter Data'
/>,
<FormattedMessage
id='verification.steps.request'
defaultMessage='Request'
/>,
<FormattedMessage
id='verification.steps.code'
defaultMessage='Enter Code'
/>,
<FormattedMessage
id='verification.steps.confirm'
defaultMessage='Confirm'
/>,
<FormattedMessage
id='verification.steps.completed'
defaultMessage='Completed'
/>
];
import {
LOADING,
QUERY_DATA,
@ -83,6 +138,7 @@ class Verification extends Component {
@observable store = null;
render () {
const { onClose } = this.props;
const store = this.store;
let phase = 0;
let error = false;
@ -95,16 +151,26 @@ class Verification extends Component {
}
return (
<Modal
actions={ this.renderDialogActions(phase, error, isStepValid) }
current={ phase }
steps={ ['Method', 'Enter Data', 'Request', 'Enter Code', 'Confirm', 'Done!'] }
title='verify your account'
visible
waiting={ error ? [] : [ 2, 4 ] }
<Portal
activeStep={ phase }
busySteps={
error
? []
: [ 2, 4 ]
}
buttons={ this.renderDialogActions(phase, error, isStepValid) }
onClose={ onClose }
open
steps={ STEPS }
title={
<FormattedMessage
id='verification.title'
defaultMessage='verify your account'
/>
}
>
{ this.renderStep(phase, error) }
</Modal>
</Portal>
);
}
@ -112,32 +178,40 @@ class Verification extends Component {
const { account, onClose } = this.props;
const store = this.store;
const cancel = (
const cancelButton = (
<Button
icon={ <CancelIcon /> }
key='cancel'
label='Cancel'
label={
<FormattedMessage
id='verification.button.cancel'
defaultMessage='Cancel'
/>
}
onClick={ onClose }
/>
);
if (error) {
return (<div>{ cancel }</div>);
return cancelButton;
}
if (phase === 5) {
return (
<div>
{ cancel }
<Button
disabled={ !isStepValid }
icon={ <DoneIcon /> }
key='done'
label='Done'
onClick={ onClose }
/>
</div>
);
return [
cancelButton,
<Button
disabled={ !isStepValid }
icon={ <DoneIcon /> }
key='done'
label={
<FormattedMessage
id='verification.button.done'
defaultMessage='Done'
/>
}
onClick={ onClose }
/>
];
}
let action = () => {};
@ -164,23 +238,26 @@ class Verification extends Component {
break;
}
return (
<div>
{ cancel }
<Button
disabled={ !isStepValid }
icon={
<IdentityIcon
address={ account }
button
/>
}
key='next'
label='Next'
onClick={ action }
/>
</div>
);
return [
cancelButton,
<Button
disabled={ !isStepValid }
icon={
<IdentityIcon
address={ account }
button
/>
}
key='next'
label={
<FormattedMessage
id='verification.button.next'
defaultMessage='Next'
/>
}
onClick={ action }
/>
];
}
renderStep (phase, error) {
@ -193,7 +270,7 @@ class Verification extends Component {
const { method } = this.state;
if (phase === 0) {
const values = Object.values(methods);
const values = Object.values(METHODS);
const value = values.findIndex((v) => v.value === method);
return (
@ -215,7 +292,14 @@ class Verification extends Component {
switch (phase) {
case 1:
if (step === LOADING) {
return (<p>Loading verification data.</p>);
return (
<p>
<FormattedMessage
id='verification.loading'
defaultMessage='Loading verification data.'
/>
</p>
);
}
const { setConsentGiven } = this.store;
@ -226,17 +310,24 @@ class Verification extends Component {
key: 'number',
label: (
<FormattedMessage
id='ui.verification.gatherData.phoneNumber.label'
id='verification.gatherData.phoneNumber.label'
defaultMessage='phone number in international format'
/>
),
hint: (
<FormattedMessage
id='ui.verification.gatherData.phoneNumber.hint'
id='verification.gatherData.phoneNumber.hint'
defaultMessage='the SMS will be sent to this number'
/>
),
error: this.store.isNumberValid ? null : 'invalid number',
error: this.store.isNumberValid
? null
: (
<FormattedMessage
id='verification.gatherDate.phoneNumber.error'
defaultMessage='invalid number'
/>
),
onChange: this.store.setNumber
});
} else if (method === 'email') {
@ -244,17 +335,24 @@ class Verification extends Component {
key: 'email',
label: (
<FormattedMessage
id='ui.verification.gatherData.email.label'
id='verification.gatherData.email.label'
defaultMessage='e-mail address'
/>
),
hint: (
<FormattedMessage
id='ui.verification.gatherData.email.hint'
id='verification.gatherData.email.hint'
defaultMessage='the code will be sent to this address'
/>
),
error: this.store.isEmailValid ? null : 'invalid e-mail',
error: this.store.isEmailValid
? null
: (
<FormattedMessage
id='verification.gatherDate.email.error'
defaultMessage='invalid e-mail'
/>
),
onChange: this.store.setEmail
});
}
@ -286,10 +384,20 @@ class Verification extends Component {
if (method === 'sms') {
receiver = this.store.number;
hint = 'Enter the code you received via SMS.';
hint = (
<FormattedMessage
id='verification.sms.enterCode'
defaultMessage='Enter the code you received via SMS.'
/>
);
} else if (method === 'email') {
receiver = this.store.email;
hint = 'Enter the code you received via e-mail.';
hint = (
<FormattedMessage
id='verification.email.enterCode'
defaultMessage='Enter the code you received via e-mail.'
/>
);
}
return (
<QueryCode

View File

@ -20,11 +20,8 @@ import { connect } from 'react-redux';
import { observer } from 'mobx-react';
import { pick } from 'lodash';
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, Input, InputAddress, AddressSelect } from '~/ui';
import { BusyStep, AddressSelect, Button, Form, TypedInput, Input, InputAddress, Portal, TxHash } from '~/ui';
import { CancelIcon, DoneIcon, NextIcon } from '~/ui/Icons';
import { fromWei } from '~/api/util/wei';
import WalletSettingsStore from './walletSettingsStore.js';
@ -50,8 +47,9 @@ class WalletSettings extends Component {
if (rejected) {
return (
<Modal
visible
<Portal
onClose={ this.onClose }
open
title={
<FormattedMessage
id='walletSettings.rejected.title'
@ -74,20 +72,21 @@ class WalletSettings extends Component {
/>
}
/>
</Modal>
</Portal>
);
}
return (
<Modal
visible
actions={ this.renderDialogActions() }
current={ stage }
steps={ steps.map((s) => s.title) }
waiting={ waiting }
<Portal
activeStep={ stage }
busySteps={ waiting }
buttons={ this.renderDialogActions() }
onClose={ this.onClose }
open
steps={ steps.map((step) => step.title) }
>
{ this.renderPage() }
</Modal>
</Portal>
);
}
@ -106,11 +105,26 @@ class WalletSettings extends Component {
const key = req.id;
if (req.txhash) {
return (<TxHash key={ key } hash={ req.txhash } />);
return (
<TxHash
key={ key }
hash={ req.txhash }
/>
);
}
if (req.rejected) {
return (<p key={ key }>The transaction #{parseInt(key, 16)} has been rejected</p>);
return (
<p key={ key }>
<FormattedMessage
id='walletSettings.rejected'
defaultMessage='The transaction #{txid} has been rejected'
values={ {
txid: parseInt(key, 16)
} }
/>
</p>
);
}
})
}
@ -291,9 +305,7 @@ class WalletSettings extends Component {
<p>
<FormattedMessage
id='walletSettings.changes.overview'
defaultMessage={
`You are about to make the following modifications`
}
defaultMessage='You are about to make the following modifications'
/>
</p>
{ modifications }
@ -323,12 +335,21 @@ class WalletSettings extends Component {
case 'require':
return (
<div className={ styles.change }>
<div className={ styles.label }>Change Required Owners</div>
<div className={ styles.label }>
<FormattedMessage
id='walletSettings.ownersChange.title'
defaultMessage='Change Required Owners'
/>
</div>
<div>
<span> from </span>
<code> { change.initial.toNumber() }</code>
<span> to </span>
<code> { change.value.toNumber() }</code>
<FormattedMessage
id='walletSettings.ownersChange.details'
defaultMessage=' from {from} to {to} '
values={ {
from: <code>change.initial.toNumber()</code>,
to: <code>change.value.toNumber()</code>
} }
/>
</div>
</div>
);
@ -336,7 +357,12 @@ class WalletSettings extends Component {
case 'add_owner':
return (
<div className={ [ styles.change, styles.add ].join(' ') }>
<div className={ styles.label }>Add Owner</div>
<div className={ styles.label }>
<FormattedMessage
id='walletSettings.addOwner.title'
defaultMessage='Add Owner'
/>
</div>
<div>
<InputAddress
disabled
@ -350,7 +376,12 @@ class WalletSettings extends Component {
case 'remove_owner':
return (
<div className={ [ styles.change, styles.remove ].join(' ') }>
<div className={ styles.label }>Remove Owner</div>
<div className={ styles.label }>
<FormattedMessage
id='walletSettings.removeOwner.title'
defaultMessage='Remove Owner'
/>
</div>
<div>
<InputAddress
disabled
@ -364,37 +395,56 @@ class WalletSettings extends Component {
}
renderDialogActions () {
const { onClose } = this.props;
const { step, hasErrors, rejected, onNext, send, done } = this.store;
const cancelBtn = (
<Button
icon={ <ContentClear /> }
label='Cancel'
onClick={ onClose }
icon={ <CancelIcon /> }
label={
<FormattedMessage
id='walletSettings.buttons.cancel'
defaultMessage='Cancel'
/>
}
onClick={ this.onClose }
/>
);
const closeBtn = (
<Button
icon={ <ContentClear /> }
label='Close'
onClick={ onClose }
icon={ <CancelIcon /> }
label={
<FormattedMessage
id='walletSettings.buttons.close'
defaultMessage='Close'
/>
}
onClick={ this.onClose }
/>
);
const sendingBtn = (
<Button
icon={ <ActionDone /> }
label='Sending...'
icon={ <DoneIcon /> }
label={
<FormattedMessage
id='walletSettings.buttons.sending'
defaultMessage='Sending...'
/>
}
disabled
/>
);
const nextBtn = (
<Button
icon={ <NavigationArrowForward /> }
label='Next'
icon={ <NextIcon /> }
label={
<FormattedMessage
id='walletSettings.buttons.next'
defaultMessage='Next'
/>
}
onClick={ onNext }
disabled={ hasErrors }
/>
@ -402,8 +452,13 @@ class WalletSettings extends Component {
const sendBtn = (
<Button
icon={ <NavigationArrowForward /> }
label='Send'
icon={ <NextIcon /> }
label={
<FormattedMessage
id='walletSettings.buttons.send'
defaultMessage='Send'
/>
}
onClick={ send }
disabled={ hasErrors }
/>
@ -415,7 +470,9 @@ class WalletSettings extends Component {
switch (step) {
case 'SENDING':
return done ? [ closeBtn ] : [ closeBtn, sendingBtn ];
return done
? [ closeBtn ]
: [ closeBtn, sendingBtn ];
case 'CONFIRMATION':
const { changes } = this.store;
@ -431,6 +488,10 @@ class WalletSettings extends Component {
return [ cancelBtn, nextBtn ];
}
}
onClose = () => {
this.props.onClose();
}
}
function mapStateToProps (initState, initProps) {

View File

@ -16,12 +16,13 @@
import React, { Component, PropTypes } from 'react';
import Dropzone from 'react-dropzone';
import FileUploadIcon from 'material-ui/svg-icons/file/file-upload';
import ContentClear from 'material-ui/svg-icons/content/clear';
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import { FormattedMessage } from 'react-intl';
import { nodeOrStringProptype } from '~/util/proptypes';
import Button from '../../Button';
import Modal from '../../Modal';
import { CancelIcon, DoneIcon, FileUploadIcon } from '../../Icons';
import Portal from '../../Portal';
import styles from './import.css';
@ -37,14 +38,19 @@ const initialState = {
export default class ActionbarImport extends Component {
static propTypes = {
className: PropTypes.string,
onConfirm: PropTypes.func.isRequired,
renderValidation: PropTypes.func,
className: PropTypes.string,
title: PropTypes.string
title: nodeOrStringProptype()
};
static defaultProps = {
title: 'Import from a file'
title: (
<FormattedMessage
id='ui.actionbar.import.title'
defaultMessage='Import from a file'
/>
)
};
state = Object.assign({}, initialState);
@ -57,7 +63,12 @@ export default class ActionbarImport extends Component {
<Button
className={ className }
icon={ <FileUploadIcon /> }
label='import'
label={
<FormattedMessage
id='ui.actiobar.import.button.import'
defaultMessage='import'
/>
}
onClick={ this.onOpenModal }
/>
{ this.renderModal() }
@ -73,20 +84,38 @@ export default class ActionbarImport extends Component {
return null;
}
const hasSteps = typeof renderValidation === 'function';
const steps = hasSteps ? [ 'select a file', error ? 'error' : 'validate' ] : null;
const steps = typeof renderValidation === 'function'
? [
<FormattedMessage
id='ui.actiobar.import.step.select'
defaultMessage='select a file'
/>,
error
? (
<FormattedMessage
id='ui.actiobar.import.step.error'
defaultMessage='error'
/>
)
: (
<FormattedMessage
id='ui.actiobar.import.step.validate'
defaultMessage='validate'
/>)
]
: null;
return (
<Modal
actions={ this.renderActions() }
title={ title }
<Portal
activeStep={ step }
buttons={ this.renderActions() }
onClose={ this.onCloseModal }
open
steps={ steps }
current={ step }
visible
title={ title }
>
{ this.renderBody() }
</Modal>
</Portal>
);
}
@ -95,9 +124,14 @@ export default class ActionbarImport extends Component {
const cancelBtn = (
<Button
icon={ <CancelIcon /> }
key='cancel'
label='Cancel'
icon={ <ContentClear /> }
label={
<FormattedMessage
id='ui.actiobar.import.button.cancel'
defaultMessage='Cancel'
/>
}
onClick={ this.onCloseModal }
/>
);
@ -109,9 +143,14 @@ export default class ActionbarImport extends Component {
if (validate) {
const confirmBtn = (
<Button
icon={ <DoneIcon /> }
key='confirm'
label='Confirm'
icon={ <ActionDoneAll /> }
label={
<FormattedMessage
id='ui.actiobar.import.button.confirm'
defaultMessage='Confirm'
/>
}
onClick={ this.onConfirm }
/>
);
@ -128,7 +167,15 @@ export default class ActionbarImport extends Component {
if (error) {
return (
<div>
<p>An error occured: { errorText }</p>
<p>
<FormattedMessage
id='ui.actiobar.import.error'
defaultMessage='An error occured: {errorText}'
values={ {
errorText
} }
/>
</p>
</div>
);
}
@ -148,7 +195,12 @@ export default class ActionbarImport extends Component {
multiple={ false }
className={ styles.importZone }
>
<div>Drop a file here, or click to select a file to upload.</div>
<div>
<FormattedMessage
id='ui.actiobar.import.dropzone'
defaultMessage='Drop a file here, or click to select a file to upload.'
/>
</div>
</Dropzone>
</div>
);
@ -160,7 +212,10 @@ export default class ActionbarImport extends Component {
return (
<div>
<p className={ styles.desc }>
Confirm that this is what was intended to import.
<FormattedMessage
id='ui.actiobar.import.confirm'
defaultMessage='Confirm that this is what was intended to import.'
/>
</p>
<div>
{ validationBody }

View File

@ -28,6 +28,8 @@ export DashboardIcon from 'material-ui/svg-icons/action/dashboard';
export DeleteIcon from 'material-ui/svg-icons/action/delete';
export DoneIcon from 'material-ui/svg-icons/action/done-all';
export EditIcon from 'material-ui/svg-icons/content/create';
export ErrorIcon from 'material-ui/svg-icons/alert/error';
export FileUploadIcon from 'material-ui/svg-icons/file/file-upload';
export FingerprintIcon from 'material-ui/svg-icons/action/fingerprint';
export KeyIcon from 'material-ui/svg-icons/communication/vpn-key';
export LinkIcon from 'material-ui/svg-icons/content/link';
@ -44,6 +46,8 @@ export StarCircleIcon from 'material-ui/svg-icons/action/stars';
export StarIcon from 'material-ui/svg-icons/toggle/star';
export StarOutlineIcon from 'material-ui/svg-icons/toggle/star-border';
export UnlockedIcon from 'material-ui/svg-icons/action/lock-open';
export UpdateIcon from 'material-ui/svg-icons/action/system-update-alt';
export UpdateWaitIcon from 'material-ui/svg-icons/action/update';
export VerifyIcon from 'material-ui/svg-icons/action/verified-user';
export VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
export VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';

View File

@ -17,8 +17,6 @@
import Busy from './Busy';
import Completed from './Completed';
export default from './modal';
export {
Busy,
Completed

View File

@ -1,55 +0,0 @@
/* Copyright 2015-2017 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 <http://www.gnu.org/licenses/>.
*/
.actions {
background: rgba(0, 0, 0, 0.25) !important;
button:not([disabled]) {
color: white !important;
svg {
fill: white !important;
}
}
}
.body {
padding: 0 !important;
}
.dialog {
}
.content {
transform: translate(0px, 0px) !important;
transition: none !important;
&>div {
background: rgba(0, 0, 0, 0.5) !important;
transition: none !important;
}
}
.title {
background: rgba(0, 0, 0, 0.25) !important;
padding: 1em;
}
.overlay {
background: rgba(255, 255, 255, 0.5) !important;
transition: none !important;
}

View File

@ -1,121 +0,0 @@
// Copyright 2015-2017 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 <http://www.gnu.org/licenses/>.
import { Dialog } from 'material-ui';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { nodeOrStringProptype } from '~/util/proptypes';
import Container from '../Container';
import Title from '../Title';
const ACTIONS_STYLE = { borderStyle: 'none' };
const TITLE_STYLE = { borderStyle: 'none' };
const DIALOG_STYLE = { paddingTop: '1px' };
import styles from './modal.css';
class Modal extends Component {
static contextTypes = {
muiTheme: PropTypes.object.isRequired
}
static propTypes = {
actions: PropTypes.node,
busy: PropTypes.bool,
children: PropTypes.node,
className: PropTypes.string,
compact: PropTypes.bool,
current: PropTypes.number,
settings: PropTypes.object.isRequired,
steps: PropTypes.array,
title: nodeOrStringProptype(),
visible: PropTypes.bool.isRequired,
waiting: PropTypes.array
}
componentDidMount () {
const element = ReactDOM.findDOMNode(this.refs.dialog);
if (element) {
element.focus();
}
}
render () {
const { muiTheme } = this.context;
const { actions, busy, children, className, current, compact, settings, steps, title, visible, waiting } = this.props;
const contentStyle = muiTheme.parity.getBackgroundStyle(null, settings.backgroundSeed);
const header = (
<Title
activeStep={ current }
busy={ busy }
busySteps={ waiting }
className={ styles.title }
steps={ steps }
title={ title }
/>
);
const classes = `${styles.dialog} ${className}`;
return (
<Dialog
actions={ actions }
actionsContainerClassName={ styles.actions }
actionsContainerStyle={ ACTIONS_STYLE }
autoDetectWindowHeight={ false }
autoScrollBodyContent
bodyClassName={ styles.body }
className={ classes }
contentClassName={ styles.content }
contentStyle={ contentStyle }
modal
open={ visible }
overlayClassName={ styles.overlay }
overlayStyle={ { transition: 'none' } }
repositionOnUpdate={ false }
style={ DIALOG_STYLE }
title={ header }
titleStyle={ TITLE_STYLE }
>
<Container
compact={ compact }
light
ref='dialog'
style={
{ transition: 'none' }
}
tabIndex={ 0 }
>
{ children }
</Container>
</Dialog>
);
}
}
function mapStateToProps (state) {
const { settings } = state;
return { settings };
}
export default connect(
mapStateToProps,
null
)(Modal);

View File

@ -19,7 +19,6 @@ import React, { Component } from 'react';
import { Button } from '~/ui';
import PlaygroundExample from '~/playground/playgroundExample';
import Modal from '../Modal';
import Portal from './portal';
export default class PortalExample extends Component {
@ -61,14 +60,6 @@ export default class PortalExample extends Component {
<div>
<button onClick={ this.handleOpen(2) }>Open</button>
<Modal
title='Modal'
visible={ open[2] || false }
>
<button onClick={ this.handleOpen(3) }>Open</button>
<button onClick={ this.handleClose }>Close</button>
</Modal>
<Portal
isChildModal
open={ open[3] || false }

View File

@ -71,10 +71,7 @@ export default class Portal extends Component {
}
return (
<ReactPortal
isOpened
onClose={ this.handleClose }
>
<ReactPortal isOpened>
<div
className={ styles.backOverlay }
onClick={ this.handleClose }
@ -159,6 +156,8 @@ export default class Portal extends Component {
if (!hideClose) {
onClose();
}
this.stopEvent(event);
}
handleKeyDown = (event) => {

View File

@ -38,6 +38,8 @@ muiTheme.textField.hintColor = 'rgba(255, 255, 255, 0.5)';
muiTheme.textField.disabledTextColor = muiTheme.textField.textColor;
muiTheme.toolbar = lightTheme.toolbar;
muiTheme.toolbar.backgroundColor = 'transparent';
muiTheme.zIndex.layer = 4000;
muiTheme.zIndex.popover = 4100;
const imageCache = {};

View File

@ -93,7 +93,6 @@ export default class VaultCard extends Component {
address={ address }
center
className={ styles.account }
key={ address }
/>
</Link>
);

View File

@ -39,7 +39,7 @@ export IdentityName from './IdentityName';
export LanguageSelector from './LanguageSelector';
export Loading from './Loading';
export MethodDecoding from './MethodDecoding';
export Modal, { Busy as BusyStep, Completed as CompletedStep } from './Modal';
export { Busy as BusyStep, Completed as CompletedStep } from './Modal';
export muiTheme from './Theme';
export Page from './Page';
export ParityBackground from './ParityBackground';

View File

@ -318,22 +318,26 @@ class Accounts extends Component {
onNewAccountClick = () => {
this.setState({
newDialog: !this.state.newDialog
newDialog: true
});
}
onNewWalletClick = () => {
this.setState({
newWalletDialog: !this.state.newWalletDialog
newWalletDialog: true
});
}
onNewAccountClose = () => {
this.onNewAccountClick();
this.setState({
newDialog: false
});
}
onNewWalletClose = () => {
this.onNewWalletClick();
this.setState({
newWalletDialog: false
});
}
onNewAccountUpdate = () => {

View File

@ -153,7 +153,7 @@ class Address extends Component {
<Button
key='delete'
icon={ <ActionDelete /> }
label='delete address'
label='forget address'
onClick={ this.showDeleteDialog }
/>
];

View File

@ -30,7 +30,7 @@ import { newError } from '~/redux/actions';
import { setVisibleAccounts } from '~/redux/providers/personalActions';
import { EditMeta, ExecuteContract } from '~/modals';
import { Actionbar, Button, Page, Modal } from '~/ui';
import { Actionbar, Button, Page, Portal } from '~/ui';
import Editor from '~/ui/Editor';
import Header from '../Account/Header';
@ -198,10 +198,11 @@ class Contract extends Component {
);
return (
<Modal
actions={ [ cancelBtn ] }
<Portal
buttons={ [ cancelBtn ] }
onClose={ this.closeDetailsDialog }
open
title={ 'contract details' }
visible
>
<div className={ styles.details }>
{ this.renderSource(contract) }
@ -216,7 +217,7 @@ class Contract extends Component {
/>
</div>
</div>
</Modal>
</Portal>
);
}