chore(git): merge with developer
This commit is contained in:
parent
495bcd7548
commit
e68583b363
|
@ -34,7 +34,7 @@
|
|||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 80,
|
||||
"code": 100,
|
||||
"tabWidth": 2,
|
||||
"ignoreUrls": true,
|
||||
"ignoreComments": true,
|
||||
|
|
|
@ -9,6 +9,9 @@ import { Link } from 'react-router-dom';
|
|||
import { darken } from 'polished';
|
||||
|
||||
const DefaultButton = styled.button`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px 30px;
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
|
@ -19,7 +22,6 @@ const DefaultButton = styled.button`
|
|||
border-radius: 100px;
|
||||
transition: background-color 0.1s
|
||||
${props => props.theme.colors.transitionEase};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Primary = styled(DefaultButton)`
|
||||
|
@ -32,7 +34,7 @@ const Primary = styled(DefaultButton)`
|
|||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #3e3c42;
|
||||
background-color: ${props => props.theme.colors.buttonBorderColor};
|
||||
cursor: not-allowed;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
@ -41,7 +43,7 @@ const Primary = styled(DefaultButton)`
|
|||
const Secondary = styled(DefaultButton)`
|
||||
background-color: transparent;
|
||||
color: ${props => props.theme.colors.secondary};
|
||||
border: 2px solid #3e3c42;
|
||||
border: 2px solid ${props => props.theme.colors.buttonBorderColor};
|
||||
|
||||
&:hover {
|
||||
border-color: ${props => props.theme.colors.primary};
|
||||
|
@ -50,16 +52,16 @@ const Secondary = styled(DefaultButton)`
|
|||
&:disabled {
|
||||
background-color: Transparent;
|
||||
cursor: not-allowed;
|
||||
color: #3e3c42;
|
||||
color: ${props => props.theme.colors.buttonBorderColor};
|
||||
|
||||
&:hover {
|
||||
border-color: #3e3c42;
|
||||
border-color: ${props => props.theme.colors.buttonBorderColor};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Icon = styled.img`
|
||||
height: 9px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin-right: 10px;
|
||||
`;
|
||||
|
|
|
@ -50,54 +50,64 @@ type Props = {
|
|||
renderTrigger: (() => void) => Element<*>,
|
||||
title: string,
|
||||
onConfirm: () => void,
|
||||
onClose?: () => void,
|
||||
showButtons?: boolean,
|
||||
width?: number,
|
||||
isLoading?: boolean,
|
||||
children: Element<*>,
|
||||
children: (() => void) => Element<*>,
|
||||
};
|
||||
|
||||
export const ConfirmDialogComponent = ({
|
||||
children,
|
||||
title,
|
||||
onConfirm,
|
||||
onClose,
|
||||
renderTrigger,
|
||||
showButtons,
|
||||
isLoading,
|
||||
width,
|
||||
}: Props) => (
|
||||
<ModalComponent
|
||||
renderTrigger={renderTrigger}
|
||||
closeOnBackdropClick={false}
|
||||
closeOnEsc={false}
|
||||
>
|
||||
{toggle => (
|
||||
<Wrapper width={width}>
|
||||
<CloseIconWrapper>
|
||||
<CloseIconImg src={CloseIcon} onClick={toggle} />
|
||||
</CloseIconWrapper>
|
||||
<TitleWrapper>
|
||||
<TextComponent value={title} align='center' />
|
||||
</TitleWrapper>
|
||||
<Divider />
|
||||
{React.Children.map(children, _ => _)}
|
||||
{showButtons && (
|
||||
<>
|
||||
<Btn label='Confirm' onClick={onConfirm} isLoading={isLoading} />
|
||||
<Btn
|
||||
label='Cancel'
|
||||
onClick={toggle}
|
||||
variant='secondary'
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Wrapper>
|
||||
)}
|
||||
</ModalComponent>
|
||||
);
|
||||
}: Props) => {
|
||||
const handleClose = toggle => () => {
|
||||
toggle();
|
||||
if (onClose) onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalComponent
|
||||
renderTrigger={renderTrigger}
|
||||
closeOnBackdropClick={false}
|
||||
closeOnEsc={false}
|
||||
>
|
||||
{toggle => (
|
||||
<Wrapper width={width}>
|
||||
<CloseIconWrapper>
|
||||
<CloseIconImg src={CloseIcon} onClick={handleClose(toggle)} />
|
||||
</CloseIconWrapper>
|
||||
<TitleWrapper>
|
||||
<TextComponent value={title} align='center' />
|
||||
</TitleWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
{children(handleClose(toggle))}
|
||||
{showButtons && (
|
||||
<>
|
||||
<Btn label='Confirm' onClick={onConfirm} isLoading={isLoading} />
|
||||
<Btn
|
||||
label='Cancel'
|
||||
onClick={handleClose(toggle)}
|
||||
variant='secondary'
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Wrapper>
|
||||
)}
|
||||
</ModalComponent>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmDialogComponent.defaultProps = {
|
||||
showButtons: true,
|
||||
width: 460,
|
||||
isLoading: false,
|
||||
onClose: () => {},
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ import { DoczWrapper } from '../theme.js'
|
|||
onConfirm={() => alert('Confirm')}
|
||||
renderTrigger={toggle => <button onClick={toggle}> Open! </button>}
|
||||
>
|
||||
<div>Confirm content</div>
|
||||
{toggle => <div>Confirm content</div>}
|
||||
</ConfirmDialogComponent>
|
||||
)}
|
||||
</DoczWrapper>
|
||||
|
|
|
@ -6,4 +6,6 @@ export const Divider = styled.div`
|
|||
height: 1px;
|
||||
background-color: ${props => props.color || props.theme.colors.text};
|
||||
opacity: ${props => props.opacity || 1};
|
||||
margin-bottom: ${props => props.marginBottom || 0};
|
||||
margin-top: ${props => props.marginTop || 0};
|
||||
`;
|
||||
|
|
|
@ -4,6 +4,6 @@ import styled from 'styled-components';
|
|||
import { TextComponent } from './text';
|
||||
|
||||
export const InputLabelComponent = styled(TextComponent)`
|
||||
margin: 20px 0 8.5px 0;
|
||||
margin: ${props => props.marginTop || '20px'} 0 8.5px 0;
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
`;
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
import React, { type Element } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import theme from '../theme';
|
||||
|
||||
const getDefaultStyles = t => styled[t]`
|
||||
border-radius: ${props => props.theme.boxBorderRadius};
|
||||
border: none;
|
||||
background-color: ${props => props.theme.colors.inputBackground};
|
||||
background-color: ${props => props.bgColor || props.theme.colors.inputBackground};
|
||||
color: ${props => props.theme.colors.text};
|
||||
padding: 15px;
|
||||
padding-right: ${props => (props.withRightElement ? '85px' : '15px')};
|
||||
width: 100%;
|
||||
outline: none;
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
|
@ -18,6 +20,16 @@ const getDefaultStyles = t => styled[t]`
|
|||
}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const RightElement = styled.div`
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
`;
|
||||
|
||||
const Input = getDefaultStyles('input');
|
||||
const Textarea = getDefaultStyles('textarea');
|
||||
|
||||
|
@ -31,19 +43,34 @@ type Props = {
|
|||
type?: string,
|
||||
step?: number,
|
||||
name?: string,
|
||||
renderRight?: () => Element<*> | null,
|
||||
bgColor?: string,
|
||||
};
|
||||
|
||||
export const InputComponent = ({
|
||||
inputType,
|
||||
bgColor,
|
||||
onChange = () => {},
|
||||
renderRight = () => null,
|
||||
...props
|
||||
}: Props) => {
|
||||
const rightElement = renderRight();
|
||||
|
||||
const inputTypes = {
|
||||
input: () => (
|
||||
<Input onChange={evt => onChange(evt.target.value)} {...props} />
|
||||
<Input
|
||||
onChange={evt => onChange(evt.target.value)}
|
||||
withRightElement={Boolean(rightElement)}
|
||||
bgColor={bgColor}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
textarea: () => (
|
||||
<Textarea onChange={evt => onChange(evt.target.value)} {...props} />
|
||||
<Textarea
|
||||
onChange={evt => onChange(evt.target.value)}
|
||||
bgColor={bgColor}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -51,7 +78,12 @@ export const InputComponent = ({
|
|||
throw new Error(`Invalid input type: ${String(inputType)}`);
|
||||
}
|
||||
|
||||
return inputTypes[inputType || 'input']();
|
||||
return (
|
||||
<Wrapper>
|
||||
{inputTypes[inputType || 'input']()}
|
||||
{rightElement && <RightElement>{rightElement}</RightElement>}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
InputComponent.defaultProps = {
|
||||
|
@ -60,4 +92,9 @@ InputComponent.defaultProps = {
|
|||
disabled: false,
|
||||
type: 'text',
|
||||
name: '',
|
||||
renderRight: () => null,
|
||||
onChange: () => {},
|
||||
onFocus: () => {},
|
||||
step: 1,
|
||||
bgColor: theme.colors.inputBackground,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Transition, animated } from 'react-spring';
|
||||
|
||||
import CircleProgressComponent from 'react-circle';
|
||||
import { TextComponent } from './text';
|
||||
|
@ -35,18 +36,59 @@ const Logo = styled.img`
|
|||
left: calc(50% - 25px);
|
||||
`;
|
||||
|
||||
export const LoadingScreen = ({ progress }: { progress: number }) => (
|
||||
<Wrapper id='loading-screen'>
|
||||
<CircleWrapper>
|
||||
<Logo src={zcashLogo} alt='Zcash logo' />
|
||||
<CircleProgressComponent
|
||||
progress={progress}
|
||||
responsive
|
||||
showPercentage={false}
|
||||
progressColor={theme.colors.activeItem}
|
||||
bgColor={theme.colors.inactiveItem}
|
||||
/>
|
||||
</CircleWrapper>
|
||||
<TextComponent value='ZEC Wallet Starting' />
|
||||
</Wrapper>
|
||||
);
|
||||
type Props = {
|
||||
progress: number,
|
||||
};
|
||||
|
||||
type State = {
|
||||
start: boolean,
|
||||
};
|
||||
|
||||
const TIME_DELAY_ANIM = 100;
|
||||
|
||||
export class LoadingScreen extends PureComponent<Props, State> {
|
||||
state = { start: false };
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.setState(() => ({ start: true }));
|
||||
}, TIME_DELAY_ANIM);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { start } = this.state;
|
||||
const { progress } = this.props;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Transition
|
||||
native
|
||||
items={start}
|
||||
enter={[{ height: 'auto' }]}
|
||||
leave={{ height: 0 }}
|
||||
from={{
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
height: 0,
|
||||
}}
|
||||
>
|
||||
{() => props => (
|
||||
<animated.div style={props}>
|
||||
<CircleWrapper>
|
||||
<Logo src={zcashLogo} alt='Zcash logo' />
|
||||
<CircleProgressComponent
|
||||
progress={progress}
|
||||
responsive
|
||||
showPercentage={false}
|
||||
progressColor={theme.colors.activeItem}
|
||||
bgColor={theme.colors.inactiveItem}
|
||||
/>
|
||||
</CircleWrapper>
|
||||
<TextComponent value='ZEC Wallet Starting' />
|
||||
</animated.div>
|
||||
)}
|
||||
</Transition>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,16 @@ import { TextComponent } from './text';
|
|||
import ChevronUp from '../assets/images/chevron-up.svg';
|
||||
import ChevronDown from '../assets/images/chevron-down.svg';
|
||||
|
||||
import theme from '../theme';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
const SelectWrapper = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-radius: ${props => props.theme.boxBorderRadius};
|
||||
border: none;
|
||||
background-color: ${props => props.theme.colors.inputBackground};
|
||||
background-color: ${props => props.bgColor || props.theme.colors.inputBackground};
|
||||
color: ${props => props.theme.colors.text};
|
||||
width: 100%;
|
||||
outline: none;
|
||||
|
@ -25,6 +28,7 @@ const SelectWrapper = styled.div`
|
|||
props.placement
|
||||
}-right-radius: 0;`}
|
||||
`;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
const ValueWrapper = styled.div`
|
||||
width: 95%;
|
||||
|
@ -51,11 +55,8 @@ const SelectMenuButton = styled.button`
|
|||
padding: 3px 7px;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
border: 1px solid ${props => props.theme.colors.text};
|
||||
border: 1px solid ${props => (props.isOpen ? props.theme.colors.primary : '#29292D')};
|
||||
border-radius: 100%;
|
||||
box-shadow: ${props => `0px 0px 10px 0px ${
|
||||
props.theme.colors.selectButtonShadow
|
||||
}, 0px 0px 10px 0px ${props.theme.colors.selectButtonShadow} inset`};
|
||||
`;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
|
@ -69,7 +70,7 @@ const OptionsWrapper = styled.div`
|
|||
flex-direction: column;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
${props => `${props.placement}: ${`-${props.optionsAmount * 60}px`}`};
|
||||
${props => `${props.placement}: ${`-${props.optionsAmount * 62}px`}`};
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
|
@ -77,7 +78,8 @@ const Option = styled.button`
|
|||
border: none;
|
||||
background: none;
|
||||
height: 60px;
|
||||
background-color: ${props => props.theme.colors.inputBackground};
|
||||
background-color: ${
|
||||
props => props.bgColor || props.theme.colors.inputBackground};
|
||||
cursor: pointer;
|
||||
z-index: 99;
|
||||
text-transform: capitalize;
|
||||
|
@ -93,6 +95,7 @@ type Props = {
|
|||
placeholder?: string,
|
||||
onChange: string => void,
|
||||
placement?: 'top' | 'bottom',
|
||||
bgColor?: string,
|
||||
};
|
||||
type State = {
|
||||
isOpen: boolean,
|
||||
|
@ -106,6 +109,7 @@ export class SelectComponent extends PureComponent<Props, State> {
|
|||
static defaultProps = {
|
||||
placeholder: '',
|
||||
placement: 'bottom',
|
||||
bgColor: theme.colors.inputBackground,
|
||||
};
|
||||
|
||||
onSelect = (value: string) => {
|
||||
|
@ -130,7 +134,7 @@ export class SelectComponent extends PureComponent<Props, State> {
|
|||
const { placement } = this.props;
|
||||
const { isOpen } = this.state;
|
||||
|
||||
if (placement === 'bottom') {
|
||||
if (placement === 'top') {
|
||||
return isOpen ? ChevronUp : ChevronDown;
|
||||
}
|
||||
|
||||
|
@ -139,7 +143,7 @@ export class SelectComponent extends PureComponent<Props, State> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
value, options, placeholder, placement,
|
||||
value, options, placeholder, placement, bgColor,
|
||||
} = this.props;
|
||||
const { isOpen } = this.state;
|
||||
|
||||
|
@ -149,12 +153,13 @@ export class SelectComponent extends PureComponent<Props, State> {
|
|||
isOpen={isOpen}
|
||||
placement={placement}
|
||||
onClick={() => this.setState(() => ({ isOpen: !isOpen }))}
|
||||
bgColor={bgColor}
|
||||
>
|
||||
<ValueWrapper hasValue={Boolean(value)}>
|
||||
{this.getSelectedLabel(value) || placeholder}
|
||||
</ValueWrapper>
|
||||
<SelectMenuButtonWrapper>
|
||||
<SelectMenuButton>
|
||||
<SelectMenuButton isOpen={isOpen}>
|
||||
<Icon src={this.getSelectIcon()} />
|
||||
</SelectMenuButton>
|
||||
</SelectMenuButtonWrapper>
|
||||
|
@ -169,6 +174,7 @@ export class SelectComponent extends PureComponent<Props, State> {
|
|||
id={optionValue}
|
||||
key={label + optionValue}
|
||||
onClick={() => this.onSelect(optionValue)}
|
||||
bgColor={bgColor}
|
||||
>
|
||||
<TextComponent value={label} />
|
||||
</Option>
|
||||
|
|
|
@ -43,6 +43,7 @@ const StatusPillLabel = styled(TextComponent)`
|
|||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
padding-top: 1px;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
type Props = {};
|
||||
|
@ -51,17 +52,17 @@ type State = {
|
|||
type: string,
|
||||
icon: string,
|
||||
progress: number,
|
||||
isSynching: boolean,
|
||||
isSyncing: boolean,
|
||||
};
|
||||
|
||||
export class StatusPill extends Component<Props, State> {
|
||||
timer: ?IntervalID = null;
|
||||
|
||||
state = {
|
||||
type: 'synching',
|
||||
type: 'syncing',
|
||||
icon: syncIcon,
|
||||
progress: 0,
|
||||
isSynching: true,
|
||||
isSyncing: true,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -88,7 +89,7 @@ export class StatusPill extends Component<Props, State> {
|
|||
? {
|
||||
type: 'ready',
|
||||
icon: readyIcon,
|
||||
isSynching: false,
|
||||
isSyncing: false,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
@ -100,13 +101,13 @@ export class StatusPill extends Component<Props, State> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
type, icon, progress, isSynching,
|
||||
type, icon, progress, isSyncing,
|
||||
} = this.state;
|
||||
const showPercent = isSynching ? `(${progress.toFixed(2)}%)` : '';
|
||||
const showPercent = isSyncing ? `(${progress.toFixed(2)}%)` : '';
|
||||
|
||||
return (
|
||||
<Wrapper id='status-pill'>
|
||||
<Icon src={icon} animated={isSynching} />
|
||||
<Icon src={icon} animated={isSyncing} />
|
||||
<StatusPillLabel value={`${type} ${showPercent}`} />
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
@ -23,7 +23,7 @@ const Wrapper = styled(RowComponent)`
|
|||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #101010;
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -39,6 +39,14 @@ const TransactionTypeLabel = styled(TextComponent)`
|
|||
text-transform: capitalize;
|
||||
`;
|
||||
|
||||
const TransactionAddress = styled(TextComponent)`
|
||||
color: #a7a7a7;
|
||||
|
||||
${Wrapper}:hover & {
|
||||
color: #fff;
|
||||
}
|
||||
`;
|
||||
|
||||
const TransactionTime = styled(TextComponent)`
|
||||
color: ${props => props.theme.colors.inactiveItem};
|
||||
`;
|
||||
|
@ -102,7 +110,7 @@ export const TransactionItemComponent = ({
|
|||
<TransactionTime value={transactionTime} />
|
||||
</TransactionColumn>
|
||||
</RowComponent>
|
||||
<TextComponent value={transactionAddress} align='left' />
|
||||
<TransactionAddress value={transactionAddress} />
|
||||
</RowComponent>
|
||||
<ColumnComponent alignItems='flex-end'>
|
||||
<TextComponent
|
||||
|
|
|
@ -34,6 +34,21 @@ const Input = styled.input`
|
|||
}
|
||||
`;
|
||||
|
||||
const Btn = styled(Button)`
|
||||
border-width: 1px;
|
||||
font-weight: ${props => props.theme.fontWeight.light};
|
||||
border-color: ${props => (props.isVisible
|
||||
? props.theme.colors.primary : props.theme.colors.buttonBorderColor
|
||||
)};
|
||||
padding: 6px 10px;
|
||||
width: 50%;
|
||||
|
||||
img {
|
||||
height: 12px;
|
||||
width: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const QRCodeWrapper = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
@ -83,11 +98,12 @@ export class WalletAddress extends Component<Props, State> {
|
|||
onChange={() => {}}
|
||||
onFocus={event => event.currentTarget.select()}
|
||||
/>
|
||||
<Button
|
||||
<Btn
|
||||
icon={eyeIcon}
|
||||
label={`${isVisible ? 'Hide' : 'Show'} full address and QR Code`}
|
||||
onClick={toggleVisibility}
|
||||
variant='secondary'
|
||||
isVisible={isVisible}
|
||||
/>
|
||||
</AddressWrapper>
|
||||
{isVisible
|
||||
|
|
|
@ -4,8 +4,6 @@ import styled from 'styled-components';
|
|||
|
||||
import { TextComponent } from './text';
|
||||
import { RowComponent } from './row';
|
||||
import { DropdownComponent } from './dropdown';
|
||||
import MenuIcon from '../assets/images/menu_icon.svg';
|
||||
|
||||
import formatNumber from '../utils/formatNumber';
|
||||
|
||||
|
@ -47,40 +45,11 @@ const ShieldedValue = styled(Label)`
|
|||
color: ${props => props.theme.colors.activeItem};
|
||||
`;
|
||||
|
||||
const SeeMoreButton = styled.button`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
outline: none;
|
||||
border-style: solid;
|
||||
border-radius: 100%;
|
||||
border-width: 1px;
|
||||
border-color: ${props => (props.isOpen
|
||||
? props.theme.colors.activeItem
|
||||
: props.theme.colors.inactiveItem)};
|
||||
background-color: transparent;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
|
||||
&:hover {
|
||||
border-color: ${props => props.theme.colors.activeItem};
|
||||
}
|
||||
`;
|
||||
|
||||
const SeeMoreIcon = styled.img`
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
total: number,
|
||||
shielded: number,
|
||||
transparent: number,
|
||||
zecPrice: number,
|
||||
addresses: string[],
|
||||
};
|
||||
|
||||
export const WalletSummaryComponent = ({
|
||||
|
@ -88,19 +57,12 @@ export const WalletSummaryComponent = ({
|
|||
shielded,
|
||||
transparent,
|
||||
zecPrice,
|
||||
addresses,
|
||||
}: Props) => (
|
||||
<Wrapper>
|
||||
<DropdownComponent
|
||||
label='All Addresses'
|
||||
renderTrigger={(toggleVisibility, isOpen) => (
|
||||
<SeeMoreButton onClick={toggleVisibility} isOpen={isOpen}>
|
||||
<SeeMoreIcon src={MenuIcon} alt='Menu Icon' />
|
||||
</SeeMoreButton>
|
||||
)}
|
||||
options={addresses.map(addr => ({ label: addr, onClick: x => x }))}
|
||||
<AllAddresses
|
||||
value='ALL ADDRESSES'
|
||||
isBold
|
||||
/>
|
||||
<AllAddresses value='ALL ADDRESSES' isBold />
|
||||
<ValueBox>
|
||||
<TextComponent
|
||||
size={theme.fontSize.medium * 2.5}
|
||||
|
@ -129,7 +91,11 @@ export const WalletSummaryComponent = ({
|
|||
/>
|
||||
</ValueBox>
|
||||
<ValueBox>
|
||||
<Label value='● TRANSPARENT' isBold size={theme.fontSize.small} />
|
||||
<Label
|
||||
value='● TRANSPARENT'
|
||||
isBold
|
||||
size={theme.fontSize.small}
|
||||
/>
|
||||
<TextComponent
|
||||
value={`ZEC ${formatNumber({ value: transparent })}`}
|
||||
isBold
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// @flow
|
||||
|
||||
export default {
|
||||
LOW: 1,
|
||||
MEDIUM: 5,
|
||||
HIGH: 9,
|
||||
LOW: 0.001,
|
||||
MEDIUM: 0.005,
|
||||
HIGH: 0.009,
|
||||
CUSTOM: 'custom',
|
||||
};
|
||||
|
|
|
@ -1,13 +1,43 @@
|
|||
// @flow
|
||||
import eres from 'eres';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReceiveView } from '../views/receive';
|
||||
|
||||
import type { AppState } from '../types/app-state';
|
||||
import {
|
||||
loadAddressesSuccess,
|
||||
loadAddressesError,
|
||||
} from '../redux/modules/receive';
|
||||
|
||||
const mapStateToProps = ({ walletSummary }: AppState) => ({
|
||||
addresses: walletSummary.addresses,
|
||||
import rpc from '../../services/api';
|
||||
|
||||
import type { AppState } from '../types/app-state';
|
||||
import type { Dispatch } from '../types/redux';
|
||||
|
||||
const mapStateToProps = ({ receive }: AppState) => ({
|
||||
addresses: receive.addresses,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||
loadAddresses: async () => {
|
||||
const [zAddressesErr, zAddresses] = await eres(rpc.z_listaddresses());
|
||||
|
||||
const [tAddressesErr, transparentAddresses] = await eres(
|
||||
rpc.getaddressesbyaccount(''),
|
||||
);
|
||||
|
||||
if (zAddressesErr || tAddressesErr) return dispatch(loadAddressesError({ error: 'Something went wrong!' }));
|
||||
|
||||
dispatch(
|
||||
loadAddressesSuccess({
|
||||
addresses: [...zAddresses, ...transparentAddresses],
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
export const ReceiveContainer = connect(mapStateToProps)(ReceiveView);
|
||||
export const ReceiveContainer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(ReceiveView);
|
||||
|
|
|
@ -3,14 +3,18 @@ import { connect } from 'react-redux';
|
|||
import eres from 'eres';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
import store from '../../config/electron-store';
|
||||
import rpc from '../../services/api';
|
||||
import { SendView } from '../views/send';
|
||||
|
||||
import {
|
||||
loadZECPrice,
|
||||
sendTransaction,
|
||||
sendTransactionSuccess,
|
||||
sendTransactionError,
|
||||
resetSendTransaction,
|
||||
validateAddressSuccess,
|
||||
validateAddressError,
|
||||
} from '../redux/modules/send';
|
||||
|
||||
import filterObjectNullKeys from '../utils/filterObjectNullKeys';
|
||||
|
@ -18,6 +22,11 @@ import filterObjectNullKeys from '../utils/filterObjectNullKeys';
|
|||
import type { AppState } from '../types/app-state';
|
||||
import type { Dispatch } from '../types/redux';
|
||||
|
||||
import {
|
||||
loadAddressesSuccess,
|
||||
loadAddressesError,
|
||||
} from '../redux/modules/receive';
|
||||
|
||||
export type SendTransactionInput = {
|
||||
from: string,
|
||||
to: string,
|
||||
|
@ -26,13 +35,14 @@ export type SendTransactionInput = {
|
|||
memo: string,
|
||||
};
|
||||
|
||||
const mapStateToProps = ({ walletSummary, sendStatus }: AppState) => ({
|
||||
const mapStateToProps = ({ walletSummary, sendStatus, receive }: AppState) => ({
|
||||
balance: walletSummary.total,
|
||||
zecPrice: walletSummary.zecPrice,
|
||||
addresses: walletSummary.addresses,
|
||||
zecPrice: sendStatus.zecPrice,
|
||||
addresses: receive.addresses,
|
||||
error: sendStatus.error,
|
||||
isSending: sendStatus.isSending,
|
||||
operationId: sendStatus.operationId,
|
||||
isToAddressValid: sendStatus.isToAddressValid,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||
|
@ -61,12 +71,87 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
|
|||
),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line max-len
|
||||
if (sendErr || !operationId) return dispatch(sendTransactionError({ error: sendErr.message }));
|
||||
|
||||
dispatch(sendTransactionSuccess({ operationId }));
|
||||
/**
|
||||
Output is a list of operation status objects.
|
||||
[
|
||||
{“operationid”: “opid-12ee…”,
|
||||
“status”: “queued”},
|
||||
{“operationid”: “opd-098a…”, “status”: ”executing”},
|
||||
{“operationid”: “opid-9876”, “status”: ”failed”}
|
||||
]
|
||||
|
||||
When the operation succeeds, the status object will also include the result.
|
||||
{“operationid”: “opid-0e0e”, “status”:”success”, “execution_time”:”25”, “result”: {“txid”:”af3887654…”,...}}
|
||||
|
||||
then the promise will only be resolved when a "success" or "failure" status is obtained
|
||||
*/
|
||||
const interval = setInterval(async () => {
|
||||
const [, status] = await eres(rpc.z_getoperationstatus());
|
||||
|
||||
const operationStatus = status.find(({ id }) => operationId === id);
|
||||
|
||||
if (operationStatus && operationStatus.status === 'success') {
|
||||
clearInterval(interval);
|
||||
dispatch(
|
||||
sendTransactionSuccess({ operationId: operationStatus.result.txid }),
|
||||
);
|
||||
}
|
||||
|
||||
if (operationStatus && operationStatus.status === 'failed') {
|
||||
clearInterval(interval);
|
||||
dispatch(
|
||||
sendTransactionError({ error: operationStatus.error.message }),
|
||||
);
|
||||
}
|
||||
}, 2000);
|
||||
},
|
||||
resetSendView: () => dispatch(resetSendTransaction()),
|
||||
validateAddress: async ({ address }: { address: string }) => {
|
||||
if (address.startsWith('z')) {
|
||||
const [, validationResult] = await eres(rpc.z_validateaddress(address));
|
||||
|
||||
return dispatch(
|
||||
validateAddressSuccess({
|
||||
isValid: Boolean(validationResult && validationResult.isvalid),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const [, validationResult] = await eres(rpc.validateaddress(address));
|
||||
|
||||
if (validationResult) {
|
||||
return dispatch(
|
||||
validateAddressSuccess({
|
||||
isValid: Boolean(validationResult && validationResult.isvalid),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return dispatch(validateAddressError());
|
||||
},
|
||||
loadAddresses: async () => {
|
||||
const [zAddressesErr, zAddresses] = await eres(rpc.z_listaddresses());
|
||||
|
||||
const [tAddressesErr, transparentAddresses] = await eres(
|
||||
rpc.getaddressesbyaccount(''),
|
||||
);
|
||||
|
||||
if (zAddressesErr || tAddressesErr) return dispatch(loadAddressesError({ error: 'Something went wrong!' }));
|
||||
|
||||
dispatch(
|
||||
loadAddressesSuccess({
|
||||
addresses: [...zAddresses, ...transparentAddresses],
|
||||
}),
|
||||
);
|
||||
},
|
||||
loadZECPrice: () => dispatch(
|
||||
loadZECPrice({
|
||||
value: store.get('ZEC_DOLLAR_PRICE'),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// @flow
|
||||
import type { Action } from '../../types/redux';
|
||||
|
||||
// Actions
|
||||
export const LOAD_ADDRESSES_SUCCESS = 'LOAD_ADDRESSES_SUCCESS';
|
||||
export const LOAD_ADDRESSES_ERROR = 'LOAD_ADDRESSES_ERROR';
|
||||
|
||||
export const loadAddressesSuccess = ({
|
||||
addresses,
|
||||
}: {
|
||||
addresses: string[],
|
||||
}) => ({
|
||||
type: LOAD_ADDRESSES_SUCCESS,
|
||||
payload: {
|
||||
addresses,
|
||||
},
|
||||
});
|
||||
|
||||
export const loadAddressesError = ({ error }: { error: string }) => ({
|
||||
type: LOAD_ADDRESSES_ERROR,
|
||||
payload: { error },
|
||||
});
|
||||
|
||||
export type State = {
|
||||
addresses: string[],
|
||||
error: string | null,
|
||||
};
|
||||
|
||||
const initialState: State = {
|
||||
addresses: [],
|
||||
error: null,
|
||||
};
|
||||
|
||||
export default (state: State = initialState, action: Action) => {
|
||||
switch (action.type) {
|
||||
case LOAD_ADDRESSES_SUCCESS:
|
||||
return {
|
||||
error: null,
|
||||
addresses: action.payload.addresses,
|
||||
};
|
||||
case LOAD_ADDRESSES_ERROR:
|
||||
return {
|
||||
error: action.payload.error,
|
||||
addresses: [],
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
|
@ -7,11 +7,13 @@ import type { RouterHistory } from 'react-router-dom';
|
|||
import wallet from './wallet';
|
||||
import transactions from './transactions';
|
||||
import send from './send';
|
||||
import receive from './receive';
|
||||
|
||||
// $FlowFixMe
|
||||
export const createRootReducer = (history: RouterHistory) => combineReducers({
|
||||
walletSummary: wallet,
|
||||
transactions,
|
||||
sendStatus: send,
|
||||
receive,
|
||||
router: connectRouter(history),
|
||||
});
|
||||
|
|
|
@ -5,6 +5,9 @@ export const SEND_TRANSACTION = 'SEND_TRANSACTION';
|
|||
export const SEND_TRANSACTION_SUCCESS = 'SEND_TRANSACTION_SUCCESS';
|
||||
export const SEND_TRANSACTION_ERROR = 'SEND_TRANSACTION_ERROR';
|
||||
export const RESET_SEND_TRANSACTION = 'RESET_SEND_TRANSACTION';
|
||||
export const VALIDATE_ADDRESS_SUCCESS = 'VALIDATE_ADDRESS_SUCCESS';
|
||||
export const VALIDATE_ADDRESS_ERROR = 'VALIDATE_ADDRESS_SUCCESS';
|
||||
export const LOAD_ZEC_PRICE = 'LOAD_ZEC_PRICE';
|
||||
|
||||
export const sendTransaction = () => ({
|
||||
type: SEND_TRANSACTION,
|
||||
|
@ -34,34 +37,76 @@ export const resetSendTransaction = () => ({
|
|||
payload: {},
|
||||
});
|
||||
|
||||
export const validateAddressSuccess = ({ isValid }: { isValid: boolean }) => ({
|
||||
type: VALIDATE_ADDRESS_SUCCESS,
|
||||
payload: {
|
||||
isValid,
|
||||
},
|
||||
});
|
||||
|
||||
export const validateAddressError = () => ({
|
||||
type: VALIDATE_ADDRESS_ERROR,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
export const loadZECPrice = ({ value }: { value: number }) => ({
|
||||
type: LOAD_ZEC_PRICE,
|
||||
payload: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
export type State = {
|
||||
isSending: boolean,
|
||||
isToAddressValid: boolean,
|
||||
error: string | null,
|
||||
operationId: string | null,
|
||||
zecPrice: number,
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
const initialState: State = {
|
||||
isSending: false,
|
||||
error: null,
|
||||
operationId: null,
|
||||
isToAddressValid: false,
|
||||
zecPrice: 0,
|
||||
};
|
||||
|
||||
export default (state: State = initialState, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case SEND_TRANSACTION:
|
||||
return { isSending: true, error: null, operationId: null };
|
||||
return {
|
||||
...state,
|
||||
isSending: true,
|
||||
error: null,
|
||||
operationId: null,
|
||||
};
|
||||
case SEND_TRANSACTION_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
isSending: false,
|
||||
error: null,
|
||||
operationId: action.payload.operationId,
|
||||
};
|
||||
case SEND_TRANSACTION_ERROR:
|
||||
return {
|
||||
...state,
|
||||
isSending: false,
|
||||
error: action.payload.error,
|
||||
operationId: null,
|
||||
};
|
||||
case VALIDATE_ADDRESS_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
isToAddressValid: action.payload.isValid,
|
||||
};
|
||||
case VALIDATE_ADDRESS_ERROR:
|
||||
return {
|
||||
...state,
|
||||
isToAddressValid: false,
|
||||
};
|
||||
case LOAD_ZEC_PRICE:
|
||||
return { ...state, zecPrice: action.payload.value };
|
||||
case RESET_SEND_TRANSACTION:
|
||||
return initialState;
|
||||
default:
|
||||
|
|
|
@ -8,10 +8,12 @@ import { normalize } from 'polished'; // eslint-disable-line
|
|||
import { DARK } from './constants/themes';
|
||||
|
||||
const darkOne = '#F4B728';
|
||||
const blackTwo = '#171717';
|
||||
const lightOne = '#ffffff';
|
||||
const brandOne = '#000';
|
||||
// const brandTwo = '#3B3B3F';
|
||||
const brandThree = '#5d5d65';
|
||||
const buttonBorderColor = '#3e3c42';
|
||||
const activeItem = '#F4B728';
|
||||
const text = '#FFF';
|
||||
const cardBackgroundColor = '#000';
|
||||
|
@ -71,6 +73,9 @@ const appTheme = {
|
|||
selectButtonShadow,
|
||||
transactionsDetailsLabel,
|
||||
statusPillLabel,
|
||||
modalItemLabel: transactionsDate,
|
||||
blackTwo,
|
||||
buttonBorderColor,
|
||||
},
|
||||
sidebarWidth: '180px',
|
||||
headerHeight: '60px',
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
import type { State as WalletSummaryState } from '../redux/modules/wallet';
|
||||
import type { State as TransactionsState } from '../redux/modules/transactions';
|
||||
import type { State as SendState } from '../redux/modules/send';
|
||||
import type { State as ReceiveState } from '../redux/modules/receive';
|
||||
|
||||
export type AppState = {
|
||||
walletSummary: WalletSummaryState,
|
||||
transactions: TransactionsState,
|
||||
sendStatus: SendState,
|
||||
receive: ReceiveState,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { InputLabelComponent } from '../components/input-label';
|
||||
|
@ -8,6 +8,7 @@ import { WalletAddress } from '../components/wallet-address';
|
|||
|
||||
type Props = {
|
||||
addresses: Array<string>,
|
||||
loadAddresses: () => void,
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
|
@ -19,21 +20,31 @@ const Row = styled(RowComponent)`
|
|||
`;
|
||||
|
||||
const Label = styled(InputLabelComponent)`
|
||||
margin: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
export const ReceiveView = ({ addresses }: Props) => (
|
||||
<Wrapper>
|
||||
<Label value='Addresses: ' />
|
||||
{(addresses || []).map(address => (
|
||||
<Row
|
||||
key={address}
|
||||
alignItems='center'
|
||||
justifyContent='space-between'
|
||||
>
|
||||
<WalletAddress address={address} />
|
||||
</Row>
|
||||
))}
|
||||
</Wrapper>
|
||||
);
|
||||
export class ReceiveView extends PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
const { loadAddresses } = this.props;
|
||||
|
||||
loadAddresses();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { addresses } = this.props;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Label value='Addresses: ' />
|
||||
{(addresses || []).map(address => (
|
||||
<Row key={address} alignItems='center' justifyContent='space-between'>
|
||||
<WalletAddress address={address} />
|
||||
</Row>
|
||||
))}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React, { PureComponent } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
import FEES from '../constants/fees';
|
||||
|
@ -13,12 +13,39 @@ import { RowComponent } from '../components/row';
|
|||
import { ColumnComponent } from '../components/column';
|
||||
import { Divider } from '../components/divider';
|
||||
import { Button } from '../components/button';
|
||||
import { ConfirmDialogComponent } from '../components/confirm-dialog';
|
||||
|
||||
import formatNumber from '../utils/formatNumber';
|
||||
|
||||
import type { SendTransactionInput } from '../containers/send';
|
||||
import type { State as SendState } from '../redux/modules/send';
|
||||
|
||||
import SentIcon from '../assets/images/transaction_sent_icon.svg';
|
||||
import MenuIcon from '../assets/images/menu_icon.svg';
|
||||
import ValidIcon from '../assets/images/green_check.png';
|
||||
import InvalidIcon from '../assets/images/error_icon.png';
|
||||
import LoadingIcon from '../assets/images/sync_icon.png';
|
||||
|
||||
import theme from '../theme';
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`;
|
||||
|
||||
const Loader = styled.img`
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
animation: 2s linear infinite;
|
||||
animation-name: ${rotate};
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
margin-top: ${props => props.theme.layoutContentPaddingTop};
|
||||
width: 71%;
|
||||
|
@ -29,6 +56,28 @@ const SendWrapper = styled(ColumnComponent)`
|
|||
width: 25%;
|
||||
`;
|
||||
|
||||
const AmountWrapper = styled.div`
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: 'ZEC';
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
position: absolute;
|
||||
margin-top: 15px;
|
||||
margin-left: 15px;
|
||||
display: block;
|
||||
transition: all 0.05s ease-in-out;
|
||||
opacity: ${props => (props.isEmpty ? '0' : '1')};
|
||||
color: #fff;
|
||||
z-index: 10;
|
||||
}
|
||||
`;
|
||||
|
||||
const AmountInput = styled(InputComponent)`
|
||||
padding-left: ${props => (props.isEmpty ? '15' : '50')}px;
|
||||
`;
|
||||
|
||||
const ShowFeeButton = styled.button`
|
||||
background: none;
|
||||
border: none;
|
||||
|
@ -36,12 +85,32 @@ const ShowFeeButton = styled.button`
|
|||
width: 100%;
|
||||
color: ${props => props.theme.colors.text};
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const SeeMoreIcon = styled.img`
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border: 1px solid ${props => props.theme.colors.text};
|
||||
border-radius: 100%;
|
||||
margin-right: 11.5px;
|
||||
`;
|
||||
|
||||
const FeeWrapper = styled.div`
|
||||
background-color: ${props => props.theme.colors.cardBackgroundColor};
|
||||
border-radius: 6px;
|
||||
padding: 13px 19px;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const InfoCard = styled.div`
|
||||
width: 100%;
|
||||
background-color: ${props => props.theme.colors.cardBackgroundColor};
|
||||
|
@ -66,18 +135,62 @@ const FormButton = styled(Button)`
|
|||
margin: 10px 0;
|
||||
`;
|
||||
|
||||
const SuccessWrapper = styled(ColumnComponent)`
|
||||
const ModalContent = styled(ColumnComponent)`
|
||||
min-height: 400px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
p {
|
||||
word-break: break-all;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = SendState & {
|
||||
const ConfirmItemWrapper = styled(RowComponent)`
|
||||
padding: 22.5px 33.5px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const ItemLabel = styled(TextComponent)`
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
font-size: ${props => props.theme.fontSize.small};
|
||||
color: ${props => props.color || props.theme.colors.modalItemLabel};
|
||||
margin-bottom: 3.5px;
|
||||
`;
|
||||
|
||||
const SendZECValue = styled(TextComponent)`
|
||||
color: ${props => props.theme.colors.transactionSent};
|
||||
font-size: ${props => `${props.theme.fontSize.large}em`};
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
`;
|
||||
|
||||
const SendUSDValue = styled(TextComponent)`
|
||||
opacity: 0.5;
|
||||
font-weight: ${props => props.theme.fontWeight.light};
|
||||
font-size: ${props => `${props.theme.fontSize.medium}em`};
|
||||
`;
|
||||
|
||||
const Icon = styled.img`
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
margin-left: 15px;
|
||||
`;
|
||||
|
||||
const ValidateStatusIcon = styled.img`
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
margin-right: 7px;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
...SendState,
|
||||
balance: number,
|
||||
zecPrice: number,
|
||||
addresses: string[],
|
||||
sendTransaction: SendTransactionInput => void,
|
||||
resetSendView: () => void,
|
||||
validateAddress: ({ address: string }) => void,
|
||||
loadAddresses: () => void,
|
||||
loadZECPrice: () => void,
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -93,7 +206,7 @@ type State = {
|
|||
const initialState = {
|
||||
showFee: false,
|
||||
from: '',
|
||||
amount: '0',
|
||||
amount: '',
|
||||
to: '',
|
||||
feeType: FEES.LOW,
|
||||
fee: FEES.LOW,
|
||||
|
@ -104,40 +217,38 @@ export class SendView extends PureComponent<Props, State> {
|
|||
state = initialState;
|
||||
|
||||
componentDidMount() {
|
||||
const { resetSendView } = this.props;
|
||||
const { resetSendView, loadAddresses, loadZECPrice } = this.props;
|
||||
|
||||
resetSendView();
|
||||
loadAddresses();
|
||||
loadZECPrice();
|
||||
}
|
||||
|
||||
handleChange = (field: string) => (value: string) => {
|
||||
if (field === 'amount') {
|
||||
if (value !== '') {
|
||||
this.setState(() => ({
|
||||
[field]: value,
|
||||
}));
|
||||
}
|
||||
const { validateAddress } = this.props;
|
||||
|
||||
if (field === 'to') {
|
||||
// eslint-disable-next-line max-len
|
||||
this.setState({ [field]: value }, () => validateAddress({ address: value }));
|
||||
} else {
|
||||
this.setState(() => ({ [field]: value }));
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeFeeType = (value: string) => {
|
||||
this.setState(
|
||||
{
|
||||
feeType: value,
|
||||
if (value === FEES.CUSTOM) {
|
||||
this.setState({
|
||||
feeType: FEES.CUSTOM,
|
||||
fee: null,
|
||||
},
|
||||
() => {
|
||||
if (
|
||||
value === String(FEES.LOW)
|
||||
|| value === String(FEES.MEDIUM)
|
||||
|| value === String(FEES.HIGH)
|
||||
) {
|
||||
this.setState(() => ({
|
||||
fee: Number(value),
|
||||
}));
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
const fee = new BigNumber(value);
|
||||
|
||||
this.setState({
|
||||
feeType: fee.toString(),
|
||||
fee: fee.toNumber(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
|
@ -157,32 +268,153 @@ export class SendView extends PureComponent<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
showModal = (toggle: void => void) => {
|
||||
const {
|
||||
from, amount, to, fee,
|
||||
} = this.state;
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const { isToAddressValid } = this.props;
|
||||
|
||||
if (!from || !amount || !to || !fee || !isToAddressValid) return;
|
||||
|
||||
toggle();
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
const { resetSendView } = this.props;
|
||||
|
||||
this.setState(initialState, () => resetSendView());
|
||||
};
|
||||
|
||||
getFeeText = () => {
|
||||
const { fee } = this.state;
|
||||
|
||||
const feeValue = new BigNumber(fee);
|
||||
|
||||
if (feeValue.isEqualTo(FEES.LOW)) return `Low ZEC ${feeValue.toString()}`;
|
||||
// eslint-disable-next-line max-len
|
||||
if (feeValue.isEqualTo(FEES.MEDIUM)) return `Medium ZEC ${feeValue.toString()}`;
|
||||
if (feeValue.isEqualTo(FEES.HIGH)) return `High ZEC ${feeValue.toString()}`;
|
||||
|
||||
return `Custom ZEC ${feeValue.toString()}`;
|
||||
};
|
||||
|
||||
renderValidationStatus = () => {
|
||||
const { isToAddressValid } = this.props;
|
||||
|
||||
return isToAddressValid ? (
|
||||
<RowComponent alignItems='center'>
|
||||
<ValidateStatusIcon src={ValidIcon} />
|
||||
<ItemLabel value='VALID' color={theme.colors.transactionReceived} />
|
||||
</RowComponent>
|
||||
) : (
|
||||
<RowComponent alignItems='center'>
|
||||
<ValidateStatusIcon src={InvalidIcon} />
|
||||
<ItemLabel value='INVALID' color={theme.colors.transactionSent} />
|
||||
</RowComponent>
|
||||
);
|
||||
};
|
||||
|
||||
renderModalContent = ({
|
||||
valueSent,
|
||||
valueSentInUsd,
|
||||
toggle,
|
||||
}: {
|
||||
/* eslint-disable react/no-unused-prop-types */
|
||||
valueSent: string,
|
||||
valueSentInUsd: string,
|
||||
toggle: () => void,
|
||||
/* eslint-enable react/no-unused-prop-types */
|
||||
}) => {
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const { operationId, isSending, error } = this.props;
|
||||
const { from, to } = this.state;
|
||||
|
||||
if (isSending) {
|
||||
return (
|
||||
<>
|
||||
<Loader src={LoadingIcon} />
|
||||
<TextComponent value='Processing transaction...' />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (operationId) {
|
||||
return (
|
||||
<>
|
||||
<TextComponent value={`Transaction ID: ${operationId}`} align='center' />
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
this.reset();
|
||||
toggle();
|
||||
}}
|
||||
>
|
||||
Send again!
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) return <TextComponent value={error} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='AMOUNT' />
|
||||
<SendZECValue value={`-${valueSent}`} />
|
||||
<SendUSDValue value={`-${valueSentInUsd}`} />
|
||||
</ColumnComponent>
|
||||
<ColumnComponent>
|
||||
<Icon src={SentIcon} alt='Transaction Type Icon' />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='FEE' />
|
||||
<TextComponent value={this.getFeeText()} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='FROM' />
|
||||
<TextComponent value={from} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='TO' />
|
||||
<TextComponent value={to} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} marginBottom='27.5px' />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
addresses,
|
||||
balance,
|
||||
zecPrice,
|
||||
isSending,
|
||||
error,
|
||||
operationId,
|
||||
addresses, balance, zecPrice, isSending, error, operationId,
|
||||
} = this.props;
|
||||
const {
|
||||
showFee, from, amount, to, memo, fee, feeType,
|
||||
} = this.state;
|
||||
|
||||
const isEmpty = amount === '';
|
||||
|
||||
const fixedAmount = isEmpty ? '0.00' : amount;
|
||||
|
||||
const zecBalance = formatNumber({ value: balance, append: 'ZEC ' });
|
||||
const zecBalanceInUsd = formatNumber({
|
||||
value: new BigNumber(balance).times(zecPrice).toNumber(),
|
||||
append: 'USD $',
|
||||
});
|
||||
const valueSent = formatNumber({
|
||||
value: new BigNumber(amount).toNumber(),
|
||||
value: new BigNumber(fixedAmount).toFormat(2),
|
||||
append: 'ZEC ',
|
||||
});
|
||||
const valueSentInUsd = formatNumber({
|
||||
|
@ -190,20 +422,9 @@ export class SendView extends PureComponent<Props, State> {
|
|||
append: 'USD $',
|
||||
});
|
||||
|
||||
return operationId ? (
|
||||
<SuccessWrapper id='send-success-wrapper'>
|
||||
<TextComponent value={`Processing operation: ${operationId}`} />
|
||||
<TextComponent value={`Amount: ${amount}`} />
|
||||
<TextComponent value={`From: ${from}`} />
|
||||
<TextComponent value={`To: ${to}`} />
|
||||
<button type='button' onClick={this.reset}>
|
||||
Send again!
|
||||
</button>
|
||||
</SuccessWrapper>
|
||||
) : (
|
||||
<RowComponent id='send-wrapper' justifyContent='space-between'>
|
||||
return (
|
||||
<RowComponent justifyContent='space-between'>
|
||||
<FormWrapper>
|
||||
{error && <TextComponent id='send-error-text' value={error} />}
|
||||
<InputLabelComponent value='From' />
|
||||
<SelectComponent
|
||||
onChange={this.handleChange('from')}
|
||||
|
@ -212,20 +433,22 @@ export class SendView extends PureComponent<Props, State> {
|
|||
options={addresses.map(addr => ({ value: addr, label: addr }))}
|
||||
/>
|
||||
<InputLabelComponent value='Amount' />
|
||||
<InputComponent
|
||||
name='amount'
|
||||
type='number'
|
||||
onChange={this.handleChange('amount')}
|
||||
value={String(amount)}
|
||||
placeholder='ZEC 0.0'
|
||||
step={0.01}
|
||||
/>
|
||||
<AmountWrapper isEmpty={isEmpty}>
|
||||
<AmountInput
|
||||
isEmpty={isEmpty}
|
||||
type='number'
|
||||
onChange={this.handleChange('amount')}
|
||||
value={String(amount)}
|
||||
placeholder='ZEC 0.0'
|
||||
min={0.01}
|
||||
/>
|
||||
</AmountWrapper>
|
||||
<InputLabelComponent value='To' />
|
||||
<InputComponent
|
||||
onChange={this.handleChange('to')}
|
||||
value={to}
|
||||
placeholder='Enter Address'
|
||||
name='to'
|
||||
renderRight={to ? this.renderValidationStatus : () => null}
|
||||
/>
|
||||
<InputLabelComponent value='Memo' />
|
||||
<InputComponent
|
||||
|
@ -237,43 +460,38 @@ export class SendView extends PureComponent<Props, State> {
|
|||
/>
|
||||
<ShowFeeButton
|
||||
id='send-show-additional-options-button'
|
||||
onClick={() => this.setState(state => ({ showFee: !state.showFee }))
|
||||
}
|
||||
onClick={() => this.setState(state => ({ showFee: !state.showFee }))}
|
||||
>
|
||||
<TextComponent
|
||||
paddingTop='10px'
|
||||
value={`${showFee ? 'Hide' : 'Show'} Additional Options`}
|
||||
align='right'
|
||||
/>
|
||||
<SeeMoreIcon src={MenuIcon} alt='Show more icon' />
|
||||
<TextComponent value={`${showFee ? 'Hide' : 'Show'} Additional Options`} />
|
||||
</ShowFeeButton>
|
||||
{showFee && (
|
||||
<RowComponent
|
||||
id='send-fee-wrapper'
|
||||
alignItems='flex-end'
|
||||
justifyContent='space-between'
|
||||
>
|
||||
<ColumnComponent width='74%'>
|
||||
<InputLabelComponent value='Fee' />
|
||||
<InputComponent
|
||||
type='number'
|
||||
onChange={this.handleChange('fee')}
|
||||
value={String(fee)}
|
||||
disabled={feeType !== FEES.CUSTOM}
|
||||
name='fee'
|
||||
/>
|
||||
</ColumnComponent>
|
||||
<ColumnComponent width='25%'>
|
||||
<SelectComponent
|
||||
onChange={this.handleChangeFeeType}
|
||||
value={String(feeType)}
|
||||
options={Object.keys(FEES).map(cur => ({
|
||||
label: cur.toLowerCase(),
|
||||
value: String(FEES[cur]),
|
||||
}))}
|
||||
placement='top'
|
||||
/>
|
||||
</ColumnComponent>
|
||||
</RowComponent>
|
||||
<FeeWrapper>
|
||||
<RowComponent alignItems='flex-end' justifyContent='space-between'>
|
||||
<ColumnComponent width='74%'>
|
||||
<InputLabelComponent value='Fee' marginTop='0px' />
|
||||
<InputComponent
|
||||
type='number'
|
||||
onChange={this.handleChange('fee')}
|
||||
value={String(fee)}
|
||||
disabled={feeType !== FEES.CUSTOM}
|
||||
bgColor={theme.colors.blackTwo}
|
||||
/>
|
||||
</ColumnComponent>
|
||||
<ColumnComponent width='25%'>
|
||||
<SelectComponent
|
||||
onChange={this.handleChangeFeeType}
|
||||
value={String(feeType)}
|
||||
options={Object.keys(FEES).map(cur => ({
|
||||
label: cur.toLowerCase(),
|
||||
value: String(FEES[cur]),
|
||||
}))}
|
||||
placement='top'
|
||||
bgColor={theme.colors.blackTwo}
|
||||
/>
|
||||
</ColumnComponent>
|
||||
</RowComponent>
|
||||
</FeeWrapper>
|
||||
)}
|
||||
{feeType === FEES.CUSTOM && (
|
||||
<TextComponent value='Custom fees may compromise your privacy since fees are transparent' />
|
||||
|
@ -286,22 +504,33 @@ export class SendView extends PureComponent<Props, State> {
|
|||
<TextComponent value={zecBalance} size={1.25} isBold />
|
||||
<InfoCardUSD value={zecBalanceInUsd} size={0.84375} />
|
||||
</InfoContent>
|
||||
<Divider opacity={0.5} />
|
||||
<Divider opacity={0.3} />
|
||||
<InfoContent>
|
||||
<InfoCardLabel value='Sending' />
|
||||
<TextComponent value={valueSent} size={1.25} isBold />
|
||||
<InfoCardUSD value={valueSentInUsd} size={0.84375} />
|
||||
</InfoContent>
|
||||
</InfoCard>
|
||||
<FormButton
|
||||
id='send-submit-button'
|
||||
label='Send'
|
||||
variant='secondary'
|
||||
focused
|
||||
onClick={this.handleSubmit}
|
||||
isLoading={isSending}
|
||||
disabled={!from || !amount || !to || !fee}
|
||||
/>
|
||||
<ConfirmDialogComponent
|
||||
title='Please Confirm Transaction Details'
|
||||
onConfirm={this.handleSubmit}
|
||||
renderTrigger={toggle => (
|
||||
<FormButton
|
||||
label='Send'
|
||||
variant='secondary'
|
||||
focused
|
||||
onClick={() => this.showModal(toggle)}
|
||||
/>
|
||||
)}
|
||||
showButtons={!isSending && !error && !operationId}
|
||||
onClose={this.reset}
|
||||
>
|
||||
{toggle => (
|
||||
<ModalContent>
|
||||
{this.renderModalContent({ valueSent, valueSentInUsd, toggle })}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
<FormButton label='Cancel' variant='secondary' />
|
||||
</SendWrapper>
|
||||
</RowComponent>
|
||||
|
|
|
@ -47,6 +47,25 @@ const ClipboardButton = styled(Clipboard)`
|
|||
margin-left: 5px;
|
||||
`;
|
||||
|
||||
const SettingsWrapper = styled.div`
|
||||
margin-bottom: 45px;
|
||||
min-width: 200px;
|
||||
width: 37%;
|
||||
`;
|
||||
|
||||
const SettingsTitle = styled(TextComponent)`
|
||||
text-transform: uppercase;
|
||||
color: ${props => props.theme.colors.transactionsDate};
|
||||
font-size: ${props => `${props.theme.fontSize.regular * 0.9}em`};
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
margin-bottom: 5px;
|
||||
`;
|
||||
|
||||
const SettingsContent = styled(TextComponent)`
|
||||
margin-bottom: 20px;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
type Key = {
|
||||
zAddress: string,
|
||||
key: string,
|
||||
|
@ -198,94 +217,117 @@ export class SettingsView extends PureComponent<Props, State> {
|
|||
<ConfirmDialogComponent
|
||||
title='Export view keys'
|
||||
renderTrigger={toggleVisibility => (
|
||||
<Btn label='Export view keys' onClick={toggleVisibility} />
|
||||
<SettingsWrapper>
|
||||
<SettingsTitle value='Export view keys' />
|
||||
<SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' />
|
||||
<Btn label='Export view keys' onClick={toggleVisibility} />
|
||||
</SettingsWrapper>
|
||||
)}
|
||||
onConfirm={this.exportViewKeys}
|
||||
showButtons={!successExportViewKeys}
|
||||
width={750}
|
||||
>
|
||||
<ModalContent>
|
||||
{successExportViewKeys ? (
|
||||
viewKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<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.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
{() => (
|
||||
<ModalContent>
|
||||
{successExportViewKeys ? (
|
||||
viewKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<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.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
|
||||
<ConfirmDialogComponent
|
||||
title='Export private keys'
|
||||
renderTrigger={toggleVisibility => (
|
||||
<Btn label='Export private keys' onClick={toggleVisibility} />
|
||||
<SettingsWrapper>
|
||||
<SettingsTitle value='Export private keys' />
|
||||
<SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' />
|
||||
<Btn label='Export private keys' onClick={toggleVisibility} />
|
||||
</SettingsWrapper>
|
||||
)}
|
||||
onConfirm={this.exportPrivateKeys}
|
||||
showButtons={!successExportPrivateKeys}
|
||||
width={750}
|
||||
>
|
||||
<ModalContent>
|
||||
{successExportPrivateKeys ? (
|
||||
privateKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<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.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
{() => (
|
||||
<ModalContent>
|
||||
{successExportPrivateKeys ? (
|
||||
privateKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<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.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
|
||||
<ConfirmDialogComponent
|
||||
title='Import private keys'
|
||||
renderTrigger={toggleVisibility => (
|
||||
<Btn label='Import private keys' onClick={toggleVisibility} />
|
||||
<SettingsWrapper>
|
||||
<SettingsTitle value='Import private keys' />
|
||||
<SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' />
|
||||
<Btn label='Import private keys' onClick={toggleVisibility} />
|
||||
</SettingsWrapper>
|
||||
)}
|
||||
onConfirm={this.importPrivateKeys}
|
||||
showButtons={!successImportPrivateKeys}
|
||||
width={900}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<ModalContent>
|
||||
<InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' />
|
||||
<InputComponent
|
||||
value={importedPrivateKeys}
|
||||
onChange={value => this.setState({ importedPrivateKeys: value })}
|
||||
inputType='textarea'
|
||||
rows={10}
|
||||
/>
|
||||
{successImportPrivateKeys && (
|
||||
<TextComponent
|
||||
value='Private keys imported in your node'
|
||||
align='center'
|
||||
{() => (
|
||||
<ModalContent>
|
||||
<InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' />
|
||||
<InputComponent
|
||||
value={importedPrivateKeys}
|
||||
onChange={value => this.setState({ importedPrivateKeys: value })
|
||||
}
|
||||
inputType='textarea'
|
||||
rows={10}
|
||||
/>
|
||||
)}
|
||||
{error && <TextComponent value={error} align='center' />}
|
||||
</ModalContent>
|
||||
{successImportPrivateKeys && (
|
||||
<TextComponent
|
||||
value='Private keys imported in your node'
|
||||
align='center'
|
||||
/>
|
||||
)}
|
||||
{error && <TextComponent value={error} align='center' />}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
|
||||
<Btn label='Backup wallet.dat' onClick={this.backupWalletDat} />
|
||||
<SettingsWrapper>
|
||||
<SettingsTitle value='Backup Wallet' />
|
||||
<SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' />
|
||||
<Btn label='Backup wallet.dat' onClick={this.backupWalletDat} />
|
||||
</SettingsWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -59,11 +59,11 @@
|
|||
"electron-positioner": "^4.1.0",
|
||||
"eslint": "^5.8.0",
|
||||
"eslint-config-airbnb": "^17.1.0",
|
||||
"eslint-plugin-flowtype": "^3.2.0",
|
||||
"eslint-plugin-flowtype": "^3.2.1",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-jest": "^22.1.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||
"eslint-plugin-react": "^7.7.0",
|
||||
"eslint-plugin-react": "^7.12.4",
|
||||
"file-loader": "^2.0.0",
|
||||
"flow-bin": "^0.91.0",
|
||||
"flow-coverage-report": "^0.6.0",
|
||||
|
@ -117,6 +117,7 @@
|
|||
"react-popover": "^0.5.10",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-spring": "^7.2.10",
|
||||
"redux": "^4.0.1",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"styled-components": "^4.1.1",
|
||||
|
|
|
@ -746,8 +746,8 @@ export type APIMethods = {
|
|||
z_exportwallet: (filename: string) => Promise<string>,
|
||||
z_getbalance: (address: string, minconf?: number) => Promise<number>,
|
||||
z_getnewaddress: (type: ?string) => Promise<string>,
|
||||
z_getoperationresult: (operationid?: string[]) => Promise<Object[]>,
|
||||
z_getoperationstatus: (operationid?: string[]) => Promise<Object[]>,
|
||||
z_getoperationresult: (operationid?: string) => Promise<Object[]>,
|
||||
z_getoperationstatus: (operationids?: string) => Promise<Object[]>,
|
||||
z_gettotalbalance: (
|
||||
minconf?: number,
|
||||
includeWatchonly?: boolean,
|
||||
|
|
49
yarn.lock
49
yarn.lock
|
@ -6436,6 +6436,18 @@ error-stack-parser@^2.0.0:
|
|||
dependencies:
|
||||
stackframe "^1.0.4"
|
||||
|
||||
es-abstract@^1.11.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
|
||||
integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==
|
||||
dependencies:
|
||||
es-to-primitive "^1.2.0"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
is-callable "^1.1.4"
|
||||
is-regex "^1.0.4"
|
||||
object-keys "^1.0.12"
|
||||
|
||||
es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
|
||||
|
@ -6447,7 +6459,7 @@ es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
|
|||
is-callable "^1.1.3"
|
||||
is-regex "^1.0.4"
|
||||
|
||||
es-to-primitive@^1.1.1:
|
||||
es-to-primitive@^1.1.1, es-to-primitive@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
|
||||
integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
|
||||
|
@ -6572,10 +6584,10 @@ eslint-module-utils@^2.2.0:
|
|||
debug "^2.6.8"
|
||||
pkg-dir "^1.0.0"
|
||||
|
||||
eslint-plugin-flowtype@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.2.0.tgz#824364ed5940a404b91326fdb5a313a2a74760df"
|
||||
integrity sha512-baJmzngM6UKbEkJ5OY3aGw2zjXBt5L2QKZvTsOlXX7yHKIjNRrlJx2ods8Rng6EdqPR9rVNIQNYHpTs0qfn2qA==
|
||||
eslint-plugin-flowtype@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.2.1.tgz#45e032aee54e695dfc41a891e92b7afedfc62c77"
|
||||
integrity sha512-1lymqM8Cawxu5xsS8TaCrLWJYUmUdoG4hCfa7yWOhCf0qZn/CvI8FxqkhdOP6bAosBn5zeYxKe3Q/4rfKN8a+A==
|
||||
dependencies:
|
||||
lodash "^4.17.10"
|
||||
|
||||
|
@ -6614,16 +6626,18 @@ eslint-plugin-jsx-a11y@^6.0.3:
|
|||
has "^1.0.3"
|
||||
jsx-ast-utils "^2.0.1"
|
||||
|
||||
eslint-plugin-react@^7.7.0:
|
||||
version "7.11.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz#c01a7af6f17519457d6116aa94fc6d2ccad5443c"
|
||||
integrity sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==
|
||||
eslint-plugin-react@^7.12.4:
|
||||
version "7.12.4"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz#b1ecf26479d61aee650da612e425c53a99f48c8c"
|
||||
integrity sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==
|
||||
dependencies:
|
||||
array-includes "^3.0.3"
|
||||
doctrine "^2.1.0"
|
||||
has "^1.0.3"
|
||||
jsx-ast-utils "^2.0.1"
|
||||
object.fromentries "^2.0.0"
|
||||
prop-types "^15.6.2"
|
||||
resolve "^1.9.0"
|
||||
|
||||
eslint-restricted-globals@^0.1.1:
|
||||
version "0.1.1"
|
||||
|
@ -11742,6 +11756,16 @@ object.entries@^1.0.4:
|
|||
function-bind "^1.1.0"
|
||||
has "^1.0.1"
|
||||
|
||||
object.fromentries@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab"
|
||||
integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==
|
||||
dependencies:
|
||||
define-properties "^1.1.2"
|
||||
es-abstract "^1.11.0"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.1"
|
||||
|
||||
object.getownpropertydescriptors@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
|
||||
|
@ -13777,6 +13801,13 @@ react-sizes@^1.0.4:
|
|||
lodash.throttle "^4.1.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-spring@^7.2.10:
|
||||
version "7.2.10"
|
||||
resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-7.2.10.tgz#eacbf429327bea058677c3a01af87b9c387f4aa6"
|
||||
integrity sha512-bsbgQh1DzTqjwuD1tDZ7Nu04G5JhNNXdezkcJ5xXNboedN/MPe0K+X/MtypUxXBUCLYT0r5oKT3zj4STqVpnPA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0"
|
||||
|
||||
react-testing-library@^5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-5.3.1.tgz#d70ab711c8cef604fd70c9ec422d59e7e51ae112"
|
||||
|
|
Loading…
Reference in New Issue