Merge branch 'master' into delete-accounts

This commit is contained in:
Jaco Greeff
2016-11-22 16:19:40 +01:00
96 changed files with 3392 additions and 230 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "parity.js",
"version": "0.2.56",
"version": "0.2.62",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",

View File

@@ -14,9 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { stringify } from 'querystring';
import React from 'react';
export default (
export const termsOfService = (
<ul>
<li>This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.</li>
<li>We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.</li>
@@ -25,3 +26,18 @@ export default (
<li><i>Parity Technology Limited</i> is registered in England and Wales under company number <code>09760015</code> and complies with the Data Protection Act 1998 (UK). You may contact us via email at <a href={ 'mailto:admin@parity.io' }>admin@parity.io</a>. Our general privacy policy can be found here: <a href={ 'https://ethcore.io/legal.html' }>https://ethcore.io/legal.html</a>.</li>
</ul>
);
export const postToServer = (query) => {
query = stringify(query);
return fetch('https://sms-verification.parity.io/?' + query, {
method: 'POST', mode: 'cors', cache: 'no-store'
})
.then((res) => {
return res.json().then((data) => {
if (res.ok) {
return data.message;
}
throw new Error(data.message || 'unknown error');
});
});
};

View File

@@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { stringify } from 'querystring';
export const checkIfVerified = (contract, account) => {
return contract.instance.certified.call({}, [account]);
};
@@ -35,18 +33,3 @@ export const checkIfRequested = (contract, account) => {
});
});
};
export const postToServer = (query) => {
query = stringify(query);
return fetch('https://sms-verification.parity.io/?' + query, {
method: 'POST', mode: 'cors', cache: 'no-store'
})
.then((res) => {
return res.json().then((data) => {
if (res.ok) {
return data.message;
}
throw new Error(data.message || 'unknown error');
});
});
};

View File

