2017-01-25 18:51:41 +01:00
|
|
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
2016-12-27 10:59:37 +01:00
|
|
|
// 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 ReactDOM from 'react-dom';
|
|
|
|
import keycode from 'keycode';
|
|
|
|
|
2017-01-31 12:21:50 +01:00
|
|
|
import Balance from '~/ui/Balance';
|
2017-04-28 12:17:19 +02:00
|
|
|
import Container from '~/ui/Container';
|
2016-12-27 10:59:37 +01:00
|
|
|
import IdentityIcon from '~/ui/IdentityIcon';
|
2017-01-23 17:33:03 +01:00
|
|
|
import IdentityName from '~/ui/IdentityName';
|
2016-12-27 10:59:37 +01:00
|
|
|
import Tags from '~/ui/Tags';
|
|
|
|
|
|
|
|
import styles from './accountCard.css';
|
|
|
|
|
|
|
|
export default class AccountCard extends Component {
|
|
|
|
static propTypes = {
|
2017-04-26 11:34:48 +02:00
|
|
|
children: PropTypes.node,
|
2016-12-27 10:59:37 +01:00
|
|
|
account: PropTypes.object.isRequired,
|
2017-01-31 12:21:50 +01:00
|
|
|
balance: PropTypes.object,
|
|
|
|
className: PropTypes.string,
|
2017-03-08 12:00:27 +01:00
|
|
|
disableAddressClick: PropTypes.bool,
|
2017-01-31 12:21:50 +01:00
|
|
|
onClick: PropTypes.func,
|
|
|
|
onFocus: PropTypes.func
|
2016-12-27 10:59:37 +01:00
|
|
|
};
|
|
|
|
|
2017-03-03 14:38:40 +01:00
|
|
|
static defaultProps = {
|
2017-03-08 12:00:27 +01:00
|
|
|
disableAddressClick: false
|
2017-03-03 14:38:40 +01:00
|
|
|
};
|
|
|
|
|
2016-12-27 10:59:37 +01:00
|
|
|
state = {
|
|
|
|
copied: false
|
|
|
|
};
|
|
|
|
|
|
|
|
render () {
|
2017-04-26 11:34:48 +02:00
|
|
|
const { account, balance, className, onFocus, children } = this.props;
|
2016-12-27 10:59:37 +01:00
|
|
|
const { copied } = this.state;
|
2017-01-23 17:33:03 +01:00
|
|
|
const { address, description, meta = {}, name } = account;
|
2016-12-27 10:59:37 +01:00
|
|
|
const { tags = [] } = meta;
|
2017-01-31 12:21:50 +01:00
|
|
|
const classes = [ styles.account, className ];
|
2016-12-27 10:59:37 +01:00
|
|
|
|
|
|
|
if (copied) {
|
|
|
|
classes.push(styles.copied);
|
|
|
|
}
|
|
|
|
|
2017-02-24 14:37:56 +01:00
|
|
|
const props = onFocus
|
|
|
|
? { tabIndex: 0 }
|
|
|
|
: {};
|
|
|
|
|
2016-12-27 10:59:37 +01:00
|
|
|
return (
|
2017-04-28 12:17:19 +02:00
|
|
|
<Container
|
2016-12-27 10:59:37 +01:00
|
|
|
key={ address }
|
|
|
|
className={ classes.join(' ') }
|
|
|
|
onClick={ this.onClick }
|
|
|
|
onFocus={ this.onFocus }
|
|
|
|
onKeyDown={ this.handleKeyDown }
|
2017-02-24 14:37:56 +01:00
|
|
|
{ ...props }
|
2016-12-27 10:59:37 +01:00
|
|
|
>
|
2017-02-16 17:42:19 +01:00
|
|
|
<div className={ styles.mainContainer }>
|
|
|
|
<div className={ styles.infoContainer }>
|
|
|
|
<IdentityIcon address={ address } />
|
|
|
|
<div className={ styles.accountInfo }>
|
|
|
|
<div className={ styles.accountName }>
|
|
|
|
<IdentityName
|
|
|
|
address={ address }
|
|
|
|
name={ name }
|
|
|
|
unknown
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
{ this.renderDescription(description) }
|
|
|
|
{ this.renderAddress(address) }
|
2017-01-31 12:21:50 +01:00
|
|
|
</div>
|
2016-12-27 10:59:37 +01:00
|
|
|
</div>
|
2017-02-16 17:42:19 +01:00
|
|
|
|
|
|
|
<Balance
|
2017-04-19 18:00:05 +02:00
|
|
|
address={ address }
|
2017-02-16 17:42:19 +01:00
|
|
|
balance={ balance }
|
|
|
|
className={ styles.balance }
|
|
|
|
showOnlyEth
|
|
|
|
/>
|
2017-04-26 11:34:48 +02:00
|
|
|
{ children }
|
2016-12-27 10:59:37 +01:00
|
|
|
</div>
|
2017-01-31 12:21:50 +01:00
|
|
|
|
2017-02-16 17:42:19 +01:00
|
|
|
{
|
|
|
|
tags && tags.length > 0
|
|
|
|
? (
|
|
|
|
<div className={ styles.tagsContainer }>
|
|
|
|
<div className={ styles.tags }>
|
|
|
|
<Tags
|
|
|
|
floating={ false }
|
|
|
|
horizontal
|
|
|
|
tags={ tags }
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : null
|
|
|
|
}
|
|
|
|
|
2017-04-28 12:17:19 +02:00
|
|
|
</Container>
|
2016-12-27 10:59:37 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-12-29 19:47:53 +01:00
|
|
|
renderDescription (description) {
|
|
|
|
if (!description) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={ styles.description }>
|
|
|
|
<span>{ description }</span>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-01-23 17:33:03 +01:00
|
|
|
renderAddress (address) {
|
2016-12-27 10:59:37 +01:00
|
|
|
return (
|
|
|
|
<div className={ styles.addressContainer }>
|
|
|
|
<span
|
|
|
|
className={ styles.address }
|
2017-03-03 14:38:40 +01:00
|
|
|
onClick={ this.handleAddressClick }
|
2016-12-27 10:59:37 +01:00
|
|
|
ref={ `address` }
|
|
|
|
title={ address }
|
|
|
|
>
|
|
|
|
{ address }
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-03-03 14:38:40 +01:00
|
|
|
handleAddressClick = (event) => {
|
2017-03-08 12:00:27 +01:00
|
|
|
const { disableAddressClick } = this.props;
|
2017-03-03 14:38:40 +01:00
|
|
|
|
2017-03-08 12:00:27 +01:00
|
|
|
// Stop the event if address click is disallowed
|
|
|
|
if (disableAddressClick) {
|
|
|
|
return this.preventEvent(event);
|
2017-03-03 14:38:40 +01:00
|
|
|
}
|
|
|
|
|
2017-03-08 12:00:27 +01:00
|
|
|
return this.onClick(event);
|
2017-03-03 14:38:40 +01:00
|
|
|
}
|
|
|
|
|
2016-12-27 10:59:37 +01:00
|
|
|
handleKeyDown = (event) => {
|
|
|
|
const codeName = keycode(event);
|
|
|
|
|
|
|
|
if (event.ctrlKey) {
|
|
|
|
// Copy the selected address if nothing selected and there is
|
|
|
|
// a focused item
|
|
|
|
const isSelection = !window.getSelection || window.getSelection().type === 'Range';
|
|
|
|
|
|
|
|
if (codeName === 'c' && !isSelection) {
|
|
|
|
const element = ReactDOM.findDOMNode(this.refs.address);
|
|
|
|
|
|
|
|
// Copy the address from the right element
|
|
|
|
// @see https://developers.google.com/web/updates/2015/04/cut-and-copy-commands
|
|
|
|
try {
|
|
|
|
const range = document.createRange();
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2016-12-27 10:59:37 +01:00
|
|
|
range.selectNode(element);
|
|
|
|
window.getSelection().addRange(range);
|
|
|
|
document.execCommand('copy');
|
|
|
|
|
|
|
|
try {
|
|
|
|
window.getSelection().removeRange(range);
|
|
|
|
} catch (e) {
|
|
|
|
window.getSelection().removeAllRanges();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({ copied: true }, () => {
|
|
|
|
window.setTimeout(() => {
|
|
|
|
this.setState({ copied: false });
|
|
|
|
}, 250);
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('could not copy');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-03 14:38:40 +01:00
|
|
|
onClick = (event) => {
|
2016-12-27 10:59:37 +01:00
|
|
|
const { account, onClick } = this.props;
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2017-03-03 14:38:40 +01:00
|
|
|
// Stop the default event if text is selected
|
|
|
|
if (window.getSelection && window.getSelection().type === 'Range') {
|
|
|
|
return this.preventEvent(event);
|
|
|
|
}
|
|
|
|
|
2017-01-31 12:21:50 +01:00
|
|
|
onClick && onClick(account.address);
|
2016-12-27 10:59:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
onFocus = () => {
|
|
|
|
const { account, onFocus } = this.props;
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2017-01-31 12:21:50 +01:00
|
|
|
onFocus && onFocus(account.index);
|
2016-12-27 10:59:37 +01:00
|
|
|
}
|
|
|
|
|
2017-03-03 14:38:40 +01:00
|
|
|
preventEvent = (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2016-12-27 10:59:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
setTagRef = (tagRef) => {
|
|
|
|
this.tagRefs.push(tagRef);
|
|
|
|
}
|
|
|
|
}
|