Initial new UI source code import (#2607)

* address -> name mappings

* expanding, loading all coin details

* send use only actual BasicCoin tokens registered (any reg)

* sending token & accounts

* form styling updates

* send form layout in place

* coin send working as expected

* api subscriptions on multiple addresses

* bring in events

* simplify

* basic events display in-place, functionally complete

* basic functionality in-place

* fix horrible event address issue

* rwork display of events slightly

* test TLA availability

* table for owner -> tokens

* fix signature lookup address

* fix signature lookup address

* basic overview styling

* txhash links

* page layout adjustments

* background import

* adjust colors

* no global registration, simplify color selection

* updated styling

* connection dialog for "busy connecting"

* initial token connection - WIP

* init token updates take place

* basic test for manual token

* rework connection display

* allow updates of the secure token

* first stab at making the build build

* update runner tags

* fix linting issues

* skip tests requiring network (should be e2e, TODO)

* re-enable javascript tag/runner

* release push does the trick

* push to any branch, CI name

* javscript-test runner as well

* swap dependencies build requires test

* revert stages swap

* retrieve images associated with tokens

* remove js build deps order

* null image when hash = 0x0

* 6x64 images (hashes for registries)

* don't pass tokens as prop to IdentityIcon

* check images against content hash pictures

* cleanup signer after connection changes

* fix naming typo

* display unknownImages for balances (not available as content hash)

* unknownImage for transfer dialog

* basic githubhint layout

* single input for commit/filename

* ethcore_hashContent call

* lookup hash

* registration in place

* fixes

* events is using a proper table

* pass value through as-is

* stop wrongly using main app IdentityIcon

* NEVER export class instance functions

* alignment back to normal

* typo  in definition

* set & get images working (mostly)

* show content retrieval info

* set exitcode via ||

* use javascript:latest images

* disable npm progress bar

* rename phase I

* rename phase II

* only send build output to GitHub on major branches

* also run the build step as part of the test (until comprehensive)

* ci-specific build (no webpack progress)

* allow for account creation via recovery phrase

* display account uuid (where available), closes #2546

* connection dialog now shows up in dapps as well, closes #2538

* token images show up as expected

* IdentityName component added and deployed

* fix padding tests

* adjust tests to map to stricter 0x-prefixed hex

* render names via common component for the address -> name

* split lint into seperate script (early exit)

* test phases changed to lint, test & pack

* pack part of test phase

* remove files marked for deletion (cleanup)

* Signer cleanups, start moving in the direction of the rest

* add personal signer methods

* basic signer request subscription

* don't poll blockNumber when not connected

* missing return, creating massive ws queue backlogs

* ΞTH -> ETH

* fix failing tests

* registry uses setAddress to actually set addresses now

* bytes mapping operates on lowerCase hex strings

* sha3 ids for each application

* add dappreg to list of contracts

* adjust alignment of queries

* show gas estimation log

* abi with payable for register function

* add key as required

* image retrieval from dappreg

* use proper Image urls

* embed and link apps from Parity, retrieved via /api/apps

* filter apps that has been replaced

* proxy entry for parity-utils

* add basiccoin abi

* add support for fallback abi type

* capture constructor paramaters

* merge master into js

* move images to assets/images/

* add font assets

* import fonts as part of build

* don't inline woff files

* Revert "merge master into js"

This reverts commit cfcfa81bd26f1b3cbc748d3afa1eb5c670b363fe.

* remove unused npm packages

* information on gas estimates (like almost everywhere else)

* don't pass gas & gasPrice to estimation

* display account passwordhint when available

* signer subscriptions based on polling & function trapping

* pending requests retrieved via jsapi

* update signer middleware

* remove all web3 instances

* remove web3 package

* last web3 dependencies removed

* no need to toChecksumAddress - api takes care of it

* expand description for personal_confirmRequest

* Signer conversion from web3 -> parity.js completed

* explicit in no return

* green circle background

* remove generated background

* convert /api/* paths to localhost:8080/api/* paths (hard-coded, temporary)

* change dapps to load from localhost:8080/ui/*

* remove dangling web3 files

* update manager test for signer

* /api/ping -> /

* additional token images

* additional token images

* add missing styles.css for 8180 error pages

* cater for txhash returning null/empty object

* adjust output directories

* Release merge with origin with ours strategy

* additional token images

* cater for development server

* s/localhost/127.0.0.1/ (cater for origin)

* Fix address selection for contract deployment

* Adjust z-index for error overlay

* better text on unique background pattern

* fix signer rejections

* Don't allow gavcoin transfer with no balance

* fix txhash rendering in signer

* remove unnecessary ParityBackground

* script to update js-precompiled

* Redirect from :8080 to :8180

* Remove extra return

* Dapp logo images
This commit is contained in:
Jaco Greeff
2016-10-18 11:52:56 +02:00
committed by Gav Wood
parent 6c7af57529
commit 1e6a2cb378
969 changed files with 57315 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.account-selector {
}
.avatar > img {
margin: 0;
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,120 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { List, ListItem } from 'material-ui/List';
import Subheader from 'material-ui/Subheader';
import Avatar from 'material-ui/Avatar';
import IdentityIcon from '../../IdentityIcon';
import styles from './account-selector.css';
class AccountSelectorItem extends Component {
static propTypes = {
onSelectAccount: PropTypes.func.isRequired,
account: PropTypes.object.isRequired
};
render () {
const account = this.props.account;
const props = Object.assign({}, this.props);
delete props.account;
delete props.onSelectAccount;
const icon = (<IdentityIcon
inline center
address={ account.address } />
);
const avatar = (<Avatar
className={ styles.avatar }
backgroundColor='none'
icon={ icon } />
);
return (
<ListItem
onClick={ this.onSelectAccount }
value={ account.address }
primaryText={ account.name }
secondaryText={ account.address }
leftAvatar={ avatar }
{ ...props } />
);
}
onSelectAccount = () => {
this.props.onSelectAccount(this.props.account.address);
}
}
export default class AccountSelector extends Component {
static propTypes = {
list: PropTypes.array.isRequired,
selected: PropTypes.object.isRequired,
handleSetSelected: PropTypes.func.isRequired
};
state = {
open: false
};
render () {
const nestedAccounts = this.renderAccounts(this.props.list);
const selectedAccount = (
<AccountSelectorItem
account={ this.props.selected }
nestedItems={ nestedAccounts }
open={ this.state.open }
onSelectAccount={ this.onToggleOpen }
autoGenerateNestedIndicator={ false } />
);
return (
<div className={ styles['account-selector'] }>
<List>
<Subheader>Select an account</Subheader>
{ selectedAccount }
</List>
</div>
);
}
renderAccounts (accounts) {
return accounts
.map((account, index) => (
<AccountSelectorItem
account={ account }
onSelectAccount={ this.onSelectAccount }
key={ index } />
));
}
onToggleOpen = () => {
this.setState({ open: !this.state.open });
}
onSelectAccount = (address) => {
this.props.handleSetSelected(address);
this.onToggleOpen();
}
}

View File

@@ -0,0 +1,45 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import AccountSelector from './account-selector';
import { setSelectedAccount } from '../actions';
class AccountSelectorContainer extends Component {
render () {
return (<AccountSelector
{ ...this.props }
/>);
}
}
const mapStateToProps = (state) => {
const { accounts } = state;
return { ...accounts };
};
const mapDispatchToProps = (dispatch) => {
return {
handleSetSelected: (address) => {
dispatch(setSelectedAccount(address));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(AccountSelectorContainer);

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './container';

View File

@@ -0,0 +1,61 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const { api } = window.parity;
export const SET_ACCOUNTS = 'SET_ACCOUNTS';
export const setAccounts = (accounts) => ({
type: SET_ACCOUNTS,
accounts
});
export const SET_ACCOUNTS_INFO = 'SET_ACCOUNTS_INFO';
export const setAccountsInfo = (accountsInfo) => ({
type: SET_ACCOUNTS_INFO,
accountsInfo
});
export const SET_SELECTED_ACCOUNT = 'SET_SELECTED_ACCOUNT';
export const setSelectedAccount = (address) => ({
type: SET_SELECTED_ACCOUNT,
address
});
export const loadAccounts = () => (dispatch) => {
Promise
.all([
api.personal.listAccounts(),
api.personal.accountsInfo()
])
.then(results => {
const [ accounts, accountsInfo ] = results;
const accountsList = accounts
.map(address => ({
...accountsInfo[address],
address
}));
console.log('accounts', accountsList);
dispatch(setAccounts(accountsList));
dispatch(setAccountsInfo(accountsInfo));
dispatch(setSelectedAccount(accountsList[0].address));
})
.catch(e => {
console.error('loadAccounts error', e);
});
};

View File

@@ -0,0 +1,57 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import {
SET_ACCOUNTS,
SET_SELECTED_ACCOUNT,
SET_ACCOUNTS_INFO
} from './actions';
const initialState = {
list: [],
accountsInfo: {},
selected: null
};
export default (state = initialState, action) => {
switch (action.type) {
case SET_ACCOUNTS:
return {
...state,
list: [].concat(action.accounts)
};
case SET_ACCOUNTS_INFO:
return {
...state,
accountsInfo: { ...action.accountsInfo }
};
case SET_SELECTED_ACCOUNT: {
const address = action.address;
const account = state.list.find(a => a.address === address);
return {
...state,
selected: account
};
}
default:
return state;
}
};

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './query';

View File

@@ -0,0 +1,207 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { Dialog, FlatButton, SelectField, MenuItem } from 'material-ui';
import InputText from '../../Inputs/Text';
import Loading from '../../Loading';
import Token from '../../Tokens/Token';
import { SIMPLE_TOKEN_ADDRESS_TYPE, SIMPLE_TLA_TYPE } from '../../Inputs/validation';
import styles from '../actions.css';
const initState = {
queryKey: 'tla',
form: {
valid: false,
value: ''
}
};
export default class QueryAction extends Component {
static propTypes = {
show: PropTypes.bool.isRequired,
loading: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
handleQueryToken: PropTypes.func.isRequired,
handleQueryMetaLookup: PropTypes.func.isRequired,
data: PropTypes.object,
notFound: PropTypes.bool,
metaLoading: PropTypes.bool,
metaData: PropTypes.object
}
state = initState;
render () {
return (
<Dialog
title={ 'search for a token' }
open={ this.props.show }
className={ styles.dialog }
onRequestClose={ this.onClose }
actions={ this.renderActions() } >
{ this.renderContent() }
</Dialog>
);
}
renderActions () {
const { loading, data, notFound } = this.props;
if (loading) {
return (
<FlatButton
label='Loading...'
primary
disabled />
);
}
const complete = data || notFound;
if (complete) {
return ([
<FlatButton
label='Close'
primary
onTouchTap={ this.onClose } />
]);
}
const isValid = this.state.form.valid;
return ([
<FlatButton
label='Cancel'
primary
onTouchTap={ this.onClose } />,
<FlatButton
label='Query'
primary
disabled={ !isValid }
onTouchTap={ this.onQuery } />
]);
}
renderContent () {
const { loading, notFound, data } = this.props;
if (loading) {
return (
<Loading size={ 1 } />
);
}
if (notFound) {
return (
<p>No token has been found in the registry...</p>
);
}
if (data) {
return this.renderData();
}
return this.renderForm();
}
renderData () {
const { data } = this.props;
return (
<Token
fullWidth
handleMetaLookup={ this.props.handleQueryMetaLookup }
isMetaLoading={ this.props.metaLoading }
meta={ this.props.metaData }
{ ...data }
/>
);
}
renderForm () {
return (
<div>
<SelectField
floatingLabelText='Select which field to query'
fullWidth
value={ this.state.queryKey }
onChange={ this.onQueryKeyChange }>
<MenuItem value='tla' label='TLA' primaryText='TLA' />
<MenuItem value='address' label='Address' primaryText='Address' />
</SelectField>
{
this.state.queryKey !== 'tla'
? (<InputText
key={ 0 }
floatingLabelText="Token's address"
hintText='0xdeadbeef...'
validationType={ SIMPLE_TOKEN_ADDRESS_TYPE }
onChange={ this.onChange }
onEnter={ this.onQuery } />)
: (<InputText
key={ 1 }
floatingLabelText="Token's TLA"
hintText='GAV'
validationType={ SIMPLE_TLA_TYPE }
onChange={ this.onChange }
onEnter={ this.onQuery } />)
}
</div>
);
}
onQueryKeyChange = (event, index, queryKey) => {
this.setState({
queryKey,
form: { valid: false, value: '' }
});
}
onChange = (valid, value) => {
this.setState({
form: {
valid, value
}
});
}
onQuery = () => {
if (!this.state.form.valid) return;
const { queryKey, form } = this.state;
this.props.handleQueryToken(queryKey, form.value);
}
onClose = () => {
this.setState(initState);
this.props.onClose();
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './register';

View File

@@ -0,0 +1,217 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { Dialog, FlatButton } from 'material-ui';
import AccountSelector from '../../Accounts/AccountSelector';
import InputText from '../../Inputs/Text';
import { TOKEN_ADDRESS_TYPE, TLA_TYPE, UINT_TYPE, STRING_TYPE } from '../../Inputs/validation';
import styles from '../actions.css';
const defaultField = { value: '', valid: false };
const initState = {
isFormValid: false,
fields: {
address: {
...defaultField,
type: TOKEN_ADDRESS_TYPE,
floatingLabelText: 'Token address',
hintText: 'The token address'
},
tla: {
...defaultField,
type: TLA_TYPE,
floatingLabelText: 'Token TLA',
hintText: 'The token short name (3 characters)'
},
base: {
...defaultField,
type: UINT_TYPE,
floatingLabelText: 'Token Base',
hintText: 'The token precision'
},
name: {
...defaultField,
type: STRING_TYPE,
floatingLabelText: 'Token name',
hintText: 'The token name'
}
}
};
export default class RegisterAction extends Component {
static propTypes = {
show: PropTypes.bool.isRequired,
sending: PropTypes.bool.isRequired,
complete: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
handleRegisterToken: PropTypes.func.isRequired,
error: PropTypes.object
}
state = initState;
render () {
const { sending, error, complete } = this.props;
return (
<Dialog
title={ error ? 'error' : 'register a new token' }
open={ this.props.show }
modal={ sending || complete }
className={ styles.dialog }
onRequestClose={ this.onClose }
actions={ this.renderActions() } >
{ this.renderContent() }
</Dialog>
);
}
renderActions () {
const { complete, sending, error } = this.props;
if (error) {
return (
<FlatButton
label='Close'
primary
onTouchTap={ this.onClose } />
);
}
if (complete) {
return (
<FlatButton
label='Done'
primary
onTouchTap={ this.onClose } />
);
}
const isValid = this.state.isFormValid;
return ([
<FlatButton
label='Cancel'
primary
onTouchTap={ this.onClose } />,
<FlatButton
label={ sending ? 'Sending...' : 'Register' }
primary
disabled={ !isValid || sending }
onTouchTap={ this.onRegister } />
]);
}
renderContent () {
const { error, complete } = this.props;
if (error) return this.renderError();
if (complete) return this.renderComplete();
return this.renderForm();
}
renderError () {
const { error } = this.props;
return (<div>
<p className={ styles.error }>{ error.toString() }</p>
</div>);
}
renderComplete () {
return (<div>
<p>Your transaction has been posted. Please visit the Parity Signer to authenticate the transfer.</p>
</div>);
}
renderForm () {
return (
<div>
<AccountSelector />
{ this.renderInputs() }
</div>
);
}
renderInputs () {
const { fields } = this.state;
return Object.keys(fields).map((fieldKey, index) => {
const onChange = this.onChange.bind(this, fieldKey);
const field = fields[fieldKey];
return (
<InputText
key={ index }
floatingLabelText={ field.floatingLabelText }
hintText={ field.hintText }
validationType={ field.type }
onChange={ onChange } />
);
});
}
onChange (fieldKey, valid, value) {
const { fields } = this.state;
const field = fields[fieldKey];
const newFields = {
...fields,
[ fieldKey ]: {
...field,
valid, value
}
};
const isFormValid = Object.keys(newFields)
.map(key => newFields[key].valid)
.reduce((current, fieldValid) => {
return current && fieldValid;
}, true);
this.setState({
fields: newFields,
isFormValid
});
}
onRegister = () => {
const { fields } = this.state;
const data = Object.keys(fields)
.reduce((dataObject, fieldKey) => {
dataObject[fieldKey] = fields[fieldKey].value;
return dataObject;
}, {});
this.props.handleRegisterToken(data);
}
onClose = () => {
this.setState(initState);
this.props.onClose();
}
}

View File

@@ -0,0 +1,50 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.actions {
padding-top: 2rem;
}
.button {
margin: 0 0.5em;
}
.button button {
background-color: #27ae60 !important;
height: 56px !important;
padding: 0 10px !important;
}
.button button[disabled] {
background-color: rgba(50, 50, 50, 0.25) !important;
}
.dialog {
}
.dialog h3 {
color: rgba(50, 100, 150, 1) !important;
text-transform: uppercase;
}
.dialogtext {
padding-top: 1em;
}
.error {
color: red;
}

View File

@@ -0,0 +1,215 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { getTokenTotalSupply } from '../utils';
const { sha3, bytesToHex } = window.parity.api.util;
export const SET_REGISTER_SENDING = 'SET_REGISTER_SENDING';
export const setRegisterSending = (isSending) => ({
type: SET_REGISTER_SENDING,
isSending
});
export const SET_REGISTER_ERROR = 'SET_REGISTER_ERROR';
export const setRegisterError = (e) => ({
type: SET_REGISTER_ERROR,
error: e
});
export const REGISTER_RESET = 'REGISTER_RESET';
export const registerReset = () => ({
type: REGISTER_RESET
});
export const REGISTER_COMPLETED = 'REGISTER_COMPLETED';
export const registerCompleted = () => ({
type: REGISTER_COMPLETED
});
export const registerToken = (tokenData) => (dispatch, getState) => {
console.log('registering token', tokenData);
const state = getState();
const contractInstance = state.status.contract.instance;
const fee = state.status.contract.fee;
const { address, base, name, tla } = tokenData;
dispatch(setRegisterSending(true));
const values = [ address, tla, base, name ];
const options = {
from: state.accounts.selected.address,
value: fee
};
Promise.resolve()
.then(() => {
return contractInstance
.fromTLA.call({}, [ tla ])
.then(([id, address, base, name, owner]) => {
if (owner !== '0x0000000000000000000000000000000000000000') {
throw new Error(`A Token has already been registered with the TLA ${tla}`);
}
});
})
.then(() => {
return contractInstance
.fromAddress.call({}, [ address ])
.then(([id, tla, base, name, owner]) => {
if (owner !== '0x0000000000000000000000000000000000000000') {
throw new Error(`A Token has already been registered with the Address ${address}`);
}
});
})
.then(() => {
return contractInstance
.register.estimateGas(options, values);
})
.then((gasEstimate) => {
options.gas = gasEstimate.mul(1.2).toFixed(0);
console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
return contractInstance.register.postTransaction(options, values);
})
.then((result) => {
dispatch(registerCompleted());
})
.catch((e) => {
console.error('registerToken error', e);
dispatch(setRegisterError(e));
});
};
export const SET_QUERY_LOADING = 'SET_QUERY_LOADING';
export const setQueryLoading = (isLoading) => ({
type: SET_QUERY_LOADING,
isLoading
});
export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
export const setQueryResult = (data) => ({
type: SET_QUERY_RESULT,
data
});
export const SET_QUERY_NOT_FOUND = 'SET_QUERY_NOT_FOUND';
export const setQueryNotFound = () => ({
type: SET_QUERY_NOT_FOUND
});
export const QUERY_RESET = 'QUERY_RESET';
export const queryReset = () => ({
type: QUERY_RESET
});
export const SET_QUERY_META_LOADING = 'SET_QUERY_META_LOADING';
export const setQueryMetaLoading = (isLoading) => ({
type: SET_QUERY_META_LOADING,
isLoading
});
export const SET_QUERY_META = 'SET_QUERY_META';
export const setQueryMeta = (data) => ({
type: SET_QUERY_META,
data
});
export const queryToken = (key, query) => (dispatch, getState) => {
const state = getState();
const contractInstance = state.status.contract.instance;
const contractFunc = (key === 'tla') ? 'fromTLA' : 'fromAddress';
dispatch(setQueryLoading(true));
contractInstance[contractFunc]
.call({}, [ query ])
.then((result) => {
const data = {
index: result[0].toNumber(),
base: result[2].toNumber(),
name: result[3],
owner: result[4]
};
if (key === 'tla') {
data.tla = query;
data.address = result[1];
}
if (key === 'address') {
data.address = query;
data.tla = result[1];
}
return data;
})
.then(data => {
return getTokenTotalSupply(data.address)
.then(totalSupply => {
data.totalSupply = totalSupply;
return data;
});
})
.then(data => {
if (data.totalSupply === null) {
dispatch(setQueryNotFound());
dispatch(setQueryLoading(false));
return false;
}
data.totalSupply = data.totalSupply.toNumber();
dispatch(setQueryResult(data));
dispatch(setQueryLoading(false));
}, () => {
dispatch(setQueryNotFound());
dispatch(setQueryLoading(false));
});
};
export const queryTokenMeta = (id, query) => (dispatch, getState) => {
console.log('loading token meta', query);
const state = getState();
const contractInstance = state.status.contract.instance;
const key = sha3(query);
const startDate = Date.now();
dispatch(setQueryMetaLoading(true));
contractInstance
.meta
.call({}, [ id, key ])
.then((value) => {
const meta = {
key, query,
value: value.find(v => v !== 0) ? bytesToHex(value) : null
};
dispatch(setQueryMeta(meta));
setTimeout(() => {
dispatch(setQueryMetaLoading(false));
}, 500 - (Date.now() - startDate));
})
.catch((e) => {
console.error('load meta query error', e);
});
};

View File

@@ -0,0 +1,119 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { RaisedButton } from 'material-ui';
import ActionSearchIcon from 'material-ui/svg-icons/action/search';
import ContentSendIcon from 'material-ui/svg-icons/content/send';
import Register from './Register';
import Query from './Query';
import styles from './actions.css';
const REGISTER_ACTION = 'REGISTER_ACTION';
const QUERY_ACTION = 'QUERY_ACTION';
export default class Actions extends Component {
static propTypes = {
handleRegisterToken: PropTypes.func.isRequired,
handleRegisterClose: PropTypes.func.isRequired,
register: PropTypes.object.isRequired,
handleQueryToken: PropTypes.func.isRequired,
handleQueryClose: PropTypes.func.isRequired,
handleQueryMetaLookup: PropTypes.func.isRequired,
query: PropTypes.object.isRequired
};
state = {
show: {
[ REGISTER_ACTION ]: false,
[ QUERY_ACTION ]: false
}
}
constructor () {
super();
this.onShowRegister = this.onShow.bind(this, REGISTER_ACTION);
this.onShowQuery = this.onShow.bind(this, QUERY_ACTION);
}
render () {
return (
<div className={ styles.actions }>
<RaisedButton
className={ styles.button }
icon={ <ContentSendIcon /> }
label='Register Token'
primary
onTouchTap={ this.onShowRegister } />
<RaisedButton
className={ styles.button }
icon={ <ActionSearchIcon /> }
label='Search Token'
primary
onTouchTap={ this.onShowQuery } />
<Register
show={ this.state.show[ REGISTER_ACTION ] }
onClose={ this.onRegisterClose }
handleRegisterToken={ this.props.handleRegisterToken }
{ ...this.props.register } />
<Query
show={ this.state.show[ QUERY_ACTION ] }
onClose={ this.onQueryClose }
handleQueryToken={ this.props.handleQueryToken }
handleQueryMetaLookup={ this.props.handleQueryMetaLookup }
{ ...this.props.query } />
</div>
);
}
onRegisterClose = () => {
this.onHide(REGISTER_ACTION);
this.props.handleRegisterClose();
}
onQueryClose = () => {
this.onHide(QUERY_ACTION);
this.props.handleQueryClose();
}
onShow (key) {
this.setState({
show: {
...this.state.show,
[ key ]: true
}
});
}
onHide (key) {
this.setState({
show: {
...this.state.show,
[ key ]: false
}
});
}
}

View File

@@ -0,0 +1,62 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Actions from './component';
import { registerToken, registerReset, queryToken, queryReset, queryTokenMeta } from './actions';
class TokensContainer extends Component {
render () {
return (<Actions
{ ...this.props }
/>);
}
}
const mapStateToProps = (state) => {
const { register, query } = state.actions;
return { register, query };
};
const mapDispatchToProps = (dispatch) => {
return {
handleRegisterToken: (tokenData) => {
dispatch(registerToken(tokenData));
},
handleRegisterClose: () => {
dispatch(registerReset());
},
handleQueryToken: (key, query) => {
dispatch(queryToken(key, query));
},
handleQueryClose: () => {
dispatch(queryReset());
},
handleQueryMetaLookup: (id, query) => {
dispatch(queryTokenMeta(id, query));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(TokensContainer);

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './container.js';

View File

@@ -0,0 +1,155 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import {
SET_REGISTER_SENDING,
SET_REGISTER_ERROR,
REGISTER_RESET,
REGISTER_COMPLETED,
SET_QUERY_LOADING,
SET_QUERY_RESULT,
SET_QUERY_NOT_FOUND,
SET_QUERY_META_LOADING,
SET_QUERY_META,
QUERY_RESET
} from './actions';
const initialState = {
register: {
sending: false,
error: null,
complete: false
},
query: {
loading: false,
data: null,
notFound: false,
metaLoading: false,
metaData: null
}
};
export default (state = initialState, action) => {
switch (action.type) {
case SET_REGISTER_SENDING: {
const registerState = state.register;
return {
...state,
register: {
...registerState,
sending: action.isSending
}
};
}
case REGISTER_COMPLETED: {
const registerState = state.register;
return {
...state,
register: {
...registerState,
sending: false,
complete: true
}
};
}
case SET_REGISTER_ERROR: {
const registerState = state.register;
return {
...state,
register: {
...registerState,
sending: false,
error: action.error
}
};
}
case REGISTER_RESET: {
return {
...state,
register: initialState.register
};
}
case SET_QUERY_LOADING: {
return {
...state,
query: {
...state.query,
loading: action.isLoading
}
};
}
case SET_QUERY_RESULT: {
return {
...state,
query: {
...state.query,
data: action.data
}
};
}
case SET_QUERY_NOT_FOUND: {
return {
...state,
query: {
...state.query,
notFound: true
}
};
}
case SET_QUERY_META_LOADING: {
return {
...state,
query: {
...state.query,
metaLoading: action.isLoading
}
};
}
case SET_QUERY_META: {
return {
...state,
query: {
...state.query,
metaData: action.data
}
};
}
case QUERY_RESET: {
return {
...state,
query: {
...initialState.query
}
};
}
default:
return state;
}
};

View File

@@ -0,0 +1,22 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.application {
display: flex;
flex-direction: column;
align-items: center;
}

View File

@@ -0,0 +1,72 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import Loading from '../Loading';
import Status from '../Status';
import Tokens from '../Tokens';
import Actions from '../Actions';
import styles from './application.css';
const muiTheme = getMuiTheme({
palette: {
primary1Color: '#27ae60'
}
});
export default class Application extends Component {
static childContextTypes = {
muiTheme: PropTypes.object
}
static propTypes = {
isLoading: PropTypes.bool.isRequired,
contract: PropTypes.object
};
render () {
const { isLoading, contract } = this.props;
if (isLoading) {
return (
<Loading />
);
}
return (
<div className={ styles.application }>
<Status
address={ contract.address }
fee={ contract.fee } />
<Actions />
<Tokens />
</div>
);
}
getChildContext () {
return {
muiTheme
};
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './application';

View File

@@ -0,0 +1,45 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.chip > span {
flex: 1;
display: flex;
flex-direction: row;
}
.chip img {
margin-bottom: -11px;
margin-left: -11px;
}
.value {
font-family: 'Roboto Mono', monospace;
color: rgba(255, 255, 255, 1);
-webkit-user-select: text;
cursor: text;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.label {
color: rgba(255, 255, 255, 0.7);
margin-left: 1em;
margin-right: 0.5em;
text-transform: uppercase;
}

View File

@@ -0,0 +1,68 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { Chip } from 'material-ui';
import IdentityIcon from '../IdentityIcon' ;
import styles from './chip.css';
export default class CustomChip extends Component {
static propTypes = {
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
isAddress: PropTypes.bool,
displayValue: PropTypes.string
};
render () {
const { isAddress, value, label } = this.props;
const displayValue = this.props.displayValue || value;
return (
<Chip
className={ styles.chip }
style={ {
margin: '0.5em',
background: '#27ae60',
display: 'flex',
flexDirection: 'column'
} }>
{ this.renderIcon(isAddress, value) }
<span className={ styles.value } title={ value }>
{ displayValue }
</span>
<span className={ styles.label }>
{ label }
</span>
</Chip>
);
}
renderIcon (isAddress, address) {
if (!isAddress) return;
return (
<IdentityIcon
inline center
address={ address } />
);
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './chip';

View File

@@ -0,0 +1,64 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Application from './Application';
import { loadContract } from './Status/actions';
import { loadAccounts } from './Accounts/actions';
class Container extends Component {
static propTypes = {
isLoading: PropTypes.bool.isRequired,
contract: PropTypes.object.isRequired,
onLoad: PropTypes.func.isRequired
};
componentDidMount () {
this.props.onLoad();
}
render () {
const { isLoading, contract } = this.props;
return (<Application
isLoading={ isLoading }
contract={ contract }
/>);
}
}
const mapStateToProps = (state) => {
const { isLoading, contract } = state.status;
return {
isLoading,
contract
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoad: () => {
dispatch(loadContract());
dispatch(loadAccounts());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Container);

View File

@@ -0,0 +1,23 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.icon {
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 0.5em;
}

View File

@@ -0,0 +1,36 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { api } from '../parity';
import styles from './identityIcon.css';
export default class IdentityIcon extends Component {
static propTypes = {
address: PropTypes.string.isRequired
}
render () {
const { address } = this.props;
return (
<img
className={ styles.icon }
src={ api.util.createIdentityImg(address, 4) } />
);
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './identityIcon';

View File

@@ -0,0 +1,37 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import InputText from './input-text';
class InputTextContainer extends Component {
render () {
return (<InputText
{ ...this.props }
/>);
}
}
const mapStateToProps = (state) => {
const { contract } = state.status;
return { contract };
};
export default connect(mapStateToProps)(InputTextContainer);

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './container';

View File

@@ -0,0 +1,141 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { TextField } from 'material-ui';
import CheckIcon from 'material-ui/svg-icons/navigation/check';
import { green500 } from 'material-ui/styles/colors';
import Loading from '../../Loading';
import { validate } from '../validation';
import styles from '../inputs.css';
const initState = {
error: null,
value: '',
valid: false,
disabled: false,
loading: false
};
export default class InputText extends Component {
static propTypes = {
validationType: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
onEnter: PropTypes.func,
floatingLabelText: PropTypes.string,
hintText: PropTypes.string,
contract: PropTypes.object
}
state = initState;
render () {
const { disabled, error } = this.state;
return (
<div className={ styles['input-container'] }>
<TextField
floatingLabelText={ this.props.floatingLabelText }
hintText={ this.props.hintText }
autoComplete='off'
floatingLabelFixed
fullWidth
disabled={ disabled }
errorText={ error }
onChange={ this.onChange }
onKeyDown={ this.onKeyDown } />
{ this.renderLoading() }
{ this.renderIsValid() }
</div>
);
}
renderLoading () {
if (!this.state.loading) return;
return (
<div className={ styles['input-loading'] }>
<Loading size={ 0.3 } />
</div>
);
}
renderIsValid () {
if (this.state.loading || !this.state.valid) return;
return (
<div className={ styles['input-icon'] }>
<CheckIcon color={ green500 } />
</div>
);
}
onChange = (event) => {
const value = event.target.value;
// So we can focus on the input after async validation
event.persist();
const { validationType, contract } = this.props;
const validation = validate(value, validationType, contract);
if (validation instanceof Promise) {
this.setState({ disabled: true, loading: true });
return validation
.then(validation => {
this.setValidation({
...validation,
disabled: false,
loading: false
});
event.target.focus();
});
}
this.setValidation(validation);
}
onKeyDown = (event) => {
if (!this.props.onEnter) return;
if (event.keyCode !== 13) return;
this.props.onEnter();
}
setValidation = (validation) => {
const { value } = validation;
this.setState({ ...validation });
if (validation.valid) {
return this.props.onChange(true, value);
}
return this.props.onChange(false, value);
}
}

View File

@@ -0,0 +1,33 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.input-container {
width: 100%;
position: relative;
}
.input-loading {
position: absolute;
right: -5px;
top: 20px;
}
.input-icon {
position: absolute;
right: 5px;
bottom: 10px;
}

View File

@@ -0,0 +1,212 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import isURL from 'validator/lib/isURL';
import { api } from '../parity';
import { getTokenTotalSupply } from '../utils';
const {
isHex,
isAddressValid,
toChecksumAddress
} = api.util;
export const ADDRESS_TYPE = 'ADDRESS_TYPE';
export const TOKEN_ADDRESS_TYPE = 'TOKEN_ADDRESS_TYPE';
export const SIMPLE_TOKEN_ADDRESS_TYPE = 'SIMPLE_TOKEN_ADDRESS_TYPE';
export const TLA_TYPE = 'TLA_TYPE';
export const SIMPLE_TLA_TYPE = 'SIMPLE_TLA_TYPE';
export const UINT_TYPE = 'UINT_TYPE';
export const STRING_TYPE = 'STRING_TYPE';
export const HEX_TYPE = 'HEX_TYPE';
export const URL_TYPE = 'URL_TYPE';
export const ERRORS = {
invalidTLA: 'The TLA should be 3 characters long',
invalidUint: 'Please enter a non-negative integer',
invalidString: 'Please enter at least a character',
invalidAccount: 'Please select an account to transact with',
invalidRecipient: 'Please select an account to send to',
invalidAddress: 'The address is not in the correct format',
invalidTokenAddress: 'The address is not a regular token contract address',
invalidHex: 'Please enter an hexadecimal string (digits and letters from a to z)',
invalidAmount: 'Please enter a positive amount > 0',
invalidTotal: 'The amount is greater than the availale balance',
tlaAlreadyTaken: 'This TLA address is already registered',
addressAlreadyTaken: 'This Token address is already registered',
invalidURL: 'Please enter a valid URL'
};
const validateAddress = (address) => {
if (!isAddressValid(address)) {
return {
error: ERRORS.invalidAddress,
valid: false
};
}
return {
value: toChecksumAddress(address),
error: null,
valid: true
};
};
const validateTokenAddress = (address, contract, simple) => {
const addressValidation = validateAddress(address);
if (!addressValidation.valid) return addressValidation;
if (simple) return addressValidation;
return getTokenTotalSupply(address)
.then(balance => {
if (balance === null) {
return {
error: ERRORS.invalidTokenAddress,
valid: false
};
}
return contract.instance
.fromAddress.call({}, [ address ])
.then(([id, tla, base, name, owner]) => {
if (owner !== '0x0000000000000000000000000000000000000000') {
return {
error: ERRORS.addressAlreadyTaken,
valid: false
};
}
});
})
.then((result) => {
if (result) return result;
return addressValidation;
});
};
const validateTLA = (tla, contract, simple) => {
if (tla.toString().length !== 3) {
return {
error: ERRORS.invalidTLA,
valid: false
};
}
const fTLA = tla.toString().toUpperCase();
if (simple) {
return {
value: fTLA,
error: null,
valid: true
};
}
return contract.instance
.fromTLA.call({}, [ fTLA ])
.then(([id, address, base, name, owner]) => {
if (owner !== '0x0000000000000000000000000000000000000000') {
return {
error: ERRORS.tlaAlreadyTaken,
valid: false
};
}
})
.then((result) => {
if (result) return result;
return {
value: fTLA,
error: null,
valid: true
};
});
};
const validateUint = (uint) => {
if (!/^\d+$/.test(uint) || parseInt(uint) <= 0) {
return {
error: ERRORS.invalidUint,
valid: false
};
}
return {
value: parseInt(uint),
error: null,
valid: true
};
};
const validateString = (string) => {
if (string.toString().length === 0) {
return {
error: ERRORS.invalidString,
valid: false
};
}
return {
value: string.toString(),
error: null,
valid: true
};
};
const validateHex = (string) => {
if (!isHex(string.toString())) {
return {
error: ERRORS.invalidHex,
valid: false
};
}
return {
value: string.toString(),
error: null,
valid: true
};
};
const validateURL = (string) => {
if (!isURL(string.toString())) {
return {
error: ERRORS.invalidURL,
valid: false
};
}
return {
value: string.toString(),
error: null,
valid: true
};
};
export const validate = (value, type, contract) => {
if (type === ADDRESS_TYPE) return validateAddress(value);
if (type === TOKEN_ADDRESS_TYPE) return validateTokenAddress(value, contract);
if (type === SIMPLE_TOKEN_ADDRESS_TYPE) return validateTokenAddress(value, contract, true);
if (type === TLA_TYPE) return validateTLA(value, contract);
if (type === SIMPLE_TLA_TYPE) return validateTLA(value, contract, true);
if (type === UINT_TYPE) return validateUint(value);
if (type === STRING_TYPE) return validateString(value);
if (type === HEX_TYPE) return validateHex(value);
if (type === URL_TYPE) return validateURL(value);
return { valid: true, error: null };
};

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './loading';

View File

@@ -0,0 +1,23 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.loading {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}

View File

@@ -0,0 +1,34 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import CircularProgress from 'material-ui/CircularProgress';
import styles from './loading.css';
export default class Loading extends Component {
static propTypes = {
size: PropTypes.number
};
render () {
return (
<div className={ styles.loading }>
<CircularProgress size={ this.props.size || 2 } />
</div>
);
}
}

View File

@@ -0,0 +1,202 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import {
registry as registryAbi,
tokenreg as tokenregAbi,
githubhint as githubhintAbi
} from '../../../contracts/abi';
import { loadToken, setTokenPending, deleteToken, setTokenData } from '../Tokens/actions';
const { api } = window.parity;
export const SET_LOADING = 'SET_LOADING';
export const setLoading = (isLoading) => ({
type: SET_LOADING,
isLoading
});
export const FIND_CONTRACT = 'FIND_CONTRACT';
export const loadContract = () => (dispatch) => {
dispatch(setLoading(true));
api.ethcore
.registryAddress()
.then((registryAddress) => {
console.log(`registry found at ${registryAddress}`);
const registry = api.newContract(registryAbi, registryAddress).instance;
return Promise.all([
registry.getAddress.call({}, [api.util.sha3('tokenreg'), 'A']),
registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A'])
]);
})
.then(([ tokenregAddress, githubhintAddress ]) => {
console.log(`tokenreg was found at ${tokenregAddress}`);
const tokenregContract = api
.newContract(tokenregAbi, tokenregAddress);
const githubhintContract = api
.newContract(githubhintAbi, githubhintAddress);
dispatch(setContractDetails({
address: tokenregAddress,
instance: tokenregContract.instance,
raw: tokenregContract
}));
dispatch(setGithubhintDetails({
address: githubhintAddress,
instance: githubhintContract.instance,
raw: githubhintContract
}));
dispatch(loadContractDetails());
dispatch(subscribeEvents());
})
.catch((error) => {
console.error('loadContract error', error);
});
};
export const LOAD_CONTRACT_DETAILS = 'LOAD_CONTRACT_DETAILS';
export const loadContractDetails = () => (dispatch, getState) => {
const state = getState();
const instance = state.status.contract.instance;
Promise
.all([
api.personal.listAccounts(),
instance.owner.call(),
instance.fee.call()
])
.then(([accounts, owner, fee]) => {
console.log(`owner as ${owner}, fee set at ${fee.toFormat()}`);
const isOwner = accounts.filter(a => a === owner).length > 0;
dispatch(setContractDetails({
fee,
owner,
isOwner
}));
dispatch(setLoading(false));
})
.catch((error) => {
console.error('loadContractDetails error', error);
});
};
export const SET_CONTRACT_DETAILS = 'SET_CONTRACT_DETAILS';
export const setContractDetails = (details) => ({
type: SET_CONTRACT_DETAILS,
details
});
export const SET_GITHUBHINT_CONTRACT = 'SET_GITHUBHINT_CONTRACT';
export const setGithubhintDetails = (details) => ({
type: SET_GITHUBHINT_CONTRACT,
details
});
export const subscribeEvents = () => (dispatch, getState) => {
const state = getState();
const contract = state.status.contract.raw;
const previousSubscriptionId = state.status.subscriptionId;
if (previousSubscriptionId) {
contract.unsubscribe(previousSubscriptionId);
}
contract
.subscribe(null, {
fromBlock: 'latest',
toBlock: 'pending',
limit: 50
}, (error, logs) => {
if (error) {
console.error('setupFilters', error);
return;
}
if (!logs || logs.length === 0) return;
logs.forEach(log => {
const event = log.event;
const type = log.type;
const params = log.params;
if (event === 'Registered' && type === 'pending') {
return dispatch(setTokenData(params.id.toNumber(), {
tla: '...',
base: -1,
address: params.addr,
name: params.name,
isPending: true
}));
}
if (event === 'Registered' && type === 'mined') {
return dispatch(loadToken(params.id.toNumber()));
}
if (event === 'Unregistered' && type === 'pending') {
return dispatch(setTokenPending(params.id.toNumber(), true));
}
if (event === 'Unregistered' && type === 'mined') {
return dispatch(deleteToken(params.id.toNumber()));
}
if (event === 'MetaChanged' && type === 'pending') {
return dispatch(setTokenData(
params.id.toNumber(),
{ metaPending: true, metaMined: false }
));
}
if (event === 'MetaChanged' && type === 'mined') {
setTimeout(() => {
dispatch(setTokenData(
params.id.toNumber(),
{ metaPending: false, metaMined: false }
));
}, 5000);
return dispatch(setTokenData(
params.id.toNumber(),
{ metaPending: false, metaMined: true }
));
}
console.log('new log event', log);
});
})
.then((subscriptionId) => {
dispatch(setSubscriptionId(subscriptionId));
});
};
export const SET_SUBSCRIPTION_ID = 'SET_SUBSCRIPTION_ID';
export const setSubscriptionId = subscriptionId => ({
type: SET_SUBSCRIPTION_ID,
subscriptionId
});

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './status';

View File

@@ -0,0 +1,65 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import {
SET_LOADING,
SET_CONTRACT_DETAILS,
SET_GITHUBHINT_CONTRACT,
SET_SUBSCRIPTION_ID
} from './actions';
const initialState = {
isLoading: true,
subscriptionId: null,
contract: {
addres: null,
instance: null,
raw: null,
owner: null,
isOwner: false,
fee: null
},
githubhint: {
address: null,
instance: null,
raw: null
}
};
export default (state = initialState, action) => {
switch (action.type) {
case SET_LOADING:
return { ...state, isLoading: action.isLoading };
case SET_SUBSCRIPTION_ID:
return { ...state, subscriptionId: action.subscriptionId };
case SET_CONTRACT_DETAILS:
return { ...state, contract: {
...state.contract,
...action.details
} };
case SET_GITHUBHINT_CONTRACT:
return { ...state, githubhint: {
...state.githubhint,
...action.details
} };
default:
return state;
}
};

View File

@@ -0,0 +1,36 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.status {
background: rgba(46, 204, 113, 0.85);
color: rgba(255, 255, 255, 1);
padding: 4em 0 2em 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
}
.title {
font-size: 3rem;
font-weight: 300;
margin-top: 0;
text-transform: uppercase;
}

View File

@@ -0,0 +1,50 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import Chip from '../Chip';
import styles from './status.css';
const { api } = window.parity;
export default class Status extends Component {
static propTypes = {
address: PropTypes.string.isRequired,
fee: PropTypes.object.isRequired
};
render () {
const { address, fee } = this.props;
return (
<div className={ styles.status }>
<h1 className={ styles.title }>Token Registry</h1>
<Chip
isAddress
value={ address }
label='Address' />
<Chip
isAddress={ false }
value={ api.util.fromWei(fee).toFixed(3) + 'ETH' }
label='Fee' />
</div>
);
}
}

View File

@@ -0,0 +1,193 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { Dialog, RaisedButton, FlatButton, SelectField, MenuItem } from 'material-ui';
import AddIcon from 'material-ui/svg-icons/content/add';
import InputText from '../../Inputs/Text';
import { ADDRESS_TYPE } from '../../Inputs/validation';
import styles from './token.css';
import { metaDataKeys } from '../../constants';
const initState = {
showDialog: false,
complete: false,
metaKeyIndex: 0,
form: {
valid: false,
value: ''
}
};
export default class AddMeta extends Component {
static propTypes = {
isTokenOwner: PropTypes.bool,
handleAddMeta: PropTypes.func,
index: PropTypes.number
};
state = initState;
render () {
if (!this.props.isTokenOwner) return null;
return (<div className={ styles['add-meta'] }>
<RaisedButton
label='Add Meta-Data'
icon={ <AddIcon /> }
primary
fullWidth
onTouchTap={ this.onShowDialog } />
<Dialog
title='add meta data'
open={ this.state.showDialog }
modal={ this.state.complete }
className={ styles.dialog }
onRequestClose={ this.onClose }
actions={ this.renderActions() } >
{ this.renderContent() }
</Dialog>
</div>);
}
renderActions () {
const { complete } = this.state;
if (complete) {
return (
<FlatButton
label='Done'
primary
onTouchTap={ this.onClose } />
);
}
const isValid = this.state.form.valid;
return ([
<FlatButton
label='Cancel'
primary
onTouchTap={ this.onClose } />,
<FlatButton
label='Add'
primary
disabled={ !isValid }
onTouchTap={ this.onAdd } />
]);
}
renderContent () {
const { complete } = this.state;
if (complete) return this.renderComplete();
return this.renderForm();
}
renderComplete () {
if (metaDataKeys[this.state.metaKeyIndex].value === 'IMG') {
return (<div>
<p>
Your transactions has been posted.
Two transactions are needed to add an Image.
Please visit the Parity Signer to authenticate the transfer.</p>
</div>);
}
return (<div>
<p>Your transaction has been posted. Please visit the Parity Signer to authenticate the transfer.</p>
</div>);
}
renderForm () {
const selectedMeta = metaDataKeys[this.state.metaKeyIndex];
return (
<div>
<SelectField
floatingLabelText='Choose the meta-data to add'
fullWidth
value={ this.state.metaKeyIndex }
onChange={ this.onMetaKeyChange }>
{ this.renderMetaKeyItems() }
</SelectField>
<InputText
key={ selectedMeta.value }
floatingLabelText={ `${selectedMeta.label} value` }
hintText={ `The value of the ${selectedMeta.label.toLowerCase()} (${selectedMeta.validation === ADDRESS_TYPE ? 'Address' : 'Url Hint'})` }
validationType={ selectedMeta.validation }
onChange={ this.onChange } />
</div>
);
}
renderMetaKeyItems () {
return metaDataKeys.map((key, index) => (
<MenuItem
value={ index }
key={ index }
label={ key.label } primaryText={ key.label } />
));
}
onShowDialog = () => {
this.setState({ showDialog: true });
}
onClose = () => {
this.setState(initState);
}
onAdd = () => {
const { index } = this.props;
const keyIndex = this.state.metaKeyIndex;
const key = metaDataKeys[keyIndex].value;
this.props.handleAddMeta(
index,
key,
this.state.form.value
);
this.setState({ complete: true });
}
onChange = (valid, value) => {
this.setState({
form: {
valid, value
}
});
}
onMetaKeyChange = (event, metaKeyIndex) => {
this.setState({ metaKeyIndex, form: {
...[this.state.form],
valid: false,
value: ''
} });
}
}

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './token';

View File

@@ -0,0 +1,140 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.token{
position: relative;
display: flex;
width: 20rem;
margin: 1rem;
padding: 1rem;
padding-bottom: 1.5rem;
}
.token-container, .token-content, .token-meta {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.token-content, .token-meta {
z-index: 50;
}
.token-bg {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
-webkit-filter: blur(5px);
filter: blur(5px);
background-color: rgba(255, 255, 255, 0.85);
}
.token-container {
flex: 1;
}
.full-width .token-container {
flex-direction: row;
align-items: flex-start;
}
.full-width .token-content {
width: 20rem;
margin-right: 1rem;
}
.token-content > div, .token-meta > div {
max-width: 100%;
}
.loading {
margin: 1rem 0;
}
.name {
padding-bottom: 0.75rem;
}
.title {
font-size: 2rem;
padding: 0 0 0.5rem;
}
.meta-query {
font-size: 0.9rem;
margin-top: 1.5rem;
margin-bottom: 0;
}
.meta-key {
font-weight: bold;
}
.meta-value {
margin-top: 0.5rem;
max-width: 100%;
overflow-wrap: break-word;
}
.meta-info {
margin-top: 1.5rem;
margin-bottom: -0.5rem;
font-size: 0.9rem;
color: gray;
}
.meta-image {
width: 100%;
margin-top: 1rem;
}
.meta-image img {
width: 100%;
}
.meta-form {
width: 100%;
}
.unregister {
margin-top: 1rem;
}
.add-meta {
margin-top: 1rem;
width: 100%;
}
.pending {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 100;
background-color: rgba(0, 0, 0, 0.35);
}
.dialog h3 {
color: rgba(50, 100, 150, 1) !important;
text-transform: uppercase;
}

View File

@@ -0,0 +1,338 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import Paper from 'material-ui/Paper';
import { RaisedButton, SelectField, MenuItem } from 'material-ui';
import FindIcon from 'material-ui/svg-icons/action/find-in-page';
import DeleteIcon from 'material-ui/svg-icons/action/delete';
import Loading from '../../Loading';
import Chip from '../../Chip';
import AddMeta from './add-meta';
import styles from './token.css';
import { metaDataKeys } from '../../constants';
import { api } from '../../parity';
export default class Token extends Component {
static propTypes = {
handleMetaLookup: PropTypes.func.isRequired,
address: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
owner: PropTypes.string.isRequired,
handleAddMeta: PropTypes.func,
handleUnregister: PropTypes.func,
tla: PropTypes.string,
base: PropTypes.number,
totalSupply: PropTypes.number,
meta: PropTypes.object,
isMetaLoading: PropTypes.bool,
ownerAccountInfo: PropTypes.shape({
name: PropTypes.string,
meta: PropTypes.object
}),
metaPending: PropTypes.bool,
metaMined: PropTypes.bool,
isLoading: PropTypes.bool,
isPending: PropTypes.bool,
isTokenOwner: PropTypes.bool.isRequired,
fullWidth: PropTypes.bool
};
state = {
metaKeyIndex: 0
};
render () {
const { isLoading, fullWidth } = this.props;
if (isLoading) {
return (
<div className={ [ styles.token, styles.loading ].join(' ') }>
<Loading size={ 1 } />
</div>
);
}
if (fullWidth) {
return (<div className={ styles['full-width'] }>
{ this.renderContent() }
</div>);
}
return (<div>
<Paper zDepth={ 1 } className={ styles.token } style={ {
backgroundColor: 'none'
} }>
<div className={ styles['token-bg'] } />
{ this.renderContent() }
</Paper>
</div>);
}
renderContent () {
const { address, tla, base, name, meta, owner, totalSupply } = this.props;
return (<div className={ styles['token-container'] }>
{ this.renderIsPending() }
<div className={ styles['token-content'] }>
<div className={ styles.title }>{ tla }</div>
<div className={ styles.name }>"{ name }"</div>
{ this.renderBase(base) }
{ this.renderTotalSupply(totalSupply, base, tla) }
{ this.renderAddress(address) }
{ this.renderOwner(owner) }
</div>
<div className={ styles['token-meta'] }>
<div className={ styles['meta-form'] }>
<SelectField
floatingLabelText='Choose the meta-data to look-up'
fullWidth
value={ this.state.metaKeyIndex }
onChange={ this.onMetaKeyChange }>
{ this.renderMetaKeyItems() }
</SelectField>
<RaisedButton
label='Lookup'
icon={ <FindIcon /> }
primary
fullWidth
onTouchTap={ this.onMetaLookup } />
</div>
{ this.renderMeta(meta) }
{ this.renderAddMeta() }
{ this.renderUnregister() }
</div>
{ this.renderMetaPending() }
{ this.renderMetaMined() }
</div>);
}
renderMetaKeyItems () {
return metaDataKeys.map((key, index) => (
<MenuItem
value={ index }
key={ index }
label={ key.label } primaryText={ key.label } />
));
}
renderBase (base) {
if (!base || base < 0) return null;
return (
<Chip
value={ base.toString() }
label='Base' />
);
}
renderAddress (address) {
if (!address) return null;
return (
<Chip
isAddress
value={ address }
label='Address' />
);
}
renderTotalSupply (totalSupply, base, tla) {
const balance = Math.round((totalSupply / base) * 100) / 100;
return (
<Chip
value={ `${balance.toString()} ${tla}` }
label='Total' />
);
}
renderOwner (owner) {
if (!owner) return null;
const ownerInfo = this.props.ownerAccountInfo;
const displayValue = (ownerInfo && ownerInfo.name)
? ownerInfo.name
: owner;
return (
<Chip
isAddress
displayValue={ displayValue }
value={ owner }
label='Owner' />
);
}
renderIsPending () {
const { isPending } = this.props;
if (!isPending) {
return null;
}
return (
<div className={ styles.pending } />
);
}
renderAddMeta () {
if (!this.props.isTokenOwner) {
return null;
}
return (
<AddMeta
handleAddMeta={ this.props.handleAddMeta }
isTokenOwner={ this.props.isTokenOwner }
index={ this.props.index } />
);
}
renderUnregister () {
if (!this.props.isTokenOwner) {
return null;
}
return (
<RaisedButton
className={ styles.unregister }
label='Unregister'
icon={ <DeleteIcon /> }
secondary
fullWidth
onTouchTap={ this.onUnregister } />
);
}
renderMeta (meta) {
const isMetaLoading = this.props.isMetaLoading;
if (isMetaLoading) {
return (<div>
<Loading size={ 0.5 } />
</div>);
}
if (!meta) return;
const metaData = metaDataKeys.find(m => m.value === meta.query);
if (!meta.value) {
return (<div>
<p className={ styles['meta-query'] }>
No <span className={ styles['meta-key'] }>
{ metaData.label.toLowerCase() }
</span> meta-data...
</p>
</div>);
}
if (meta.query === 'IMG') {
const imageHash = meta.value.replace(/^0x/, '');
return (<div>
<p className={ styles['meta-query'] }>
<span className={ styles['meta-key'] }>
{ metaData.label }
</span> meta-data:
</p>
<div className={ styles['meta-image'] }>
<img src={ `http://127.0.0.1:8080/api/content/${imageHash}/` } />
</div>
</div>);
}
if (meta.query === 'A') {
const address = meta.value.slice(0, 42);
return (<div>
<p className={ styles['meta-query'] }>
<span className={ styles['meta-key'] }>
{ metaData.label }
</span> meta-data:
</p>
<p className={ styles['meta-value'] }>
{ api.util.toChecksumAddress(address) }
</p>
</div>);
}
return (<div>
<p className={ styles['meta-query'] }>
<span className={ styles['meta-key'] }>
{ metaData.label }
</span> meta-data:
</p>
<p className={ styles['meta-value'] }>{ meta.value }</p>
</div>);
}
renderMetaPending () {
const isMetaPending = this.props.metaPending;
if (!isMetaPending) return;
return (<div>
<p className={ styles['meta-info'] }>
Meta-Data pending...
</p>
</div>);
}
renderMetaMined () {
const isMetaMined = this.props.metaMined;
if (!isMetaMined) return;
return (<div>
<p className={ styles['meta-info'] }>
Meta-Data saved on the blockchain!
</p>
</div>);
}
onUnregister = () => {
const index = this.props.index;
this.props.handleUnregister(index);
}
onMetaLookup = () => {
const keyIndex = this.state.metaKeyIndex;
const key = metaDataKeys[keyIndex].value;
const index = this.props.index;
this.props.handleMetaLookup(index, key);
}
onMetaKeyChange = (event, metaKeyIndex) => {
this.setState({ metaKeyIndex });
}
}

View File

@@ -0,0 +1,262 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { getTokenTotalSupply } from '../utils';
const { bytesToHex } = window.parity.api.util;
export const SET_TOKENS_LOADING = 'SET_TOKENS_LOADING';
export const setTokensLoading = (isLoading) => ({
type: SET_TOKENS_LOADING,
isLoading
});
export const SET_TOKEN_COUNT = 'SET_TOKEN_COUNT';
export const setTokenCount = (tokenCount) => ({
type: SET_TOKEN_COUNT,
tokenCount
});
export const SET_TOKEN_DATA = 'SET_TOKEN_DATA';
export const setTokenData = (index, tokenData) => ({
type: SET_TOKEN_DATA,
index, tokenData
});
export const SET_TOKEN_META = 'SET_TOKEN_META';
export const setTokenMeta = (index, meta) => ({
type: SET_TOKEN_META,
index, meta
});
export const SET_TOKEN_LOADING = 'SET_TOKEN_LOADING';
export const setTokenLoading = (index, isLoading) => ({
type: SET_TOKEN_LOADING,
index, isLoading
});
export const SET_TOKEN_META_LOADING = 'SET_TOKEN_META_LOADING';
export const setTokenMetaLoading = (index, isMetaLoading) => ({
type: SET_TOKEN_META_LOADING,
index, isMetaLoading
});
export const SET_TOKEN_PENDING = 'SET_TOKEN_PENDING';
export const setTokenPending = (index, isPending) => ({
type: SET_TOKEN_PENDING,
index, isPending
});
export const DELETE_TOKEN = 'DELETE_TOKEN';
export const deleteToken = (index) => ({
type: DELETE_TOKEN,
index
});
export const loadTokens = () => (dispatch, getState) => {
console.log('loading tokens...');
const state = getState();
const contractInstance = state.status.contract.instance;
dispatch(setTokensLoading(true));
contractInstance
.tokenCount
.call()
.then((count) => {
const tokenCount = parseInt(count);
console.log(`token count: ${tokenCount}`);
dispatch(setTokenCount(tokenCount));
for (let i = 0; i < tokenCount; i++) {
dispatch(loadToken(i));
}
dispatch(setTokensLoading(false));
})
.catch((e) => {
console.error('loadTokens error', e);
});
};
export const loadToken = (index) => (dispatch, getState) => {
console.log('loading token', index);
const state = getState();
const contractInstance = state.status.contract.instance;
const userAccounts = state.accounts.list;
const accountsInfo = state.accounts.accountsInfo;
dispatch(setTokenLoading(index, true));
contractInstance
.token
.call({}, [ parseInt(index) ])
.then((result) => {
const tokenOwner = result[4];
const isTokenOwner = userAccounts
.filter(a => a.address === tokenOwner)
.length > 0;
const data = {
index: parseInt(index),
address: result[0],
tla: result[1],
base: result[2].toNumber(),
name: result[3],
owner: tokenOwner,
ownerAccountInfo: accountsInfo[tokenOwner],
isPending: false,
isTokenOwner
};
return data;
})
.then(data => {
return getTokenTotalSupply(data.address)
.then(totalSupply => {
data.totalSupply = totalSupply;
return data;
});
})
.then(data => {
// If no total supply, must not be a proper token
if (data.totalSupply === null) {
dispatch(setTokenData(index, null));
dispatch(setTokenLoading(index, false));
return;
}
data.totalSupply = data.totalSupply.toNumber();
console.log(`token loaded: #${index}`, data);
dispatch(setTokenData(index, data));
dispatch(setTokenLoading(index, false));
})
.catch((e) => {
dispatch(setTokenData(index, null));
dispatch(setTokenLoading(index, false));
if (!e instanceof TypeError) {
console.error(`loadToken #${index} error`, e);
}
});
};
export const queryTokenMeta = (index, query) => (dispatch, getState) => {
console.log('loading token meta', index, query);
const state = getState();
const contractInstance = state.status.contract.instance;
const startDate = Date.now();
dispatch(setTokenMetaLoading(index, true));
contractInstance
.meta
.call({}, [ index, query ])
.then((value) => {
const meta = {
query,
value: value.find(v => v !== 0) ? bytesToHex(value) : null
};
console.log(`token meta loaded: #${index}`, value);
dispatch(setTokenMeta(index, meta));
setTimeout(() => {
dispatch(setTokenMetaLoading(index, false));
}, 500 - (Date.now() - startDate));
})
.catch((e) => {
console.error(`loadToken #${index} error`, e);
});
};
export const addTokenMeta = (index, key, value) => (dispatch, getState) => {
console.log('add token meta', index, key, value);
const state = getState();
const contractInstance = state.status.contract.instance;
const token = state.tokens.tokens.find(t => t.index === index);
const options = { from: token.owner };
const values = [ index, key, value ];
contractInstance
.setMeta
.estimateGas(options, values)
.then((gasEstimate) => {
options.gas = gasEstimate.mul(1.2).toFixed(0);
console.log(`addTokenMeta: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
return contractInstance.setMeta.postTransaction(options, values);
})
.catch((e) => {
console.error(`addTokenMeta: #${index} error`, e);
});
};
export const addGithubhintURL = (from, key, url) => (dispatch, getState) => {
console.log('add githubhint url', key, url);
const state = getState();
const contractInstance = state.status.githubhint.instance;
const options = { from };
const values = [ key, url ];
contractInstance
.hintURL
.estimateGas(options, values)
.then((gasEstimate) => {
options.gas = gasEstimate.mul(1.2).toFixed(0);
console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
return contractInstance.hintURL.postTransaction(options, values);
})
.catch((e) => {
console.error('addGithubhintURL error', e);
});
};
export const unregisterToken = (index) => (dispatch, getState) => {
console.log('unregistering token', index);
const state = getState();
const contractInstance = state.status.contract.instance;
const values = [ index ];
const options = {
from: state.accounts.selected.address
};
contractInstance
.unregister
.estimateGas(options, values)
.then((gasEstimate) => {
options.gas = gasEstimate.mul(1.2).toFixed(0);
console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
return contractInstance.unregister.postTransaction(options, values);
})
.catch((e) => {
console.error(`unregisterToken #${index} error`, e);
});
};

View File

@@ -0,0 +1,80 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Tokens from './tokens';
import { loadTokens, queryTokenMeta, unregisterToken, addTokenMeta } from './actions';
class TokensContainer extends Component {
static propTypes = {
isOwner: PropTypes.bool,
isLoading: PropTypes.bool,
tokens: PropTypes.array,
tokenCount: PropTypes.number,
onLoadTokens: PropTypes.func,
accounts: PropTypes.array
};
componentDidMount () {
this.props.onLoadTokens();
}
render () {
console.log(this.props);
return (
<Tokens
{ ...this.props }
/>
);
}
}
const mapStateToProps = (state) => {
const { list } = state.accounts;
const { isLoading, tokens, tokenCount } = state.tokens;
const { isOwner } = state.status.contract;
return { isLoading, tokens, tokenCount, isOwner, accounts: list };
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadTokens: () => {
dispatch(loadTokens());
},
handleMetaLookup: (index, query) => {
dispatch(queryTokenMeta(index, query));
},
handleUnregister: (index) => {
dispatch(unregisterToken(index));
},
handleAddMeta: (index, key, value) => {
dispatch(addTokenMeta(index, key, value));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(TokensContainer);

View File

@@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './container';

View File

@@ -0,0 +1,114 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import {
SET_TOKENS_LOADING,
SET_TOKEN_COUNT,
SET_TOKEN_DATA,
SET_TOKEN_META,
SET_TOKEN_LOADING,
SET_TOKEN_META_LOADING,
SET_TOKEN_PENDING,
DELETE_TOKEN
} from './actions';
const initialState = {
isLoading: true,
tokens: [],
tokenCount: 0
};
export default (state = initialState, action) => {
switch (action.type) {
case SET_TOKENS_LOADING:
return { ...state, isLoading: action.isLoading };
case SET_TOKEN_COUNT:
return { ...state, tokenCount: action.tokenCount };
case SET_TOKEN_DATA: {
const index = action.index;
const tokens = [].concat(state.tokens);
tokens[index] = {
...tokens[index],
...action.tokenData
};
return { ...state, tokens: tokens };
}
case SET_TOKEN_META: {
const index = action.index;
const tokens = [].concat(state.tokens);
tokens[index] = {
...tokens[index],
meta: action.meta
};
return { ...state, tokens: tokens };
}
case SET_TOKEN_LOADING: {
const index = action.index;
const tokens = [].concat(state.tokens);
tokens[index] = {
...tokens[index],
isLoading: action.isLoading
};
return { ...state, tokens: tokens };
}
case SET_TOKEN_META_LOADING: {
const index = action.index;
const tokens = [].concat(state.tokens);
tokens[index] = {
...tokens[index],
isMetaLoading: action.isMetaLoading
};
return { ...state, tokens: tokens };
}
case SET_TOKEN_PENDING: {
const index = action.index;
const tokens = [].concat(state.tokens);
tokens[index] = {
...tokens[index],
isPending: action.isPending
};
return { ...state, tokens: tokens };
}
case DELETE_TOKEN: {
const index = action.index;
const tokens = [].concat(state.tokens);
delete tokens[index];
return { ...state, tokens: tokens };
}
default:
return state;
}
};

View File

@@ -0,0 +1,24 @@
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.tokens {
width: 90%;
padding-top: 2rem;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}

View File

@@ -0,0 +1,68 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import Token from './Token';
import Loading from '../Loading';
import styles from './tokens.css';
export default class Tokens extends Component {
static propTypes = {
handleAddMeta: PropTypes.func.isRequired,
handleUnregister: PropTypes.func.isRequired,
handleMetaLookup: PropTypes.func.isRequired,
isOwner: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
tokens: PropTypes.array,
accounts: PropTypes.array
};
render () {
const { isLoading, tokens } = this.props;
const loading = isLoading ? (<Loading size={ 2 } />) : null;
return (
<div className={ styles.tokens }>
{ this.renderTokens(tokens) }
{ loading }
</div>
);
}
renderTokens (tokens) {
const { accounts } = this.props;
return tokens.map((token, index) => {
if (!token || !token.tla) {
return null;
}
const isTokenOwner = !!accounts.find((account) => account.address === token.owner);
return (
<Token
{ ...token }
handleUnregister={ this.props.handleUnregister }
handleMetaLookup={ this.props.handleMetaLookup }
handleAddMeta={ this.props.handleAddMeta }
key={ index }
isTokenOwner={ isTokenOwner } />
);
});
}
}

View File

@@ -0,0 +1,30 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { HEX_TYPE, ADDRESS_TYPE } from './Inputs/validation';
export const metaDataKeys = [
{
label: 'Image',
value: 'IMG',
validation: HEX_TYPE
},
{
label: 'Address',
value: 'A',
validation: ADDRESS_TYPE
}
];

View File

@@ -0,0 +1,21 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const { api } = window.parity;
export {
api
};

View File

@@ -0,0 +1,28 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { combineReducers } from 'redux';
import status from './Status/reducer';
import tokens from './Tokens/reducer';
import actions from './Actions/reducer';
import accounts from './Accounts/reducer';
const rootReducer = combineReducers({
status, tokens, actions, accounts
});
export default rootReducer;

View File

@@ -0,0 +1,22 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
export default createStore(reducer, applyMiddleware(thunk));

View File

@@ -0,0 +1,37 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { api } from './parity';
import { eip20 as eip20Abi } from '../../contracts/abi';
export const getTokenTotalSupply = (tokenAddress) => {
return api
.eth
.getCode(tokenAddress)
.then(code => {
if (!code || /^(0x)?0?$/.test(code)) {
return null;
}
const contract = api.newContract(eip20Abi, tokenAddress);
return contract
.instance
.totalSupply
.call({}, []);
});
};