Merge branch 'master' into jg-signer-api-queries-2
This commit is contained in:
commit
59f4252011
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1263,7 +1263,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#f46188126257e03c775e76a3cea82b5f70549400"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#f8dba85e17d32860d4070fb444fa5cf13bcc6b3c"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parity.js",
|
||||
"version": "0.2.61",
|
||||
"version": "0.2.63",
|
||||
"main": "release/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
@ -102,6 +102,7 @@
|
||||
"postcss-nested": "^1.0.0",
|
||||
"postcss-simple-vars": "^3.0.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react-addons-perf": "~15.3.2",
|
||||
"react-addons-test-utils": "~15.3.2",
|
||||
"react-copy-to-clipboard": "^4.2.3",
|
||||
"react-dom": "~15.3.2",
|
||||
|
17
js/src/dapps/dappreg.html
Normal file
17
js/src/dapps/dappreg.html
Normal 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
35
js/src/dapps/dappreg.js
Normal 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')
|
||||
);
|
58
js/src/dapps/dappreg/Application/application.css
Normal file
58
js/src/dapps/dappreg/Application/application.css
Normal 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;
|
||||
}
|
64
js/src/dapps/dappreg/Application/application.js
Normal file
64
js/src/dapps/dappreg/Application/application.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/Application/index.js
Normal file
17
js/src/dapps/dappreg/Application/index.js
Normal 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';
|
38
js/src/dapps/dappreg/Button/button.css
Normal file
38
js/src/dapps/dappreg/Button/button.css
Normal 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;
|
||||
}
|
||||
}
|
52
js/src/dapps/dappreg/Button/button.js
Normal file
52
js/src/dapps/dappreg/Button/button.js
Normal 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);
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/Button/index.js
Normal file
17
js/src/dapps/dappreg/Button/index.js
Normal 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';
|
21
js/src/dapps/dappreg/ButtonBar/buttonBar.css
Normal file
21
js/src/dapps/dappreg/ButtonBar/buttonBar.css
Normal 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;
|
||||
}
|
101
js/src/dapps/dappreg/ButtonBar/buttonBar.js
Normal file
101
js/src/dapps/dappreg/ButtonBar/buttonBar.js
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/ButtonBar/index.js
Normal file
17
js/src/dapps/dappreg/ButtonBar/index.js
Normal 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';
|
19
js/src/dapps/dappreg/Dapp/dapp.css
Normal file
19
js/src/dapps/dappreg/Dapp/dapp.css
Normal 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 {
|
||||
}
|
162
js/src/dapps/dappreg/Dapp/dapp.js
Normal file
162
js/src/dapps/dappreg/Dapp/dapp.js
Normal 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/Dapp/index.js
Normal file
17
js/src/dapps/dappreg/Dapp/index.js
Normal 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';
|
17
js/src/dapps/dappreg/Input/index.js
Normal file
17
js/src/dapps/dappreg/Input/index.js
Normal 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';
|
92
js/src/dapps/dappreg/Input/input.css
Normal file
92
js/src/dapps/dappreg/Input/input.css
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
47
js/src/dapps/dappreg/Input/input.js
Normal file
47
js/src/dapps/dappreg/Input/input.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/Modal/index.js
Normal file
17
js/src/dapps/dappreg/Modal/index.js
Normal 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';
|
116
js/src/dapps/dappreg/Modal/modal.css
Normal file
116
js/src/dapps/dappreg/Modal/modal.css
Normal 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;
|
||||
}
|
66
js/src/dapps/dappreg/Modal/modal.js
Normal file
66
js/src/dapps/dappreg/Modal/modal.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/ModalDelete/index.js
Normal file
17
js/src/dapps/dappreg/ModalDelete/index.js
Normal 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';
|
159
js/src/dapps/dappreg/ModalDelete/modalDelete.js
Normal file
159
js/src/dapps/dappreg/ModalDelete/modalDelete.js
Normal 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();
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/ModalRegister/index.js
Normal file
17
js/src/dapps/dappreg/ModalRegister/index.js
Normal 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';
|
159
js/src/dapps/dappreg/ModalRegister/modalRegister.js
Normal file
159
js/src/dapps/dappreg/ModalRegister/modalRegister.js
Normal 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();
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/ModalUpdate/index.js
Normal file
17
js/src/dapps/dappreg/ModalUpdate/index.js
Normal 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';
|
169
js/src/dapps/dappreg/ModalUpdate/modalUpdate.js
Normal file
169
js/src/dapps/dappreg/ModalUpdate/modalUpdate.js
Normal 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();
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/SelectAccount/index.js
Normal file
17
js/src/dapps/dappreg/SelectAccount/index.js
Normal 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';
|
49
js/src/dapps/dappreg/SelectAccount/selectAccount.js
Normal file
49
js/src/dapps/dappreg/SelectAccount/selectAccount.js
Normal 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);
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/SelectDapp/index.js
Normal file
17
js/src/dapps/dappreg/SelectDapp/index.js
Normal 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';
|
76
js/src/dapps/dappreg/SelectDapp/selectDapp.js
Normal file
76
js/src/dapps/dappreg/SelectDapp/selectDapp.js
Normal 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);
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/Warning/index.js
Normal file
17
js/src/dapps/dappreg/Warning/index.js
Normal 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';
|
36
js/src/dapps/dappreg/Warning/warning.css
Normal file
36
js/src/dapps/dappreg/Warning/warning.css
Normal 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;
|
||||
}
|
||||
}
|
51
js/src/dapps/dappreg/Warning/warning.js
Normal file
51
js/src/dapps/dappreg/Warning/warning.js
Normal 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();
|
||||
}
|
||||
}
|
482
js/src/dapps/dappreg/dappsStore.js
Normal file
482
js/src/dapps/dappreg/dappsStore.js
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
266
js/src/dapps/dappreg/modalStore.js
Normal file
266
js/src/dapps/dappreg/modalStore.js
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
53
js/src/dapps/dappreg/parity.js
Normal file
53
js/src/dapps/dappreg/parity.js
Normal 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
|
||||
};
|
@ -46,6 +46,12 @@ import './index.html';
|
||||
|
||||
injectTapEventPlugin();
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// Expose the React Performance Tools on the`window` object
|
||||
const Perf = require('react-addons-perf');
|
||||
window.Perf = Perf;
|
||||
}
|
||||
|
||||
const AUTH_HASH = '#/auth?';
|
||||
const parityUrl = process.env.PARITY_URL ||
|
||||
(
|
||||
|
@ -36,6 +36,7 @@ export default class DetailsStep extends Component {
|
||||
onFuncChange: PropTypes.func,
|
||||
values: PropTypes.array.isRequired,
|
||||
valuesError: PropTypes.array.isRequired,
|
||||
warning: PropTypes.string,
|
||||
onValueChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
@ -44,6 +45,7 @@ export default class DetailsStep extends Component {
|
||||
|
||||
return (
|
||||
<Form>
|
||||
{ this.renderWarning() }
|
||||
<AddressSelect
|
||||
label='from account'
|
||||
hint='the account to transact with'
|
||||
@ -178,6 +180,20 @@ export default class DetailsStep extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
renderWarning () {
|
||||
const { warning } = this.props;
|
||||
|
||||
if (!warning) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.warning }>
|
||||
{ warning }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onFuncChange = (event, index, signature) => {
|
||||
const { contract, onFuncChange } = this.props;
|
||||
|
||||
|
@ -33,3 +33,12 @@
|
||||
.txhash {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-radius: 0.5em;
|
||||
background: #f80;
|
||||
color: white;
|
||||
font-size: 0.75em;
|
||||
padding: 0.75em;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -15,17 +15,21 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
|
||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
||||
import { MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||
import { validateAddress, validateUint } from '../../util/validation';
|
||||
|
||||
import DetailsStep from './DetailsStep';
|
||||
|
||||
import ERRORS from '../Transfer/errors';
|
||||
import { ERROR_CODES } from '../../api/transport/error';
|
||||
|
||||
export default class ExecuteContract extends Component {
|
||||
class ExecuteContract extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
@ -36,6 +40,7 @@ export default class ExecuteContract extends Component {
|
||||
fromAddress: PropTypes.string,
|
||||
accounts: PropTypes.object,
|
||||
contract: PropTypes.object,
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onFromAddressChange: PropTypes.func.isRequired
|
||||
}
|
||||
@ -46,6 +51,8 @@ export default class ExecuteContract extends Component {
|
||||
fromAddressError: null,
|
||||
func: null,
|
||||
funcError: null,
|
||||
gas: null,
|
||||
gasLimitError: null,
|
||||
values: [],
|
||||
valuesError: [],
|
||||
step: 0,
|
||||
@ -64,6 +71,12 @@ export default class ExecuteContract extends Component {
|
||||
this.onFuncChange(null, functions[0]);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
if (newProps.fromAddress !== this.props.fromAddress) {
|
||||
this.estimateGas(newProps.fromAddress);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { sending } = this.state;
|
||||
|
||||
@ -119,7 +132,7 @@ export default class ExecuteContract extends Component {
|
||||
|
||||
renderStep () {
|
||||
const { onFromAddressChange } = this.props;
|
||||
const { step, busyState, txhash, rejected } = this.state;
|
||||
const { step, busyState, gasLimitError, txhash, rejected } = this.state;
|
||||
|
||||
if (rejected) {
|
||||
return (
|
||||
@ -135,6 +148,7 @@ export default class ExecuteContract extends Component {
|
||||
<DetailsStep
|
||||
{ ...this.props }
|
||||
{ ...this.state }
|
||||
warning={ gasLimitError }
|
||||
onAmountChange={ this.onAmountChange }
|
||||
onFromAddressChange={ onFromAddressChange }
|
||||
onFuncChange={ this.onFuncChange }
|
||||
@ -156,7 +170,7 @@ export default class ExecuteContract extends Component {
|
||||
}
|
||||
|
||||
onAmountChange = (amount) => {
|
||||
this.setState({ amount });
|
||||
this.setState({ amount }, this.estimateGas);
|
||||
}
|
||||
|
||||
onFuncChange = (event, func) => {
|
||||
@ -182,7 +196,7 @@ export default class ExecuteContract extends Component {
|
||||
this.setState({
|
||||
func,
|
||||
values
|
||||
});
|
||||
}, this.estimateGas);
|
||||
}
|
||||
|
||||
onValueChange = (event, index, _value) => {
|
||||
@ -211,14 +225,55 @@ export default class ExecuteContract extends Component {
|
||||
this.setState({
|
||||
values: [].concat(values),
|
||||
valuesError: [].concat(valuesError)
|
||||
}, () => {
|
||||
if (!valueError) {
|
||||
this.estimateGas();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
estimateGas = (_fromAddress) => {
|
||||
const { api } = this.context;
|
||||
const { fromAddress, gasLimit } = this.props;
|
||||
const { amount, func, values } = this.state;
|
||||
const options = {
|
||||
gas: MAX_GAS_ESTIMATION,
|
||||
from: _fromAddress || fromAddress,
|
||||
value: api.util.toWei(amount || 0)
|
||||
};
|
||||
|
||||
if (!func) {
|
||||
return;
|
||||
}
|
||||
|
||||
func
|
||||
.estimateGas(options, values)
|
||||
.then((gasEst) => {
|
||||
const gas = gasEst.mul(1.2);
|
||||
let gasLimitError = null;
|
||||
|
||||
if (gas.gte(MAX_GAS_ESTIMATION)) {
|
||||
gasLimitError = ERRORS.gasException;
|
||||
} else if (gas.gt(gasLimit)) {
|
||||
gasLimitError = ERRORS.gasBlockLimit;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
gas,
|
||||
gasLimitError
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('estimateGas', error);
|
||||
});
|
||||
}
|
||||
|
||||
postTransaction = () => {
|
||||
const { api, store } = this.context;
|
||||
const { fromAddress } = this.props;
|
||||
const { amount, func, values } = this.state;
|
||||
const options = {
|
||||
gas: MAX_GAS_ESTIMATION,
|
||||
from: fromAddress,
|
||||
value: api.util.toWei(amount || 0)
|
||||
};
|
||||
@ -237,13 +292,13 @@ export default class ExecuteContract extends Component {
|
||||
|
||||
return api
|
||||
.pollMethod('parity_checkRequest', requestId)
|
||||
.catch((e) => {
|
||||
if (e.code === ERROR_CODES.REQUEST_REJECTED) {
|
||||
.catch((error) => {
|
||||
if (error.code === ERROR_CODES.REQUEST_REJECTED) {
|
||||
this.setState({ rejected: true });
|
||||
return false;
|
||||
}
|
||||
|
||||
throw e;
|
||||
throw error;
|
||||
});
|
||||
})
|
||||
.then((txhash) => {
|
||||
@ -255,3 +310,18 @@ export default class ExecuteContract extends Component {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { gasLimit } = state.nodeStatus;
|
||||
|
||||
return { gasLimit };
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ExecuteContract);
|
||||
|
@ -19,7 +19,9 @@ const ERRORS = {
|
||||
invalidAddress: 'the supplied address is an invalid network address',
|
||||
invalidAmount: 'the supplied amount should be a valid positive number',
|
||||
invalidDecimals: 'the supplied amount exceeds the allowed decimals',
|
||||
largeAmount: 'the transaction total is higher than the available balance'
|
||||
largeAmount: 'the transaction total is higher than the available balance',
|
||||
gasException: 'the transaction will throw an exception with the current values',
|
||||
gasBlockLimit: 'the transaction execution will exceed the block gas limit'
|
||||
};
|
||||
|
||||
export default ERRORS;
|
||||
|
@ -14,6 +14,7 @@
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.info {
|
||||
line-height: 1.618em;
|
||||
width: 100%;
|
||||
@ -151,3 +152,13 @@
|
||||
.gasPriceDesc {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-radius: 0.5em;
|
||||
background: #f80;
|
||||
color: white;
|
||||
font-size: 0.75em;
|
||||
margin-bottom: 1em;
|
||||
padding: 0.75em;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -16,12 +16,15 @@
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||
|
||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
||||
import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||
|
||||
import Details from './Details';
|
||||
import Extras from './Extras';
|
||||
@ -30,8 +33,6 @@ import styles from './transfer.css';
|
||||
|
||||
import { ERROR_CODES } from '../../api/transport/error';
|
||||
|
||||
const DEFAULT_GAS = '21000';
|
||||
const DEFAULT_GASPRICE = '20000000000';
|
||||
const TITLES = {
|
||||
transfer: 'transfer details',
|
||||
sending: 'sending',
|
||||
@ -42,7 +43,7 @@ const TITLES = {
|
||||
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
||||
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
|
||||
|
||||
export default class Transfer extends Component {
|
||||
class Transfer extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
@ -52,6 +53,7 @@ export default class Transfer extends Component {
|
||||
account: PropTypes.object,
|
||||
balance: PropTypes.object,
|
||||
balances: PropTypes.object,
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
images: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
@ -64,6 +66,7 @@ export default class Transfer extends Component {
|
||||
gas: DEFAULT_GAS,
|
||||
gasEst: '0',
|
||||
gasError: null,
|
||||
gasLimitError: null,
|
||||
gasPrice: DEFAULT_GASPRICE,
|
||||
gasPriceHistogram: {},
|
||||
gasPriceError: null,
|
||||
@ -103,6 +106,7 @@ export default class Transfer extends Component {
|
||||
visible
|
||||
scroll
|
||||
>
|
||||
{ this.renderWarning() }
|
||||
{ this.renderPage() }
|
||||
</Modal>
|
||||
);
|
||||
@ -264,6 +268,20 @@ export default class Transfer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderWarning () {
|
||||
const { gasLimitError } = this.state;
|
||||
|
||||
if (!gasLimitError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.warning }>
|
||||
{ gasLimitError }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
isValid () {
|
||||
const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError;
|
||||
const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError;
|
||||
@ -519,6 +537,7 @@ export default class Transfer extends Component {
|
||||
|
||||
return token.contract.instance.transfer
|
||||
.estimateGas({
|
||||
gas: MAX_GAS_ESTIMATION,
|
||||
from: account.address,
|
||||
to: token.address
|
||||
}, [
|
||||
@ -532,6 +551,7 @@ export default class Transfer extends Component {
|
||||
const { account } = this.props;
|
||||
const { data, recipient, value } = this.state;
|
||||
const options = {
|
||||
gas: MAX_GAS_ESTIMATION,
|
||||
from: account.address,
|
||||
to: recipient,
|
||||
value: api.util.toWei(value || 0)
|
||||
@ -552,19 +572,29 @@ export default class Transfer extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const { gasLimit } = this.props;
|
||||
|
||||
(this.state.isEth
|
||||
? this._estimateGasEth()
|
||||
: this._estimateGasToken()
|
||||
).then((_value) => {
|
||||
let gas = _value;
|
||||
).then((gasEst) => {
|
||||
let gas = gasEst;
|
||||
let gasLimitError = null;
|
||||
|
||||
if (gas.gt(DEFAULT_GAS)) {
|
||||
gas = gas.mul(1.2);
|
||||
}
|
||||
|
||||
if (gas.gte(MAX_GAS_ESTIMATION)) {
|
||||
gasLimitError = ERRORS.gasException;
|
||||
} else if (gas.gt(gasLimit)) {
|
||||
gasLimitError = ERRORS.gasBlockLimit;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
gas: gas.toFixed(0),
|
||||
gasEst: _value.toFormat()
|
||||
gasEst: gasEst.toFormat(),
|
||||
gasLimitError
|
||||
}, this.recalculate);
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -649,3 +679,18 @@ export default class Transfer extends Component {
|
||||
store.dispatch({ type: 'newError', error });
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { gasLimit } = state.nodeStatus;
|
||||
|
||||
return { gasLimit };
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({}, dispatch);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Transfer);
|
||||
|
@ -16,26 +16,17 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
class ParityBackground extends Component {
|
||||
static contextTypes = {
|
||||
muiTheme: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
style: PropTypes.object.isRequired,
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
gradient: PropTypes.string,
|
||||
seed: PropTypes.any,
|
||||
settings: PropTypes.object.isRequired,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { muiTheme } = this.context;
|
||||
const { children, className, gradient, seed, settings, onClick } = this.props;
|
||||
const style = muiTheme.parity.getBackgroundStyle(gradient, seed || settings.backgroundSeed);
|
||||
const { children, className, style, onClick } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -48,17 +39,29 @@ class ParityBackground extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { settings } = state;
|
||||
function mapStateToProps (_, initProps) {
|
||||
const { gradient, seed, muiTheme } = initProps;
|
||||
|
||||
return { settings };
|
||||
}
|
||||
let _seed = seed;
|
||||
let _props = { style: muiTheme.parity.getBackgroundStyle(gradient, seed) };
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return bindActionCreators({}, dispatch);
|
||||
return (state, props) => {
|
||||
const { backgroundSeed } = state.settings;
|
||||
const { seed } = props;
|
||||
|
||||
const newSeed = seed || backgroundSeed;
|
||||
|
||||
if (newSeed === _seed) {
|
||||
return _props;
|
||||
}
|
||||
|
||||
_seed = newSeed;
|
||||
_props = { style: muiTheme.parity.getBackgroundStyle(gradient, newSeed) };
|
||||
|
||||
return _props;
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps
|
||||
)(ParityBackground);
|
||||
|
26
js/src/util/constants.js
Normal file
26
js/src/util/constants.js
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 DEFAULT_GAS = '21000';
|
||||
const DEFAULT_GASPRICE = '20000000000';
|
||||
|
||||
const MAX_GAS_ESTIMATION = '50000000';
|
||||
|
||||
export {
|
||||
DEFAULT_GAS,
|
||||
DEFAULT_GASPRICE,
|
||||
MAX_GAS_ESTIMATION
|
||||
};
|
@ -16,6 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '../../../ui';
|
||||
|
||||
@ -30,7 +31,6 @@ export default class Summary extends Component {
|
||||
link: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
noLink: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
handleAddSearchToken: PropTypes.func
|
||||
};
|
||||
|
||||
@ -42,8 +42,42 @@ export default class Summary extends Component {
|
||||
name: 'Unnamed'
|
||||
};
|
||||
|
||||
shouldComponentUpdate (nextProps) {
|
||||
const prev = {
|
||||
link: this.props.link, name: this.props.name,
|
||||
noLink: this.props.noLink,
|
||||
meta: this.props.account.meta, address: this.props.account.address
|
||||
};
|
||||
|
||||
const next = {
|
||||
link: nextProps.link, name: nextProps.name,
|
||||
noLink: nextProps.noLink,
|
||||
meta: nextProps.account.meta, address: nextProps.account.address
|
||||
};
|
||||
|
||||
if (!isEqual(next, prev)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const prevTokens = this.props.balance.tokens || [];
|
||||
const nextTokens = nextProps.balance.tokens || [];
|
||||
|
||||
if (prevTokens.length !== nextTokens.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const prevValues = prevTokens.map((t) => t.value.toNumber());
|
||||
const nextValues = nextTokens.map((t) => t.value.toNumber());
|
||||
|
||||
if (!isEqual(prevValues, nextValues)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { account, children, handleAddSearchToken } = this.props;
|
||||
const { account, handleAddSearchToken } = this.props;
|
||||
const { tags } = account.meta;
|
||||
|
||||
if (!account) {
|
||||
@ -71,7 +105,6 @@ export default class Summary extends Component {
|
||||
byline={ addressComponent } />
|
||||
|
||||
{ this.renderBalance() }
|
||||
{ children }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -30,3 +30,25 @@
|
||||
right: 1em;
|
||||
top: 4em;
|
||||
}
|
||||
|
||||
.loadings {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.loading {
|
||||
flex: 0 1 50%;
|
||||
width: 50%;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,33 +42,63 @@ class Accounts extends Component {
|
||||
newDialog: false,
|
||||
sortOrder: '',
|
||||
searchValues: [],
|
||||
searchTokens: []
|
||||
searchTokens: [],
|
||||
show: false
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
window.setTimeout(() => {
|
||||
this.setState({ show: true });
|
||||
}, 100);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, hasAccounts, balances } = this.props;
|
||||
const { searchValues, sortOrder } = this.state;
|
||||
|
||||
return (
|
||||
<div className={ styles.accounts }>
|
||||
{ this.renderNewDialog() }
|
||||
{ this.renderActionbar() }
|
||||
<Page>
|
||||
<List
|
||||
search={ searchValues }
|
||||
accounts={ accounts }
|
||||
balances={ balances }
|
||||
empty={ !hasAccounts }
|
||||
order={ sortOrder }
|
||||
handleAddSearchToken={ this.onAddSearchToken } />
|
||||
<Tooltip
|
||||
className={ styles.accountTooltip }
|
||||
text='your accounts are visible for easy access, allowing you to edit the meta information, make transfers, view transactions and fund the account' />
|
||||
</Page>
|
||||
|
||||
{ this.state.show ? this.renderAccounts() : this.renderLoading() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderLoading () {
|
||||
const { accounts } = this.props;
|
||||
|
||||
const loadings = ((accounts && Object.keys(accounts)) || []).map((_, idx) => (
|
||||
<div key={ idx } className={ styles.loading }>
|
||||
<div></div>
|
||||
</div>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={ styles.loadings }>
|
||||
{ loadings }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccounts () {
|
||||
const { accounts, hasAccounts, balances } = this.props;
|
||||
const { searchValues, sortOrder } = this.state;
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<List
|
||||
search={ searchValues }
|
||||
accounts={ accounts }
|
||||
balances={ balances }
|
||||
empty={ !hasAccounts }
|
||||
order={ sortOrder }
|
||||
handleAddSearchToken={ this.onAddSearchToken } />
|
||||
<Tooltip
|
||||
className={ styles.accountTooltip }
|
||||
text='your accounts are visible for easy access, allowing you to edit the meta information, make transfers, view transactions and fund the account' />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
renderSearchButton () {
|
||||
const onChange = (searchTokens, searchValues) => {
|
||||
this.setState({ searchTokens, searchValues });
|
||||
|
@ -22,6 +22,10 @@ import { Errors, ParityBackground, Tooltips } from '../../../ui';
|
||||
import styles from '../application.css';
|
||||
|
||||
export default class Container extends Component {
|
||||
static contextTypes = {
|
||||
muiTheme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
showFirstRun: PropTypes.bool,
|
||||
@ -30,9 +34,10 @@ export default class Container extends Component {
|
||||
|
||||
render () {
|
||||
const { children, showFirstRun, onCloseFirstRun } = this.props;
|
||||
const { muiTheme } = this.context;
|
||||
|
||||
return (
|
||||
<ParityBackground className={ styles.container }>
|
||||
<ParityBackground className={ styles.container } muiTheme={ muiTheme }>
|
||||
<FirstRun
|
||||
visible={ showFirstRun }
|
||||
onClose={ onCloseFirstRun } />
|
||||
|
@ -23,6 +23,11 @@
|
||||
.tabs {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
& > * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs button,
|
||||
@ -38,6 +43,7 @@
|
||||
|
||||
button.tabactive,
|
||||
button.tabactive:hover {
|
||||
color: white !important;
|
||||
background: rgba(0, 0, 0, 0.25) !important;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||
import { Tab as MUITab } from 'material-ui/Tabs';
|
||||
|
||||
import { Badge, Tooltip } from '../../../ui';
|
||||
|
||||
@ -33,20 +33,138 @@ const TABMAP = {
|
||||
deploy: 'contract'
|
||||
};
|
||||
|
||||
class Tab extends Component {
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
view: PropTypes.object,
|
||||
children: PropTypes.node,
|
||||
pendings: PropTypes.number,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
shouldComponentUpdate (nextProps) {
|
||||
return nextProps.active !== this.props.active ||
|
||||
(nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { active, view, children } = this.props;
|
||||
|
||||
const label = this.getLabel(view);
|
||||
|
||||
return (
|
||||
<MUITab
|
||||
className={ active ? styles.tabactive : '' }
|
||||
selected={ active }
|
||||
icon={ view.icon }
|
||||
label={ label }
|
||||
onClick={ this.handleClick }
|
||||
>
|
||||
{ children }
|
||||
</MUITab>
|
||||
);
|
||||
}
|
||||
|
||||
getLabel (view) {
|
||||
const { label } = view;
|
||||
|
||||
if (view.id === 'signer') {
|
||||
return this.renderSignerLabel(label);
|
||||
}
|
||||
|
||||
if (view.id === 'status') {
|
||||
return this.renderStatusLabel(label);
|
||||
}
|
||||
|
||||
return this.renderLabel(label);
|
||||
}
|
||||
|
||||
renderLabel (name, bubble) {
|
||||
return (
|
||||
<div className={ styles.label }>
|
||||
{ name }
|
||||
{ bubble }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSignerLabel (label) {
|
||||
const { pendings } = this.props;
|
||||
|
||||
if (pendings) {
|
||||
const bubble = (
|
||||
<Badge
|
||||
color='red'
|
||||
className={ styles.labelBubble }
|
||||
value={ pendings } />
|
||||
);
|
||||
|
||||
return this.renderLabel(label, bubble);
|
||||
}
|
||||
|
||||
return this.renderLabel(label);
|
||||
}
|
||||
|
||||
renderStatusLabel (label) {
|
||||
// const { isTest, netChain } = this.props;
|
||||
// const bubble = (
|
||||
// <Badge
|
||||
// color={ isTest ? 'red' : 'default' }
|
||||
// className={ styles.labelBubble }
|
||||
// value={ isTest ? 'TEST' : netChain } />
|
||||
// );
|
||||
|
||||
return this.renderLabel(label, null);
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const { onChange, view } = this.props;
|
||||
onChange(view);
|
||||
}
|
||||
}
|
||||
|
||||
class TabBar extends Component {
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired
|
||||
}
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
views: PropTypes.array.isRequired,
|
||||
hash: PropTypes.string.isRequired,
|
||||
pending: PropTypes.array,
|
||||
isTest: PropTypes.bool,
|
||||
netChain: PropTypes.string,
|
||||
settings: PropTypes.object.isRequired
|
||||
}
|
||||
netChain: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
pending: []
|
||||
};
|
||||
|
||||
state = {
|
||||
activeRoute: '/accounts'
|
||||
activeViewId: ''
|
||||
};
|
||||
|
||||
setActiveView (props = this.props) {
|
||||
const { hash, views } = props;
|
||||
const view = views.find((view) => view.value === hash);
|
||||
|
||||
this.setState({ activeViewId: view.id });
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.setActiveView();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.hash !== this.props.hash) {
|
||||
this.setActiveView(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
return (nextProps.hash !== this.props.hash) ||
|
||||
(nextProps.pending.length !== this.props.pending.length) ||
|
||||
(nextState.activeViewId !== this.state.activeViewId);
|
||||
}
|
||||
|
||||
render () {
|
||||
@ -81,100 +199,64 @@ class TabBar extends Component {
|
||||
}
|
||||
|
||||
renderTabs () {
|
||||
const { settings } = this.props;
|
||||
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
|
||||
const hash = TABMAP[windowHash] || windowHash;
|
||||
const { views, pending } = this.props;
|
||||
const { activeViewId } = this.state;
|
||||
|
||||
const items = Object.keys(settings.views)
|
||||
.filter((id) => settings.views[id].fixed || settings.views[id].active)
|
||||
.map((id) => {
|
||||
const view = settings.views[id];
|
||||
let label = this.renderLabel(view.label);
|
||||
let body = null;
|
||||
const items = views
|
||||
.map((view, index) => {
|
||||
const body = (view.id === 'accounts')
|
||||
? (
|
||||
<Tooltip className={ styles.tabbarTooltip } text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view' />
|
||||
)
|
||||
: null;
|
||||
|
||||
if (id === 'accounts') {
|
||||
body = (
|
||||
<Tooltip className={ styles.tabbarTooltip } text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view' />
|
||||
);
|
||||
} else if (id === 'signer') {
|
||||
label = this.renderSignerLabel(label);
|
||||
} else if (id === 'status') {
|
||||
label = this.renderStatusLabel(label);
|
||||
}
|
||||
const active = activeViewId === view.id;
|
||||
|
||||
return (
|
||||
<Tab
|
||||
className={ hash === view.value ? styles.tabactive : '' }
|
||||
value={ view.value }
|
||||
icon={ view.icon }
|
||||
key={ id }
|
||||
label={ label }
|
||||
onActive={ this.onActivate(view.route) }>
|
||||
active={ active }
|
||||
view={ view }
|
||||
onChange={ this.onChange }
|
||||
key={ index }
|
||||
pendings={ pending.length }
|
||||
>
|
||||
{ body }
|
||||
</Tab>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
<div
|
||||
className={ styles.tabs }
|
||||
value={ hash }>
|
||||
onChange={ this.onChange }>
|
||||
{ items }
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
renderLabel = (name, bubble) => {
|
||||
return (
|
||||
<div className={ styles.label }>
|
||||
{ name }
|
||||
{ bubble }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSignerLabel = (label) => {
|
||||
const { pending } = this.props;
|
||||
let bubble = null;
|
||||
|
||||
if (pending && pending.length) {
|
||||
bubble = (
|
||||
<Badge
|
||||
color='red'
|
||||
className={ styles.labelBubble }
|
||||
value={ pending.length } />
|
||||
);
|
||||
}
|
||||
|
||||
return this.renderLabel(label, bubble);
|
||||
}
|
||||
|
||||
renderStatusLabel = (label) => {
|
||||
// const { isTest, netChain } = this.props;
|
||||
// const bubble = (
|
||||
// <Badge
|
||||
// color={ isTest ? 'red' : 'default' }
|
||||
// className={ styles.labelBubble }
|
||||
// value={ isTest ? 'TEST' : netChain } />
|
||||
// );
|
||||
|
||||
return this.renderLabel(label, null);
|
||||
}
|
||||
|
||||
onActivate = (activeRoute) => {
|
||||
onChange = (view) => {
|
||||
const { router } = this.context;
|
||||
|
||||
return (event) => {
|
||||
router.push(activeRoute);
|
||||
this.setState({ activeRoute });
|
||||
};
|
||||
router.push(view.route);
|
||||
this.setState({ activeViewId: view.id });
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { settings } = state;
|
||||
const { views } = state.settings;
|
||||
|
||||
return { settings };
|
||||
const filteredViews = Object
|
||||
.keys(views)
|
||||
.filter((id) => views[id].fixed || views[id].active)
|
||||
.map((id) => ({
|
||||
...views[id],
|
||||
id
|
||||
}));
|
||||
|
||||
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
|
||||
const hash = TABMAP[windowHash] || windowHash;
|
||||
|
||||
return { views: filteredViews, hash };
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
|
@ -28,6 +28,10 @@ import imagesEthcoreBlock from '../../../assets/images/parity-logo-white-no-text
|
||||
import styles from './parityBar.css';
|
||||
|
||||
class ParityBar extends Component {
|
||||
static contextTypes = {
|
||||
muiTheme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
pending: PropTypes.array,
|
||||
dapp: PropTypes.bool
|
||||
@ -62,6 +66,7 @@ class ParityBar extends Component {
|
||||
|
||||
renderBar () {
|
||||
const { dapp } = this.props;
|
||||
const { muiTheme } = this.context;
|
||||
|
||||
if (!dapp) {
|
||||
return null;
|
||||
@ -75,7 +80,7 @@ class ParityBar extends Component {
|
||||
|
||||
return (
|
||||
<div className={ styles.bar }>
|
||||
<ParityBackground className={ styles.corner }>
|
||||
<ParityBackground className={ styles.corner } muiTheme={ muiTheme }>
|
||||
<div className={ styles.cornercolor }>
|
||||
<Link to='/apps'>
|
||||
<Button
|
||||
@ -95,9 +100,11 @@ class ParityBar extends Component {
|
||||
}
|
||||
|
||||
renderExpanded () {
|
||||
const { muiTheme } = this.context;
|
||||
|
||||
return (
|
||||
<div className={ styles.overlay }>
|
||||
<ParityBackground className={ styles.expanded }>
|
||||
<ParityBackground className={ styles.expanded } muiTheme={ muiTheme }>
|
||||
<div className={ styles.header }>
|
||||
<div className={ styles.title }>
|
||||
<ContainerTitle title='Parity Signer: Pending' />
|
||||
|
@ -81,6 +81,7 @@ class Background extends Component {
|
||||
renderBackgrounds () {
|
||||
const { settings } = this.props;
|
||||
const { seeds } = this.state;
|
||||
const { muiTheme } = this.context;
|
||||
|
||||
return seeds.map((seed, index) => {
|
||||
return (
|
||||
@ -89,7 +90,9 @@ class Background extends Component {
|
||||
<ParityBackground
|
||||
className={ settings.backgroundSeed === seed ? styles.seedactive : styles.seed }
|
||||
seed={ seed }
|
||||
onClick={ this.onSelect(seed) } />
|
||||
onClick={ this.onSelect(seed) }
|
||||
muiTheme={ muiTheme }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -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'],
|
||||
|
Loading…
Reference in New Issue
Block a user