@@ -105,7 +105,7 @@ export function attachInstances () {
])
.then(([registryAddress, netChain]) => {
const registry = api.newContract(abis.registry, registryAddress).instance;
isTest = netChain === 'morden' || netChain === 'testnet';
isTest = ['morden', 'ropsten', 'testnet'].includes(netChain);
console.log(`contract was found at registry=${registryAddress}`);
console.log(`running on ${netChain}, isTest=${isTest}`);

17
js/src/dapps/dappreg.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" href="/parity-logo-black-no-text.png" type="image/png">
<title>Dapp Registry</title>
</head>
<body>
<div id="container"></div>
<script src="vendor.js"></script>
<script src="commons.js"></script>
<script src="/parity-utils/parity.js"></script>
<script src="dappreg.js"></script>
</body>
</html>

35
js/src/dapps/dappreg.js Normal file
View File

@@ -0,0 +1,35 @@
// 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 React from 'react';
import ReactDOM from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import { useStrict } from 'mobx';
injectTapEventPlugin();
useStrict(true);
import Application from './dappreg/Application';
import '../../assets/fonts/Roboto/font.css';
import '../../assets/fonts/RobotoMono/font.css';
import './style.css';
import './dappreg.html';
ReactDOM.render(
<Application />,
document.querySelector('#container')
);

View File

@@ -0,0 +1,58 @@
/* 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/>.
*/
.body {
color: #333;
background: #eee;
padding: 4.5em 0;
text-align: center;
}
.apps {
background: #fff;
border-radius: 0.5em;
margin: 0 auto;
max-width: 980px;
padding: 1.5em;
text-align: left;
}
.footer {
font-size: 0.75em;
margin: 1em;
padding: 1.5em;
text-align: center;
}
.header {
background: #44e;
border-radius: 0 0 0.25em 0.25em;
color: #fff;
left: 0;
padding: 1em;
position: fixed;
right: 0;
top: 0;
z-index: 25;
}
.loading {
text-align: center;
padding-top: 5em;
font-size: 2em;
color: #999;
}

View File

@@ -0,0 +1,64 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import DappsStore from '../dappsStore';
import ButtonBar from '../ButtonBar';
import Dapp from '../Dapp';
import ModalDelete from '../ModalDelete';
import ModalRegister from '../ModalRegister';
import ModalUpdate from '../ModalUpdate';
import SelectDapp from '../SelectDapp';
import Warning from '../Warning';
import styles from './application.css';
@observer
export default class Application extends Component {
dappsStore = DappsStore.instance();
render () {
if (this.dappsStore.isLoading) {
return (
<div className={ styles.loading }>
Loading application
</div>
);
}
return (
<div className={ styles.body }>
<div className={ styles.header }>
DAPP REGISTRY, a global view of distributed applications available on the network. Putting the puzzle together.
</div>
<div className={ styles.apps }>
<SelectDapp />
<ButtonBar />
<Dapp />
</div>
<div className={ styles.footer }>
{ this.dappsStore.count } applications registered, { this.dappsStore.ownedCount } owned by user
</div>
<Warning />
<ModalDelete />
<ModalRegister />
<ModalUpdate />
</div>
);
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './application';

View File

@@ -0,0 +1,38 @@
/* 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/>.
*/
.button {
background: #44e;
border: none;
border-radius: 0.25em;
color: #fff;
cursor: pointer;
font-size: 1em;
margin: 1em 0.375em;
opacity: 0.85;
padding: 0.75em 2em;
&[disabled] {
opacity: 0.5;
cursor: default;
background: #aaa;
}
&[data-warning="true"] {
background: #e44;
}
}

View File

@@ -0,0 +1,52 @@
// 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 React, { Component, PropTypes } from 'react';
import styles from './button.css';
export default class Button extends Component {
static propTypes = {
className: PropTypes.string,
disabled: PropTypes.bool,
label: PropTypes.string.isRequired,
warning: PropTypes.bool,
onClick: PropTypes.func.isRequired
}
render () {
const { className, disabled, label, warning } = this.props;
const classes = `${styles.button} ${className}`;
return (
<button
className={ classes }
data-warning={ warning }
disabled={ disabled }
onClick={ this.onClick }>
{ label }
</button>
);
}
onClick = (event) => {
if (this.props.disabled) {
return;
}
this.props.onClick(event);
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './button';

View File

@@ -0,0 +1,21 @@
/* 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/>.
*/
.buttonbar {
text-align: center;
margin: 1em 0 0 0;
}

View File

@@ -0,0 +1,101 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import DappsStore from '../dappsStore';
import ModalStore from '../modalStore';
import Button from '../Button';
import styles from './buttonBar.css';
@observer
export default class ButtonBar extends Component {
dappsStore = DappsStore.instance();
modalStore = ModalStore.instance();
render () {
let buttons = [];
if (this.dappsStore.isEditing || this.dappsStore.isNew) {
buttons = [
<Button
key='cancel'
label='Cancel'
warning
onClick={ this.onCancelClick } />,
<Button
key='save'
label={ this.dappsStore.isNew ? 'Register' : 'Update' }
disabled={ !this.dappsStore.canSave }
onClick={ this.onSaveClick } />
];
} else {
buttons = [
<Button
key='delete'
label='Delete'
warning
disabled={ !this.dappsStore.currentApp.isOwner && !this.dappsStore.isContractOwner }
onClick={ this.onDeleteClick } />,
<Button
key='edit'
label='Edit'
disabled={ !this.dappsStore.currentApp.isOwner }
onClick={ this.onEditClick } />,
<Button
key='new'
label='New'
onClick={ this.onNewClick } />
];
}
return (
<div className={ styles.buttonbar }>
{ buttons }
</div>
);
}
onCancelClick = () => {
if (this.dappsStore.isEditing) {
this.dappsStore.setEditing(false);
} else {
this.dappsStore.setNew(false);
}
}
onDeleteClick = () => {
this.modalStore.showDelete();
}
onEditClick = () => {
this.dappsStore.setEditing(true);
}
onNewClick = () => {
this.dappsStore.setNew(true);
}
onSaveClick = () => {
if (this.dappsStore.isEditing) {
this.modalStore.showUpdate();
} else {
this.modalStore.showRegister();
}
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './buttonBar';

View File

@@ -0,0 +1,19 @@
/* 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/>.
*/
.app {
}

View File

@@ -0,0 +1,162 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import { api } from '../parity';
import DappsStore from '../dappsStore';
import Input from '../Input';
import SelectAccount from '../SelectAccount';
import styles from './dapp.css';
@observer
export default class Dapp extends Component {
dappsStore = DappsStore.instance();
render () {
const app = this.dappsStore.isNew || this.dappsStore.isEditing
? this.dappsStore.wipApp
: this.dappsStore.currentApp;
return (
<div className={ styles.app }>
{ this.dappsStore.isNew ? this.renderOwnerSelect(app) : this.renderOwnerStatic(app) }
{ this.renderInputs(app) }
</div>
);
}
renderInputs (app) {
if (this.dappsStore.isNew) {
return null;
}
return [
this.renderHashInput(app, 'image', 'Image hash, as generated by Githubhint', true),
this.renderHashInput(app, 'manifest', 'Manifest hash, as generated by Githubhint'),
this.renderHashInput(app, 'content', 'Content hash, as generated by Githubhint')
];
}
renderOwnerSelect (app) {
const overlayImage = (
<img
className={ styles.overlayImage }
src={ api.util.createIdentityImg(this.dappsStore.currentAccount.address, 4) } />
);
return (
<Input
hint={ this.dappsStore.currentAccount.address }
label='Owner, select the application owner and editor'
overlay={ overlayImage }>
<SelectAccount />
</Input>
);
}
renderOwnerStatic (app) {
const overlayImage = (
<img
className={ styles.overlayImage }
src={ api.util.createIdentityImg(app.owner, 4) } />
);
return (
<Input
hint={ app.owner }
label='Owner, the application owner and editor'
overlay={ overlayImage }>
<input value={ app.ownerName } readOnly />
</Input>
);
}
renderHashInput (app, type, label, withImage = false) {
const onChange = (event) => this.onChangeHash(event, type);
const hash = app[`${type}Hash`];
let overlayImage = null;
if (withImage && hash) {
overlayImage = (
<img
className={ styles.overlayImage }
src={ `/api/content/${hash.substr(2)}` } />
);
}
return (
<Input
hint={ app[`${type}Error`] || app[`${type}Url`] || '...' }
label={ label }
key={ `${type}Edit` }
overlay={ overlayImage }>
<input
value={ app[`${type}Hash`] || '' }
data-dirty={ app[`${type}Changed`] }
data-error={ !!app[`${type}Error`] }
readOnly={ !this.dappsStore.isEditing && !this.dappsStore.isNew }
onChange={ onChange } />
</Input>
);
}
onChangeHash (event, type) {
if (!this.dappsStore.isNew && !this.dappsStore.isEditing) {
return;
}
const hash = event.target.value;
let changed = false;
let url = null;
if (this.dappsStore.isNew) {
if (hash && hash.length) {
changed = true;
}
} else {
if (this.dappsStore.currentApp[`${type}Hash`] !== hash) {
changed = true;
} else {
url = this.dappsStore.currentApp[`${type}Url`];
}
}
this.dappsStore.editWip({
[`${type}Changed`]: changed,
[`${type}Error`]: null,
[`${type}Hash`]: hash,
[`${type}Url`]: changed ? 'Resolving url from hash' : url
});
if (changed) {
if (hash.length) {
this.dappsStore
.lookupHash(hash)
.then((url) => {
this.dappsStore.editWip({
[`${type}Error`]: url ? null : 'Unable to resolve url',
[`${type}Url`]: url
});
});
} else {
this.dappsStore.editWip({ [`${type}Url`]: null });
}
}
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './dapp';

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './input';

View File

@@ -0,0 +1,92 @@
/* 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/>.
*/
.input {
position: relative;
input, select {
background: rgba(255, 255, 255, 0.85);
border: 4px solid rgba(223, 223, 223, 0.85);
border-radius: 0.25em;
box-sizing: border-box;
color: #333;
font-size: 1em;
margin: 0.25em 0 0.25em 0;
padding: 0.5em 0.5em 1.5em 0.5em;
width: 100%;
}
input {
padding-bottom: 1.5em;
&[data-dirty="true"] {
background: rgba(255, 255, 203, 0.85);
border-color: rgba(203, 203, 151, 0.85);
}
&[data-error="true"] {
background: rgba(255, 223, 223, 0.85) !important;
border-color: rgba(223, 191, 191, 0.85) !important;
}
&[readonly] {
background: rgba(239, 239, 239, 0.85);
border-color: rgba(223, 223, 223, 0.85);
}
}
label {
color: #888;
display: block;
font-size: 0.75em;
margin-top: 1.5em;
}
select {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
height: 58px;
&[disabled] {
background: rgba(239, 239, 239, 0.85);
border-color: rgba(223, 223, 223, 0.85);
}
}
.hint {
color: #888;
display: block;
font-size: 0.75em;
position: absolute;
right: 52px;
text-align: right;
top: 52px;
}
.overlay {
right: 10px;
position: absolute;
top: 30px;
img {
border-radius: 50%;
height: 32px;
width: 32px;
}
}
}

View File

@@ -0,0 +1,47 @@
// 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 React, { Component, PropTypes } from 'react';
import styles from './input.css';
export default class Input extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
hint: PropTypes.string,
label: PropTypes.string.isRequired,
overlay: PropTypes.node
}
render () {
const { children, hint, label, overlay } = this.props;
return (
<div className={ styles.input }>
<label>
{ label }
</label>
{ children }
<div className={ styles.hint }>
{ hint }
</div>
<div className={ styles.overlay }>
{ overlay }
</div>
</div>
);
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './modal';

View File

@@ -0,0 +1,116 @@
/* 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/>.
*/
.modal {
.body {
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
text-align: center;
z-index: 50;
.dialog {
background: #fff;
border-radius: 0 0 0.25em 0.25em;
margin: 0 auto;
max-width: 840px;
text-align: left;
.content {
line-height: 1.5em;
padding: 2em;
text-align: center;
.section {
margin: 0;
padding: 0;
&.error {
color: #f44;
}
}
.section+.section {
margin-top: 1em;
}
}
.footer {
padding: 0.5em 1.625em;
text-align: right;
}
.header {
background: #44e;
color: #fff;
opacity: 0.85;
padding: 1em;
&.error {
background: #e44;
}
}
}
}
.overlay {
background: rgba(204, 204, 204, 0.7);
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
z-index: 49;
}
}
.account {
div {
display: inline-block;
vertical-align: top;
}
img {
border-radius: 50%;
margin-right: 0.5em;
}
}
.hint {
display: block !important;
color: #888;
font-size: 0.75em;
margin-top: -0.5em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.center {
text-align: center;
}
.heading {
color: #888;
font-size: 0.75em;
}
.light {
color: #888;
}

View File

@@ -0,0 +1,66 @@
// 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 React, { Component, PropTypes } from 'react';
import styles from './modal.css';
export default class Modal extends Component {
static propTypes = {
buttons: PropTypes.node,
children: PropTypes.node,
error: PropTypes.object,
header: PropTypes.string
}
render () {
const { children, buttons, error, header } = this.props;
return (
<div className={ styles.modal }>
<div className={ styles.overlay } />
<div className={ styles.body }>
<div className={ styles.dialog }>
<div className={ `${styles.header} ${error ? styles.error : ''}` }>
{ header }
</div>
<div className={ styles.content }>
{ error ? this.renderError() : children }
</div>
<div className={ styles.footer }>
{ buttons }
</div>
</div>
</div>
</div>
);
}
renderError () {
const { error } = this.props;
return (
<div>
<div className={ styles.section }>
Your operation failed to complete sucessfully. The following error was returned:
</div>
<div className={ `${styles.section} ${styles.error}` }>
{ error.toString() }
</div>
</div>
);
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './modalDelete';

View File

@@ -0,0 +1,159 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import { api } from '../parity';
import DappsStore from '../dappsStore';
import ModalStore from '../modalStore';
import Button from '../Button';
import Modal from '../Modal';
import styles from '../Modal/modal.css';
const HEADERS = [
'Error During Deletion',
'Confirm Application Deletion',
'Waiting for Signer Confirmation',
'Waiting for Transaction Receipt',
'Deletion Completed'
];
const STEP_ERROR = 0;
const STEP_CONFIRM = 1;
const STEP_SIGNER = 2;
const STEP_TXRECEIPT = 3;
const STEP_DONE = 4;
@observer
export default class ModalDelete extends Component {
dappsStore = DappsStore.instance();
modalStore = ModalStore.instance();
render () {
if (!this.modalStore.showingDelete) {
return null;
}
return (
<Modal
buttons={ this.renderButtons() }
error={ this.modalStore.errorDelete }
header={ HEADERS[this.modalStore.stepDelete] }>
{ this.renderStep() }
</Modal>
);
}
renderButtons () {
switch (this.modalStore.stepDelete) {
case STEP_ERROR:
case STEP_DONE:
return [
<Button
key='close'
label='Close'
onClick={ this.onClickClose } />
];
case STEP_CONFIRM:
return [
<Button
key='cancel'
label='No, Cancel'
onClick={ this.onClickClose } />,
<Button
key='delete'
label='Yes, Delete'
warning
onClick={ this.onClickYes } />
];
default:
return null;
}
}
renderStep () {
switch (this.modalStore.stepDelete) {
case STEP_CONFIRM:
return this.renderStepConfirm();
case STEP_SIGNER:
return this.renderStepWait('Waiting for transaction confirmation in the Parity secure signer');
case STEP_TXRECEIPT:
return this.renderStepWait('Waiting for the transaction receipt from the network');
case STEP_DONE:
return this.renderStepCompleted();
default:
return null;
}
}
renderStepCompleted () {
return (
<div>
<div className={ styles.section }>
Your application has been removed from the registry.
</div>
</div>
);
}
renderStepConfirm () {
return (
<div>
<div className={ styles.section }>
You are about to remove a distributed application from the registry, the details of this application is given below. Removal does not return any fees, however the application will not be available to users anymore.
</div>
<div className={ styles.section }>
<div className={ styles.heading }>
Owner account
</div>
<div className={ styles.account }>
<img src={ api.util.createIdentityImg(this.dappsStore.currentApp.owner, 3) } />
<div>{ this.dappsStore.currentApp.ownerName }</div>
<div className={ styles.address }>{ this.dappsStore.currentApp.owner }</div>
</div>
</div>
<div className={ styles.section }>
<div className={ styles.heading }>
Application identifier
</div>
<div>
{ this.dappsStore.currentApp.id }
</div>
</div>
</div>
);
}
renderStepWait (waitingFor) {
return (
<div>
<div className={ styles.section }>
{ waitingFor }
</div>
</div>
);
}
onClickClose = () => {
this.modalStore.hideDelete();
}
onClickYes = () => {
this.modalStore.doDelete();
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './modalRegister';

View File

@@ -0,0 +1,159 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import { api } from '../parity';
import DappsStore from '../dappsStore';
import ModalStore from '../modalStore';
import Button from '../Button';
import Modal from '../Modal';
import styles from '../Modal/modal.css';
const HEADERS = [
'Error During Registration',
'Confirm Application Registration',
'Waiting for Signer Confirmation',
'Waiting for Transaction Receipt',
'Registration Completed'
];
const STEP_ERROR = 0;
const STEP_CONFIRM = 1;
const STEP_SIGNER = 2;
const STEP_TXRECEIPT = 3;
const STEP_DONE = 4;
@observer
export default class ModalRegister extends Component {
dappsStore = DappsStore.instance();
modalStore = ModalStore.instance();
render () {
if (!this.modalStore.showingRegister) {
return null;
}
return (
<Modal
buttons={ this.renderButtons() }
error={ this.modalStore.errorRegister }
header={ HEADERS[this.modalStore.stepRegister] }>
{ this.renderStep() }
</Modal>
);
}
renderButtons () {
switch (this.modalStore.stepRegister) {
case STEP_ERROR:
case STEP_DONE:
return [
<Button
key='close'
label='Close'
onClick={ this.onClickClose } />
];
case STEP_CONFIRM:
return [
<Button
key='cancel'
label='No, Cancel'
onClick={ this.onClickClose } />,
<Button
key='register'
label='Yes, Register'
warning
onClick={ this.onClickConfirmYes } />
];
default:
return null;
}
}
renderStep () {
switch (this.modalStore.stepRegister) {
case STEP_CONFIRM:
return this.renderStepConfirm();
case STEP_SIGNER:
return this.renderStepWait('Waiting for transaction confirmation in the Parity secure signer');
case STEP_TXRECEIPT:
return this.renderStepWait('Waiting for the transaction receipt from the network');
case STEP_DONE:
return this.renderStepCompleted();
default:
return null;
}
}
renderStepCompleted () {
return (
<div>
<div className={ styles.section }>
Your application has been registered in the registry.
</div>
</div>
);
}
renderStepConfirm () {
return (
<div>
<div className={ styles.section }>
You are about to register a new distributed application on the network, the details of this application is given below. This will require a non-refundable fee of { api.util.fromWei(this.dappsStore.fee).toFormat(3) }<small>ETH</small>.
</div>
<div className={ styles.section }>
<div className={ styles.heading }>
Selected owner account
</div>
<div className={ styles.account }>
<img src={ api.util.createIdentityImg(this.dappsStore.currentAccount.address, 3) } />
<div>{ this.dappsStore.currentAccount.name }</div>
<div className={ styles.hint }>{ this.dappsStore.currentAccount.address }</div>
</div>
</div>
<div className={ styles.section }>
<div className={ styles.heading }>
Unique assigned application identifier
</div>
<div>
{ this.dappsStore.wipApp.id }
</div>
</div>
</div>
);
}
renderStepWait (waitingFor) {
return (
<div>
<div className={ styles.section }>
{ waitingFor }
</div>
</div>
);
}
onClickClose = () => {
this.modalStore.hideRegister();
}
onClickConfirmYes = () => {
this.modalStore.doRegister();
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './modalUpdate';

View File

@@ -0,0 +1,169 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import DappsStore from '../dappsStore';
import ModalStore from '../modalStore';
import Button from '../Button';
import Modal from '../Modal';
import styles from '../Modal/modal.css';
const HEADERS = [
'Error During Update',
'Confirm Application Update',
'Waiting for Signer Confirmation',
'Waiting for Transaction Receipt',
'Update Completed'
];
const STEP_ERROR = 0;
const STEP_CONFIRM = 1;
const STEP_SIGNER = 2;
const STEP_TXRECEIPT = 3;
const STEP_DONE = 4;
@observer
export default class ModalUpdate extends Component {
dappsStore = DappsStore.instance();
modalStore = ModalStore.instance();
render () {
if (!this.modalStore.showingUpdate) {
return null;
}
return (
<Modal
buttons={ this.renderButtons() }
error={ this.modalStore.errorUpdate }
header={ HEADERS[this.modalStore.stepUpdate] }>
{ this.renderStep() }
</Modal>
);
}
renderButtons () {
switch (this.modalStore.stepUpdate) {
case STEP_ERROR:
case STEP_DONE:
return [
<Button
key='close'
label='Close'
onClick={ this.onClickClose } />
];
case STEP_CONFIRM:
return [
<Button
key='cancel'
label='No, Cancel'
onClick={ this.onClickClose } />,
<Button
key='delete'
label='Yes, Update'
warning
onClick={ this.onClickYes } />
];
default:
return null;
}
}
renderStep () {
switch (this.modalStore.stepUpdate) {
case STEP_CONFIRM:
return this.renderStepConfirm();
case STEP_SIGNER:
return this.renderStepWait('Waiting for transaction confirmation in the Parity secure signer');
case STEP_TXRECEIPT:
return this.renderStepWait('Waiting for the transaction receipt from the network');
case STEP_DONE:
return this.renderStepCompleted();
default:
return null;
}
}
renderStepCompleted () {
return (
<div>
<div className={ styles.section }>
Your application metadata has been updated in the registry.
</div>
</div>
);
}
renderStepConfirm () {
return (
<div>
<div className={ styles.section }>
You are about to update the application details in the registry, the details of these updates are given below. Please note that each update will generate a seperate transaction.
</div>
<div className={ styles.section }>
<div className={ styles.heading }>
Application identifier
</div>
<div>
{ this.dappsStore.wipApp.id }
</div>
</div>
{ this.renderChanges() }
</div>
);
}
renderChanges () {
return ['content', 'image', 'manifest']
.filter((type) => this.dappsStore.wipApp[`${type}Changed`])
.map((type) => {
return (
<div className={ styles.section } key={ `${type}Update` }>
<div className={ styles.heading }>
Updates to { type } hash
</div>
<div>
<div>{ this.dappsStore.wipApp[`${type}Hash`] || '(removed)' }</div>
<div className={ styles.hint }>
{ this.dappsStore.wipApp[`${type}Url`] || 'current url to be removed from registry' }
</div>
</div>
</div>
);
});
}
renderStepWait (waitingFor) {
return (
<div>
<div className={ styles.section }>
{ waitingFor }
</div>
</div>
);
}
onClickClose = () => {
this.modalStore.hideUpdate();
}
onClickYes = () => {
this.modalStore.doUpdate();
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './selectAccount';

View File

@@ -0,0 +1,49 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import DappsStore from '../dappsStore';
@observer
export default class SelectAccount extends Component {
dappsStore = DappsStore.instance();
render () {
return (
<select
value={ this.dappsStore.currentAccount.address }
onChange={ this.onSelect }>
{ this.renderOptions() }
</select>
);
}
renderOptions () {
return this.dappsStore.accounts.map((account) => {
return (
<option value={ account.address } key={ account.address }>
{ account.name }
</option>
);
});
}
onSelect = (event) => {
this.dappsStore.setCurrentAccount(event.target.value);
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './selectDapp';

View File

@@ -0,0 +1,76 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import DappsStore from '../dappsStore';
import Input from '../Input';
@observer
export default class SelectDapp extends Component {
dappsStore = DappsStore.instance();
render () {
if (this.dappsStore.isNew) {
return (
<Input
hint='...'
label='Application Id, the unique assigned identifier'>
<input value={ this.dappsStore.wipApp.id } readOnly />
</Input>
);
}
let overlayImg = null;
if (this.dappsStore.currentApp.imageHash) {
overlayImg = (
<img src={ `/api/content/${this.dappsStore.currentApp.imageHash.substr(2)}` } />
);
}
return (
<Input
hint={ this.dappsStore.currentApp.id }
label='Application, the actual application details to show below'
overlay={ overlayImg }>
<select
disabled={ this.dappsStore.isEditing }
value={ this.dappsStore.currentApp.id }
onChange={ this.onSelect }>
{ this.renderOptions() }
</select>
</Input>
);
}
renderOptions () {
return this.dappsStore.apps.map((app) => {
return (
<option
value={ app.id }
key={ app.id }>
{ app.name }
</option>
);
});
}
onSelect = (event) => {
this.dappsStore.setCurrentApp(event.target.value);
}
}

View File

@@ -0,0 +1,17 @@
// 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/>.
export default from './warning';

View File

@@ -0,0 +1,36 @@
/* 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/>.
*/
.warning {
background: #f44;
border-top-right-radius: 0.25em;
bottom: 0;
color: #fff;
cursor: pointer;
font-size: 0.75em;
left: 0;
line-height: 1.5em;
opacity: 1;
padding: 1.5em;
position: fixed;
max-width: 540px;
z-index: 100;
div+div {
margin-top: 1.5em;
}
}

View File

@@ -0,0 +1,51 @@
// 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 React, { Component } from 'react';
import { observer } from 'mobx-react';
import { api } from '../parity';
import DappsStore from '../dappsStore';
import ModalStore from '../modalStore';
import styles from './warning.css';
@observer
export default class Warning extends Component {
dappsStore = DappsStore.instance();
modalStore = ModalStore.instance();
render () {
if (!this.modalStore.showingWarning) {
return null;
}
return (
<div className={ styles.warning } onClick={ this.onClose }>
<div>
WARNING: Registering a dapp is for developers only. Please ensure you understand the steps needed to develop and deploy applications, should you wish to use this dapp for anything apart from queries.
</div>
<div>
A non-refundable fee of { api.util.fromWei(this.dappsStore.fee).toFormat(3) }<small>ETH</small> is required for any registration.
</div>
</div>
);
}
onClose = () => {
this.modalStore.hideWarning();
}
}

View File

@@ -0,0 +1,482 @@
// 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 BigNumber from 'bignumber.js';
import { action, computed, observable, transaction } from 'mobx';
import * as abis from '../../contracts/abi';
import builtins from '../../views/Dapps/builtin.json';
import { api } from './parity';
let instance = null;
export default class DappsStore {
@observable accounts = [];
@observable addresses = [];
@observable apps = [];
@observable contractOwner = null;
@observable currentAccount = null;
@observable currentApp = null;
@observable count = 0;
@observable fee = new BigNumber(0);
@observable isContractOwner = false;
@observable isEditing = false;
@observable isLoading = true;
@observable isNew = false;
@observable wipApp = null;
_startTime = Date.now();
constructor () {
this._loadDapps();
}
static instance () {
if (!instance) {
instance = new DappsStore();
}
return instance;
}
@computed get canSave () {
const app = this.wipApp;
const hasError = app.contentError || app.imageError || app.manifestError;
const isDirty = this.isNew || app.contentChanged || app.imageChanged || app.manifestChanged;
const isEditMode = this.isEditing || this.isNew;
return isEditMode && isDirty && !hasError;
}
@computed get isCurrentEditable () {
return !!this.accounts.find((account) => account.address === this.currentApp.owner);
}
@computed get ownedCount () {
return (this.apps.filter((app) => app.isOwner) || []).length;
}
@action copyToWip = () => {
let wipApp;
if (this.isNew) {
wipApp = {
id: api.util.sha3(`${this._startTime}_${Date.now()}`),
contentHash: null,
contentUrl: null,
imageHash: null,
imageUrl: null,
manifestHash: null,
manifestUrl: null
};
} else {
const app = this.currentApp;
wipApp = {
id: app.id,
contentHash: app.contentHash,
contentUrl: app.contentUrl,
imageHash: app.imageHash,
imageUrl: app.imageUrl,
manifestHash: app.manifestHash,
manifestUrl: app.manifestUrl,
owner: app.owner,
ownerName: app.ownerName
};
}
this.wipApp = Object.assign(wipApp, {
contentChanged: false,
contentError: null,
imageChanged: false,
imageError: null,
manifestChanged: false,
manifestError: null
});
return this.wipApp;
}
@action editWip = (details) => {
if (this.isNew || this.isEditing) {
transaction(() => {
Object
.keys(details)
.forEach((key) => {
this.wipApp[key] = details[key];
});
});
}
return this.wipApp;
}
@action sortApps = (apps = this.apps) => {
transaction(() => {
const ownApps = apps
.filter((app) => app.isOwner)
.sort((a, b) => a.name.localeCompare(b.name));
const otherApps = apps
.filter((app) => !app.isOwner)
.sort((a, b) => a.name.localeCompare(b.name));
this.apps = ownApps.concat(otherApps);
this.currentApp = this.apps[0];
});
}
@action setApps = (apps) => {
this.sortApps(apps.filter((app) => {
const bnid = new BigNumber(app.id);
return bnid.gt(0);
}));
return this.apps;
}
@action _addApp = (app) => {
transaction(() => {
this.setApps(this.apps.concat([app]));
this.setCurrentApp(app.id);
});
}
@action addApp = (appId, account) => {
this
._loadDapp({
id: appId,
isOwner: true,
name: `- ${appId}`,
owner: account.address,
ownerName: account.name
})
.then(this._addApp);
}
@action refreshApp = (appId) => {
this._loadDapp(this.apps.find((app) => app.id === appId));
}
@action removeApp = (appId) => {
this.setApps(this.apps.filter((app) => app.id !== appId));
}
@action setAppInfo = (app, info) => {
transaction(() => {
Object.keys(info).forEach((key) => {
app[key] = info[key];
});
});
return app;
}
@action setAccounts = (accountsInfo) => {
transaction(() => {
this.addresses = Object
.keys(accountsInfo)
.map((address) => {
const account = accountsInfo[address];
account.address = address;
return account;
});
this.accounts = this.addresses.filter((account) => account.uuid);
this.currentAccount = this.accounts[0];
});
return this.accounts;
}
@action setContractOwner = (contractOwner) => {
transaction(() => {
this.contractOwner = contractOwner;
this.isContractOwner = !!this.accounts.find((account) => account.address === contractOwner);
});
return contractOwner;
}
@action setCurrentApp = (id) => {
this.currentApp = this.apps.find((app) => app.id === id);
return this.currentApp;
}
@action setCurrentAccount = (address) => {
this.currentAccount = this.accounts.find((account) => account.address === address);
return this.currentAccount;
}
@action setCount = (count) => {
this.count = count;
return count;
}
@action setEditing = (mode) => {
transaction(() => {
this.isEditing = mode;
this.copyToWip();
});
return mode;
}
@action setFee = (fee) => {
this.fee = fee;
return fee;
}
@action setLoading = (loading) => {
this.isLoading = loading;
return loading;
}
@action setNew = (mode) => {
transaction(() => {
this.isNew = mode;
this.copyToWip();
});
return mode;
}
lookupHash (hash) {
return this._retrieveUrl(hash);
}
_getCount () {
return this._instanceReg
.count.call()
.then((count) => {
this.setCount(count.toNumber());
})
.catch((error) => {
console.error('Store:getCount', error);
});
}
_getFee () {
return this._instanceReg
.fee.call()
.then(this.setFee)
.catch((error) => {
console.error('Store:getFee', error);
});
}
_getOwner () {
return this._instanceReg
.owner.call()
.then(this.setContractOwner)
.catch((error) => {
console.error('Store:getOwner', error);
});
}
_loadDapps () {
return this._loadRegistry()
.then(() => Promise.all([
this._attachContracts(),
this._loadAccounts()
]))
.then(() => Promise.all([
this._getCount(),
this._getFee(),
this._getOwner()
]))
.then(() => {
const promises = [];
for (let index = 0; index < this.count; index++) {
promises.push(this._instanceReg.at.call({}, [index]));
}
return Promise.all(promises);
})
.then((appsInfo) => {
return Promise.all(
this
.setApps(appsInfo.map(([appId, owner]) => {
const isOwner = !!this.accounts.find((account) => account.address === owner);
const account = this.addresses.find((account) => account.address === owner);
const id = api.util.bytesToHex(appId);
return {
id,
owner,
ownerName: account ? account.name : owner,
isOwner,
name: `- ${id}`
};
}))
.map(this._loadDapp)
);
})
.then(() => {
this.sortApps();
this.setLoading(this.count === 0);
})
.catch((error) => {
console.error('Store:loadDapps', error);
});
}
_loadDapp = (app) => {
return Promise
.all([
this._loadMeta(app.id, 'CONTENT'),
this._loadMeta(app.id, 'IMG'),
this._loadMeta(app.id, 'MANIFEST')
])
.then(([contentHash, imageHash, manifestHash]) => {
return Promise
.all([
this._retrieveUrl(contentHash),
this._retrieveUrl(imageHash),
this._retrieveUrl(manifestHash)
])
.then(([contentUrl, imageUrl, manifestUrl]) => {
return this
._loadManifest(app.id, manifestHash)
.then((manifest) => {
this.setAppInfo(app, {
manifest,
manifestHash,
manifestUrl,
contentHash,
contentUrl,
imageHash,
imageUrl,
name: (manifest && manifest.name) || `- ${app.id}`
});
return app;
});
});
})
.catch((error) => {
console.error('Store:loadDapp', error);
});
}
_loadMeta (appId, key) {
return this._instanceReg
.meta.call({}, [appId, key])
.then((meta) => {
const hash = api.util.bytesToHex(meta);
const bnhash = new BigNumber(hash);
return bnhash.gt(0)
? hash
: null;
})
.catch((error) => {
console.error('Store:loadMeta', error);
return null;
});
}
_loadManifest (appId, manifestHash) {
const builtin = builtins.find((app) => app.id === appId);
if (builtin) {
return Promise.resolve(builtin);
} else if (!manifestHash) {
return Promise.resolve(null);
}
return fetch(`/api/content/${manifestHash.substr(2)}/`, { redirect: 'follow', mode: 'cors' })
.then((response) => {
return response.ok
? response.json()
: null;
})
.catch((error) => {
console.error('Store:loadManifest', error);
return null;
});
}
_retrieveUrl (urlHash) {
if (!urlHash) {
return Promise.resolve(null);
}
return this._instanceGhh
.entries.call({}, [urlHash])
.then(([repo, _commit, owner]) => {
const bnowner = new BigNumber(owner);
if (bnowner.eq(0)) {
return null;
}
const commit = api.util.bytesToHex(_commit);
const bncommit = new BigNumber(commit);
if (bncommit.eq(0)) {
return repo;
} else {
return `https://codeload.github.com/${repo}/zip/${commit.substr(2)}`;
}
})
.catch((error) => {
console.error('Store:retriveUrl', error);
return null;
});
}
_loadAccounts () {
return api.parity
.accounts()
.then(this.setAccounts)
.catch((error) => {
console.error('Store:loadAccounts', error);
});
}
_loadRegistry () {
return api.parity
.registryAddress()
.then((registryAddress) => {
console.log(`the registry was found at ${registryAddress}`);
this._registry = api.newContract(abis.registry, registryAddress).instance;
})
.catch((error) => {
console.error('Store:loadRegistry', error);
});
}
_attachContracts () {
return Promise
.all([
this._registry.getAddress.call({}, [api.util.sha3('dappreg'), 'A']),
this._registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A'])
])
.then(([dappregAddress, ghhAddress]) => {
console.log(`dappreg was found at ${dappregAddress}`);
this._contractReg = api.newContract(abis.dappreg, dappregAddress);
this._instanceReg = this._contractReg.instance;
console.log(`githubhint was found at ${ghhAddress}`);
this._contractGhh = api.newContract(abis.githubhint, ghhAddress);
this._instanceGhh = this._contractGhh.instance;
})
.catch((error) => {
console.error('Store:attachContract', error);
});
}
}

View File

@@ -0,0 +1,266 @@
// 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 { action, observable, transaction } from 'mobx';
import { trackRequest } from './parity';
import DappsStore from './dappsStore';
let instance = null;
export default class ModalStore {
@observable errorDelete = null;
@observable errorRegister = null;
@observable errorUpdate = null;
@observable stepDelete = 0;
@observable stepRegister = 0;
@observable stepUpdate = 0;
@observable showingDelete = false;
@observable showingRegister = false;
@observable showingUpdate = false;
@observable showingWarning = true;
_dappsStore = DappsStore.instance();
static instance () {
if (!instance) {
instance = new ModalStore();
}
return instance;
}
@action setDeleteError (error) {
transaction(() => {
this.setDeleteStep(0);
this.errorDelete = error;
});
}
@action setDeleteStep (step) {
this.stepDelete = step;
}
@action showDelete () {
transaction(() => {
this.setDeleteStep(1);
this.errorDelete = null;
this.showingDelete = true;
});
}
@action hideDelete () {
this.showingDelete = false;
}
@action setRegisterError (error) {
transaction(() => {
this.setRegisterStep(0);
this.errorRegister = error;
});
}
@action setRegisterStep (step) {
this.stepRegister = step;
}
@action showRegister () {
transaction(() => {
this.setRegisterStep(1);
this.errorRegister = null;
this.showingRegister = true;
});
}
@action hideRegister () {
transaction(() => {
this._dappsStore.setEditing(false);
this._dappsStore.setNew(false);
this.showingRegister = false;
});
}
@action setUpdateError (error) {
transaction(() => {
this.setUpdateStep(0);
this.errorUpdate = error;
});
}
@action setUpdateStep (step) {
this.stepUpdate = step;
}
@action showUpdate () {
transaction(() => {
this.setUpdateStep(1);
this.errorUpdate = null;
this.showingUpdate = true;
});
}
@action hideUpdate () {
transaction(() => {
this._dappsStore.setEditing(false);
this._dappsStore.setNew(false);
this.showingUpdate = false;
});
}
@action hideWarning () {
this.showingWarning = false;
}
doDelete () {
this.setDeleteStep(2);
const appId = this._dappsStore.currentApp.id;
const values = [appId];
const options = {
from: this._dappsStore.currentApp.isOwner ? this._dappsStore.currentApp.owner : this._dappsStore.contractOwner
};
console.log('ModalStore:doDelete', `performing deletion for ${appId} from ${options.from}`);
this._dappsStore._instanceReg
.unregister.estimateGas(options, values)
.then((gas) => {
const newGas = gas.mul(1.2);
console.log('ModalStore:doDelete', `gas estimated as ${gas.toFormat(0)}, setting to ${newGas.toFormat(0)}`);
options.gas = newGas.toFixed(0);
const request = this._dappsStore._instanceReg.unregister.postTransaction(options, values);
const statusCallback = (error, status) => {
if (error) {
} else if (status.signerRequestId) {
} else if (status.transactionHash) {
this.setDeleteStep(3);
} else if (status.transactionReceipt) {
this.setDeleteStep(4);
this._dappsStore.removeApp(appId);
}
};
return trackRequest(request, statusCallback);
})
.catch((error) => {
console.error('ModalStore:doDelete', error);
this.setDeleteError(error);
});
}
doRegister () {
this.setRegisterStep(2);
const appId = this._dappsStore.wipApp.id;
const values = [appId];
const options = {
from: this._dappsStore.currentAccount.address,
value: this._dappsStore.fee
};
console.log('ModalStore:doRegister', `performing registration for ${appId} from ${this._dappsStore.currentAccount.address}`);
this._dappsStore._instanceReg
.register.estimateGas(options, values)
.then((gas) => {
const newGas = gas.mul(1.2);
console.log('ModalStore:doRegister', `gas estimated as ${gas.toFormat(0)}, setting to ${newGas.toFormat(0)}`);
options.gas = newGas.toFixed(0);
const request = this._dappsStore._instanceReg.register.postTransaction(options, values);
const statusCallback = (error, status) => {
if (error) {
} else if (status.signerRequestId) {
} else if (status.transactionHash) {
this.setRegisterStep(3);
} else if (status.transactionReceipt) {
this.setRegisterStep(4);
this._dappsStore.addApp(appId, this._dappsStore.currentAccount);
}
};
return trackRequest(request, statusCallback);
})
.catch((error) => {
console.error('ModalStore:doRegister', error);
this.setRegisterError(error);
});
}
doUpdate () {
this.setUpdateStep(2);
const appId = this._dappsStore.wipApp.id;
const options = {
from: this._dappsStore.wipApp.owner
};
const types = {
'content': 'CONTENT',
'image': 'IMG',
'manifest': 'MANIFEST'
};
const values = Object
.keys(types)
.filter((type) => this._dappsStore.wipApp[`${type}Changed`])
.map((type) => {
return [appId, types[type], this._dappsStore.wipApp[`${type}Hash`] || '0x0'];
});
console.log('ModalStore:doUpdate', `performing updates for ${appId} from ${options.from}`);
Promise
.all(values.map((value) => this._dappsStore._instanceReg.setMeta.estimateGas(options, value)))
.then((gas) => {
const newGas = gas.map((gas) => gas.mul(1.2));
gas.forEach((gas, index) => {
console.log('ModalStore:doUpdate', `${values[index][1]} gas estimated as ${gas.toFormat(0)}, setting to ${newGas[index].toFormat(0)}`);
});
const statusCallback = (error, status) => {
if (error) {
} else if (status.signerRequestId) {
} else if (status.transactionHash) {
this.setUpdateStep(3);
} else if (status.transactionReceipt) {
this.setUpdateStep(4);
}
};
return Promise.all(
newGas.map((gas, index) => {
return trackRequest(
this._dappsStore._instanceReg.setMeta.postTransaction(
Object.assign(options, { gas: gas.toFixed(0) }), values[index]
), statusCallback
);
})
);
})
.then(() => {
this._dappsStore.refreshApp(appId);
})
.catch((error) => {
console.error('ModalStore:doUpdate', error);
this.setUpdateError(error);
});
}
}

View File

@@ -0,0 +1,53 @@
// 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/>.
const { api } = window.parity;
function trackRequest (requestPromise, statusCallback) {
return requestPromise
.then((signerRequestId) => {
console.log('trackRequest', `posted to signer with requestId ${signerRequestId.toString()}`);
statusCallback(null, { signerRequestId });
return api.pollMethod('parity_checkRequest', signerRequestId);
})
.then((transactionHash) => {
console.log('trackRequest', `received transaction hash ${transactionHash}`);
statusCallback(null, { transactionHash });
return api.pollMethod('eth_getTransactionReceipt', transactionHash, (receipt) => {
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
return false;
}
return true;
});
})
.then((transactionReceipt) => {
console.log('trackRequest', 'received transaction receipt', transactionReceipt);
statusCallback(null, { transactionReceipt });
})
.catch((error) => {
console.error('trackRequest', error);
statusCallback(error);
throw error;
});
}
export {
api,
trackRequest
};

View File

@@ -77,7 +77,7 @@ export default class Lookup extends Component {
label='Lookup'
primary
icon={ <SearchIcon /> }
onClick={ this.onLookupClick }
onTouchTap={ this.onLookupClick }
/>
</div>
<CardText>{ output }</CardText>

View File

@@ -14,7 +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 { sha3, toWei } from '../parity.js';
import { sha3, api } from '../parity.js';
const alreadyQueued = (queue, action, name) =>
!!queue.find((entry) => entry.action === action && entry.name === name);
@@ -29,6 +29,8 @@ export const reserve = (name) => (dispatch, getState) => {
const state = getState();
const account = state.accounts.selected;
const contract = state.contract;
const fee = state.fee;
if (!contract || !account) return;
if (alreadyQueued(state.names.queue, 'reserve', name)) return;
const reserve = contract.functions.find((f) => f.name === 'reserve');
@@ -36,19 +38,28 @@ export const reserve = (name) => (dispatch, getState) => {
name = name.toLowerCase();
const options = {
from: account.address,
value: toWei(1).toString()
value: fee
};
const values = [ sha3(name) ];
dispatch(reserveStart(name));
reserve.estimateGas(options, values)
.then((gas) => {
options.gas = gas.mul(1.2).toFixed(0);
return reserve.postTransaction(options, values);
})
.then((data) => {
.then((requestId) => {
return api.pollMethod('parity_checkRequest', requestId);
})
.then((txhash) => {
dispatch(reserveSuccess(name));
}).catch((err) => {
})
.catch((err) => {
if (err && err.type === 'REQUEST_REJECTED') {
return dispatch(reserveFail(name));
}
console.error(`could not reserve ${name}`);
if (err) console.error(err.stack);
dispatch(reserveFail(name));
@@ -79,9 +90,17 @@ export const drop = (name) => (dispatch, getState) => {
options.gas = gas.mul(1.2).toFixed(0);
return drop.postTransaction(options, values);
})
.then((data) => {
.then((requestId) => {
return api.pollMethod('parity_checkRequest', requestId);
})
.then((txhash) => {
dispatch(dropSuccess(name));
}).catch((err) => {
})
.catch((err) => {
if (err && err.type === 'REQUEST_REJECTED') {
dispatch(reserveFail(name));
}
console.error(`could not drop ${name}`);
if (err) console.error(err.stack);
dispatch(reserveFail(name));

View File

@@ -86,6 +86,22 @@ export default class Names extends Component {
name: ''
};
componentWillReceiveProps (nextProps) {
const nextQueue = nextProps.queue;
const prevQueue = this.props.queue;
if (nextQueue.length > prevQueue.length) {
const newQueued = nextQueue[nextQueue.length - 1];
const newName = newQueued.name;
if (newName !== this.state.name) {
return;
}
this.setState({ name: '' });
}
}
render () {
const { action, name } = this.state;
const { fee, pending, queue } = this.props;
@@ -120,7 +136,7 @@ export default class Names extends Component {
label={ action === 'reserve' ? 'Reserve' : 'Drop' }
primary
icon={ <CheckIcon /> }
onClick={ this.onSubmitClick }
onTouchTap={ this.onSubmitClick }
/>
{ queue.length > 0
? (<div>{ useSignerText }{ renderQueue(queue) }</div>)

View File

@@ -52,7 +52,7 @@ export default class Records extends Component {
label='Save'
primary
icon={ <SaveIcon /> }
onClick={ this.onSaveClick }
onTouchTap={ this.onSaveClick }
/>
</CardText>
</Card>

View File

@@ -174,7 +174,7 @@ export default class LoadContract extends Component {
const secondaryText = description || `Saved ${moment(timestamp).fromNow()}`;
const remove = removable
? (
<IconButton onClick={ onDelete }>
<IconButton onTouchTap={ onDelete }>
<DeleteIcon />
</IconButton>
)

View File

@@ -25,7 +25,7 @@ import ErrorIcon from 'material-ui/svg-icons/navigation/close';
import { fromWei } from '../../../api/util/wei';
import { Form, Input } from '../../../ui';
import terms from '../terms-of-service';
import { termsOfService } from '../../../3rdparty/sms-verification';
import styles from './gatherData.css';
export default class GatherData extends Component {
@@ -66,7 +66,7 @@ export default class GatherData extends Component {
disabled={ isVerified }
onCheck={ this.consentOnChange }
/>
<div className={ styles.terms }>{ terms }</div>
<div className={ styles.terms }>{ termsOfService }</div>
</Form>
);
}
@@ -123,8 +123,7 @@ export default class GatherData extends Component {
<p className={ styles.message }>You already requested verification.</p>
</div>
);
}
if (hasRequested === false) {
} else if (hasRequested === false) {
return (
<div className={ styles.container }>
<SuccessIcon />

View File

@@ -16,8 +16,8 @@
import React, { Component, PropTypes } from 'react';
import { observer } from 'mobx-react';
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import ContentClear from 'material-ui/svg-icons/content/clear';
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';
@@ -77,7 +77,7 @@ export default class SMSVerification extends Component {
const cancel = (
<Button
key='cancel' label='Cancel'
icon={ <ContentClear /> }
icon={ <CancelIcon /> }
onClick={ onClose }
/>
);
@@ -92,7 +92,7 @@ export default class SMSVerification extends Component {
<Button
key='done' label='Done'
disabled={ !isStepValid }
icon={ <ActionDoneAll /> }
icon={ <DoneIcon /> }
onClick={ onClose }
/>
</div>
@@ -140,37 +140,47 @@ export default class SMSVerification extends Component {
setNumber, setConsentGiven, setCode
} = this.props.store;
if (phase === 5) {
return (<Done />);
}
if (phase === 4) {
return (<SendConfirmation step={ step } tx={ confirmationTx } />);
}
if (phase === 3) {
return (
<QueryCode
number={ number } fee={ fee } isCodeValid={ isCodeValid }
setCode={ setCode }
/>
);
}
if (phase === 2) {
return (<SendRequest step={ step } tx={ requestTx } />);
}
if (phase === 1) {
const { setNumber, setConsentGiven } = this.props.store;
return (
<GatherData
fee={ fee } isNumberValid={ isNumberValid }
isVerified={ isVerified } hasRequested={ hasRequested }
setNumber={ setNumber } setConsentGiven={ setConsentGiven }
/>
);
}
if (phase === 0) {
return (<p>Preparing awesomeness!</p>);
}
switch (phase) {
case 0:
return (
<p>Loading SMS Verification.</p>
);
return null;
case 1:
const { setNumber, setConsentGiven } = this.props.store;
return (
<GatherData
fee={ fee } isNumberValid={ isNumberValid }
isVerified={ isVerified } hasRequested={ hasRequested }
setNumber={ setNumber } setConsentGiven={ setConsentGiven }
/>
);
case 2:
return (
<SendRequest step={ step } tx={ requestTx } />
);
case 3:
return (
<QueryCode
number={ number } fee={ fee } isCodeValid={ isCodeValid }
setCode={ setCode }
/>
);
case 4:
return (
<SendConfirmation step={ step } tx={ confirmationTx } />
);
case 5:
return (
<Done />
);
default:
return null;
}
}
}

View File

@@ -20,7 +20,8 @@ import { sha3 } from '../../api/util/sha3';
import Contracts from '../../contracts';
import { checkIfVerified, checkIfRequested, postToServer } from '../../contracts/sms-verification';
import { checkIfVerified, checkIfRequested } from '../../contracts/sms-verification';
import { postToServer } from '../../3rdparty/sms-verification';
import checkIfTxFailed from '../../util/check-if-tx-failed';
import waitForConfirmations from '../../util/wait-for-block-confirmations';
@@ -87,7 +88,7 @@ export default class VerificationStore {
this.account = account;
this.step = LOADING;
Contracts.create(api).registry.getContract('smsVerification')
Contracts.create(api).registry.getContract('smsverification')
.then((contract) => {
this.contract = contract;
this.load();

View File

@@ -155,24 +155,20 @@ export default class Status {
const { refreshStatus } = this._store.getState().nodeStatus;
const statusPromises = [ this._api.eth.syncing() ];
const statusPromises = [ this._api.eth.syncing(), this._api.parity.netPeers() ];
if (refreshStatus) {
statusPromises.push(this._api.eth.hashrate());
statusPromises.push(this._api.parity.netPeers());
}
Promise
.all(statusPromises)
.then((statusResults) => {
const status = statusResults.length === 1
? {
syncing: statusResults[0]
}
.then(([ syncing, netPeers, ...statusResults ]) => {
const status = statusResults.length === 0
? { syncing, netPeers }
: {
syncing: statusResults[0],
hashrate: statusResults[1],
netPeers: statusResults[2]
syncing, netPeers,
hashrate: statusResults[0]
};
if (!isEqual(status, this._status)) {
@@ -251,7 +247,7 @@ export default class Status {
.then(([
clientVersion, defaultExtraData, netChain, netPort, rpcSettings, enode
]) => {
const isTest = netChain === 'morden' || netChain === 'testnet';
const isTest = netChain === 'morden' || netChain === 'ropsten' || netChain === 'testnet';
const longStatus = {
clientVersion,

View File

@@ -31,7 +31,7 @@ const initialState = {
gasLimit: new BigNumber(0),
hashrate: new BigNumber(0),
minGasPrice: new BigNumber(0),
netChain: 'morden',
netChain: 'ropsten',
netPeers: {
active: new BigNumber(0),
connected: new BigNumber(0),

View File

@@ -96,7 +96,7 @@ export default class TypedInput extends Component {
<IconButton
iconStyle={ iconStyle }
style={ style }
onClick={ this.onAddField }
onTouchTap={ this.onAddField }
>
<AddIcon />
</IconButton>
@@ -104,7 +104,7 @@ export default class TypedInput extends Component {
<IconButton
iconStyle={ iconStyle }
style={ style }
onClick={ this.onRemoveField }
onTouchTap={ this.onRemoveField }
>
<RemoveIcon />
</IconButton>

View File

@@ -52,6 +52,7 @@
"description": "Have a peak on internals of transaction queue of your node.",
"author": "Parity Team <admin@ethcore.io>",
"version": "1.0.0",
"visible": true,
"secure": true
}
]

View File

@@ -67,7 +67,7 @@ export default class Dapps extends Component {
label='edit'
key='edit'
icon={ <EyeIcon /> }
onClick={ this.store.openModal }
onTouchTap={ this.store.openModal }
/>
] }
/>

View File

@@ -25,7 +25,7 @@ export default class Account extends Component {
static propTypes = {
className: PropTypes.string,
address: PropTypes.string.isRequired,
chain: PropTypes.string.isRequired,
isTest: PropTypes.bool.isRequired,
balance: PropTypes.object // eth BigNumber, not required since it mght take time to fetch
};
@@ -51,11 +51,13 @@ export default class Account extends Component {
}
render () {
const { address, chain, className } = this.props;
const { address, isTest, className } = this.props;
return (
<div className={ `${styles.acc} ${className}` }>
<AccountLink address={ address } chain={ chain }>
<AccountLink
address={ address }
isTest={ isTest }>
<IdentityIcon
center
address={ address } />
@@ -74,19 +76,23 @@ export default class Account extends Component {
}
renderName () {
const { address } = this.props;
const { address, isTest } = this.props;
const name = <IdentityName address={ address } empty />;
if (!name) {
return (
<AccountLink address={ address } chain={ this.props.chain }>
<AccountLink
address={ address }
isTest={ isTest }>
[{ this.shortAddress(address) }]
</AccountLink>
);
}
return (
<AccountLink address={ address } chain={ this.props.chain } >
<AccountLink
address={ address }
isTest={ isTest } >
<span>
<span className={ styles.name }>{ name }</span>
<span className={ styles.address }>[{ this.tinyAddress(address) }]</span>

View File

@@ -21,7 +21,7 @@ import styles from './AccountLink.css';
export default class AccountLink extends Component {
static propTypes = {
chain: PropTypes.string.isRequired,
isTest: PropTypes.bool.isRequired,
address: PropTypes.string.isRequired,
className: PropTypes.string,
children: PropTypes.node
@@ -32,15 +32,15 @@ export default class AccountLink extends Component {
};
componentWillMount () {
const { address, chain } = this.props;
const { address, isTest } = this.props;
this.updateLink(address, chain);
this.updateLink(address, isTest);
}
componentWillReceiveProps (nextProps) {
const { address, chain } = nextProps;
const { address, isTest } = nextProps;
this.updateLink(address, chain);
this.updateLink(address, isTest);
}
render () {
@@ -56,8 +56,8 @@ export default class AccountLink extends Component {
);
}
updateLink (address, chain) {
const link = addressLink(address, chain === 'morden' || chain === 'testnet');
updateLink (address, isTest) {
const link = addressLink(address, isTest);
this.setState({
link

View File

@@ -31,14 +31,16 @@ export default class RequestFinishedWeb3 extends Component {
msg: PropTypes.string,
status: PropTypes.string,
error: PropTypes.string,
className: PropTypes.string
className: PropTypes.string,
isTest: PropTypes.bool.isRequired
}
render () {
const { payload, id, result, msg, status, error, date, className } = this.props;
const { payload, id, result, msg, status, error, date, className, isTest } = this.props;
if (payload.sign) {
const { sign } = payload;
return (
<SignRequest
className={ className }
@@ -49,12 +51,14 @@ export default class RequestFinishedWeb3 extends Component {
msg={ msg }
status={ status }
error={ error }
isTest={ isTest }
/>
);
}
if (payload.transaction) {
const { transaction } = payload;
return (
<TransactionFinished
className={ className }
@@ -69,6 +73,7 @@ export default class RequestFinishedWeb3 extends Component {
date={ date }
status={ status }
error={ error }
isTest={ isTest }
/>
);
}

View File

@@ -30,7 +30,8 @@ export default class RequestPendingWeb3 extends Component {
PropTypes.shape({ transaction: PropTypes.object.isRequired }),
PropTypes.shape({ sign: PropTypes.object.isRequired })
]).isRequired,
className: PropTypes.string
className: PropTypes.string,
isTest: PropTypes.bool.isRequired
};
onConfirm = data => {
@@ -41,10 +42,11 @@ export default class RequestPendingWeb3 extends Component {
};
render () {
const { payload, id, className, isSending, date, onReject } = this.props;
const { payload, id, className, isSending, date, onReject, isTest } = this.props;
if (payload.sign) {
const { sign } = payload;
return (
<SignRequest
className={ className }
@@ -55,12 +57,14 @@ export default class RequestPendingWeb3 extends Component {
id={ id }
address={ sign.address }
hash={ sign.hash }
isTest={ isTest }
/>
);
}
if (payload.transaction) {
const { transaction } = payload;
return (
<TransactionPending
className={ className }
@@ -75,6 +79,7 @@ export default class RequestPendingWeb3 extends Component {
to={ transaction.to }
value={ transaction.value }
date={ date }
isTest={ isTest }
/>
);
}

View File

@@ -39,24 +39,15 @@ export default class SignRequest extends Component {
onReject: PropTypes.func,
status: PropTypes.string,
className: PropTypes.string,
chain: nullable(PropTypes.object),
isTest: PropTypes.bool.isRequired,
balance: nullable(PropTypes.object)
};
state = {
chain: null,
balance: null
}
componentWillMount () {
this.context.api.parity.netChain()
.then((chain) => {
this.setState({ chain });
})
.catch((err) => {
console.error('could not fetch chain', err);
});
this.context.api.eth.getBalance(this.props.address)
.then((balance) => {
this.setState({ balance });
@@ -68,6 +59,7 @@ export default class SignRequest extends Component {
render () {
const { className } = this.props;
return (
<div className={ `${styles.container} ${className || ''}` }>
{ this.renderDetails() }
@@ -77,15 +69,20 @@ export default class SignRequest extends Component {
}
renderDetails () {
const { address, hash } = this.props;
const { balance, chain } = this.state;
const { address, hash, isTest } = this.props;
const { balance } = this.state;
if (!balance || !chain) return (<div />);
if (!balance) {
return <div />;
}
return (
<div className={ styles.signDetails }>
<div className={ styles.address }>
<Account address={ address } balance={ balance } chain={ chain } />
<Account
address={ address }
balance={ balance }
isTest={ isTest } />
</div>
<div className={ styles.info } title={ hash }>
<p>Dapp is requesting to sign arbitrary transaction using this account.</p>
@@ -100,15 +97,17 @@ export default class SignRequest extends Component {
if (isFinished) {
if (status === 'confirmed') {
const { hash } = this.props;
const { chain } = this.state;
const { hash, isTest } = this.props;
return (
<div className={ styles.actions }>
<span className={ styles.isConfirmed }>Confirmed</span>
<div>
Transaction hash:
<TxHashLink chain={ chain } txHash={ hash } className={ styles.txHash } />
<TxHashLink
isTest={ isTest }
txHash={ hash }
className={ styles.txHash } />
</div>
</div>
);

View File

@@ -47,13 +47,12 @@ export default class TransactionFinished extends Component {
txHash: PropTypes.string, // undefined if transacation is rejected
className: PropTypes.string,
data: PropTypes.string,
chain: nullable(PropTypes.object),
isTest: PropTypes.bool.isRequired,
fromBalance: nullable(PropTypes.object),
toBalance: nullable(PropTypes.object)
};
state = {
chain: null,
fromBalance: null,
toBalance: null
};
@@ -64,14 +63,6 @@ export default class TransactionFinished extends Component {
const totalValue = tUtil.getTotalValue(fee, value);
this.setState({ totalValue });
this.context.api.parity.netChain()
.then((chain) => {
this.setState({ chain });
})
.catch((err) => {
console.error('could not fetch chain', err);
});
this.fetchBalance(from, 'fromBalance');
if (to) {
this.fetchBalance(to, 'toBalance');
@@ -79,8 +70,9 @@ export default class TransactionFinished extends Component {
}
render () {
const { chain, fromBalance, toBalance } = this.state;
if (!chain || !fromBalance || !toBalance) {
const { fromBalance, toBalance } = this.state;
if (!fromBalance || !toBalance) {
return (
<div className={ `${styles.container} ${className}` }>
<CircularProgress size={ 60 } />
@@ -130,16 +122,19 @@ export default class TransactionFinished extends Component {
}
renderTxHash () {
const { txHash } = this.props;
const { chain } = this.state;
if (!txHash || !chain) {
const { txHash, isTest } = this.props;
if (!txHash) {
return;
}
return (
<div>
Transaction hash:
<TxHashLink chain={ chain } txHash={ txHash } className={ styles.txHash } />
<TxHashLink
isTest={ isTest }
txHash={ txHash }
className={ styles.txHash } />
</div>
);
}

View File

@@ -30,7 +30,7 @@ export default class TransactionMainDetails extends Component {
fromBalance: PropTypes.object, // eth BigNumber, not required since it might take time to fetch
value: PropTypes.object.isRequired, // wei hex
totalValue: PropTypes.object.isRequired, // wei BigNumber
chain: PropTypes.string.isRequired,
isTest: PropTypes.bool.isRequired,
to: PropTypes.string, // undefined if it's a contract
toBalance: PropTypes.object, // eth BigNumber - undefined if it's a contract or until it's fetched
className: PropTypes.string,
@@ -39,11 +39,13 @@ export default class TransactionMainDetails extends Component {
componentWillMount () {
const { value, totalValue } = this.props;
this.updateDisplayValues(value, totalValue);
}
componentWillReceiveProps (nextProps) {
const { value, totalValue } = nextProps;
this.updateDisplayValues(value, totalValue);
}
@@ -59,6 +61,7 @@ export default class TransactionMainDetails extends Component {
render () {
const { className, children } = this.props;
return (
<div className={ className }>
{ this.renderTransfer() }
@@ -69,7 +72,8 @@ export default class TransactionMainDetails extends Component {
}
renderTransfer () {
const { from, fromBalance, to, toBalance, chain } = this.props;
const { from, fromBalance, to, toBalance, isTest } = this.props;
if (!to) {
return;
}
@@ -78,7 +82,10 @@ export default class TransactionMainDetails extends Component {
<div className={ styles.transaction }>
<div className={ styles.from }>
<div className={ styles.account }>
<Account address={ from } balance={ fromBalance } chain={ chain } />
<Account
address={ from }
balance={ fromBalance }
isTest={ isTest } />
</div>
</div>
<div className={ styles.tx }>
@@ -88,7 +95,10 @@ export default class TransactionMainDetails extends Component {
</div>
<div className={ styles.to }>
<div className={ styles.account }>
<Account address={ to } balance={ toBalance } chain={ chain } />
<Account
address={ to }
balance={ toBalance }
isTest={ isTest } />
</div>
</div>
</div>
@@ -96,15 +106,20 @@ export default class TransactionMainDetails extends Component {
}
renderContract () {
const { from, fromBalance, to, chain } = this.props;
const { from, fromBalance, to, isTest } = this.props;
if (to) {
return;
}
return (
<div className={ styles.transaction }>
<div className={ styles.from }>
<div className={ styles.account }>
<Account address={ from } balance={ fromBalance } chain={ chain } />
<Account
address={ from }
balance={ fromBalance }
isTest={ isTest } />
</div>
</div>
<div className={ styles.tx }>
@@ -126,6 +141,7 @@ export default class TransactionMainDetails extends Component {
renderValue () {
const { id } = this.props;
const { valueDisplay, valueDisplayWei } = this.state;
return (
<div>
<div
@@ -147,6 +163,7 @@ export default class TransactionMainDetails extends Component {
renderTotalValue () {
const { id } = this.props;
const { totalValueDisplay, totalValueDisplayWei, feeEth } = this.state;
return (
<div>
<div
@@ -164,5 +181,4 @@ export default class TransactionMainDetails extends Component {
</div>
);
}
}

View File

@@ -16,7 +16,6 @@
import React, { Component, PropTypes } from 'react';
import CircularProgress from 'material-ui/CircularProgress';
import TransactionMainDetails from '../TransactionMainDetails';
import TransactionPendingForm from '../TransactionPendingForm';
import TransactionSecondaryDetails from '../TransactionSecondaryDetails';
@@ -43,7 +42,8 @@ export default class TransactionPending extends Component {
onConfirm: PropTypes.func.isRequired,
onReject: PropTypes.func.isRequired,
isSending: PropTypes.bool.isRequired,
className: PropTypes.string
className: PropTypes.string,
isTest: PropTypes.bool.isRequired
};
static defaultProps = {
@@ -51,7 +51,6 @@ export default class TransactionPending extends Component {
};
state = {
chain: null,
fromBalance: null,
toBalance: null
};
@@ -64,28 +63,12 @@ export default class TransactionPending extends Component {
const gasToDisplay = tUtil.getGasDisplay(gas);
this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay });
this.context.api.parity.netChain()
.then((chain) => {
this.setState({ chain });
})
.catch((err) => {
console.error('could not fetch chain', err);
});
const { from, to } = this.props;
this.fetchBalance(from, 'fromBalance');
if (to) this.fetchBalance(to, 'toBalance');
}
render () {
if (!this.state.chain) {
return (
<div className={ `${styles.container} ${className}` }>
<CircularProgress size={ 60 } />
</div>
);
}
const { totalValue, gasPriceEthmDisplay, gasToDisplay } = this.state;
const { className, id, date, data, from } = this.props;

View File

@@ -74,7 +74,7 @@ class TransactionPendingFormConfirm extends Component {
data-effect='solid'
>
<RaisedButton
onClick={ this.onConfirm }
onTouchTap={ this.onConfirm }
className={ styles.confirmButton }
fullWidth
primary

View File

@@ -37,7 +37,7 @@ export default class TransactionPendingFormReject extends Component {
<strong>This cannot be undone</strong>
</div>
<RaisedButton
onClick={ onReject }
onTouchTap={ onReject }
className={ styles.rejectButton }
fullWidth
label={ 'Reject Transaction' }

View File

@@ -59,10 +59,13 @@ export default class TransactionSecondaryDetails extends Component {
}
renderGasPrice () {
if (!this.props.gasPriceEthmDisplay && !this.props.gasToDisplay) return null;
if (!this.props.gasPriceEthmDisplay && !this.props.gasToDisplay) {
return null;
}
const { id } = this.props;
const { gasPriceEthmDisplay, gasToDisplay } = this.props;
return (
<div
data-tip
@@ -83,11 +86,14 @@ export default class TransactionSecondaryDetails extends Component {
}
renderData () {
if (!this.props.data) return null;
if (!this.props.data) {
return null;
}
const { data, id } = this.props;
let dataToDisplay = this.noData() ? 'no data' : tUtil.getShortData(data);
const noDataClass = this.noData() ? styles.noData : '';
return (
<div
className={ `${styles.data} ${noDataClass}` }

View File

@@ -22,17 +22,17 @@ export default class TxHashLink extends Component {
static propTypes = {
txHash: PropTypes.string.isRequired,
chain: PropTypes.string.isRequired,
isTest: PropTypes.bool.isRequired,
children: PropTypes.node,
className: PropTypes.string
}
render () {
const { children, txHash, className, chain } = this.props;
const { children, txHash, className, isTest } = this.props;
return (
<a
href={ txLink(txHash, chain === 'morden' || chain === 'testnet') }
href={ txLink(txHash, isTest) }
target='_blank'
className={ className }>
{ children || txHash }

View File

@@ -35,7 +35,8 @@ class Embedded extends Component {
actions: PropTypes.shape({
startConfirmRequest: PropTypes.func.isRequired,
startRejectRequest: PropTypes.func.isRequired
}).isRequired
}).isRequired,
isTest: PropTypes.bool.isRequired
};
render () {
@@ -70,7 +71,7 @@ class Embedded extends Component {
}
renderPending = (data) => {
const { actions } = this.props;
const { actions, isTest } = this.props;
const { payload, id, isSending, date } = data;
return (
@@ -83,6 +84,7 @@ class Embedded extends Component {
id={ id }
payload={ payload }
date={ date }
isTest={ isTest }
/>
);
}
@@ -93,11 +95,13 @@ class Embedded extends Component {
}
function mapStateToProps (state) {
const { isTest } = state.nodeStatus;
const { actions, signer } = state;
return {
actions,
signer
signer,
isTest
};
}

View File

@@ -35,7 +35,8 @@ class RequestsPage extends Component {
actions: PropTypes.shape({
startConfirmRequest: PropTypes.func.isRequired,
startRejectRequest: PropTypes.func.isRequired
}).isRequired
}).isRequired,
isTest: PropTypes.bool.isRequired
};
render () {
@@ -96,7 +97,7 @@ class RequestsPage extends Component {
}
renderPending = (data) => {
const { actions } = this.props;
const { actions, isTest } = this.props;
const { payload, id, isSending, date } = data;
return (
@@ -109,11 +110,13 @@ class RequestsPage extends Component {
id={ id }
payload={ payload }
date={ date }
isTest={ isTest }
/>
);
}
renderFinished = (data) => {
const { isTest } = this.props;
const { payload, id, result, msg, status, error, date } = data;
return (
@@ -127,6 +130,7 @@ class RequestsPage extends Component {
error={ error }
payload={ payload }
date={ date }
isTest={ isTest }
/>
);
}
@@ -143,7 +147,14 @@ class RequestsPage extends Component {
}
function mapStateToProps (state) {
return state;
const { isTest } = state.nodeStatus;
const { actions, signer } = state;
return {
actions,
signer,
isTest
};
}
function mapDispatchToProps (dispatch) {

View File

@@ -57,7 +57,7 @@ export default class CallsToolbar extends Component {
<div className={ styles.callActions } { ...this._test('button-container') }>
<IconButton
className={ styles.callAction }
onClick={ this.setCall }
onTouchTap={ this.setCall }
tooltip='Set'
tooltipPosition='top-left'
{ ...this._test('button-setCall') }
@@ -66,7 +66,7 @@ export default class CallsToolbar extends Component {
</IconButton>
<IconButton
className={ styles.callAction }
onClick={ this.makeCall }
onTouchTap={ this.makeCall }
tooltip='Fire again'
tooltipPosition='top-left'
{ ...this._test('button-makeCall') }

View File

@@ -45,7 +45,7 @@ export default class ScrollTopButton extends Component {
return (
<IconButton
className={ `${styles.scrollButton} ${hiddenClass}` }
onClick={ this._scrollToTop }>
onTouchTap={ this._scrollToTop }>
<ArrowUpwardIcon />
</IconButton>
);

View File

@@ -37,6 +37,7 @@ module.exports = {
entry: {
// dapps
'basiccoin': ['./dapps/basiccoin.js'],
'dappreg': ['./dapps/dappreg.js'],
'githubhint': ['./dapps/githubhint.js'],
'registry': ['./dapps/registry.js'],
'signaturereg': ['./dapps/signaturereg.js'],