Refactoring of the Dapp Registry (#4589)
* Add React Hot Loader to DappReg dapp * Updated colours * Add DappCards * Dapp Modal with manifest displayed * Add input to the Dapp Modal * WIP // Editing a Dapp * Clean-Up * Linting * CleanUp and separate dapp from dappS * Semi-working updates * Working Editing of a Dapp * OCD * Linting * Add a Dapp -- WIP * Register a new Dapp * WIP Dapps * Working update / delete / register * Better promises * Working updates for DappReg * Fully functional again ! * Generic Card Component * Dashed Register Card * Cleanups * Cleanups * Add Actions to Modal * Clean-Up * Better Close Icon * Single place for Registry version // Fetch meta-data from Registry * Fixing test * Fix saving changes in dapp reg * PR Grumbles - Part I * PR Grumble - Part I * PR Grumble - Part II * DappReg Contract owner can delete dapps
This commit is contained in:
parent
e15f60b819
commit
eebb8b87a4
12
js/assets/images/dapps/close.svg
Normal file
12
js/assets/images/dapps/close.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 512 512">
|
||||
<g>
|
||||
</g>
|
||||
<path d="M295.516 216.494h154v78.992h-154v-78.992z" fill="#FFFFFF" />
|
||||
<path d="M62.474 216.514h154.050v78.971h-154.050v-78.971z" fill="#FFFFFF" />
|
||||
<path d="M216.525 295.465h79.001v154.050h-79.001v-154.050z" fill="#FFFFFF" />
|
||||
<path d="M216.525 62.474h79.001v154.041h-79.001v-154.041z" fill="#FFFFFF" />
|
||||
<path d="M216.525 216.514h79.001v78.971h-79.001v-78.971z" fill="#FFFFFF" />
|
||||
</svg>
|
After Width: | Height: | Size: 720 B |
12
js/assets/images/dapps/plus.svg
Normal file
12
js/assets/images/dapps/plus.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 512 512">
|
||||
<g>
|
||||
</g>
|
||||
<path d="M295.516 216.494h154v78.992h-154v-78.992z" fill="#000000" />
|
||||
<path d="M62.474 216.514h154.050v78.971h-154.050v-78.971z" fill="#000000" />
|
||||
<path d="M216.525 295.465h79.001v154.050h-79.001v-154.050z" fill="#000000" />
|
||||
<path d="M216.525 62.474h79.001v154.041h-79.001v-154.041z" fill="#000000" />
|
||||
<path d="M216.525 216.514h79.001v78.971h-79.001v-78.971z" fill="#000000" />
|
||||
</svg>
|
After Width: | Height: | Size: 715 B |
@ -16,7 +16,14 @@
|
||||
|
||||
import * as abis from './abi';
|
||||
|
||||
const REGISTRY_V1_HASHES = [
|
||||
'0x34f7c51bbb1b1902fbdabfdf04811100f5c9f998f26dd535d2f6f977492c748e', // ropsten
|
||||
'0x64c3ee34851517a9faecd995c102b339f03e564ad6772dc43a26f993238b20ec' // homestead
|
||||
];
|
||||
|
||||
export default class Registry {
|
||||
_registryContract = null;
|
||||
|
||||
constructor (api) {
|
||||
this._api = api;
|
||||
|
||||
@ -43,11 +50,10 @@ export default class Registry {
|
||||
|
||||
this._fetching = true;
|
||||
|
||||
return this._api.parity
|
||||
.registryAddress()
|
||||
.then((address) => {
|
||||
return this.fetchContract()
|
||||
.then((contract) => {
|
||||
this._fetching = false;
|
||||
this._instance = this._api.newContract(abis.registry, address).instance;
|
||||
this._instance = contract.instance;
|
||||
|
||||
this._queue.forEach((queued) => {
|
||||
queued.resolve(this._instance);
|
||||
@ -89,6 +95,47 @@ export default class Registry {
|
||||
.then((contract) => contract.instance);
|
||||
}
|
||||
|
||||
fetchContract () {
|
||||
if (this._registryContract) {
|
||||
return Promise.resolve(this._registryContract);
|
||||
}
|
||||
|
||||
return this._api.parity
|
||||
.registryAddress()
|
||||
.then((address) => Promise.all([ address, this._api.eth.getCode(address) ]))
|
||||
.then(([ address, code ]) => {
|
||||
const codeHash = this._api.util.sha3(code);
|
||||
const version = REGISTRY_V1_HASHES.includes(codeHash)
|
||||
? 1
|
||||
: 2;
|
||||
const abi = version === 1
|
||||
? abis.registry
|
||||
: abis.registry2;
|
||||
const contract = this._api.newContract(abi, address);
|
||||
|
||||
// Add support for previous `set` and `get` methods
|
||||
if (!contract.instance.get && contract.instance.getData) {
|
||||
contract.instance.get = contract.instance.getData;
|
||||
}
|
||||
|
||||
if (contract.instance.get && !contract.instance.getData) {
|
||||
contract.instance.getData = contract.instance.get;
|
||||
}
|
||||
|
||||
if (!contract.instance.set && contract.instance.setData) {
|
||||
contract.instance.set = contract.instance.setData;
|
||||
}
|
||||
|
||||
if (contract.instance.set && !contract.instance.setData) {
|
||||
contract.instance.setData = contract.instance.set;
|
||||
}
|
||||
|
||||
console.log(`registry at ${address}, code ${codeHash}, version ${version}`);
|
||||
this._registryContract = contract;
|
||||
return this._registryContract;
|
||||
});
|
||||
}
|
||||
|
||||
_createGetParams (_name, key) {
|
||||
const name = _name.toLowerCase();
|
||||
const sha3 = this._api.util.sha3.text(name);
|
||||
|
@ -35,6 +35,9 @@ function create () {
|
||||
}
|
||||
};
|
||||
api = {
|
||||
eth: {
|
||||
getCode: sinon.stub().resolves('0x123456')
|
||||
},
|
||||
parity: {
|
||||
registryAddress: sinon.stub().resolves('testRegistryAddress')
|
||||
},
|
||||
|
@ -17,6 +17,7 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
injectTapEventPlugin();
|
||||
|
||||
@ -27,6 +28,21 @@ import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<Application />,
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./dappreg/Application/index.js', () => {
|
||||
require('./dappreg/Application/index.js');
|
||||
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Application />
|
||||
</AppContainer>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -15,15 +15,17 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../_colors.css';
|
||||
|
||||
.body {
|
||||
color: #333;
|
||||
background: #eee;
|
||||
padding: 4.5em 0;
|
||||
color: $text-color;
|
||||
background: $background-color;
|
||||
padding: 3em 0 6em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.apps {
|
||||
background: #fff;
|
||||
background: white;
|
||||
border-radius: 0.5em;
|
||||
margin: 0 auto;
|
||||
max-width: 980px;
|
||||
@ -39,9 +41,8 @@
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #44e;
|
||||
border-radius: 0 0 0.25em 0.25em;
|
||||
color: #fff;
|
||||
background: $blue;
|
||||
color: white;
|
||||
left: 0;
|
||||
padding: 1em;
|
||||
position: fixed;
|
||||
@ -54,5 +55,5 @@
|
||||
text-align: center;
|
||||
padding-top: 5em;
|
||||
font-size: 2em;
|
||||
color: #999;
|
||||
color: $loading-color;
|
||||
}
|
||||
|
@ -19,18 +19,14 @@ 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 Dapps from '../Dapps';
|
||||
import Transactions from '../Transactions';
|
||||
import Warning from '../Warning';
|
||||
import styles from './application.css';
|
||||
|
||||
@observer
|
||||
export default class Application extends Component {
|
||||
dappsStore = DappsStore.instance();
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
render () {
|
||||
if (this.dappsStore.isLoading) {
|
||||
@ -41,23 +37,32 @@ export default class Application extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
const { ownDapps, otherDapps } = this.dappsStore;
|
||||
|
||||
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>
|
||||
<Dapps
|
||||
dapps={ ownDapps }
|
||||
own
|
||||
title='My Dapps'
|
||||
/>
|
||||
<Dapps
|
||||
dapps={ otherDapps }
|
||||
title='Other Dapps'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={ styles.footer }>
|
||||
{ this.dappsStore.count } applications registered, { this.dappsStore.ownedCount } owned by user
|
||||
</div>
|
||||
|
||||
<Transactions />
|
||||
<Warning />
|
||||
<ModalDelete />
|
||||
<ModalRegister />
|
||||
<ModalUpdate />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -15,11 +15,16 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../_colors.css';
|
||||
@import '../_utils.css';
|
||||
|
||||
.button {
|
||||
background: #44e;
|
||||
composes: bezier-transform;
|
||||
|
||||
background: $blue;
|
||||
border: none;
|
||||
border-radius: 0.25em;
|
||||
color: #fff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
margin: 1em 0.375em;
|
||||
@ -29,10 +34,14 @@
|
||||
&[disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
background: #aaa;
|
||||
background: $disabled-bg;
|
||||
}
|
||||
|
||||
&[data-warning="true"] {
|
||||
background: #e44;
|
||||
background: $warning-bg;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
@ -24,27 +24,29 @@ export default class Button extends Component {
|
||||
disabled: PropTypes.bool,
|
||||
label: PropTypes.string.isRequired,
|
||||
warning: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, disabled, label, warning } = this.props;
|
||||
const classes = `${styles.button} ${className}`;
|
||||
const classes = [ styles.button, className ];
|
||||
|
||||
return (
|
||||
<button
|
||||
className={ classes }
|
||||
className={ classes.join(' ') }
|
||||
data-warning={ warning }
|
||||
disabled={ disabled }
|
||||
onClick={ this.onClick }
|
||||
onClick={ this.handleClick }
|
||||
>
|
||||
{ label }
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
onClick = (event) => {
|
||||
handleClick = (event) => {
|
||||
if (this.props.disabled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,106 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import 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 || (!this.dappsStore.currentApp.isOwner && !this.dappsStore.isContractOwner) }
|
||||
onClick={ this.onDeleteClick }
|
||||
/>,
|
||||
<Button
|
||||
key='edit'
|
||||
label='Edit'
|
||||
disabled={ !this.dappsStore.currentApp || !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();
|
||||
}
|
||||
}
|
||||
}
|
66
js/src/dapps/dappreg/Card/card.css
Normal file
66
js/src/dapps/dappreg/Card/card.css
Normal file
@ -0,0 +1,66 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../_utils.css';
|
||||
|
||||
$imgSize: 6rem;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.card {
|
||||
composes: bezier-transform;
|
||||
|
||||
align-items: center;
|
||||
background-color: rgba(240, 240, 240, 0.75);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1rem;
|
||||
padding: 1rem;
|
||||
width: 10rem;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
cursor: pointer;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border: 1px dashed black;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-bottom: 0.75rem;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
height: $imgSize;
|
||||
width: $imgSize;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
99
js/src/dapps/dappreg/Card/card.js
Normal file
99
js/src/dapps/dappreg/Card/card.js
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import keycode from 'keycode';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import styles from './card.css';
|
||||
|
||||
export default class Card extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
dashed: PropTypes.bool,
|
||||
focus: PropTypes.bool,
|
||||
icon: PropTypes.object,
|
||||
name: PropTypes.object,
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
dashed: false,
|
||||
focus: false,
|
||||
name: { value: '' }
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.focus && !this.props.focus) {
|
||||
this.handleFocus();
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, dashed, icon, name } = this.props;
|
||||
|
||||
const cardClasses = [ styles.card ];
|
||||
|
||||
if (dashed) {
|
||||
cardClasses.push(styles.dashed);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<div
|
||||
className={ cardClasses.join(' ') }
|
||||
onClick={ this.handleClick }
|
||||
onKeyPress={ this.handleKeyPress }
|
||||
ref='card'
|
||||
tabIndex={ 0 }
|
||||
>
|
||||
<div className={ styles.icon }>
|
||||
{ icon }
|
||||
</div>
|
||||
<span
|
||||
className={ styles.name }
|
||||
title={ name.title || name.value }
|
||||
>
|
||||
{ name.value }
|
||||
</span>
|
||||
{ children }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleKeyPress = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName === 'enter') {
|
||||
return this.handleClick();
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
handleFocus = () => {
|
||||
setTimeout(() => {
|
||||
const element = ReactDOM.findDOMNode(this.refs.card);
|
||||
|
||||
element && element.focus();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
@ -14,4 +14,4 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './dapp';
|
||||
export default from './card';
|
84
js/src/dapps/dappreg/CreateDappCard/createDappCard.js
Normal file
84
js/src/dapps/dappreg/CreateDappCard/createDappCard.js
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import DappsStore from '../dappsStore';
|
||||
import Card from '../Card';
|
||||
import ModalRegister from '../ModalRegister';
|
||||
|
||||
import PlusImage from '~/../assets/images/dapps/plus.svg';
|
||||
|
||||
export default class CreateDappCard extends Component {
|
||||
state = {
|
||||
dappId: null,
|
||||
focus: false,
|
||||
open: false
|
||||
};
|
||||
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
render () {
|
||||
const { focus } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ this.renderModal() }
|
||||
|
||||
<Card
|
||||
dashed
|
||||
focus={ focus }
|
||||
icon={ (<img src={ PlusImage } />) }
|
||||
name={ { value: 'Register a dapp' } }
|
||||
onClick={ this.handleOpen }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderModal () {
|
||||
const { dappId, open } = this.state;
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalRegister
|
||||
dappId={ dappId }
|
||||
onClose={ this.handleClose }
|
||||
onRegister={ this.handleRegister }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleOpen = () => {
|
||||
const dappId = this.dappsStore.createDappId();
|
||||
|
||||
this.setState({ focus: false, open: true, dappId });
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ focus: true, open: false, dappId: null });
|
||||
}
|
||||
|
||||
handleRegister = () => {
|
||||
const { dappId } = this.state;
|
||||
|
||||
this.dappsStore.register(dappId);
|
||||
this.handleClose();
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/CreateDappCard/index.js
Normal file
17
js/src/dapps/dappreg/CreateDappCard/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './createDappCard';
|
@ -1,174 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import 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;
|
||||
|
||||
if (!app) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,8 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.buttonbar {
|
||||
.author,
|
||||
.version {
|
||||
font-size: 0.75rem;
|
||||
text-align: center;
|
||||
margin: 1em 0 0 0;
|
||||
}
|
110
js/src/dapps/dappreg/DappCard/dappCard.js
Normal file
110
js/src/dapps/dappreg/DappCard/dappCard.js
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import Card from '../Card';
|
||||
import DappsStore from '../dappsStore';
|
||||
import DappModal from '../DappModal';
|
||||
|
||||
import styles from './dappCard.css';
|
||||
|
||||
export default class DappCard extends Component {
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
static propTypes = {
|
||||
dapp: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
focus: false,
|
||||
open: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { dapp } = this.props;
|
||||
const { focus } = this.state;
|
||||
const { id, image } = dapp;
|
||||
const manifest = dapp.manifest.content;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ this.renderModal() }
|
||||
|
||||
<Card
|
||||
focus={ focus }
|
||||
icon={ this.renderImage(image.url) }
|
||||
name={ { title: id, value: manifest && manifest.name || id } }
|
||||
onClick={ this.handleOpen }
|
||||
>
|
||||
{ this.renderVersion(manifest) }
|
||||
{ this.renderAuthor(manifest) }
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderModal () {
|
||||
const { dapp } = this.props;
|
||||
const { open } = this.state;
|
||||
|
||||
return (
|
||||
<DappModal
|
||||
dapp={ dapp }
|
||||
onClose={ this.handleClose }
|
||||
open={ open }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderImage (url) {
|
||||
return (
|
||||
<img src={ url } />
|
||||
);
|
||||
}
|
||||
|
||||
renderVersion (manifest) {
|
||||
if (!manifest || !manifest.version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={ styles.version }>
|
||||
v{ manifest.version }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderAuthor (manifest) {
|
||||
if (!manifest || !manifest.author) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={ styles.author }>
|
||||
by { manifest && manifest.author }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ focus: true, open: false });
|
||||
}
|
||||
|
||||
handleOpen = () => {
|
||||
this.setState({ focus: false, open: true });
|
||||
}
|
||||
}
|
@ -14,4 +14,4 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './buttonBar';
|
||||
export default from './dappCard';
|
82
js/src/dapps/dappreg/DappModal/dappModal.css
Normal file
82
js/src/dapps/dappreg/DappModal/dappModal.css
Normal file
@ -0,0 +1,82 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../_colors.css';
|
||||
@import '../_utils.css';
|
||||
|
||||
$imgSize: 5rem;
|
||||
|
||||
.code {
|
||||
color: $code-color;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
margin-top: 1rem;
|
||||
|
||||
.codeTitle {
|
||||
align-items: center;
|
||||
background-color: $code-title-bg;
|
||||
color: $code-title-color;
|
||||
display: inline-flex;
|
||||
height: 2rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.codeContainer {
|
||||
background-color: $code-bg;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.75rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
height: 0.5rem;
|
||||
position: relative;
|
||||
text-align: right;
|
||||
top: 0.5rem;
|
||||
|
||||
.button {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
padding: 0.5rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 1.5rem;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 50%;
|
||||
height: $imgSize;
|
||||
width: $imgSize;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #ddd;
|
||||
font-size: 0.75rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
423
js/src/dapps/dappreg/DappModal/dappModal.js
Normal file
423
js/src/dapps/dappreg/DappModal/dappModal.js
Normal file
@ -0,0 +1,423 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { api } from '../parity';
|
||||
import DappsStore from '../dappsStore';
|
||||
import Button from '../Button';
|
||||
import Input from '../Input';
|
||||
import Modal from '../Modal';
|
||||
import ModalDelete from '../ModalDelete';
|
||||
import ModalUpdate from '../ModalUpdate';
|
||||
import SelectAccount from '../SelectAccount';
|
||||
|
||||
import styles from './dappModal.css';
|
||||
|
||||
@observer
|
||||
export default class DappModal extends Component {
|
||||
static propTypes = {
|
||||
dapp: PropTypes.object.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
showDelete: false,
|
||||
showUpdate: false,
|
||||
updates: null,
|
||||
updating: false
|
||||
};
|
||||
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
render () {
|
||||
const { dapp, open } = this.props;
|
||||
const { showDelete, showUpdate, updates } = this.state;
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
showDelete
|
||||
? (
|
||||
<ModalDelete
|
||||
dappId={ dapp.id }
|
||||
onClose={ this.handleDeleteClose }
|
||||
onDelete={ this.handleDeleteConfirm }
|
||||
/>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{
|
||||
showUpdate
|
||||
? (
|
||||
<ModalUpdate
|
||||
dappId={ dapp.id }
|
||||
onClose={ this.handleUpdateClose }
|
||||
onConfirm={ this.handleUpdateConfirm }
|
||||
updates={ updates }
|
||||
/>
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
||||
<Modal
|
||||
header={ this.renderHeader(dapp) }
|
||||
onClose={ this.handleClose }
|
||||
>
|
||||
{ this.renderContent(dapp) }
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent (dapp) {
|
||||
const manifest = dapp.manifest.content || {};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{ this.renderInputs(dapp) }
|
||||
</div>
|
||||
|
||||
{ this.renderActions(dapp) }
|
||||
|
||||
<div className={ styles.code }>
|
||||
<div className={ styles.codeTitle }>manifest.json</div>
|
||||
<div className={ styles.codeContainer }>
|
||||
<code>{ JSON.stringify(manifest, null, 2) }</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions (dapp) {
|
||||
if (!dapp.isOwner && !dapp.isContractOwner) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!dapp.isOwner && dapp.isContractOwner) {
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
label='Delete'
|
||||
onClick={ this.handleDelete }
|
||||
warning
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { isEditing } = dapp;
|
||||
const { updating } = this.state;
|
||||
|
||||
if (updating) {
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
disabled
|
||||
label='Updating...'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
disabled={ !dapp.canSave }
|
||||
label='Save'
|
||||
onClick={ this.handleSave }
|
||||
/>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
label='Fetch Registry'
|
||||
onClick={ this.handleFetchRegistry }
|
||||
/>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
label='Cancel'
|
||||
onClick={ this.handleCancel }
|
||||
warning
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
label='Edit'
|
||||
onClick={ this.handleEdit }
|
||||
/>
|
||||
<Button
|
||||
className={ styles.button }
|
||||
label='Delete'
|
||||
onClick={ this.handleDelete }
|
||||
warning
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeader (dapp) {
|
||||
const { id, image } = dapp;
|
||||
const manifest = dapp.manifest.content || {};
|
||||
|
||||
const infos = [];
|
||||
|
||||
if (manifest.version) {
|
||||
infos.push(`v${manifest.version}`);
|
||||
}
|
||||
|
||||
if (manifest.author) {
|
||||
infos.push(`by ${manifest.author}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.icon }>
|
||||
<img src={ image.url } />
|
||||
</div>
|
||||
<div>
|
||||
<div className={ styles.name }>
|
||||
{ manifest.name || 'Unnamed' }
|
||||
</div>
|
||||
<div className={ styles.info }>
|
||||
{ id }
|
||||
</div>
|
||||
<div className={ styles.info }>
|
||||
{ infos.length > 0 ? infos.join(', ') : null }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInputs (dapp) {
|
||||
return [
|
||||
this.renderOwner(dapp),
|
||||
this.renderHashInput(dapp, 'image', 'Image URL', true),
|
||||
this.renderHashInput(dapp, 'manifest', 'Manifest URL'),
|
||||
this.renderHashInput(dapp, 'content', 'Content URL')
|
||||
];
|
||||
}
|
||||
|
||||
renderOwner (dapp) {
|
||||
const { isEditing } = dapp;
|
||||
|
||||
if (isEditing) {
|
||||
return this.renderOwnerSelect(dapp);
|
||||
}
|
||||
|
||||
return this.renderOwnerStatic(dapp);
|
||||
}
|
||||
|
||||
renderOwnerSelect (dapp) {
|
||||
const overlayImage = (
|
||||
<img
|
||||
className={ styles.overlayImage }
|
||||
src={ api.util.createIdentityImg(this.props.dapp.wip.owner.address, 4) }
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Input
|
||||
key='owner_select'
|
||||
hint={ this.props.dapp.wip.owner.address }
|
||||
label='Owner, select the application owner and editor'
|
||||
overlay={ overlayImage }
|
||||
>
|
||||
<SelectAccount
|
||||
onSelect={ this.handleSelectOwner }
|
||||
value={ dapp.wip.owner.address }
|
||||
/>
|
||||
</Input>
|
||||
);
|
||||
}
|
||||
|
||||
renderOwnerStatic (dapp) {
|
||||
const overlayImage = (
|
||||
<img
|
||||
className={ styles.overlayImage }
|
||||
src={ api.util.createIdentityImg(dapp.owner.address, 4) }
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Input
|
||||
key='owner_static'
|
||||
hint={ dapp.owner.address }
|
||||
label='Owner, the application owner and editor'
|
||||
overlay={ overlayImage }
|
||||
>
|
||||
<input
|
||||
readOnly
|
||||
tabIndex={ -1 }
|
||||
value={ dapp.owner.name || dapp.owner.address }
|
||||
/>
|
||||
</Input>
|
||||
);
|
||||
}
|
||||
|
||||
renderHashInput (dapp, type, label, isImage = false) {
|
||||
const handleChange = (event) => {
|
||||
return this.handleChangeHash(event, type);
|
||||
};
|
||||
|
||||
const { isEditing, wip } = dapp;
|
||||
|
||||
const changed = wip && wip[type].changed;
|
||||
const error = wip && wip[type].error;
|
||||
|
||||
const hash = dapp[type].hash;
|
||||
const url = dapp[type].url;
|
||||
|
||||
const overlayImage = (isImage && hash)
|
||||
? (
|
||||
<img
|
||||
className={ styles.overlayImage }
|
||||
src={ `/api/content/${hash.substr(2)}` }
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
|
||||
const wipUrl = isEditing && wip && wip[type].url;
|
||||
|
||||
const hint = error || (!changed && hash) || '...';
|
||||
const value = typeof wipUrl !== 'string'
|
||||
? url || ''
|
||||
: wipUrl;
|
||||
|
||||
return (
|
||||
<Input
|
||||
key={ `${type}Edit` }
|
||||
hint={ hint }
|
||||
label={ label }
|
||||
overlay={ overlayImage }
|
||||
>
|
||||
<input
|
||||
data-dirty={ changed }
|
||||
data-error={ !!error }
|
||||
onChange={ handleChange }
|
||||
readOnly={ !isEditing }
|
||||
tabIndex={ isEditing ? 0 : -1 }
|
||||
value={ value }
|
||||
/>
|
||||
</Input>
|
||||
);
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.handleCancel();
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
handleSelectOwner = (event) => {
|
||||
const { value } = event.target;
|
||||
|
||||
const changed = (this.props.dapp.owner.address !== value);
|
||||
|
||||
this.props.dapp.handleChange({
|
||||
owner: {
|
||||
address: value,
|
||||
changed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeHash = (event, type) => {
|
||||
if (!this.props.dapp.isEditing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = event.target.value;
|
||||
const changed = (this.props.dapp[type].url !== url);
|
||||
|
||||
this.props.dapp.handleChange({
|
||||
[ type ]: {
|
||||
error: null,
|
||||
changed,
|
||||
url
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleFetchRegistry = () => {
|
||||
this.dappsStore.fetchRegistryData(this.props.dapp);
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.props.dapp.setEditing(false);
|
||||
}
|
||||
|
||||
handleEdit = () => {
|
||||
this.props.dapp.setEditing(true);
|
||||
}
|
||||
|
||||
handleDelete = () => {
|
||||
this.setState({ showDelete: true });
|
||||
}
|
||||
|
||||
handleDeleteClose = () => {
|
||||
this.setState({ showDelete: false });
|
||||
}
|
||||
|
||||
handleDeleteConfirm = () => {
|
||||
this.dappsStore.delete(this.props.dapp);
|
||||
this.handleDeleteClose();
|
||||
this.handleClose();
|
||||
}
|
||||
|
||||
handleSave = () => {
|
||||
const updates = this.props.dapp.handleSave();
|
||||
|
||||
this.setState({ showUpdate: true, updates });
|
||||
}
|
||||
|
||||
handleUpdateClose = () => {
|
||||
this.setState({ showUpdate: false, updates: null });
|
||||
}
|
||||
|
||||
handleUpdateConfirm = () => {
|
||||
const { id, owner } = this.props.dapp;
|
||||
const { updates } = this.state;
|
||||
|
||||
this.handleUpdateClose();
|
||||
this.handleCancel();
|
||||
this.setState({ updating: true });
|
||||
|
||||
return this.dappsStore.update(id, owner.address, updates)
|
||||
.then(() => {
|
||||
this.setState({ updating: false });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({ updating: false });
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
@ -14,4 +14,4 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './selectDapp';
|
||||
export default from './dappModal';
|
38
js/src/dapps/dappreg/Dapps/dapps.css
Normal file
38
js/src/dapps/dappreg/Dapps/dapps.css
Normal file
@ -0,0 +1,38 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.dapps {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
margin: 1rem;
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
73
js/src/dapps/dappreg/Dapps/dapps.js
Normal file
73
js/src/dapps/dappreg/Dapps/dapps.js
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import CreateDappCard from '../CreateDappCard';
|
||||
import DappCard from '../DappCard';
|
||||
|
||||
import styles from './dapps.css';
|
||||
|
||||
export default class Dapps extends Component {
|
||||
static propTypes = {
|
||||
dapps: PropTypes.array.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
own: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
own: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { dapps, title } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.dapps }>
|
||||
<h2 className={ styles.title }>{ title }</h2>
|
||||
<div className={ styles.container }>
|
||||
{ this.renderAddDapp() }
|
||||
{ this.renderDapps(dapps) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddDapp () {
|
||||
const { own } = this.props;
|
||||
|
||||
if (!own) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<CreateDappCard />
|
||||
);
|
||||
}
|
||||
|
||||
renderDapps (dapps) {
|
||||
return dapps.map((dapp) => {
|
||||
const { id } = dapp;
|
||||
|
||||
return (
|
||||
<DappCard
|
||||
dapp={ dapp }
|
||||
key={ id }
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
17
js/src/dapps/dappreg/Dapps/index.js
Normal file
17
js/src/dapps/dappreg/Dapps/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './dapps';
|
@ -30,6 +30,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.withOverlay input {
|
||||
padding-right: 3em;
|
||||
}
|
||||
|
||||
input {
|
||||
padding-bottom: 1.5em;
|
||||
|
||||
|
@ -29,8 +29,14 @@ export default class Input extends Component {
|
||||
render () {
|
||||
const { children, hint, label, overlay } = this.props;
|
||||
|
||||
const inputClasses = [ styles.input ];
|
||||
|
||||
if (overlay) {
|
||||
inputClasses.push(styles.withOverlay);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.input }>
|
||||
<div className={ inputClasses.join(' ') }>
|
||||
<label>
|
||||
{ label }
|
||||
</label>
|
||||
|
@ -15,34 +15,95 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../_colors.css';
|
||||
@import '../_utils.css';
|
||||
|
||||
.modal {
|
||||
.body {
|
||||
align-items: center;
|
||||
background-color: $modal-bg;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
z-index: 50;
|
||||
z-index: 150;
|
||||
|
||||
.dialog {
|
||||
background: #fff;
|
||||
border-radius: 0 0 0.25em 0.25em;
|
||||
margin: 0 auto;
|
||||
max-width: 840px;
|
||||
text-align: left;
|
||||
&.secondary {
|
||||
align-items: flex-start;
|
||||
z-index: 200;
|
||||
|
||||
.content {
|
||||
line-height: 1.5em;
|
||||
padding: 2em;
|
||||
line-height: 1.5rem;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
max-width: 840px;
|
||||
width: 85vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
composes: bezier-transform;
|
||||
|
||||
color: white;
|
||||
display: inline-block;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 4rem;
|
||||
opacity: 0.75;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
right: 1rem;
|
||||
top: 0.25rem;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
|
||||
.closeIcon {
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
composes: bezier-transform;
|
||||
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.dialog {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 90vh;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
max-width: 740px;
|
||||
width: 75vw;
|
||||
|
||||
> * {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
|
||||
.section {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
&.error {
|
||||
color: #f44;
|
||||
* {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,54 +118,18 @@
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #44e;
|
||||
color: #fff;
|
||||
opacity: 0.85;
|
||||
padding: 1em;
|
||||
background-color: $blue;
|
||||
color: white;
|
||||
min-height: 3rem;
|
||||
|
||||
&.error {
|
||||
background: #e44;
|
||||
&,
|
||||
& > * {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
|
@ -14,53 +14,147 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import keycode from 'keycode';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Button from '../Button';
|
||||
|
||||
import styles from './modal.css';
|
||||
import CloseImage from '~/../assets/images/dapps/close.svg';
|
||||
|
||||
export default class Modal extends Component {
|
||||
static propTypes = {
|
||||
buttons: PropTypes.node,
|
||||
actions: PropTypes.array,
|
||||
children: PropTypes.node,
|
||||
error: PropTypes.object,
|
||||
header: PropTypes.string
|
||||
}
|
||||
header: PropTypes.node,
|
||||
secondary: PropTypes.bool,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onConfirm: PropTypes.func
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
actions: null,
|
||||
secondary: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { children, buttons, error, header } = this.props;
|
||||
const { children, actions, header, secondary } = this.props;
|
||||
|
||||
const modalClasses = [ styles.modal ];
|
||||
|
||||
if (secondary) {
|
||||
modalClasses.push(styles.secondary);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.modal }>
|
||||
<div className={ styles.overlay } />
|
||||
<div className={ styles.body }>
|
||||
<div className={ styles.dialog }>
|
||||
<div className={ `${styles.header} ${error ? styles.error : ''}` }>
|
||||
<div
|
||||
className={ modalClasses.join(' ') }
|
||||
onClick={ this.handleClose }
|
||||
onKeyUp={ this.handleKeyPress }
|
||||
>
|
||||
<div
|
||||
className={ styles.dialog }
|
||||
onClick={ this.stopEvent }
|
||||
ref={ this.handleSetRef }
|
||||
tabIndex={ open ? 0 : null }
|
||||
>
|
||||
<div className={ styles.header }>
|
||||
{ header }
|
||||
<div
|
||||
className={ styles.close }
|
||||
onClick={ this.handleClose }
|
||||
onKeyPress={ this.handleCloseKeyPress }
|
||||
tabIndex={ open ? 0 : null }
|
||||
title='close'
|
||||
>
|
||||
<img
|
||||
className={ styles.closeIcon }
|
||||
src={ CloseImage }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={ styles.content }>
|
||||
{ error ? this.renderError() : children }
|
||||
</div>
|
||||
<div className={ styles.footer }>
|
||||
{ buttons }
|
||||
</div>
|
||||
{ children }
|
||||
</div>
|
||||
|
||||
{ actions ? this.renderActions(actions) : null }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderError () {
|
||||
const { error } = this.props;
|
||||
renderActions (actions) {
|
||||
return (
|
||||
<div className={ styles.footer }>
|
||||
{ actions.map((action) => {
|
||||
let onClick = () => {};
|
||||
|
||||
switch (action.type) {
|
||||
case 'confirm':
|
||||
onClick = this.handleConfirm;
|
||||
break;
|
||||
|
||||
case 'close':
|
||||
onClick = this.handleClose;
|
||||
break;
|
||||
}
|
||||
|
||||
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>
|
||||
<Button
|
||||
key={ action.type }
|
||||
label={ action.label }
|
||||
warning={ action.warning }
|
||||
onClick={ onClick }
|
||||
/>
|
||||
);
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
stopEvent = (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handleKeyPress = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName === 'esc') {
|
||||
return this.handleClose();
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
handleCloseKeyPress = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName === 'enter') {
|
||||
return this.handleClose();
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
handleSetRef = (containerRef) => {
|
||||
// Focus after the modal is open
|
||||
setTimeout(() => {
|
||||
const element = ReactDOM.findDOMNode(containerRef);
|
||||
|
||||
element && element.focus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
handleConfirm = () => {
|
||||
this.props.onConfirm && this.props.onConfirm();
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
@ -14,150 +14,48 @@
|
||||
// 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 React, { Component, PropTypes } from '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();
|
||||
static propTypes = {
|
||||
dappId: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
if (!this.modalStore.showingDelete) {
|
||||
return null;
|
||||
}
|
||||
const { dappId, onClose, onDelete } = this.props;
|
||||
const actions = [
|
||||
{ type: 'close', label: 'No, Cancel' },
|
||||
{ type: 'confirm', label: 'Yes, Delete', warning: true }
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
buttons={ this.renderButtons() }
|
||||
error={ this.modalStore.errorDelete }
|
||||
header={ HEADERS[this.modalStore.stepDelete] }
|
||||
actions={ actions }
|
||||
header='Confirm Application Deletion'
|
||||
onClose={ onClose }
|
||||
onConfirm={ onDelete }
|
||||
secondary
|
||||
>
|
||||
{ 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>
|
||||
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 }>
|
||||
Application identifier
|
||||
</div>
|
||||
<div>
|
||||
{ this.dappsStore.currentApp.id }
|
||||
</div>
|
||||
{ dappId }
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderStepWait (waitingFor) {
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.section }>
|
||||
{ waitingFor }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onClickClose = () => {
|
||||
this.modalStore.hideDelete();
|
||||
}
|
||||
|
||||
onClickYes = () => {
|
||||
this.modalStore.doDelete();
|
||||
}
|
||||
}
|
||||
|
@ -14,150 +14,55 @@
|
||||
// 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 React, { Component, PropTypes } 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();
|
||||
static propTypes = {
|
||||
dappId: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onRegister: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
render () {
|
||||
if (!this.modalStore.showingRegister) {
|
||||
return null;
|
||||
}
|
||||
const { onClose, onRegister } = this.props;
|
||||
const actions = [
|
||||
{ type: 'close', label: 'No, Cancel' },
|
||||
{ type: 'confirm', label: 'Yes, Register', warning: true }
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
buttons={ this.renderButtons() }
|
||||
error={ this.modalStore.errorRegister }
|
||||
header={ HEADERS[this.modalStore.stepRegister] }
|
||||
actions={ actions }
|
||||
header='Confirm Application Registration'
|
||||
onClose={ onClose }
|
||||
onConfirm={ onRegister }
|
||||
secondary
|
||||
>
|
||||
{ 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>
|
||||
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 }>
|
||||
Unique assigned application identifier
|
||||
</div>
|
||||
<div>
|
||||
{ this.dappsStore.wipApp.id }
|
||||
</div>
|
||||
{ this.props.dappId }
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderStepWait (waitingFor) {
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.section }>
|
||||
{ waitingFor }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onClickClose = () => {
|
||||
this.modalStore.hideRegister();
|
||||
}
|
||||
|
||||
onClickConfirmYes = () => {
|
||||
this.modalStore.doRegister();
|
||||
}
|
||||
}
|
||||
|
@ -14,160 +14,71 @@
|
||||
// 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 React, { Component, PropTypes } from '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();
|
||||
static propTypes = {
|
||||
dappId: PropTypes.string.isRequired,
|
||||
updates: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
if (!this.modalStore.showingUpdate) {
|
||||
return null;
|
||||
}
|
||||
const { dappId, onClose, onConfirm } = this.props;
|
||||
const actions = [
|
||||
{ type: 'close', label: 'No, Cancel' },
|
||||
{ type: 'confirm', label: 'Yes, Update', warning: true }
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
buttons={ this.renderButtons() }
|
||||
error={ this.modalStore.errorUpdate }
|
||||
header={ HEADERS[this.modalStore.stepUpdate] }
|
||||
actions={ actions }
|
||||
header='Confirm Application Update'
|
||||
onClose={ onClose }
|
||||
onConfirm={ onConfirm }
|
||||
secondary
|
||||
>
|
||||
{ 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.
|
||||
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 }
|
||||
{ dappId }
|
||||
</div>
|
||||
</div>
|
||||
{ this.renderChanges() }
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderChanges () {
|
||||
return ['content', 'image', 'manifest']
|
||||
.filter((type) => this.dappsStore.wipApp[`${type}Changed`])
|
||||
const { updates } = this.props;
|
||||
|
||||
return Object.keys(updates)
|
||||
.map((type) => {
|
||||
return (
|
||||
<div className={ styles.section } key={ `${type}Update` }>
|
||||
<div
|
||||
className={ styles.section }
|
||||
key={ `${type}Update` }
|
||||
>
|
||||
<div className={ styles.heading }>
|
||||
Updates to { type } hash
|
||||
Updates to { type }
|
||||
</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>{ updates[type] || '(removed)' }</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderStepWait (waitingFor) {
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.section }>
|
||||
{ waitingFor }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onClickClose = () => {
|
||||
this.modalStore.hideUpdate();
|
||||
}
|
||||
|
||||
onClickYes = () => {
|
||||
this.modalStore.doUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -14,20 +14,27 @@
|
||||
// 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 React, { Component, PropTypes } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import DappsStore from '../dappsStore';
|
||||
|
||||
@observer
|
||||
export default class SelectAccount extends Component {
|
||||
dappsStore = DappsStore.instance();
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
onSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value } = this.props;
|
||||
|
||||
return (
|
||||
<select
|
||||
value={ this.dappsStore.currentAccount.address }
|
||||
onChange={ this.onSelect }
|
||||
value={ value }
|
||||
onChange={ this.handleSelect }
|
||||
>
|
||||
{ this.renderOptions() }
|
||||
</select>
|
||||
@ -44,7 +51,7 @@ export default class SelectAccount extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
onSelect = (event) => {
|
||||
this.dappsStore.setCurrentAccount(event.target.value);
|
||||
handleSelect = (event) => {
|
||||
this.props.onSelect && this.props.onSelect(event);
|
||||
}
|
||||
}
|
||||
|
@ -1,85 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import 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>
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.dappsStore.currentApp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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/Transactions/index.js
Normal file
17
js/src/dapps/dappreg/Transactions/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './transactions';
|
95
js/src/dapps/dappreg/Transactions/transactions.css
Normal file
95
js/src/dapps/dappreg/Transactions/transactions.css
Normal file
@ -0,0 +1,95 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
bottom: 4rem;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
z-index: 150;
|
||||
}
|
||||
|
||||
.transaction {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 0.5rem;
|
||||
width: 30rem;
|
||||
|
||||
.header {
|
||||
flex: 0 0 auto;
|
||||
height: 1.5rem;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
> * {
|
||||
display: inline-block;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 0.75rem;
|
||||
overflow: hidden;
|
||||
padding: 0.25rem 0.5rem;
|
||||
position: absolute;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
background-color: rgba(40, 40, 40, 0.95);
|
||||
color: white;
|
||||
left: 0.5rem;
|
||||
margin-right: 1.5rem;
|
||||
max-width: 20rem;
|
||||
}
|
||||
|
||||
.date {
|
||||
background-color: rgba(215, 215, 215, 0.95);
|
||||
color: black;
|
||||
max-width: 5rem;
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
background-color: rgba(80, 80, 80, 0.95);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
padding: 1rem 0.75rem;
|
||||
|
||||
> * {
|
||||
max-width: 100%;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.error {
|
||||
background-color: rgba(255, 68, 68, 0.95);
|
||||
}
|
||||
|
||||
&.pending {
|
||||
background-color: rgba(243, 156, 18, 0.95);
|
||||
}
|
||||
|
||||
&.confirmed {
|
||||
background-color: rgba(39, 174, 96, 0.95);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
136
js/src/dapps/dappreg/Transactions/transactions.js
Normal file
136
js/src/dapps/dappreg/Transactions/transactions.js
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import DappsStore from '../dappsStore';
|
||||
|
||||
import styles from './transactions.css';
|
||||
|
||||
@observer
|
||||
export default class Transactions extends Component {
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
render () {
|
||||
const { transactions } = this.dappsStore;
|
||||
const displayedTransactions = Object.values(transactions)
|
||||
.filter((tx) => !tx.hide)
|
||||
.sort((txA, txB) => txB.start - txA.start);
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
{ displayedTransactions.map((transaction) => this.renderTransaction(transaction)) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTransaction (transaction) {
|
||||
const { error, name, requestId, start, transactionHash, transactionReceipt } = transaction;
|
||||
|
||||
const date = new Date(start);
|
||||
const isError = !!error;
|
||||
const isPendingNetwork = transactionHash && !transactionReceipt;
|
||||
const isConfirmed = !!transactionReceipt;
|
||||
|
||||
const transactionClasses = [ styles.content ];
|
||||
const handleHideTransaction = (event) => this.handleHideTransaction(event, requestId);
|
||||
|
||||
if (isError) {
|
||||
transactionClasses.push(styles.error);
|
||||
}
|
||||
|
||||
if (isPendingNetwork) {
|
||||
transactionClasses.push(styles.pending);
|
||||
}
|
||||
|
||||
if (isConfirmed) {
|
||||
transactionClasses.push(styles.confirmed);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ styles.transaction }
|
||||
key={ requestId }
|
||||
>
|
||||
<div className={ styles.header }>
|
||||
{
|
||||
name
|
||||
? (
|
||||
<div
|
||||
className={ styles.name }
|
||||
title={ name }
|
||||
>
|
||||
{ name }
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div
|
||||
className={ styles.date }
|
||||
title={ date.toISOString() }
|
||||
>
|
||||
{ date.toLocaleTimeString() }
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={ transactionClasses.join(' ') }
|
||||
onClick={ handleHideTransaction }
|
||||
>
|
||||
{ this.renderTransactionContent(transaction) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTransactionContent (transaction) {
|
||||
const { error, transactionHash, transactionReceipt } = transaction;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
{ error.text || error.message || error.toString() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (transactionReceipt) {
|
||||
return (
|
||||
<div>
|
||||
Transaction mined at block { transactionReceipt.blockNumber.toFormat(0) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (transactionHash) {
|
||||
return (
|
||||
<div>
|
||||
Transaction sent to network with hash { transactionHash }..
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
Transaction waiting to be signed...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleHideTransaction = (event, requestId) => {
|
||||
this.dappsStore.updateTransaction(requestId, { hide: true });
|
||||
}
|
||||
}
|
@ -15,11 +15,13 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../_colors.css';
|
||||
|
||||
.warning {
|
||||
background: #f44;
|
||||
background: $warning-bg;
|
||||
border-top-right-radius: 0.25em;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 0.75em;
|
||||
left: 0;
|
||||
|
@ -15,37 +15,41 @@
|
||||
// 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();
|
||||
dappsStore = DappsStore.get();
|
||||
|
||||
state = {
|
||||
show: true
|
||||
};
|
||||
|
||||
render () {
|
||||
if (!this.modalStore.showingWarning) {
|
||||
if (!this.state.show) {
|
||||
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.
|
||||
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.
|
||||
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();
|
||||
this.setState({ show: false });
|
||||
}
|
||||
}
|
||||
|
30
js/src/dapps/dappreg/_colors.css
Normal file
30
js/src/dapps/dappreg/_colors.css
Normal file
@ -0,0 +1,30 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
$blue: rgb(41, 128, 185);
|
||||
$disabled-bg: #aaa;
|
||||
$warning-bg: #e44;
|
||||
$modal-bg: rgba(40, 40, 40, 0.75);
|
||||
|
||||
$background-color: #eee;
|
||||
$text-color: #333;
|
||||
$loading-color: #999;
|
||||
|
||||
$code-bg: #002b36;
|
||||
$code-color: #b58900;
|
||||
$code-title-bg: #073642;
|
||||
$code-title-color: #859900;
|
@ -15,5 +15,8 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.app {
|
||||
.bezier-transform {
|
||||
transition-duration: 0.1s;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
|
||||
}
|
153
js/src/dapps/dappreg/dappStore.js
Normal file
153
js/src/dapps/dappreg/dappStore.js
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, computed, observable, transaction } from 'mobx';
|
||||
|
||||
export default class DappStore {
|
||||
@observable id = null;
|
||||
@observable content = null;
|
||||
@observable image = null;
|
||||
@observable manifest = null;
|
||||
@observable owner = null;
|
||||
@observable isOwner = false;
|
||||
|
||||
@observable isEditing = false;
|
||||
@observable wip = null;
|
||||
|
||||
contractOwner = '';
|
||||
isContractOwner = false;
|
||||
|
||||
constructor (data) {
|
||||
const { id, content = {}, image = {}, manifest = {}, owner = {}, isOwner = false, contractOwner = '', isContractOwner = false } = data;
|
||||
|
||||
transaction(() => {
|
||||
this.id = id;
|
||||
this.content = content;
|
||||
this.image = image;
|
||||
this.manifest = manifest;
|
||||
this.owner = owner;
|
||||
this.isOwner = isOwner;
|
||||
|
||||
this.copyToWip();
|
||||
});
|
||||
|
||||
this.contractOwner = contractOwner;
|
||||
this.isContractOwner = isContractOwner;
|
||||
}
|
||||
|
||||
@computed get canSave () {
|
||||
if (!this.wip) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { content, image, manifest, owner } = this.wip;
|
||||
const fields = [ content, image, manifest, owner ];
|
||||
|
||||
const hasError = !!fields.find((field) => field.error);
|
||||
const hasChanged = !!fields.find((field) => field.changed);
|
||||
const isEditMode = this.isEditing;
|
||||
|
||||
return isEditMode && hasChanged && !hasError;
|
||||
}
|
||||
|
||||
@action copyToWip = () => {
|
||||
const defaults = {
|
||||
changed: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
const wip = {
|
||||
id: this.id,
|
||||
content: {
|
||||
...defaults,
|
||||
url: this.content.url
|
||||
},
|
||||
image: {
|
||||
...defaults,
|
||||
url: this.image.url
|
||||
},
|
||||
manifest: {
|
||||
...defaults,
|
||||
url: this.manifest.url
|
||||
},
|
||||
owner: {
|
||||
...defaults,
|
||||
address: this.owner.address
|
||||
}
|
||||
};
|
||||
|
||||
this.wip = { ...wip };
|
||||
}
|
||||
|
||||
@action handleChange = (details) => {
|
||||
if (!this.isEditing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.wip = {
|
||||
...this.wip,
|
||||
...details
|
||||
};
|
||||
|
||||
return this.wip;
|
||||
}
|
||||
|
||||
@action handleSave = () => {
|
||||
const updates = {};
|
||||
|
||||
if (this.wip.content.url !== this.content.url) {
|
||||
updates.content = this.wip.content.url;
|
||||
}
|
||||
|
||||
if (this.wip.image.url !== this.image.url) {
|
||||
updates.image = this.wip.image.url;
|
||||
}
|
||||
|
||||
if (this.wip.manifest.url !== this.manifest.url) {
|
||||
updates.manifest = this.wip.manifest.url;
|
||||
}
|
||||
|
||||
if (this.wip.owner.address !== this.owner.address) {
|
||||
updates.owner = this.wip.owner.address;
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
@action setEditing = (mode) => {
|
||||
transaction(() => {
|
||||
this.isEditing = mode;
|
||||
this.copyToWip();
|
||||
});
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
update = (updates) => {
|
||||
const { image, content } = updates;
|
||||
const changes = {};
|
||||
|
||||
if (image && image !== this.wip.image.url) {
|
||||
changes.image = { url: image, changed: true };
|
||||
}
|
||||
|
||||
if (content && content !== this.wip.content.url) {
|
||||
changes.content = { url: content, changed: true };
|
||||
}
|
||||
|
||||
return this.handleChange(changes);
|
||||
}
|
||||
}
|
@ -16,11 +16,15 @@
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { action, computed, observable, transaction } from 'mobx';
|
||||
import { flatten } from 'lodash';
|
||||
|
||||
import * as abis from '~/contracts/abi';
|
||||
import Contracts from '~/contracts';
|
||||
import builtins from '~/views/Dapps/builtin.json';
|
||||
import Dapp from './dappStore.js';
|
||||
import { deleteDapp, registerDapp, updateDapp } from './utils';
|
||||
|
||||
import { api } from './parity';
|
||||
import { api, trackRequest } from './parity';
|
||||
|
||||
let instance = null;
|
||||
|
||||
@ -28,23 +32,22 @@ export default class DappsStore {
|
||||
@observable accounts = [];
|
||||
@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;
|
||||
@observable transactions = {};
|
||||
|
||||
_instanceGhh = null;
|
||||
_instanceReg = null;
|
||||
_registry = null;
|
||||
_startTime = Date.now();
|
||||
|
||||
constructor () {
|
||||
this._loadDapps();
|
||||
}
|
||||
|
||||
static instance () {
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new DappsStore();
|
||||
}
|
||||
@ -52,141 +55,69 @@ export default class 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);
|
||||
createDappId () {
|
||||
return api.util.sha3(`${this._startTime}_${Date.now()}`);
|
||||
}
|
||||
|
||||
@computed get ownedCount () {
|
||||
return (this.apps.filter((app) => app.isOwner) || []).length;
|
||||
return this.ownDapps.length;
|
||||
}
|
||||
|
||||
@action copyToWip = () => {
|
||||
let wipApp;
|
||||
@computed get ownDapps () {
|
||||
return this.apps.filter((app) => app.isOwner);
|
||||
}
|
||||
|
||||
if (this.isNew) {
|
||||
wipApp = {
|
||||
id: api.util.sha3(`${this._startTime}_${Date.now()}`),
|
||||
contentHash: null,
|
||||
contentUrl: null,
|
||||
imageHash: null,
|
||||
imageUrl: null,
|
||||
manifestHash: null,
|
||||
manifestUrl: null
|
||||
@computed get otherDapps () {
|
||||
return this.apps.filter((app) => !app.isOwner);
|
||||
}
|
||||
|
||||
@action sortApps = () => {
|
||||
// Sort dapps per name, then per id
|
||||
const sort = (a, b) => {
|
||||
if (a.name && b.name) {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
|
||||
if (a.name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b.name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.id - b.id;
|
||||
};
|
||||
} 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];
|
||||
});
|
||||
const ownDapps = this.ownDapps.sort(sort);
|
||||
const otherDapps = this.otherDapps.sort(sort);
|
||||
|
||||
this.apps = ownDapps.concat(otherDapps);
|
||||
});
|
||||
}
|
||||
|
||||
return this.wipApp;
|
||||
}
|
||||
@action setApps = (dapps) => {
|
||||
const filteredDapps = dapps.filter((dapp) => {
|
||||
return new BigNumber(dapp.id).gt(0);
|
||||
});
|
||||
|
||||
@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);
|
||||
|
||||
if (this.apps.length) {
|
||||
this.currentApp = this.apps[0];
|
||||
}
|
||||
this.apps = filteredDapps;
|
||||
this.sortApps();
|
||||
});
|
||||
}
|
||||
|
||||
@action setApps = (apps) => {
|
||||
this.sortApps(apps.filter((app) => {
|
||||
const bnid = new BigNumber(app.id);
|
||||
@action refreshApp = (dappId) => {
|
||||
const dapp = this.apps.find((dapp) => dapp.id === dappId);
|
||||
|
||||
return bnid.gt(0);
|
||||
}));
|
||||
|
||||
return this.apps;
|
||||
this._loadDapp(dapp);
|
||||
}
|
||||
|
||||
@action _addApp = (app) => {
|
||||
transaction(() => {
|
||||
this.setApps(this.apps.concat([app]));
|
||||
this.setCurrentApp(app.id);
|
||||
});
|
||||
}
|
||||
@action removeApp = (dappId) => {
|
||||
const dapps = this.apps.filter((dapp) => dapp.id !== dappId);
|
||||
|
||||
@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;
|
||||
this.setApps(dapps);
|
||||
}
|
||||
|
||||
@action setAccounts = (accountsInfo) => {
|
||||
@ -199,8 +130,6 @@ export default class DappsStore {
|
||||
account.address = address;
|
||||
return account;
|
||||
});
|
||||
|
||||
this.currentAccount = this.accounts[0];
|
||||
});
|
||||
|
||||
return this.accounts;
|
||||
@ -214,30 +143,11 @@ export default class DappsStore {
|
||||
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;
|
||||
@ -248,13 +158,106 @@ export default class DappsStore {
|
||||
return loading;
|
||||
}
|
||||
|
||||
@action setNew = (mode) => {
|
||||
transaction(() => {
|
||||
this.isNew = mode;
|
||||
this.copyToWip();
|
||||
@action updateTransaction = (requestId, nextData) => {
|
||||
const prevTransaction = this.transactions[requestId] || { requestId };
|
||||
const nextTransaction = {
|
||||
...prevTransaction,
|
||||
hide: false,
|
||||
...nextData
|
||||
};
|
||||
|
||||
this.transactions = {
|
||||
...this.transactions,
|
||||
[ requestId ]: nextTransaction
|
||||
};
|
||||
}
|
||||
|
||||
fetchRegistryData (dapp) {
|
||||
const ownerAddress = (dapp.wip && dapp.wip.owner.address) || dapp.owner.address;
|
||||
|
||||
this._registry.reverse
|
||||
.call({}, [ ownerAddress ])
|
||||
.then((name) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = api.util.sha3.text(name);
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this._registry.get.call({}, [ key, 'IMG' ])
|
||||
.then((bytes) => api.util.bytesToHex(bytes))
|
||||
.then((hash) => this._instanceGhh.entries.call({}, [ hash ])),
|
||||
this._registry.get.call({}, [ key, 'CONTENT' ])
|
||||
.then((bytes) => api.util.bytesToHex(bytes))
|
||||
.then((hash) => this._instanceGhh.entries.call({}, [ hash ]))
|
||||
])
|
||||
.then(([ imageGHH, contentGHH ]) => {
|
||||
const imageUrl = imageGHH[0];
|
||||
const contentUrl = contentGHH[0];
|
||||
|
||||
return dapp.update({
|
||||
image: imageUrl,
|
||||
content: contentUrl
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
register (dappId) {
|
||||
const dappRegInstance = this._instanceReg;
|
||||
const dappRegFee = this.fee;
|
||||
|
||||
return registerDapp(dappId, dappRegInstance, dappRegFee)
|
||||
.then((request) => this.trackRequest(request, `Registering ${dappId}`))
|
||||
.then(() => this._loadDapps());
|
||||
}
|
||||
|
||||
delete (dapp) {
|
||||
const dappRegInstance = this._instanceReg;
|
||||
|
||||
return deleteDapp(dapp, dappRegInstance)
|
||||
.then((request) => this.trackRequest(request, `Deleting ${dapp.id}`))
|
||||
.then(() => this._loadDapps());
|
||||
}
|
||||
|
||||
update (dappId, dappOwner, updates) {
|
||||
const dappRegInstance = this._instanceReg;
|
||||
const ghhRegInstance = this._instanceGhh;
|
||||
|
||||
const promises = updateDapp(dappId, dappOwner, updates, dappRegInstance, ghhRegInstance);
|
||||
const handledPromises = promises.map((promise) => {
|
||||
return promise
|
||||
.then((requests) => {
|
||||
const requestPromises = flatten([].concat(requests))
|
||||
.filter((request) => request)
|
||||
.map((request) => this.trackRequest(request.id, request.name));
|
||||
|
||||
return Promise.all(requestPromises);
|
||||
})
|
||||
.catch((error) => {
|
||||
const randomRequestId = api.util.sha3(Date.now()).slice(0, 5);
|
||||
|
||||
this.updateTransaction(randomRequestId, { start: Date.now(), error });
|
||||
});
|
||||
});
|
||||
|
||||
return mode;
|
||||
return Promise.all(handledPromises)
|
||||
.then(() => this._loadDapps());
|
||||
}
|
||||
|
||||
trackRequest (requestId, name) {
|
||||
const statusCallback = (error, data) => {
|
||||
if (error) {
|
||||
return this.updateTransaction(requestId, { error });
|
||||
}
|
||||
|
||||
return this.updateTransaction(requestId, data);
|
||||
};
|
||||
|
||||
this.updateTransaction(requestId, { name, start: Date.now() });
|
||||
return trackRequest(requestId, statusCallback);
|
||||
}
|
||||
|
||||
lookupHash (hash) {
|
||||
@ -310,26 +313,30 @@ export default class DappsStore {
|
||||
|
||||
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.accounts.find((account) => account.address === owner);
|
||||
const id = api.util.bytesToHex(appId);
|
||||
|
||||
return {
|
||||
.then((dappsInfo) => {
|
||||
const dapps = dappsInfo.reduce((dapps, [dappId, ownerAddress]) => {
|
||||
const id = api.util.bytesToHex(dappId);
|
||||
const owner = this.accounts.find((account) => account.address === ownerAddress);
|
||||
const isOwner = !!owner;
|
||||
const dapp = {
|
||||
id,
|
||||
owner,
|
||||
ownerName: account ? account.name : owner,
|
||||
isOwner,
|
||||
name: `- ${id}`
|
||||
owner: owner || { address: ownerAddress },
|
||||
isOwner
|
||||
};
|
||||
}))
|
||||
.map(this._loadDapp)
|
||||
);
|
||||
|
||||
dapps[id] = dapp;
|
||||
return dapps;
|
||||
}, {});
|
||||
|
||||
const promises = Object.values(dapps)
|
||||
// Only show dapps with id and owners
|
||||
.filter((dapp) => dapp.id && dapp.owner && !/^(0x)?0*$/.test(dapp.owner.address))
|
||||
.map((dapp) => this._loadDapp(dapp));
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(() => {
|
||||
.then((dapps) => {
|
||||
this.setApps(dapps);
|
||||
this.sortApps();
|
||||
this.setLoading(false);
|
||||
})
|
||||
@ -338,12 +345,14 @@ export default class DappsStore {
|
||||
});
|
||||
}
|
||||
|
||||
_loadDapp = (app) => {
|
||||
_loadDapp = (dappData) => {
|
||||
const { id, owner, isOwner } = dappData;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
this._loadMeta(app.id, 'CONTENT'),
|
||||
this._loadMeta(app.id, 'IMG'),
|
||||
this._loadMeta(app.id, 'MANIFEST')
|
||||
this._loadMeta(id, 'CONTENT'),
|
||||
this._loadMeta(id, 'IMG'),
|
||||
this._loadMeta(id, 'MANIFEST')
|
||||
])
|
||||
.then(([contentHash, imageHash, manifestHash]) => {
|
||||
return Promise
|
||||
@ -354,25 +363,47 @@ export default class DappsStore {
|
||||
])
|
||||
.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}`
|
||||
});
|
||||
._loadManifest(id, manifestHash, manifestUrl)
|
||||
.then((manifestContent) => {
|
||||
const content = {
|
||||
hash: contentHash,
|
||||
url: contentUrl
|
||||
};
|
||||
|
||||
return app;
|
||||
const image = {
|
||||
hash: imageHash,
|
||||
url: imageUrl
|
||||
};
|
||||
|
||||
const manifest = {
|
||||
content: manifestContent,
|
||||
hash: manifestHash,
|
||||
url: manifestUrl
|
||||
};
|
||||
|
||||
return { content, image, manifest };
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Store:loadDapp', error);
|
||||
console.error('dappsStore::loadDapp', error);
|
||||
return {};
|
||||
})
|
||||
.then((data) => {
|
||||
const { content, image, manifest } = data;
|
||||
|
||||
const dapp = new Dapp({
|
||||
contractOwner: this.contractOwner,
|
||||
isContractOwner: this.isContractOwner,
|
||||
id,
|
||||
content,
|
||||
image,
|
||||
manifest,
|
||||
owner,
|
||||
isOwner
|
||||
});
|
||||
|
||||
return dapp;
|
||||
});
|
||||
}
|
||||
|
||||
@ -398,7 +429,9 @@ export default class DappsStore {
|
||||
|
||||
if (builtin) {
|
||||
return Promise.resolve(builtin);
|
||||
} else if (!manifestHash) {
|
||||
}
|
||||
|
||||
if (!manifestHash) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
@ -453,11 +486,10 @@ export default class DappsStore {
|
||||
}
|
||||
|
||||
_loadRegistry () {
|
||||
return api.parity
|
||||
.registryAddress()
|
||||
.then((registryAddress) => {
|
||||
console.log(`the registry was found at ${registryAddress}`);
|
||||
this._registry = api.newContract(abis.registry, registryAddress).instance;
|
||||
return Contracts.create(api).registry
|
||||
.fetchContract()
|
||||
.then((contract) => {
|
||||
this._registry = contract.instance;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Store:loadRegistry', error);
|
||||
@ -472,9 +504,11 @@ export default class DappsStore {
|
||||
])
|
||||
.then(([dappregAddress, ghhAddress]) => {
|
||||
console.log(`dappreg was found at ${dappregAddress}`);
|
||||
console.log(`githubhint was found at ${ghhAddress}`);
|
||||
|
||||
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;
|
||||
})
|
||||
|
@ -1,266 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { 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);
|
||||
});
|
||||
}
|
||||
}
|
@ -14,16 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const { api } = window.parity;
|
||||
const api = window.parent.secureApi;
|
||||
|
||||
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);
|
||||
})
|
||||
function trackRequest (signerRequestId, statusCallback) {
|
||||
return api.pollMethod('parity_checkRequest', signerRequestId)
|
||||
.then((transactionHash) => {
|
||||
console.log('trackRequest', `received transaction hash ${transactionHash}`);
|
||||
statusCallback(null, { transactionHash });
|
||||
@ -41,9 +35,7 @@ function trackRequest (requestPromise, statusCallback) {
|
||||
statusCallback(null, { transactionReceipt });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('trackRequest', error);
|
||||
statusCallback(error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
|
184
js/src/dapps/dappreg/utils.js
Normal file
184
js/src/dapps/dappreg/utils.js
Normal file
@ -0,0 +1,184 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { api } from './parity';
|
||||
|
||||
export const INVALID_URL_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
|
||||
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
/**
|
||||
* Convert the given URL to a content hash,
|
||||
* and checks if it is already registered in GHH
|
||||
*/
|
||||
export const urlToHash = (api, instance, url) => {
|
||||
if (!url || !url.length) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return api.parity
|
||||
.hashContent(url)
|
||||
.catch((error) => {
|
||||
const message = error.text || error.message || error.toString();
|
||||
|
||||
throw new Error(`${message} (${url})`);
|
||||
})
|
||||
.then((contentHash) => {
|
||||
console.log('lookupHash', url, contentHash);
|
||||
|
||||
if (contentHash === INVALID_URL_HASH) {
|
||||
throw new Error(`"${url}" is not a valid URL`);
|
||||
}
|
||||
|
||||
return instance.entries
|
||||
.call({}, [contentHash])
|
||||
.then(([accountSlashRepo, commit, contentHashOwner]) => {
|
||||
const registered = (contentHashOwner !== ZERO_ADDRESS);
|
||||
|
||||
return {
|
||||
hash: contentHash,
|
||||
registered
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the given URL to GithubHint
|
||||
* registry contract
|
||||
*/
|
||||
export const registerGHH = (instance, url, hash, owner) => {
|
||||
const values = [ hash, url ];
|
||||
const options = {
|
||||
from: owner
|
||||
};
|
||||
|
||||
return instance
|
||||
.hintURL.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2).toFixed(0);
|
||||
return instance.hintURL.postTransaction(options, values);
|
||||
});
|
||||
};
|
||||
|
||||
export const registerDapp = (dappId, dappRegInstance) => {
|
||||
const values = [ dappId ];
|
||||
const options = {};
|
||||
|
||||
return dappRegInstance
|
||||
.fee.call({}, [])
|
||||
.then((fee) => {
|
||||
options.value = fee;
|
||||
|
||||
return dappRegInstance
|
||||
.register.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2).toFixed(0);
|
||||
return dappRegInstance.register.postTransaction(options, values);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteDapp = (dapp, dappRegInstance) => {
|
||||
const { id, owner } = dapp;
|
||||
|
||||
const fromAddress = dapp.isOwner
|
||||
? owner.address
|
||||
: dapp.contractOwner;
|
||||
|
||||
const values = [ id ];
|
||||
const options = {
|
||||
from: fromAddress
|
||||
};
|
||||
|
||||
return dappRegInstance
|
||||
.unregister.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2).toFixed(0);
|
||||
|
||||
return dappRegInstance.unregister.postTransaction(options, values);
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDappOwner = (dappId, dappOwner, nextOwnerAddress, dappRegInstance) => {
|
||||
const options = {
|
||||
from: dappOwner
|
||||
};
|
||||
|
||||
const values = [ dappId, nextOwnerAddress ];
|
||||
|
||||
return dappRegInstance.setDappOwner
|
||||
.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2);
|
||||
|
||||
return dappRegInstance.setDappOwner.postTransaction(options, values);
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDapp = (dappId, dappOwner, updates, dappRegInstance, ghhRegInstance) => {
|
||||
const options = {
|
||||
from: dappOwner
|
||||
};
|
||||
|
||||
const types = {
|
||||
content: 'CONTENT',
|
||||
image: 'IMG',
|
||||
manifest: 'MANIFEST'
|
||||
};
|
||||
|
||||
const promises = Object
|
||||
.keys(updates)
|
||||
.map((type) => {
|
||||
const key = types[type];
|
||||
const url = updates[type];
|
||||
let promise;
|
||||
|
||||
if (!url) {
|
||||
promise = Promise.resolve([ null, '' ]);
|
||||
} else {
|
||||
promise = urlToHash(api, ghhRegInstance, url)
|
||||
.then((ghhResult) => {
|
||||
const { hash, registered } = ghhResult;
|
||||
|
||||
if (!registered) {
|
||||
return registerGHH(ghhRegInstance, url, hash, dappOwner)
|
||||
.then((requestId) => [ { id: requestId, name: `Registering ${url}` }, hash ]);
|
||||
}
|
||||
|
||||
return [ null, hash ];
|
||||
});
|
||||
}
|
||||
|
||||
return promise
|
||||
.then(([ ghhRequest, hash ]) => {
|
||||
const values = [ dappId, key, hash ];
|
||||
|
||||
return dappRegInstance.setMeta.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2).toFixed(0);
|
||||
return dappRegInstance.setMeta.postTransaction(options, values);
|
||||
})
|
||||
.then((requestId) => [ ghhRequest, { id: requestId, name: `Updating ${type} of ${dappId}` } ]);
|
||||
});
|
||||
});
|
||||
|
||||
if (updates.owner) {
|
||||
promises.push(updateDappOwner(updates.owner).then((reqId) => ({ id: reqId, name: `Updating owner of ${dappId}` })));
|
||||
}
|
||||
|
||||
return promises;
|
||||
};
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { registry as registryAbi, registry2 as registryAbi2 } from '~/contracts/abi';
|
||||
import Contracts from '~/contracts';
|
||||
|
||||
import { api } from './parity.js';
|
||||
import * as addresses from './addresses/actions.js';
|
||||
@ -27,11 +27,6 @@ import * as reverse from './Reverse/actions.js';
|
||||
|
||||
export { addresses, accounts, lookup, events, names, records, reverse };
|
||||
|
||||
const REGISTRY_V1_HASHES = [
|
||||
'0x34f7c51bbb1b1902fbdabfdf04811100f5c9f998f26dd535d2f6f977492c748e', // ropsten
|
||||
'0x64c3ee34851517a9faecd995c102b339f03e564ad6772dc43a26f993238b20ec' // homestead
|
||||
];
|
||||
|
||||
export const setNetVersion = (netVersion) => ({ type: 'set netVersion', netVersion });
|
||||
|
||||
export const fetchIsTestnet = () => (dispatch) =>
|
||||
@ -48,36 +43,18 @@ export const fetchIsTestnet = () => (dispatch) =>
|
||||
|
||||
export const setContract = (contract) => ({ type: 'set contract', contract });
|
||||
|
||||
export const fetchContract = () => (dispatch) =>
|
||||
api.parity
|
||||
.registryAddress()
|
||||
.then((address) => {
|
||||
return api.eth
|
||||
.getCode(address)
|
||||
.then((code) => {
|
||||
const codeHash = api.util.sha3(code);
|
||||
const isVersion1 = REGISTRY_V1_HASHES.includes(codeHash);
|
||||
|
||||
console.log(`registry at ${address}, code ${codeHash}, version ${isVersion1 ? 1 : 2}`);
|
||||
|
||||
const contract = api.newContract(
|
||||
isVersion1
|
||||
? registryAbi
|
||||
: registryAbi2,
|
||||
address
|
||||
);
|
||||
|
||||
export const fetchContract = () => (dispatch) => {
|
||||
return Contracts.create(api).registry
|
||||
.fetchContract()
|
||||
.then((contract) => {
|
||||
dispatch(setContract(contract));
|
||||
dispatch(fetchFee());
|
||||
dispatch(fetchOwner());
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('could not fetch contract');
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
.catch((error) => {
|
||||
console.error('could not fetch contract', error);
|
||||
});
|
||||
};
|
||||
|
||||
export const setFee = (fee) => ({ type: 'set fee', fee });
|
||||
|
||||
|
@ -170,6 +170,10 @@ export default class WalletsUtils {
|
||||
* to make unnecessary calls on non-wallet accounts
|
||||
*/
|
||||
static isWallet (api, address) {
|
||||
if (!address) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!_cachedWalletLookup[address]) {
|
||||
const walletContract = new Contract(api, WalletAbi);
|
||||
|
||||
|
@ -62,7 +62,8 @@
|
||||
"description": "Enables the registration and content management of dapps on the network",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0",
|
||||
"visible": false
|
||||
"visible": false,
|
||||
"secure": true
|
||||
},
|
||||
{
|
||||
"id": "0x9042323cd85c6576992d211de34b3ecc183f15e4f639aa87859882f839c374e5",
|
||||
|
Loading…
Reference in New Issue
Block a user