Merge pull request #37 from andrerfneves/feature/settings-export
Feature/settings export
This commit is contained in:
commit
7ab19e0029
|
@ -17,7 +17,8 @@ const DefaultButton = styled.button`
|
||||||
outline: none;
|
outline: none;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
transition: background-color 0.1s ${props => props.theme.colors.transitionEase};
|
transition: background-color 0.1s
|
||||||
|
${props => props.theme.colors.transitionEase};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ const Icon = styled.img`
|
||||||
type Props = {
|
type Props = {
|
||||||
label: string,
|
label: string,
|
||||||
onClick?: () => void,
|
onClick?: () => void,
|
||||||
to?: string,
|
to?: ?string,
|
||||||
variant?: 'primary' | 'secondary',
|
variant?: 'primary' | 'secondary',
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
icon?: string,
|
icon?: string,
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// @flow
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { Button } from './button';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
text: string,
|
||||||
|
className?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = { copied: boolean };
|
||||||
|
|
||||||
|
export class Clipboard extends PureComponent<Props, State> {
|
||||||
|
static defaultProps = {
|
||||||
|
className: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
copied: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
const { text } = this.props;
|
||||||
|
|
||||||
|
const el = document.createElement('textarea');
|
||||||
|
el.value = text;
|
||||||
|
|
||||||
|
if (document.body) document.body.appendChild(el);
|
||||||
|
|
||||||
|
el.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
if (document.body) document.body.removeChild(el);
|
||||||
|
|
||||||
|
this.setState({ copied: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { className } = this.props;
|
||||||
|
const { copied } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
label={copied ? 'Copied!' : 'Copy!'}
|
||||||
|
className={className}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
disabled={copied}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
// @flow
|
||||||
|
import React, { type Element } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { TextComponent } from './text';
|
||||||
|
import { Button } from './button';
|
||||||
|
import { Divider } from './divider';
|
||||||
|
import { ModalComponent } from './modal';
|
||||||
|
|
||||||
|
import CloseIcon from '../assets/images/close_icon.svg';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
width: ${props => `${props.width}px`};
|
||||||
|
background-color: ${props => props.theme.colors.background};
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0px 0px 30px 0px black;
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CloseIconWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TitleWrapper = styled.div`
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CloseIconImg = styled.img`
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Btn = styled(Button)`
|
||||||
|
width: 95%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
renderTrigger: (() => void) => Element<*>,
|
||||||
|
title: string,
|
||||||
|
onConfirm: () => void,
|
||||||
|
showButtons?: boolean,
|
||||||
|
width?: number,
|
||||||
|
isLoading?: boolean,
|
||||||
|
children: Element<*>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConfirmDialogComponent = ({
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
onConfirm,
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
|
||||||
|
ConfirmDialogComponent.defaultProps = {
|
||||||
|
showButtons: true,
|
||||||
|
width: 460,
|
||||||
|
isLoading: false,
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
name: Confirm Dialog
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Playground, PropsTable } from 'docz'
|
||||||
|
|
||||||
|
import { Button } from './button.js'
|
||||||
|
import { ConfirmDialogComponent } from './confirm-dialog.js'
|
||||||
|
import { DoczWrapper } from '../theme.js'
|
||||||
|
|
||||||
|
# Confirm Dialog
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
<PropsTable of={ConfirmDialogComponent} />
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
<Playground>
|
||||||
|
<DoczWrapper>
|
||||||
|
{() => (
|
||||||
|
<ConfirmDialogComponent
|
||||||
|
title="Confirm example"
|
||||||
|
onConfirm={() => alert('Confirm')}
|
||||||
|
renderTrigger={toggle => <button onClick={toggle}> Open! </button>}
|
||||||
|
>
|
||||||
|
<div>Confirm content</div>
|
||||||
|
</ConfirmDialogComponent>
|
||||||
|
)}
|
||||||
|
</DoczWrapper>
|
||||||
|
</Playground>
|
|
@ -4,18 +4,14 @@ import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const getDefaultStyles = t => styled[t]`
|
const getDefaultStyles = t => styled[t]`
|
||||||
border-radius: ${// $FlowFixMe
|
border-radius: ${props => props.theme.boxBorderRadius};
|
||||||
props => props.theme.boxBorderRadius};
|
|
||||||
border: none;
|
border: none;
|
||||||
background-color: ${// $FlowFixMe
|
background-color: ${props => props.theme.colors.inputBackground};
|
||||||
props => props.theme.colors.inputBackground};
|
color: ${props => props.theme.colors.text};
|
||||||
color: ${// $FlowFixMe
|
|
||||||
props => props.theme.colors.text};
|
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: ${// $FlowFixMe
|
font-family: ${props => props.theme.fontFamily};
|
||||||
props => props.theme.fontFamily};
|
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
@ -28,7 +24,7 @@ const Textarea = getDefaultStyles('textarea');
|
||||||
type Props = {
|
type Props = {
|
||||||
inputType?: 'input' | 'textarea',
|
inputType?: 'input' | 'textarea',
|
||||||
value: string,
|
value: string,
|
||||||
onChange: string => void,
|
onChange?: string => void,
|
||||||
onFocus?: (SyntheticFocusEvent<HTMLInputElement>) => void,
|
onFocus?: (SyntheticFocusEvent<HTMLInputElement>) => void,
|
||||||
rows?: number,
|
rows?: number,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
|
@ -36,7 +32,11 @@ type Props = {
|
||||||
step?: number,
|
step?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InputComponent = ({ inputType, onChange, ...props }: Props) => {
|
export const InputComponent = ({
|
||||||
|
inputType,
|
||||||
|
onChange = () => {},
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
const inputTypes = {
|
const inputTypes = {
|
||||||
input: () => (
|
input: () => (
|
||||||
<Input onChange={evt => onChange(evt.target.value)} {...props} />
|
<Input onChange={evt => onChange(evt.target.value)} {...props} />
|
||||||
|
|
|
@ -43,7 +43,7 @@ const StyledLink = styled(Link)`
|
||||||
color: ${/* eslint-disable-next-line max-len */
|
color: ${/* eslint-disable-next-line max-len */
|
||||||
props => (props.isActive
|
props => (props.isActive
|
||||||
? props.theme.colors.sidebarItemActive
|
? props.theme.colors.sidebarItemActive
|
||||||
: props.theme.colors.sidebarHoveredItemLabel)};
|
: '#ddd')}
|
||||||
background-color: ${props => props.theme.colors.sidebarHoveredItem};
|
background-color: ${props => props.theme.colors.sidebarHoveredItem};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -54,7 +54,7 @@ const Icon = styled.img`
|
||||||
margin-right: 13px;
|
margin-right: 13px;
|
||||||
|
|
||||||
${StyledLink}:hover & {
|
${StyledLink}:hover & {
|
||||||
filter: ${props => (props.isActive ? 'none' : 'brightness(200%)')};
|
filter: ${props => (props.isActive ? 'none' : 'brightness(300%)')};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ export const SidebarComponent = ({ options, location }: Props) => (
|
||||||
<Icon
|
<Icon
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
src={item.icon(isActive)}
|
src={item.icon(isActive)}
|
||||||
alt={`Sidebar Icon ${item.route}`}
|
alt={`${item.route}`}
|
||||||
/>
|
/>
|
||||||
{item.label}
|
{item.label}
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
|
|
|
@ -26,7 +26,7 @@ const Wrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
border-radius: 27px;
|
border-radius: 27px;
|
||||||
padding: 7px 13px;
|
padding: 8px 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Icon = styled.img`
|
const Icon = styled.img`
|
||||||
|
@ -41,6 +41,8 @@ const StatusPillLabel = styled(TextComponent)`
|
||||||
color: ${props => props.theme.colors.statusPillLabel};
|
color: ${props => props.theme.colors.statusPillLabel};
|
||||||
font-weight: ${props => props.theme.fontWeight.bold};
|
font-weight: ${props => props.theme.fontWeight.bold};
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
font-size: 10px;
|
||||||
|
padding-top: 1px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {};
|
type Props = {};
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// @flow
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { SettingsView } from '../views/settings';
|
||||||
|
|
||||||
|
import type { AppState } from '../types/app-state';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ walletSummary }: AppState) => ({
|
||||||
|
addresses: walletSummary.addresses,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SettingsContainer = connect(mapStateToProps)(SettingsView);
|
|
@ -10,7 +10,7 @@ import { DashboardContainer } from '../containers/dashboard';
|
||||||
import { TransactionsContainer } from '../containers/transactions';
|
import { TransactionsContainer } from '../containers/transactions';
|
||||||
import { SendContainer } from '../containers/send';
|
import { SendContainer } from '../containers/send';
|
||||||
import { ReceiveContainer } from '../containers/receive';
|
import { ReceiveContainer } from '../containers/receive';
|
||||||
import { SettingsView } from '../views/settings';
|
import { SettingsContainer } from '../containers/settings';
|
||||||
import { NotFoundView } from '../views/not-found';
|
import { NotFoundView } from '../views/not-found';
|
||||||
import { ConsoleView } from '../views/console';
|
import { ConsoleView } from '../views/console';
|
||||||
import { LayoutComponent } from '../components/layout';
|
import { LayoutComponent } from '../components/layout';
|
||||||
|
@ -59,7 +59,7 @@ export const RouterComponent = ({ location }: { location: Location }) => (
|
||||||
/>
|
/>
|
||||||
<Route path={SEND_ROUTE} component={SendContainer} />
|
<Route path={SEND_ROUTE} component={SendContainer} />
|
||||||
<Route path={RECEIVE_ROUTE} component={ReceiveContainer} />
|
<Route path={RECEIVE_ROUTE} component={ReceiveContainer} />
|
||||||
<Route path={SETTINGS_ROUTE} component={SettingsView} />
|
<Route path={SETTINGS_ROUTE} component={SettingsContainer} />
|
||||||
<Route path={CONSOLE_ROUTE} component={ConsoleView} />
|
<Route path={CONSOLE_ROUTE} component={ConsoleView} />
|
||||||
<Route
|
<Route
|
||||||
path={TRANSACTIONS_ROUTE}
|
path={TRANSACTIONS_ROUTE}
|
||||||
|
|
|
@ -10,14 +10,15 @@ import { DARK } from './constants/themes';
|
||||||
const darkOne = '#F4B728';
|
const darkOne = '#F4B728';
|
||||||
const lightOne = '#ffffff';
|
const lightOne = '#ffffff';
|
||||||
const brandOne = '#000';
|
const brandOne = '#000';
|
||||||
const brandTwo = '#3B3B3F';
|
// const brandTwo = '#3B3B3F';
|
||||||
|
const brandThree = '#5d5d65';
|
||||||
const activeItem = '#F4B728';
|
const activeItem = '#F4B728';
|
||||||
const text = '#FFF';
|
const text = '#FFF';
|
||||||
const cardBackgroundColor = '#000';
|
const cardBackgroundColor = '#000';
|
||||||
const sidebarLogoGradientBegin = '#F4B728';
|
const sidebarLogoGradientBegin = '#F4B728';
|
||||||
const sidebarLogoGradientEnd = '#FFE240';
|
const sidebarLogoGradientEnd = '#FFE240';
|
||||||
const sidebarHoveredItem = '#1C1C1C';
|
const sidebarHoveredItem = '#1C1C1C';
|
||||||
const sidebarHoveredItemLabel = '#76767e';
|
const sidebarHoveredItemLabel = '#8e8e96';
|
||||||
const background = '#212124';
|
const background = '#212124';
|
||||||
const transactionSent = '#FF6C6C';
|
const transactionSent = '#FF6C6C';
|
||||||
const transactionReceived = '#6AEAC0';
|
const transactionReceived = '#6AEAC0';
|
||||||
|
@ -51,14 +52,14 @@ const appTheme = {
|
||||||
dark: lightOne,
|
dark: lightOne,
|
||||||
}),
|
}),
|
||||||
sidebarBg: brandOne,
|
sidebarBg: brandOne,
|
||||||
sidebarItem: brandTwo,
|
sidebarItem: brandThree,
|
||||||
sidebarItemActive: activeItem,
|
sidebarItemActive: activeItem,
|
||||||
sidebarHoveredItem,
|
sidebarHoveredItem,
|
||||||
sidebarHoveredItemLabel,
|
sidebarHoveredItemLabel,
|
||||||
cardBackgroundColor,
|
cardBackgroundColor,
|
||||||
text,
|
text,
|
||||||
activeItem,
|
activeItem,
|
||||||
inactiveItem: brandTwo,
|
inactiveItem: brandThree,
|
||||||
sidebarLogoGradientBegin,
|
sidebarLogoGradientBegin,
|
||||||
sidebarLogoGradientEnd,
|
sidebarLogoGradientEnd,
|
||||||
background,
|
background,
|
||||||
|
|
|
@ -1,5 +1,292 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import fs from 'fs';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import electron from 'electron';
|
||||||
|
import isDev from 'electron-is-dev';
|
||||||
|
import dateFns from 'date-fns';
|
||||||
|
import eres from 'eres';
|
||||||
|
|
||||||
import React from 'react';
|
import { Button } from '../components/button';
|
||||||
|
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';
|
||||||
|
|
||||||
export const SettingsView = () => <div className='settings'>settings</div>;
|
import rpc from '../../services/api';
|
||||||
|
|
||||||
|
const HOME_DIR = electron.remote.app.getPath('home');
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
margin-top: ${props => props.theme.layoutContentPaddingTop};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ModalContent = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
p {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Btn = styled(Button)`
|
||||||
|
margin-bottom: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ClipboardButton = styled(Clipboard)`
|
||||||
|
width: 50px;
|
||||||
|
border-radius: ${props => props.theme.boxBorderRadius};
|
||||||
|
height: 45px;
|
||||||
|
margin-left: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Key = {
|
||||||
|
zAddress: string,
|
||||||
|
key: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
addresses: string[],
|
||||||
|
};
|
||||||
|
type State = {
|
||||||
|
viewKeys: Key[],
|
||||||
|
privateKeys: Key[],
|
||||||
|
importedPrivateKeys: string,
|
||||||
|
successExportViewKeys: boolean,
|
||||||
|
successExportPrivateKeys: boolean,
|
||||||
|
successImportPrivateKeys: boolean,
|
||||||
|
isLoading: boolean,
|
||||||
|
error: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SettingsView extends PureComponent<Props, State> {
|
||||||
|
state = {
|
||||||
|
viewKeys: [],
|
||||||
|
privateKeys: [],
|
||||||
|
importedPrivateKeys: '',
|
||||||
|
isLoading: false,
|
||||||
|
successExportViewKeys: false,
|
||||||
|
successExportPrivateKeys: false,
|
||||||
|
successImportPrivateKeys: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
exportViewKeys = () => {
|
||||||
|
const { addresses } = this.props;
|
||||||
|
|
||||||
|
const zAddresses = addresses.filter(addr => addr.startsWith('z'));
|
||||||
|
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
|
Promise.all(
|
||||||
|
zAddresses.map(async (zAddr) => {
|
||||||
|
const viewKey = await rpc.z_exportviewingkey(zAddr);
|
||||||
|
return { zAddress: zAddr, key: viewKey };
|
||||||
|
}),
|
||||||
|
).then((viewKeys) => {
|
||||||
|
this.setState({
|
||||||
|
viewKeys,
|
||||||
|
successExportViewKeys: true,
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exportPrivateKeys = () => {
|
||||||
|
const { addresses } = this.props;
|
||||||
|
|
||||||
|
const zAddresses = addresses.filter(addr => addr.startsWith('z'));
|
||||||
|
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
|
Promise.all(
|
||||||
|
zAddresses.map(async (zAddr) => {
|
||||||
|
const privateKey = await rpc.z_exportkey(zAddr);
|
||||||
|
return { zAddress: zAddr, key: privateKey };
|
||||||
|
}),
|
||||||
|
).then((privateKeys) => {
|
||||||
|
this.setState({
|
||||||
|
privateKeys,
|
||||||
|
successExportPrivateKeys: true,
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
importPrivateKeys = () => {
|
||||||
|
const { importedPrivateKeys } = this.state;
|
||||||
|
|
||||||
|
if (!importedPrivateKeys) return;
|
||||||
|
|
||||||
|
const keys = importedPrivateKeys
|
||||||
|
.split('\n')
|
||||||
|
.map(key => key.trim())
|
||||||
|
.filter(key => !!key);
|
||||||
|
|
||||||
|
this.setState({ isLoading: true, error: null });
|
||||||
|
|
||||||
|
Promise.all(keys.map(key => rpc.z_importkey(key)))
|
||||||
|
.then(() => {
|
||||||
|
this.setState({
|
||||||
|
successImportPrivateKeys: true,
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setState({ isLoading: false, error: error.message });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
backupWalletDat = async () => {
|
||||||
|
const backupFileName = `zcash-wallet-backup-${dateFns.format(
|
||||||
|
new Date(),
|
||||||
|
'YYYY-MM-DD-mm-ss',
|
||||||
|
)}.dat`;
|
||||||
|
|
||||||
|
electron.remote.dialog.showSaveDialog(
|
||||||
|
{ defaultPath: backupFileName },
|
||||||
|
async (pathToSave) => {
|
||||||
|
if (!pathToSave) return;
|
||||||
|
|
||||||
|
const zcashDir = isDev ? `${HOME_DIR}/.zcash/testnet3` : HOME_DIR;
|
||||||
|
const walletDatPath = `${zcashDir}/wallet.dat`;
|
||||||
|
|
||||||
|
const [cannotAccess] = await eres(promisify(fs.access)(walletDatPath));
|
||||||
|
|
||||||
|
/* eslint-disable no-alert */
|
||||||
|
|
||||||
|
if (cannotAccess) {
|
||||||
|
alert(
|
||||||
|
"Couldn't backup the wallet.dat file. You need to back it up manually.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [error] = await eres(
|
||||||
|
promisify(fs.copyFile)(walletDatPath, pathToSave),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
alert(
|
||||||
|
"Couldn't backup the wallet.dat file. You need to back it up manually.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
const {
|
||||||
|
viewKeys,
|
||||||
|
privateKeys,
|
||||||
|
importedPrivateKeys,
|
||||||
|
successExportViewKeys,
|
||||||
|
successExportPrivateKeys,
|
||||||
|
successImportPrivateKeys,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<ConfirmDialogComponent
|
||||||
|
title='Export view keys'
|
||||||
|
renderTrigger={toggleVisibility => (
|
||||||
|
<Btn label='Export view keys' onClick={toggleVisibility} />
|
||||||
|
)}
|
||||||
|
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>
|
||||||
|
</ConfirmDialogComponent>
|
||||||
|
|
||||||
|
<ConfirmDialogComponent
|
||||||
|
title='Export private keys'
|
||||||
|
renderTrigger={toggleVisibility => (
|
||||||
|
<Btn label='Export private keys' onClick={toggleVisibility} />
|
||||||
|
)}
|
||||||
|
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>
|
||||||
|
</ConfirmDialogComponent>
|
||||||
|
|
||||||
|
<ConfirmDialogComponent
|
||||||
|
title='Import private keys'
|
||||||
|
renderTrigger={toggleVisibility => (
|
||||||
|
<Btn label='Import private keys' onClick={toggleVisibility} />
|
||||||
|
)}
|
||||||
|
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'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{error && <TextComponent value={error} align='center' />}
|
||||||
|
</ModalContent>
|
||||||
|
</ConfirmDialogComponent>
|
||||||
|
|
||||||
|
<Btn label='Backup wallet.dat' onClick={this.backupWalletDat} />
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ const createWindow = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 800,
|
width: 1000,
|
||||||
height: 600,
|
height: 600,
|
||||||
transparent: false,
|
transparent: false,
|
||||||
frame: true,
|
frame: true,
|
||||||
|
|
Loading…
Reference in New Issue