2018-10-08 18:06:41 -07:00
|
|
|
import React from 'react';
|
|
|
|
import { Button, Spin, Icon, Alert } from 'antd';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
import Identicon from 'components/Identicon';
|
|
|
|
import ShortAddress from 'components/ShortAddress';
|
|
|
|
import './ChooseAddress.less';
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
addresses: string[];
|
|
|
|
loadingMessage: string;
|
|
|
|
handleDeriveAddresses(index: number, numNeeded: number): Promise<void>;
|
|
|
|
onSelectAddress(address: string): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface State {
|
|
|
|
index: number;
|
|
|
|
isLoading: boolean;
|
|
|
|
error: null | string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ADDRESSES_PER_PAGE = 6;
|
|
|
|
|
|
|
|
export default class ChooseAddress extends React.PureComponent<Props, State> {
|
|
|
|
state: State = {
|
|
|
|
index: 0,
|
|
|
|
isLoading: false,
|
|
|
|
error: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
this.deriveAddresses();
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps: Props) {
|
|
|
|
// Detect resets of the array, kick off derive
|
|
|
|
if (prevProps.addresses !== this.props.addresses && !this.props.addresses.length) {
|
|
|
|
this.setState({ index: 0 }, () => {
|
|
|
|
this.deriveAddresses();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { addresses } = this.props;
|
|
|
|
const { index, isLoading, error } = this.state;
|
|
|
|
|
|
|
|
let content;
|
|
|
|
if (error) {
|
|
|
|
content = (
|
|
|
|
<div className="ChooseAddress-error">
|
|
|
|
<Alert
|
|
|
|
type="error"
|
|
|
|
message="Something went wrong"
|
|
|
|
description={error}
|
|
|
|
showIcon
|
|
|
|
/>
|
|
|
|
<Button size="large" onClick={this.deriveAddresses}>
|
|
|
|
Try again
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
if (isLoading) {
|
|
|
|
content = (
|
|
|
|
<Spin size="large">
|
|
|
|
<div className="ChooseAddress-loading">
|
|
|
|
{new Array(ADDRESSES_PER_PAGE).fill(null).map((_, idx) => (
|
|
|
|
<AddressChoice key={idx} isFake={true} name="Loading" address="0x0" />
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</Spin>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const pageAddresses = addresses.slice(index, index + ADDRESSES_PER_PAGE);
|
|
|
|
content = (
|
|
|
|
<div className="ChooseAddress-addresses">
|
|
|
|
{pageAddresses.map(address => (
|
|
|
|
<AddressChoice
|
|
|
|
key={address}
|
|
|
|
address={address}
|
|
|
|
name={`Address #${addresses.indexOf(address) + 1}`}
|
|
|
|
onClick={this.props.onSelectAddress}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
content = (
|
|
|
|
<>
|
|
|
|
{content}
|
|
|
|
<div className="ChooseAddress-buttons">
|
|
|
|
<Button
|
|
|
|
className="ChooseAddress-buttons-button"
|
|
|
|
disabled={index <= 0}
|
|
|
|
onClick={this.prev}
|
|
|
|
>
|
|
|
|
<Icon type="arrow-left" />
|
|
|
|
</Button>
|
|
|
|
<Button className="ChooseAddress-buttons-button" onClick={this.next}>
|
|
|
|
<Icon type="arrow-right" />
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return <div className="ChooseAddress">{content}</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
private deriveAddresses = () => {
|
|
|
|
this.setState(
|
|
|
|
{
|
|
|
|
isLoading: true,
|
|
|
|
error: null,
|
|
|
|
},
|
|
|
|
() => {
|
|
|
|
this.props
|
|
|
|
.handleDeriveAddresses(this.state.index, ADDRESSES_PER_PAGE)
|
|
|
|
.then(() => this.setState({ isLoading: false }))
|
|
|
|
.catch(err => this.setState({ isLoading: false, error: err.message }));
|
|
|
|
},
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
private next = () => {
|
|
|
|
this.setState({ index: this.state.index + ADDRESSES_PER_PAGE }, () => {
|
|
|
|
if (!this.props.addresses[this.state.index + ADDRESSES_PER_PAGE]) {
|
|
|
|
this.deriveAddresses();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
private prev = () => {
|
|
|
|
this.setState({ index: Math.max(0, this.state.index - ADDRESSES_PER_PAGE) });
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
interface AddressChoiceProps {
|
|
|
|
address: string;
|
|
|
|
name: string;
|
|
|
|
isFake?: boolean;
|
|
|
|
onClick?(address: string): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AddressChoice: React.SFC<AddressChoiceProps> = props => (
|
|
|
|
<button
|
|
|
|
className={classnames('AddressChoice', props.isFake && 'is-fake')}
|
2018-10-19 15:03:37 -07:00
|
|
|
onClick={() => props.onClick && props.onClick(props.address)}
|
2018-10-08 18:06:41 -07:00
|
|
|
>
|
|
|
|
{/* TODO: Use user avatar + name if they have an account */}
|
|
|
|
{props.isFake ? (
|
|
|
|
<div className="AddressChoice-avatar" />
|
|
|
|
) : (
|
|
|
|
<Identicon className="AddressChoice-avatar" address={props.address} />
|
|
|
|
)}
|
|
|
|
<div className="AddressChoice-name">{props.name}</div>
|
|
|
|
<div className="AddressChoice-address">
|
|
|
|
<ShortAddress address={props.address} />
|
|
|
|
</div>
|
|
|
|
</button>
|
|
|
|
);
|