Merge pull request #80 from andrerfneves/feature/save-shielded-txid
Feature/save shielded txid
This commit is contained in:
commit
65313f01d5
|
@ -13,6 +13,9 @@ jobs:
|
||||||
- run: apt-get -y install libusb-1.0-0-dev graphicsmagick libudev-dev
|
- run: apt-get -y install libusb-1.0-0-dev graphicsmagick libudev-dev
|
||||||
- run: apt-get -y install tmux xvfb libxtst6 libxss1 libgtk2.0-0 libnss3 libasound2 libgconf-2-4 ffmpeg frei0r-plugins
|
- run: apt-get -y install tmux xvfb libxtst6 libxss1 libgtk2.0-0 libnss3 libasound2 libgconf-2-4 ffmpeg frei0r-plugins
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
|
- run:
|
||||||
|
name: Run Unit Tests
|
||||||
|
command: yarn test:unit
|
||||||
- run:
|
- run:
|
||||||
name: Run Webpack
|
name: Run Webpack
|
||||||
command: yarn dev
|
command: yarn dev
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
app: {
|
||||||
|
isPackaged: false,
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<TransactionItem /> should render a transaction item correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="sc-jzJRlG bgSReS sc-bdVaJa bWRfen"
|
||||||
|
id="transaction-item-a0s9dujo23j0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdVaJa eFCRqo"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdVaJa eFCRqo"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Transaction Type Icon"
|
||||||
|
class="sc-cSHVUG fQARKX"
|
||||||
|
src="[object Object]"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="sc-kGXeez iHFhdz sc-bwzfXH iiePXZ"
|
||||||
|
width=""
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-kAzzGY gABhMI sc-htpNat ccIOzB"
|
||||||
|
>
|
||||||
|
send
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sc-kgoBCf bfmmtg sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
16:31 PM
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="sc-chPdSV bQobpM sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
12345678912345678912...9123456789
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sc-bwzfXH jExuEg"
|
||||||
|
width=""
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-htpNat jAbxuE"
|
||||||
|
color="#FF6C6C"
|
||||||
|
>
|
||||||
|
-ZEC 0.865
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sc-htpNat dhSLjI"
|
||||||
|
color="#5d5d65"
|
||||||
|
>
|
||||||
|
-USD $2.544
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -0,0 +1,130 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<TransactionDailyComponent /> render() should render user daily transactions 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="sc-kpOJdX inMzwo"
|
||||||
|
data-testid="TransactionsDaily"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-ckVGcZ kFpbuU sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
2019-02-20T19:31:57.117Z
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="sc-dxgOiQ dHRuWE"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-jzJRlG bgSReS sc-bdVaJa bWRfen"
|
||||||
|
id="transaction-item-s0a8das098fgh2348a"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdVaJa eFCRqo"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdVaJa eFCRqo"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Transaction Type Icon"
|
||||||
|
class="sc-cSHVUG fQARKX"
|
||||||
|
src="[object Object]"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="sc-kGXeez iHFhdz sc-bwzfXH iiePXZ"
|
||||||
|
width=""
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-kAzzGY edxhBx sc-htpNat ccIOzB"
|
||||||
|
>
|
||||||
|
receive
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sc-kgoBCf bfmmtg sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
16:31 PM
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="sc-chPdSV bQobpM sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
12345678912345678912...9123456789
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sc-bwzfXH jExuEg"
|
||||||
|
width=""
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-htpNat dztgbW"
|
||||||
|
color="#6AEAC0"
|
||||||
|
>
|
||||||
|
+ZEC 1.789
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sc-htpNat dhSLjI"
|
||||||
|
color="#5d5d65"
|
||||||
|
>
|
||||||
|
+USD $2.406
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sc-jzJRlG bgSReS sc-bdVaJa bWRfen"
|
||||||
|
id="transaction-item-0asd908fgj90f01"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdVaJa eFCRqo"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sc-bdVaJa eFCRqo"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Transaction Type Icon"
|
||||||
|
class="sc-cSHVUG fQARKX"
|
||||||
|
src="[object Object]"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="sc-kGXeez iHFhdz sc-bwzfXH iiePXZ"
|
||||||
|
width=""
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-kAzzGY gABhMI sc-htpNat ccIOzB"
|
||||||
|
>
|
||||||
|
send
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sc-kgoBCf bfmmtg sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
16:31 PM
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="sc-chPdSV bQobpM sc-htpNat pRhzD"
|
||||||
|
>
|
||||||
|
12345678912345678912...9123456789
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="sc-bwzfXH jExuEg"
|
||||||
|
width=""
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sc-htpNat jAbxuE"
|
||||||
|
color="#FF6C6C"
|
||||||
|
>
|
||||||
|
-ZEC 0.846
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="sc-htpNat dhSLjI"
|
||||||
|
color="#5d5d65"
|
||||||
|
>
|
||||||
|
-USD $1.138
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -11,20 +11,14 @@ import { appTheme } from '../../app/theme';
|
||||||
afterEach(cleanup);
|
afterEach(cleanup);
|
||||||
|
|
||||||
describe('<StatusPill />', () => {
|
describe('<StatusPill />', () => {
|
||||||
test('should render status pill correctly', () => {
|
|
||||||
const { queryByTestId } = render(
|
|
||||||
<ThemeProvider theme={appTheme}>
|
|
||||||
<StatusPill progress={83.0} type='syncing' />
|
|
||||||
</ThemeProvider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(queryByTestId('StatusPill')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show percentage on status pill syncing', () => {
|
test('should show percentage on status pill syncing', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ThemeProvider theme={appTheme}>
|
<ThemeProvider theme={appTheme}>
|
||||||
<StatusPill progress={56.0} type='syncing' />
|
<StatusPill
|
||||||
|
getBlockchainStatus={() => Promise.resolve()}
|
||||||
|
nodeSyncProgress={56.0}
|
||||||
|
nodeSyncType='syncing'
|
||||||
|
/>
|
||||||
</ThemeProvider>,
|
</ThemeProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -34,7 +28,11 @@ describe('<StatusPill />', () => {
|
||||||
test('should hide percentage on status pill', () => {
|
test('should hide percentage on status pill', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ThemeProvider theme={appTheme}>
|
<ThemeProvider theme={appTheme}>
|
||||||
<StatusPill progress={100.0} type='ready' />
|
<StatusPill
|
||||||
|
getBlockchainStatus={() => Promise.resolve()}
|
||||||
|
nodeSyncProgress={100.0}
|
||||||
|
nodeSyncType='ready'
|
||||||
|
/>
|
||||||
</ThemeProvider>,
|
</ThemeProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -44,7 +42,11 @@ describe('<StatusPill />', () => {
|
||||||
test('should show error string and hide percentage on status pill', () => {
|
test('should show error string and hide percentage on status pill', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ThemeProvider theme={appTheme}>
|
<ThemeProvider theme={appTheme}>
|
||||||
<StatusPill progress={0.0} type='error' />
|
<StatusPill
|
||||||
|
getBlockchainStatus={() => Promise.resolve()}
|
||||||
|
nodeSyncProgress={0.0}
|
||||||
|
nodeSyncType='error'
|
||||||
|
/>
|
||||||
</ThemeProvider>,
|
</ThemeProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ describe('<TransactionItem />', () => {
|
||||||
address='123456789123456789123456789123456789'
|
address='123456789123456789123456789123456789'
|
||||||
transactionId='a0s9dujo23j0'
|
transactionId='a0s9dujo23j0'
|
||||||
amount={0.8652}
|
amount={0.8652}
|
||||||
date={new Date().toISOString()}
|
date='2019-02-20T19:31:57.117Z'
|
||||||
zecPrice={2.94}
|
zecPrice={2.94}
|
||||||
fees={0.0001}
|
fees={0.0001}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('<TransactionDailyComponent />', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ThemeProvider theme={appTheme}>
|
<ThemeProvider theme={appTheme}>
|
||||||
<TransactionDailyComponent
|
<TransactionDailyComponent
|
||||||
transactionsDate={new Date().toISOString()}
|
transactionsDate='2019-02-20T19:31:57.117Z'
|
||||||
zecPrice={1.345}
|
zecPrice={1.345}
|
||||||
transactions={[
|
transactions={[
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ describe('<TransactionDailyComponent />', () => {
|
||||||
address: '123456789123456789123456789123456789',
|
address: '123456789123456789123456789123456789',
|
||||||
amount: 1.7891,
|
amount: 1.7891,
|
||||||
zecPrice: 1.345,
|
zecPrice: 1.345,
|
||||||
date: new Date().toISOString(),
|
date: '2019-02-20T19:31:57.117Z',
|
||||||
theme: appTheme,
|
theme: appTheme,
|
||||||
fees: 0.001,
|
fees: 0.001,
|
||||||
},
|
},
|
||||||
|
@ -35,7 +35,7 @@ describe('<TransactionDailyComponent />', () => {
|
||||||
address: '123456789123456789123456789123456789',
|
address: '123456789123456789123456789123456789',
|
||||||
amount: 0.8458,
|
amount: 0.8458,
|
||||||
zecPrice: 1.344,
|
zecPrice: 1.344,
|
||||||
date: new Date().toISOString(),
|
date: '2019-02-20T19:31:57.117Z',
|
||||||
theme: appTheme,
|
theme: appTheme,
|
||||||
fees: 0.001,
|
fees: 0.001,
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,16 +12,14 @@ afterEach(cleanup);
|
||||||
|
|
||||||
describe('<WalletAddress />', () => {
|
describe('<WalletAddress />', () => {
|
||||||
test('should render wallet address component correctly', () => {
|
test('should render wallet address component correctly', () => {
|
||||||
const { queryByTestId } = render(
|
const { getByText } = render(
|
||||||
<ThemeProvider theme={appTheme}>
|
<ThemeProvider theme={appTheme}>
|
||||||
<div style={{ width: '700px' }}>
|
<div style={{ width: '700px' }}>
|
||||||
<WalletAddress
|
<WalletAddress address='t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1' />
|
||||||
address='t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</ThemeProvider>,
|
</ThemeProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(queryByTestId('Address')).toBeInTheDocument();
|
expect(getByText('t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import getZECPrice from '../../services/zec-price';
|
||||||
|
|
||||||
|
describe('ZEC PRICE Services', () => {
|
||||||
|
test('should return the right value', async () => {
|
||||||
|
const response = await getZECPrice(['BRL', 'EUR', 'USD']);
|
||||||
|
|
||||||
|
expect(response).toEqual({
|
||||||
|
USD: expect.any(Number),
|
||||||
|
BRL: expect.any(Number),
|
||||||
|
EUR: expect.any(Number),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,9 +7,7 @@ import { ZcashLogo } from './zcash-logo';
|
||||||
import { TextComponent } from './text';
|
import { TextComponent } from './text';
|
||||||
import { Divider } from './divider';
|
import { Divider } from './divider';
|
||||||
import { RowComponent } from './row';
|
import { RowComponent } from './row';
|
||||||
import { StatusPill } from './status-pill';
|
import { StatusPillContainer } from '../containers/status-pill';
|
||||||
|
|
||||||
import { withSyncStatus } from '../../services/sync-status';
|
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
height: ${props => props.theme.headerHeight};
|
height: ${props => props.theme.headerHeight};
|
||||||
|
@ -62,8 +60,6 @@ type Props = {
|
||||||
title: string,
|
title: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Status = withSyncStatus(StatusPill);
|
|
||||||
|
|
||||||
export const HeaderComponent = ({ title }: Props) => (
|
export const HeaderComponent = ({ title }: Props) => (
|
||||||
<Wrapper id='header'>
|
<Wrapper id='header'>
|
||||||
<LogoWrapper>
|
<LogoWrapper>
|
||||||
|
@ -72,7 +68,7 @@ export const HeaderComponent = ({ title }: Props) => (
|
||||||
<TitleWrapper>
|
<TitleWrapper>
|
||||||
<TitleRow alignItems='center' justifyContent='space-around'>
|
<TitleRow alignItems='center' justifyContent='space-around'>
|
||||||
<Title value={title} />
|
<Title value={title} />
|
||||||
<Status type='syncing' progress={0} />
|
<StatusPillContainer />
|
||||||
</TitleRow>
|
</TitleRow>
|
||||||
<Divider opacity={0.2} />
|
<Divider opacity={0.2} />
|
||||||
</TitleWrapper>
|
</TitleWrapper>
|
||||||
|
|
|
@ -99,7 +99,7 @@ export const Component = ({
|
||||||
<Icon
|
<Icon
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
src={item.icon(isActive, theme.mode)}
|
src={item.icon(isActive, theme.mode)}
|
||||||
Alt={`${item.route}`}
|
alt={`${item.route}`}
|
||||||
/>
|
/>
|
||||||
{item.label}
|
{item.label}
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import styled, { keyframes, withTheme } from 'styled-components';
|
import styled, { keyframes, withTheme } from 'styled-components';
|
||||||
import eres from 'eres';
|
|
||||||
|
|
||||||
import { TextComponent } from './text';
|
import { TextComponent } from './text';
|
||||||
|
|
||||||
import rpc from '../../services/api';
|
|
||||||
import { DARK } from '../constants/themes';
|
import { DARK } from '../constants/themes';
|
||||||
|
|
||||||
import readyIconDark from '../assets/images/green_check_dark.png';
|
import readyIconDark from '../assets/images/green_check_dark.png';
|
||||||
|
@ -16,6 +14,8 @@ import syncIconLight from '../assets/images/sync_icon_light.png';
|
||||||
import errorIconDark from '../assets/images/error_icon_dark.png';
|
import errorIconDark from '../assets/images/error_icon_dark.png';
|
||||||
import errorIconLight from '../assets/images/error_icon_light.png';
|
import errorIconLight from '../assets/images/error_icon_light.png';
|
||||||
|
|
||||||
|
import type { MapDispatchToProps, MapStateToProps } from '../containers/status-pill';
|
||||||
|
|
||||||
const rotate = keyframes`
|
const rotate = keyframes`
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
@ -55,94 +55,85 @@ const StatusPillLabel = styled(TextComponent)`
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
theme: AppTheme,
|
theme: AppTheme,
|
||||||
};
|
} & MapStateToProps &
|
||||||
|
MapDispatchToProps;
|
||||||
|
|
||||||
type State = {
|
const MINUTE_IN_MILI = 60000;
|
||||||
type: string,
|
|
||||||
icon: string,
|
|
||||||
progress: number,
|
|
||||||
isSyncing: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
class Component extends PureComponent<Props, State> {
|
class Component extends PureComponent<Props> {
|
||||||
timer: ?IntervalID = null;
|
timer: ?IntervalID = null;
|
||||||
|
|
||||||
constructor(props: Props) {
|
componentDidMount() {
|
||||||
super(props);
|
const { getBlockchainStatus } = this.props;
|
||||||
|
|
||||||
const { theme } = props;
|
this.timer = setInterval(() => getBlockchainStatus(), 2000);
|
||||||
|
|
||||||
const syncIcon = theme.mode === DARK
|
|
||||||
? syncIconDark
|
|
||||||
: syncIconLight;
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
type: 'syncing',
|
|
||||||
icon: syncIcon,
|
|
||||||
progress: 0,
|
|
||||||
isSyncing: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidUpdate(prevProps: Props) {
|
||||||
this.timer = setInterval(() => {
|
const { getBlockchainStatus, nodeSyncType } = this.props;
|
||||||
this.getBlockchainStatus();
|
if (prevProps.nodeSyncType === 'syncing' && nodeSyncType === 'ready') {
|
||||||
}, 2000);
|
// if the status is "ready", we can increase the interval to avoid useless rpc calls
|
||||||
|
this.cleanUpdateInterval();
|
||||||
|
this.timer = setInterval(() => getBlockchainStatus(), MINUTE_IN_MILI);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
this.cleanUpdateInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpdateInterval = () => {
|
||||||
if (this.timer) {
|
if (this.timer) {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
getBlockchainStatus = async () => {
|
isSyncing = () => {
|
||||||
|
const { nodeSyncType } = this.props;
|
||||||
|
return nodeSyncType === 'syncing';
|
||||||
|
};
|
||||||
|
|
||||||
|
getReadyIcon = () => {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
|
return theme.mode === DARK ? readyIconDark : readyIconLight;
|
||||||
|
};
|
||||||
|
|
||||||
const readyIcon = theme.mode === DARK
|
getErrorIcon = () => {
|
||||||
? readyIconDark
|
const { theme } = this.props;
|
||||||
: readyIconLight;
|
return theme.mode === DARK ? errorIconDark : errorIconLight;
|
||||||
const errorIcon = theme.mode === DARK
|
};
|
||||||
? errorIconDark
|
|
||||||
: errorIconLight;
|
|
||||||
|
|
||||||
const [blockchainErr, blockchaininfo] = await eres(rpc.getblockchaininfo());
|
getSyncingIcon = () => {
|
||||||
|
const { theme } = this.props;
|
||||||
|
return theme.mode === DARK ? syncIconDark : syncIconLight;
|
||||||
|
};
|
||||||
|
|
||||||
if (blockchainErr || !blockchaininfo) return;
|
getIcon = () => {
|
||||||
|
const { nodeSyncType } = this.props;
|
||||||
|
|
||||||
const newProgress = blockchaininfo.verificationprogress * 100;
|
switch (nodeSyncType) {
|
||||||
|
case 'syncing':
|
||||||
this.setState({
|
return this.getSyncingIcon();
|
||||||
progress: newProgress,
|
case 'ready':
|
||||||
...(newProgress > 99.99
|
return this.getReadyIcon();
|
||||||
? {
|
case 'error':
|
||||||
type: 'ready',
|
return this.getErrorIcon();
|
||||||
icon: readyIcon,
|
default:
|
||||||
isSyncing: false,
|
return null;
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (blockchainErr) {
|
|
||||||
this.setState(() => ({
|
|
||||||
type: 'error',
|
|
||||||
icon: errorIcon,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const icon = this.getIcon();
|
||||||
type, icon, progress, isSyncing,
|
const { nodeSyncType, nodeSyncProgress } = this.props;
|
||||||
} = this.state;
|
const percent = nodeSyncType === 'syncing' ? `(${nodeSyncProgress.toFixed(2)}%)` : '';
|
||||||
const showPercent = isSyncing ? `(${progress.toFixed(2)}%)` : '';
|
const typeText = nodeSyncType === 'ready' ? 'Synced' : nodeSyncType;
|
||||||
const typeText = type === 'ready' ? 'Synced' : type;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper id='status-pill'>
|
<Wrapper id='status-pill'>
|
||||||
<Icon src={icon} animated={isSyncing} />
|
{icon && <Icon src={icon} animated={this.isSyncing()} />}
|
||||||
<StatusPillLabel value={`${typeText} ${showPercent}`} />
|
<StatusPillLabel value={`${typeText} ${percent}`} />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,13 +204,7 @@ const Component = ({
|
||||||
<InfoRow>
|
<InfoRow>
|
||||||
<ColumnComponent width='100%'>
|
<ColumnComponent width='100%'>
|
||||||
<Label value='TRANSACTION ID' />
|
<Label value='TRANSACTION ID' />
|
||||||
<TransactionId
|
<TransactionId onClick={() => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)}>
|
||||||
onClick={
|
|
||||||
address !== '(Shielded)'
|
|
||||||
? () => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)
|
|
||||||
: () => {}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Ellipsis value={transactionId} />
|
<Ellipsis value={transactionId} />
|
||||||
</TransactionId>
|
</TransactionId>
|
||||||
</ColumnComponent>
|
</ColumnComponent>
|
||||||
|
|
|
@ -8,12 +8,21 @@ import { LayoutComponent } from '../components/layout';
|
||||||
import type { Dispatch } from '../types/redux';
|
import type { Dispatch } from '../types/redux';
|
||||||
import type { AppState } from '../types/app-state';
|
import type { AppState } from '../types/app-state';
|
||||||
|
|
||||||
const mapStateToProps = ({ app }: AppState) => ({
|
export type MapStateToProps = {|
|
||||||
|
isErrorModalVisible: boolean,
|
||||||
|
error: string | null,
|
||||||
|
|};
|
||||||
|
|
||||||
|
const mapStateToProps = ({ app }: AppState): MapStateToProps => ({
|
||||||
isErrorModalVisible: app.isErrorModalVisible,
|
isErrorModalVisible: app.isErrorModalVisible,
|
||||||
error: app.error,
|
error: app.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
export type MapDispatchToProps = {|
|
||||||
|
closeErrorModal: () => void,
|
||||||
|
|};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||||
closeErrorModal: () => dispatch(closeErrorModal()),
|
closeErrorModal: () => dispatch(closeErrorModal()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -50,10 +50,10 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||||
|
|
||||||
const formattedTransactions: Array<Object> = flow([
|
const formattedTransactions: Array<Object> = flow([
|
||||||
arr => arr.map(transaction => ({
|
arr => arr.map(transaction => ({
|
||||||
transactionId: transaction.txid || 'N/A',
|
transactionId: transaction.txid,
|
||||||
type: transaction.category,
|
type: transaction.category,
|
||||||
date: new Date(transaction.time * 1000).toISOString(),
|
date: new Date(transaction.time * 1000).toISOString(),
|
||||||
address: transaction.address,
|
address: transaction.address || '(Shielded)',
|
||||||
amount: Math.abs(transaction.amount),
|
amount: Math.abs(transaction.amount),
|
||||||
fees: transaction.fee ? new BigNumber(transaction.fee).abs().toFormat(4) : 'N/A',
|
fees: transaction.fee ? new BigNumber(transaction.fee).abs().toFormat(4) : 'N/A',
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -114,6 +114,7 @@ const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
if (from.startsWith('z')) {
|
if (from.startsWith('z')) {
|
||||||
saveShieldedTransaction({
|
saveShieldedTransaction({
|
||||||
|
txid: operationStatus.result.txid,
|
||||||
category: 'send',
|
category: 'send',
|
||||||
time: Date.now() / 1000,
|
time: Date.now() / 1000,
|
||||||
address: '(Shielded)',
|
address: '(Shielded)',
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import eres from 'eres';
|
||||||
|
import { BigNumber } from 'bignumber.js';
|
||||||
|
import { updateNodeSyncStatus } from '../redux/modules/app';
|
||||||
|
|
||||||
|
import { StatusPill } from '../components/status-pill';
|
||||||
|
|
||||||
|
import rpc from '../../services/api';
|
||||||
|
|
||||||
|
import type { Dispatch } from '../types/redux';
|
||||||
|
import type { AppState } from '../types/app-state';
|
||||||
|
|
||||||
|
export type MapStateToProps = {|
|
||||||
|
nodeSyncProgress: number,
|
||||||
|
nodeSyncType: 'ready' | 'syncing' | 'error',
|
||||||
|
|};
|
||||||
|
|
||||||
|
const mapStateToProps = ({ app }: AppState): MapStateToProps => ({
|
||||||
|
nodeSyncProgress: app.nodeSyncProgress,
|
||||||
|
nodeSyncType: app.nodeSyncType,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type MapDispatchToProps = {|
|
||||||
|
getBlockchainStatus: () => Promise<void>,
|
||||||
|
|};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||||
|
getBlockchainStatus: async () => {
|
||||||
|
const [blockchainErr, blockchaininfo] = await eres(rpc.getblockchaininfo());
|
||||||
|
|
||||||
|
if (blockchainErr || !blockchaininfo) {
|
||||||
|
return dispatch(
|
||||||
|
updateNodeSyncStatus({
|
||||||
|
nodeSyncProgress: 0,
|
||||||
|
nodeSyncType: 'error',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newProgress = blockchaininfo.verificationprogress * 100;
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
updateNodeSyncStatus({
|
||||||
|
nodeSyncProgress: newProgress,
|
||||||
|
nodeSyncType: new BigNumber(newProgress).gt(99.99) ? 'ready' : 'syncing',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
export const StatusPillContainer = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(StatusPill);
|
|
@ -3,7 +3,6 @@
|
||||||
import eres from 'eres';
|
import eres from 'eres';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
import uuidv4 from 'uuid/v4';
|
|
||||||
|
|
||||||
import { TransactionsView } from '../views/transactions';
|
import { TransactionsView } from '../views/transactions';
|
||||||
import {
|
import {
|
||||||
|
@ -65,10 +64,10 @@ const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||||
...transactions,
|
...transactions,
|
||||||
...listShieldedTransactions({ count, offset: shieldedTransactionsCount }),
|
...listShieldedTransactions({ count, offset: shieldedTransactionsCount }),
|
||||||
].map(transaction => ({
|
].map(transaction => ({
|
||||||
transactionId: transaction.txid ? transaction.txid : uuidv4(),
|
transactionId: transaction.txid,
|
||||||
type: transaction.category,
|
type: transaction.category,
|
||||||
date: new Date(transaction.time * 1000).toISOString(),
|
date: new Date(transaction.time * 1000).toISOString(),
|
||||||
address: transaction.address,
|
address: transaction.address || '(Shielded)',
|
||||||
amount: new BigNumber(transaction.amount).absoluteValue().toNumber(),
|
amount: new BigNumber(transaction.amount).absoluteValue().toNumber(),
|
||||||
fees: transaction.fee ? new BigNumber(transaction.fee).abs().toFormat(4) : 'N/A',
|
fees: transaction.fee ? new BigNumber(transaction.fee).abs().toFormat(4) : 'N/A',
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -2,9 +2,17 @@
|
||||||
|
|
||||||
import type { Action } from '../../types/redux';
|
import type { Action } from '../../types/redux';
|
||||||
|
|
||||||
|
export type State = {|
|
||||||
|
isErrorModalVisible: boolean,
|
||||||
|
error: string | null,
|
||||||
|
nodeSyncProgress: number,
|
||||||
|
nodeSyncType: 'ready' | 'syncing' | 'error',
|
||||||
|
|};
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
export const SHOW_ERROR_MODAL = 'SHOW_ERROR_MODAL';
|
export const SHOW_ERROR_MODAL = 'SHOW_ERROR_MODAL';
|
||||||
export const HIDE_ERROR_MODAL = 'HIDE_ERROR_MODAL';
|
export const HIDE_ERROR_MODAL = 'HIDE_ERROR_MODAL';
|
||||||
|
export const UPDATE_NODE_SYNC_STATUS = 'UPDATE_NODE_SYNC_STATUS';
|
||||||
|
|
||||||
export const showErrorModal = ({ error }: { error: string }) => ({
|
export const showErrorModal = ({ error }: { error: string }) => ({
|
||||||
type: SHOW_ERROR_MODAL,
|
type: SHOW_ERROR_MODAL,
|
||||||
|
@ -18,14 +26,25 @@ export const closeErrorModal = () => ({
|
||||||
payload: {},
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export type State = {
|
export const updateNodeSyncStatus = ({
|
||||||
isErrorModalVisible: boolean,
|
nodeSyncProgress,
|
||||||
error: string | null,
|
nodeSyncType,
|
||||||
};
|
}: {
|
||||||
|
nodeSyncProgress: number,
|
||||||
|
nodeSyncType: $PropertyType<State, 'nodeSyncType'>,
|
||||||
|
}) => ({
|
||||||
|
type: UPDATE_NODE_SYNC_STATUS,
|
||||||
|
payload: {
|
||||||
|
nodeSyncProgress,
|
||||||
|
nodeSyncType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
isErrorModalVisible: false,
|
isErrorModalVisible: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
nodeSyncProgress: 0,
|
||||||
|
nodeSyncType: 'syncing',
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@ -35,6 +54,12 @@ export default (state: State = initialState, action: Action) => {
|
||||||
return { isErrorModalVisible: true, error: action.payload.error };
|
return { isErrorModalVisible: true, error: action.payload.error };
|
||||||
case HIDE_ERROR_MODAL:
|
case HIDE_ERROR_MODAL:
|
||||||
return { isErrorModalVisible: false, error: null };
|
return { isErrorModalVisible: false, error: null };
|
||||||
|
case UPDATE_NODE_SYNC_STATUS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
nodeSyncProgress: action.payload.nodeSyncProgress,
|
||||||
|
nodeSyncType: action.payload.nodeSyncType,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,7 @@ const createWindow = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
getZecPrice().then((obj) => {
|
getZecPrice().then(({ USD }) => store.set('ZEC_DOLLAR_PRICE', String(USD)));
|
||||||
store.set('ZEC_DOLLAR_PRICE', String(obj.USD));
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.setVisibleOnAllWorkspaces(true);
|
mainWindow.setVisibleOnAllWorkspaces(true);
|
||||||
registerDebugShortcut(app, mainWindow);
|
registerDebugShortcut(app, mainWindow);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"electron:prepare": "yarn icon:build && rm -rf dist && mkdir dist",
|
"electron:prepare": "yarn icon:build && rm -rf dist && mkdir dist",
|
||||||
"electron:pack": "yarn electron:prepare && electron-builder --dir",
|
"electron:pack": "yarn electron:prepare && electron-builder --dir",
|
||||||
"electron:dist": "yarn electron:prepare && electron-builder",
|
"electron:dist": "yarn electron:prepare && electron-builder",
|
||||||
|
"electron:clean-store": "electron -r @babel/register ./utils/clean-electron-store.js",
|
||||||
"preelectron:prepare": "yarn build",
|
"preelectron:prepare": "yarn build",
|
||||||
"icon:build": "./node_modules/.bin/electron-icon-maker --input=build-assets/icon.png --output=./build",
|
"icon:build": "./node_modules/.bin/electron-icon-maker --input=build-assets/icon.png --output=./build",
|
||||||
"docz:dev": "docz dev",
|
"docz:dev": "docz dev",
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
"docz:deploy": "yarn docz:build && cd ./.docz/dist && now && now alias zec-docz.now.sh",
|
"docz:deploy": "yarn docz:build && cd ./.docz/dist && now && now alias zec-docz.now.sh",
|
||||||
"test": "jest --runInBand",
|
"test": "jest --runInBand",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
|
"test:unit": "jest --testPathIgnorePatterns=e2e --testPathIgnorePatterns=setup",
|
||||||
"e2e:serve": "node -r @babel/register ./__tests__/setup/mockAPI.js",
|
"e2e:serve": "node -r @babel/register ./__tests__/setup/mockAPI.js",
|
||||||
"e2e:run": "yarn test e2e"
|
"e2e:run": "yarn test e2e"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="125" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h91v20H0z"/><path fill="#4C1" d="M91 0h34v20H91z"/><path fill="url(#b)" d="M0 0h125v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,Geneva,sans-serif" font-size="11"><text x="45.5" y="15" fill="#010101" fill-opacity=".3">flow-coverage</text><text x="45.5" y="14">flow-coverage</text><text x="107" y="15" fill="#010101" fill-opacity=".3">89%</text><text x="107" y="14">89%</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="125" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h91v20H0z"/><path fill="#4C1" d="M91 0h34v20H91z"/><path fill="url(#b)" d="M0 0h125v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,Geneva,sans-serif" font-size="11"><text x="45.5" y="15" fill="#010101" fill-opacity=".3">flow-coverage</text><text x="45.5" y="14">flow-coverage</text><text x="107" y="15" fill="#010101" fill-opacity=".3">88%</text><text x="107" y="14">88%</text></g></svg>
|
Before Width: | Height: | Size: 745 B After Width: | Height: | Size: 745 B |
|
@ -4,6 +4,7 @@ import electronStore from '../config/electron-store';
|
||||||
const STORE_KEY = 'SHIELDED_TRANSACTIONS';
|
const STORE_KEY = 'SHIELDED_TRANSACTIONS';
|
||||||
|
|
||||||
type ShieldedTransaction = {|
|
type ShieldedTransaction = {|
|
||||||
|
txid: string,
|
||||||
category: 'send' | 'receive',
|
category: 'send' | 'receive',
|
||||||
time: number,
|
time: number,
|
||||||
address: string,
|
address: string,
|
||||||
|
@ -28,6 +29,7 @@ export const listShieldedTransactions = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveShieldedTransaction = ({
|
export const saveShieldedTransaction = ({
|
||||||
|
txid,
|
||||||
category,
|
category,
|
||||||
time,
|
time,
|
||||||
address,
|
address,
|
||||||
|
@ -37,6 +39,7 @@ export const saveShieldedTransaction = ({
|
||||||
electronStore.set(
|
electronStore.set(
|
||||||
STORE_KEY,
|
STORE_KEY,
|
||||||
listShieldedTransactions().concat({
|
listShieldedTransactions().concat({
|
||||||
|
txid,
|
||||||
category,
|
category,
|
||||||
time,
|
time,
|
||||||
address,
|
address,
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import React, { type ComponentType, Component } from 'react';
|
|
||||||
import eres from 'eres';
|
|
||||||
|
|
||||||
import rpc from './api';
|
|
||||||
|
|
||||||
type Props = {};
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
type?: 'syncing' | 'ready' | 'error',
|
|
||||||
progress: number,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
|
||||||
export const withSyncStatus = <PassedProps: {}>(
|
|
||||||
WrappedComponent: ComponentType<PassedProps>,
|
|
||||||
): ComponentType<$Diff<PassedProps, Props>> => class extends Component<PassedProps, State> {
|
|
||||||
timer: ?IntervalID = null;
|
|
||||||
|
|
||||||
state = {
|
|
||||||
type: 'syncing',
|
|
||||||
progress: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.timer = setInterval(() => {
|
|
||||||
this.getBlockchainStatus();
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.timer) {
|
|
||||||
clearInterval(this.timer);
|
|
||||||
this.timer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getBlockchainStatus = async () => {
|
|
||||||
const [blockchainErr, blockchaininfo] = await eres(
|
|
||||||
rpc.getblockchaininfo(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const newProgress = blockchaininfo.verificationprogress * 100;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
progress: newProgress,
|
|
||||||
...(newProgress > 99.99 ? {
|
|
||||||
type: 'ready',
|
|
||||||
} : {}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (blockchainErr) {
|
|
||||||
this.setState(() => ({ type: 'error' }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { type, progress } = this.state;
|
|
||||||
|
|
||||||
return <WrappedComponent {...this.props} {...this.state} type={type} progress={progress} />;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,8 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
import got from 'got';
|
||||||
// $FlowFixMe
|
|
||||||
import { net } from 'electron';
|
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
[currency: string]: number,
|
[currency: string]: number,
|
||||||
|
@ -18,18 +16,7 @@ export default (currencies: string[] = ['USD']): Promise<Payload> => new Promise
|
||||||
',',
|
',',
|
||||||
)}&api_key=${String(process.env.ZEC_PRICE_API_KEY)}`;
|
)}&api_key=${String(process.env.ZEC_PRICE_API_KEY)}`;
|
||||||
|
|
||||||
const request = net.request(ENDPOINT);
|
got(ENDPOINT)
|
||||||
request.on('response', (response) => {
|
.then(response => resolve(JSON.parse(response.body)))
|
||||||
let data = '';
|
.catch(reject);
|
||||||
/* eslint-disable-next-line no-return-assign */
|
|
||||||
response.on('data', chunk => (data += chunk));
|
|
||||||
response.on('end', () => {
|
|
||||||
try {
|
|
||||||
resolve(JSON.parse(data));
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
request.end();
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// @flow
|
||||||
|
/* eslint-disable */
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { app } from 'electron';
|
||||||
|
|
||||||
|
fs.unlink(path.join(app.getPath('appData'), 'zec-react-wallet', 'config.json'), err => {
|
||||||
|
if (err) {
|
||||||
|
console.log("Couldn't remove config.json", err);
|
||||||
|
} else {
|
||||||
|
console.log('electron-store cleaned');
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
});
|
Loading…
Reference in New Issue