chore: settings copy and styles

This commit is contained in:
Andre Neves 2019-02-24 16:26:07 -05:00
parent 9ab1c07d0c
commit 62cb8e417a
6 changed files with 234 additions and 67 deletions

View File

@ -33,6 +33,10 @@ export class Clipboard extends PureComponent<Props, State> {
if (document.body) document.body.removeChild(el);
this.setState({ copied: true });
setTimeout(() => {
this.setState(() => ({ copied: false }));
}, 1500);
};
render() {
@ -42,7 +46,7 @@ export class Clipboard extends PureComponent<Props, State> {
return (
<div data-testid='Clipboard'>
<Button
label={copied ? 'Copied!' : 'Copy!'}
label={copied ? 'Copied!' : 'Copy'}
className={className}
onClick={this.handleClick}
disabled={copied}

View File

@ -13,6 +13,8 @@ const brandThree = '#5d5d65';
export const DARK_COLORS = {
// General
text,
success,
error,
background: '#212124',
divider: black,
inactiveItem: brandThree,

View File

@ -16,6 +16,8 @@ const border = '#DDDDDD';
export const LIGHT_COLORS = {
// General
text,
success,
error,
background: offWhite,
divider: '#AAAAAA',
inactiveItem: success,

View File

@ -1,6 +1,6 @@
// @flow
import React, { Fragment, PureComponent } from 'react';
import React, { PureComponent } from 'react';
import styled, { withTheme, keyframes } from 'styled-components';
import { BigNumber } from 'bignumber.js';
import { Transition, animated } from 'react-spring';
@ -27,7 +27,8 @@ import MenuIconDark from '../assets/images/menu_icon_dark.svg';
import MenuIconLight from '../assets/images/menu_icon_light.svg';
import ValidIcon from '../assets/images/green_check_dark.png';
import InvalidIcon from '../assets/images/error_icon_dark.png';
import LoadingIcon from '../assets/images/sync_icon_dark.png';
import LoadingIconDark from '../assets/images/sync_icon_dark.png';
import LoadingIconLight from '../assets/images/sync_icon_light.png';
import ArrowUpIconDark from '../assets/images/arrow_up_dark.png';
import ArrowUpIconLight from '../assets/images/arrow_up_light.png';
@ -43,14 +44,6 @@ const rotate = keyframes`
}
`;
const Loader = styled.img`
width: 25px;
height: 25px;
animation: 2s linear infinite;
animation-name: ${rotate};
margin-bottom: 10px;
`;
const FormWrapper = styled.div`
width: 71%;
`;
@ -170,12 +163,12 @@ const ModalContent = styled(ColumnComponent)`
justify-content: center;
p {
word-break: break-all;
word-break: break-word;
}
`;
const ConfirmItemWrapper = styled(RowComponent)`
padding: 22.5px 33.5px;
padding: 22.5px 40px;
width: 100%;
`;
@ -294,8 +287,9 @@ const HexadecimalText = styled(TextComponent)`
const SimpleTooltip = styled.div`
background: ${props => props.theme.colors.walletAddressTooltipBg};
position: absolute;
top: -30px;
right: 0px;
top: -24px;
left: 0;
right: 0;
padding: 6px 10px;
border-radius: ${props => props.theme.boxBorderRadius};
`;
@ -313,6 +307,74 @@ const SendButtonWrapper = styled.div`
display: flex;
`;
const ErrorWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
padding: 20px 40px;
`;
const ErrorLabel = styled(TextComponent)`
font-weight: 700;
font-size: 20px;
margin-bottom: 16px;
`;
const ErrorMessage = styled(TextComponent)`
font-size: 14px;
font-weight: 700;
color: ${props => props.theme.colors.error};
text-align: center;
margin-bottom: 20px;
`;
const LoaderWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Loader = styled.img`
width: 45px;
height: 45px;
animation: 2s linear infinite;
animation-name: ${rotate};
margin-bottom: 30px;
`;
const ZSuccessWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px 40px;
`;
const ZSuccessContentWrapper = styled.div`
padding: 0 0 40px 0;
`;
const ZSuccessLabel = styled(TextComponent)`
color: ${props => props.theme.colors.success};
font-weight: 700;
font-size: 30px;
`;
const ZSuccessMessage = styled(TextComponent)`
text-align: center;
margin-bottom: 40px;
margin-top: 5px;
`;
const ZSuccessTransactionId = styled(TextComponent)`
text-align: center;
word-break: break-all !important;
`;
type Props = {
match: Match,
theme: AppTheme,
@ -368,10 +430,13 @@ class Component extends PureComponent<Props, State> {
}
updateTooltipVisibility = ({ balance, amount }: { balance: number, amount: number }) => {
const { from, to } = this.state;
const { from, to, fee } = this.state;
const feeValue = fee || 0;
this.setState({
showBalanceTooltip: !from || !to ? false : new BigNumber(amount).gt(balance),
showBalanceTooltip: (!from || !to)
? false
: new BigNumber(amount).plus(feeValue).gt(balance),
});
};
@ -514,36 +579,63 @@ class Component extends PureComponent<Props, State> {
/* eslint-enable react/no-unused-prop-types */
}) => {
// eslint-disable-next-line react/prop-types
const { operationId, isSending, error } = this.props;
const {
operationId, isSending, error, theme,
} = this.props;
const { from, to } = this.state;
const loadingIcon = theme.mode === DARK
? LoadingIconDark
: LoadingIconLight;
if (isSending) {
return (
<Fragment>
<Loader src={LoadingIcon} />
<LoaderWrapper>
<Loader src={loadingIcon} />
<TextComponent value='Processing transaction...' />
</Fragment>
</LoaderWrapper>
);
}
if (operationId) {
return (
<ColumnComponent width='100%' id='send-success-wrapper'>
<TextComponent value={`Transaction ID: ${operationId}`} align='center' />
<button
type='button'
<ZSuccessWrapper id='send-success-wrapper'>
<ZSuccessLabel value='Success!' />
<ZSuccessContentWrapper>
<ZSuccessMessage value='Your transaction was sent successfully.' />
<ZSuccessTransactionId value={`Transaction ID: ${operationId}`} />
</ZSuccessContentWrapper>
<FormButton
label='Done'
variant='primary'
onClick={() => {
this.reset();
toggle();
}}
>
Send again!
</button>
</ColumnComponent>
/>
</ZSuccessWrapper>
);
}
if (error) return <TextComponent id='send-error-message' value={error} />;
if (error) {
return (
<ErrorWrapper>
<ErrorLabel value='Error' />
<ErrorMessage
id='send-error-message'
value={error}
/>
<FormButton
label='Try Again'
variant='primary'
onClick={() => {
this.reset();
toggle();
}}
/>
</ErrorWrapper>
);
}
return (
<>
@ -646,7 +738,7 @@ class Component extends PureComponent<Props, State> {
<AmountInput
renderRight={() => (
<MaxAvailableAmount
onClick={() => this.handleChange('amount')(balance)}
onClick={() => this.handleChange('amount')(balance - (Number(fee) || 0))}
disabled={!from}
>
<MaxAvailableAmountImg src={arrowUpIcon} />
@ -774,23 +866,22 @@ class Component extends PureComponent<Props, State> {
</InfoContent>
</InfoCard>
<ConfirmDialogComponent
title='Please Confirm Transaction Details'
title='Transaction Status'
onConfirm={this.handleSubmit}
showButtons={!isSending && !error && !operationId}
onClose={this.reset}
renderTrigger={toggle => (
<SendButtonWrapper>
{showBalanceTooltip ? (
{!showBalanceTooltip ? null : (
<SimpleTooltip>
<TooltipText value='You do not seem' />
<TooltipText value='to have enough funds' />
<TooltipText value='Not enough funds!' />
</SimpleTooltip>
) : null}
)}
<FormButton
onClick={() => this.showModal(toggle)}
id='send-submit-button'
label='Send'
variant='secondary'
variant='primary'
focused
isFluid
disabled={this.shouldDisableSendButton()}
@ -808,7 +899,11 @@ class Component extends PureComponent<Props, State> {
</ModalContent>
)}
</ConfirmDialogComponent>
<FormButton label='Clear Form' variant='secondary' />
<FormButton
label='Clear Form'
variant='secondary'
onClick={this.reset}
/>
</SendWrapper>
</RowComponent>
);

View File

@ -17,7 +17,6 @@ import { ConfirmDialogComponent } from '../components/confirm-dialog';
import { TextComponent } from '../components/text';
import { InputComponent } from '../components/input';
import { InputLabelComponent } from '../components/input-label';
import { RowComponent } from '../components/row';
import { Clipboard } from '../components/clipboard';
import { SelectComponent } from '../components/select';
@ -33,23 +32,25 @@ const EXPORT_VIEW_KEYS_CONTENT = 'Viewing keys for shielded addresses allow for
const EXPORT_VIEW_KEYS_LEARN_MORE = 'https://z.cash/blog/viewing-keys-selective-disclosure';
const IMPORT_PRIV_KEYS_TITLE = 'Import Private Keys';
const IMPORT_PRIV_KEYS_CONTENT = 'Importing private keys will add the spendable coins to this wallet.';
const IMPORT_PRIV_KEYS_CONTENT_MODAL = 'Paste your private keys here, one per line. These spending keys will be imported into your wallet.';
const IMPORT_PRIV_KEYS_SUCCESS_CONTENT = 'Private keys imported in your wallet. Any spendable coins should now be available.';
const EXPORT_PRIV_KEYS_TITLE = 'Export Private Keys';
const EXPORT_PRIV_KEYS_CONTENT = 'Beware: exporting your private keys will allow anyone controlling them to spend your coins. Only perform this action on a trusted machine.';
const BACKUP_WALLET_TITLE = 'Backup Wallet';
const BACKUP_WALLET_CONTENT = 'It is recommended that you backup your wallet often.';
const BACKUP_WALLET_CONTENT = 'It is recommended that you backup your wallet often to avoid possible issues arising from data corruption.';
const Wrapper = styled.div`
margin-top: ${props => props.theme.layoutContentPaddingTop};
`;
const ModalContent = styled.div`
padding: 20px 30px;
padding: 20px 40px;
width: 100%;
max-height: 600px;
overflow-y: auto;
p {
word-break: break-all;
word-break: break-word;
}
`;
@ -128,6 +129,59 @@ const SettingsActionWrapper = styled.div`
justify-content: space-between;
`;
const StatusWrapper = styled.div`
padding: 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
`;
const StatusTextError = styled(TextComponent)`
font-weight: 700;
color: ${props => props.theme.colors.error};
`;
const StatusTextSuccess = styled(TextComponent)`
font-weight: 700;
color: ${props => props.theme.colors.success};
`;
const ViewKeyHeader = styled.div`
display: flex;
flex-direction: column;
padding: 30px 0 10px 0;
`;
const ViewKeyLabel = styled(TextComponent)`
font-weight: ${props => String(props.theme.fontWeight.bold)};
font-size: ${props => String(props.theme.fontSize.small)};
color: ${props => props.theme.colors.modalItemLabel};
margin-bottom: 3.5px;
`;
const ViewKeyAddress = styled(TextComponent)`
font-size: 12px;
padding: 10px 0;
line-height: 1.5;
word-break: break-all !important;
`;
const ViewKeyContentWrapper = styled.div`
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
> :first-child {
width: 100%;
}
`;
const ViewKeyInputComponent = styled(InputComponent)`
font-size: 12px;
`;
type Key = {
zAddress: string,
key: string,
@ -321,27 +375,28 @@ export class SettingsView extends PureComponent<Props, State> {
)}
onConfirm={this.exportViewKeys}
showButtons={!successExportViewKeys}
width={450}
width={550}
>
{() => (
<ModalContent>
{successExportViewKeys ? (
viewKeys.map(({ zAddress, key }) => (
viewKeys.map(({ zAddress, key }, index) => (
<>
<InputLabelComponent value={zAddress} />
<RowComponent alignItems='center'>
<InputComponent
<ViewKeyHeader>
<ViewKeyLabel value={`View Key for Address #${index + 1}`} />
<ViewKeyAddress value={`Address: ${zAddress}`} />
</ViewKeyHeader>
<ViewKeyContentWrapper>
<ViewKeyInputComponent
value={key}
onFocus={(event) => {
event.currentTarget.select();
}}
onFocus={event => event.currentTarget.select()}
/>
<ClipboardButton text={key} />
</RowComponent>
</ViewKeyContentWrapper>
</>
))
) : (
<TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' />
<TextComponent value={EXPORT_VIEW_KEYS_CONTENT} />
)}
</ModalContent>
)}
@ -359,27 +414,29 @@ export class SettingsView extends PureComponent<Props, State> {
)}
onConfirm={this.exportPrivateKeys}
showButtons={!successExportPrivateKeys}
width={450}
width={550}
>
{() => (
<ModalContent>
{successExportPrivateKeys ? (
privateKeys.map(({ zAddress, key }) => (
privateKeys.map(({ zAddress, key }, index) => (
<>
<InputLabelComponent value={zAddress} />
<RowComponent alignItems='center'>
<InputComponent
<ViewKeyHeader>
<ViewKeyLabel value={`Private Key for Address #${index + 1}`} />
<ViewKeyAddress value={`Address: ${zAddress}`} />
</ViewKeyHeader>
<ViewKeyContentWrapper>
<ViewKeyInputComponent
value={key}
onFocus={(event) => {
event.currentTarget.select();
}}
width='100%'
onFocus={event => event.currentTarget.select()}
/>
<ClipboardButton text={key} />
</RowComponent>
</ViewKeyContentWrapper>
</>
))
) : (
<TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' />
<TextComponent value={EXPORT_PRIV_KEYS_CONTENT} />
)}
</ModalContent>
)}
@ -400,17 +457,22 @@ export class SettingsView extends PureComponent<Props, State> {
>
{() => (
<ModalContent>
<InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' />
<InputLabelComponent
marginTop='0'
value={IMPORT_PRIV_KEYS_CONTENT_MODAL}
/>
<InputComponent
value={importedPrivateKeys}
onChange={value => this.setState({ importedPrivateKeys: value })}
inputType='textarea'
rows={10}
/>
{successImportPrivateKeys && (
<TextComponent value='Private keys imported in your node' align='center' />
)}
{error && <TextComponent value={error} align='center' />}
<StatusWrapper>
{successImportPrivateKeys && (
<StatusTextSuccess value={IMPORT_PRIV_KEYS_SUCCESS_CONTENT} />
)}
{error && <StatusTextError value={error} align='center' />}
</StatusWrapper>
</ModalContent>
)}
</ConfirmDialogComponent>

View File

@ -5,6 +5,8 @@ import { ThemeSet } from 'styled-theming';
type Colors = {
// General
text: ThemeSet,
success: ThemeSet,
error: ThemeSet,
background: ThemeSet,
divider: ThemeSet,
activeItem: ThemeSet,