zcash-grant-system/frontend/client/components/AuthFlow/providers/ChooseAddress.tsx

163 lines
4.3 KiB
TypeScript

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')}
onClick={() => props.onClick && props.onClick(props.address)}
>
{/* 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>
);