diff --git a/.circleci/config.yml b/.circleci/config.yml index 7a04354..9308fdb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,6 +13,9 @@ jobs: - 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: yarn install + - run: + name: Run Unit Tests + command: yarn test:unit - run: name: Run Webpack command: yarn dev diff --git a/__mocks__/electron-is-dev.js b/__mocks__/electron-is-dev.js new file mode 100644 index 0000000..35d4a32 --- /dev/null +++ b/__mocks__/electron-is-dev.js @@ -0,0 +1,5 @@ +module.exports = { + app: { + isPackaged: false, + }, +}; diff --git a/__tests__/components/__snapshots__/transaction-item.test.js.snap b/__tests__/components/__snapshots__/transaction-item.test.js.snap new file mode 100644 index 0000000..b6618f3 --- /dev/null +++ b/__tests__/components/__snapshots__/transaction-item.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render a transaction item correctly 1`] = ` +
+
+
+
+ Transaction Type Icon +
+

+ send +

+

+ 16:31 PM +

+
+
+

+ 12345678912345678912...9123456789 +

+
+
+

+ -ZEC 0.865 +

+

+ -USD $2.544 +

+
+
+
+`; diff --git a/__tests__/components/__snapshots__/transactions-daily.test.js.snap b/__tests__/components/__snapshots__/transactions-daily.test.js.snap new file mode 100644 index 0000000..017aff2 --- /dev/null +++ b/__tests__/components/__snapshots__/transactions-daily.test.js.snap @@ -0,0 +1,130 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` render() should render user daily transactions 1`] = ` +
+
+

+ 2019-02-20T19:31:57.117Z +

+
+
+
+
+ Transaction Type Icon +
+

+ receive +

+

+ 16:31 PM +

+
+
+

+ 12345678912345678912...9123456789 +

+
+
+

+ +ZEC 1.789 +

+

+ +USD $2.406 +

+
+
+
+
+
+ Transaction Type Icon +
+

+ send +

+

+ 16:31 PM +

+
+
+

+ 12345678912345678912...9123456789 +

+
+
+

+ -ZEC 0.846 +

+

+ -USD $1.138 +

+
+
+
+
+
+`; diff --git a/__tests__/components/status-pill.test.js b/__tests__/components/status-pill.test.js index 83250ea..ea690e7 100644 --- a/__tests__/components/status-pill.test.js +++ b/__tests__/components/status-pill.test.js @@ -11,20 +11,14 @@ import { appTheme } from '../../app/theme'; afterEach(cleanup); describe('', () => { - test('should render status pill correctly', () => { - const { queryByTestId } = render( - - - , - ); - - expect(queryByTestId('StatusPill')).toBeInTheDocument(); - }); - test('should show percentage on status pill syncing', () => { const { container } = render( - + Promise.resolve()} + nodeSyncProgress={56.0} + nodeSyncType='syncing' + /> , ); @@ -34,7 +28,11 @@ describe('', () => { test('should hide percentage on status pill', () => { const { container } = render( - + Promise.resolve()} + nodeSyncProgress={100.0} + nodeSyncType='ready' + /> , ); @@ -44,7 +42,11 @@ describe('', () => { test('should show error string and hide percentage on status pill', () => { const { container } = render( - + Promise.resolve()} + nodeSyncProgress={0.0} + nodeSyncType='error' + /> , ); diff --git a/__tests__/components/transaction-item.test.js b/__tests__/components/transaction-item.test.js index 44ff1ed..4f23fe4 100644 --- a/__tests__/components/transaction-item.test.js +++ b/__tests__/components/transaction-item.test.js @@ -19,7 +19,7 @@ describe('', () => { address='123456789123456789123456789123456789' transactionId='a0s9dujo23j0' amount={0.8652} - date={new Date().toISOString()} + date='2019-02-20T19:31:57.117Z' zecPrice={2.94} fees={0.0001} /> diff --git a/__tests__/components/transactions-daily.test.js b/__tests__/components/transactions-daily.test.js index 44f768f..3a5fc8d 100644 --- a/__tests__/components/transactions-daily.test.js +++ b/__tests__/components/transactions-daily.test.js @@ -16,7 +16,7 @@ describe('', () => { const { container } = render( ', () => { address: '123456789123456789123456789123456789', amount: 1.7891, zecPrice: 1.345, - date: new Date().toISOString(), + date: '2019-02-20T19:31:57.117Z', theme: appTheme, fees: 0.001, }, @@ -35,7 +35,7 @@ describe('', () => { address: '123456789123456789123456789123456789', amount: 0.8458, zecPrice: 1.344, - date: new Date().toISOString(), + date: '2019-02-20T19:31:57.117Z', theme: appTheme, fees: 0.001, }, diff --git a/__tests__/components/wallet-address.test.js b/__tests__/components/wallet-address.test.js index 9940fe9..c79003a 100644 --- a/__tests__/components/wallet-address.test.js +++ b/__tests__/components/wallet-address.test.js @@ -12,16 +12,14 @@ afterEach(cleanup); describe('', () => { test('should render wallet address component correctly', () => { - const { queryByTestId } = render( + const { getByText } = render(
- +
, ); - expect(queryByTestId('Address')).toBeInTheDocument(); + expect(getByText('t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1')).toBeInTheDocument(); }); }); diff --git a/__tests__/services/zec-price.test.js b/__tests__/services/zec-price.test.js new file mode 100644 index 0000000..3ce272f --- /dev/null +++ b/__tests__/services/zec-price.test.js @@ -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), + }); + }); +}); diff --git a/app/components/header.js b/app/components/header.js index 97b23ed..bfaa431 100644 --- a/app/components/header.js +++ b/app/components/header.js @@ -7,9 +7,7 @@ import { ZcashLogo } from './zcash-logo'; import { TextComponent } from './text'; import { Divider } from './divider'; import { RowComponent } from './row'; -import { StatusPill } from './status-pill'; - -import { withSyncStatus } from '../../services/sync-status'; +import { StatusPillContainer } from '../containers/status-pill'; const Wrapper = styled.div` height: ${props => props.theme.headerHeight}; @@ -62,8 +60,6 @@ type Props = { title: string, }; -const Status = withSyncStatus(StatusPill); - export const HeaderComponent = ({ title }: Props) => ( @@ -72,7 +68,7 @@ export const HeaderComponent = ({ title }: Props) => ( - <Status type='syncing' progress={0} /> + <StatusPillContainer /> </TitleRow> <Divider opacity={0.2} /> </TitleWrapper> diff --git a/app/components/sidebar.js b/app/components/sidebar.js index c756926..6b3d1a6 100644 --- a/app/components/sidebar.js +++ b/app/components/sidebar.js @@ -99,7 +99,7 @@ export const Component = ({ <Icon isActive={isActive} src={item.icon(isActive, theme.mode)} - Alt={`${item.route}`} + alt={`${item.route}`} /> {item.label} </StyledLink> diff --git a/app/components/status-pill.js b/app/components/status-pill.js index 33842cc..25c2d8d 100644 --- a/app/components/status-pill.js +++ b/app/components/status-pill.js @@ -2,11 +2,9 @@ import React, { PureComponent } from 'react'; import styled, { keyframes, withTheme } from 'styled-components'; -import eres from 'eres'; import { TextComponent } from './text'; -import rpc from '../../services/api'; import { DARK } from '../constants/themes'; 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 errorIconLight from '../assets/images/error_icon_light.png'; +import type { MapDispatchToProps, MapStateToProps } from '../containers/status-pill'; + const rotate = keyframes` from { transform: rotate(0deg); @@ -55,94 +55,85 @@ const StatusPillLabel = styled(TextComponent)` type Props = { theme: AppTheme, -}; +} & MapStateToProps & + MapDispatchToProps; -type State = { - type: string, - icon: string, - progress: number, - isSyncing: boolean, -}; +const MINUTE_IN_MILI = 60000; -class Component extends PureComponent<Props, State> { +class Component extends PureComponent<Props> { timer: ?IntervalID = null; - constructor(props: Props) { - super(props); + componentDidMount() { + const { getBlockchainStatus } = this.props; - const { theme } = props; - - const syncIcon = theme.mode === DARK - ? syncIconDark - : syncIconLight; - - this.state = { - type: 'syncing', - icon: syncIcon, - progress: 0, - isSyncing: true, - }; + this.timer = setInterval(() => getBlockchainStatus(), 2000); } - componentDidMount() { - this.timer = setInterval(() => { - this.getBlockchainStatus(); - }, 2000); + componentDidUpdate(prevProps: Props) { + const { getBlockchainStatus, nodeSyncType } = this.props; + if (prevProps.nodeSyncType === 'syncing' && nodeSyncType === 'ready') { + // 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() { + this.cleanUpdateInterval(); + } + + cleanUpdateInterval = () => { if (this.timer) { clearInterval(this.timer); this.timer = null; } - } + }; - getBlockchainStatus = async () => { + isSyncing = () => { + const { nodeSyncType } = this.props; + return nodeSyncType === 'syncing'; + }; + + getReadyIcon = () => { const { theme } = this.props; + return theme.mode === DARK ? readyIconDark : readyIconLight; + }; - const readyIcon = theme.mode === DARK - ? readyIconDark - : readyIconLight; - const errorIcon = theme.mode === DARK - ? errorIconDark - : errorIconLight; + getErrorIcon = () => { + const { theme } = this.props; + return 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; - - this.setState({ - progress: newProgress, - ...(newProgress > 99.99 - ? { - type: 'ready', - icon: readyIcon, - isSyncing: false, - } - : {}), - }); - - if (blockchainErr) { - this.setState(() => ({ - type: 'error', - icon: errorIcon, - })); + switch (nodeSyncType) { + case 'syncing': + return this.getSyncingIcon(); + case 'ready': + return this.getReadyIcon(); + case 'error': + return this.getErrorIcon(); + default: + return null; } }; render() { - const { - type, icon, progress, isSyncing, - } = this.state; - const showPercent = isSyncing ? `(${progress.toFixed(2)}%)` : ''; - const typeText = type === 'ready' ? 'Synced' : type; + const icon = this.getIcon(); + const { nodeSyncType, nodeSyncProgress } = this.props; + const percent = nodeSyncType === 'syncing' ? `(${nodeSyncProgress.toFixed(2)}%)` : ''; + const typeText = nodeSyncType === 'ready' ? 'Synced' : nodeSyncType; return ( <Wrapper id='status-pill'> - <Icon src={icon} animated={isSyncing} /> - <StatusPillLabel value={`${typeText} ${showPercent}`} /> + {icon && <Icon src={icon} animated={this.isSyncing()} />} + <StatusPillLabel value={`${typeText} ${percent}`} /> </Wrapper> ); } diff --git a/app/components/transaction-details.js b/app/components/transaction-details.js index 6915e5e..f8b95bf 100644 --- a/app/components/transaction-details.js +++ b/app/components/transaction-details.js @@ -204,13 +204,7 @@ const Component = ({ <InfoRow> <ColumnComponent width='100%'> <Label value='TRANSACTION ID' /> - <TransactionId - onClick={ - address !== '(Shielded)' - ? () => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId) - : () => {} - } - > + <TransactionId onClick={() => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)}> <Ellipsis value={transactionId} /> </TransactionId> </ColumnComponent> diff --git a/app/containers/app.js b/app/containers/app.js index dd6f147..28381d2 100644 --- a/app/containers/app.js +++ b/app/containers/app.js @@ -8,12 +8,21 @@ import { LayoutComponent } from '../components/layout'; import type { Dispatch } from '../types/redux'; 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, error: app.error, }); -const mapDispatchToProps = (dispatch: Dispatch) => ({ +export type MapDispatchToProps = {| + closeErrorModal: () => void, +|}; + +const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({ closeErrorModal: () => dispatch(closeErrorModal()), }); diff --git a/app/containers/dashboard.js b/app/containers/dashboard.js index 27c8c9f..dbfd5c3 100644 --- a/app/containers/dashboard.js +++ b/app/containers/dashboard.js @@ -50,10 +50,10 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ const formattedTransactions: Array<Object> = flow([ arr => arr.map(transaction => ({ - transactionId: transaction.txid || 'N/A', + transactionId: transaction.txid, type: transaction.category, date: new Date(transaction.time * 1000).toISOString(), - address: transaction.address, + address: transaction.address || '(Shielded)', amount: Math.abs(transaction.amount), fees: transaction.fee ? new BigNumber(transaction.fee).abs().toFormat(4) : 'N/A', })), diff --git a/app/containers/send.js b/app/containers/send.js index c5d246c..a6e8f86 100644 --- a/app/containers/send.js +++ b/app/containers/send.js @@ -114,6 +114,7 @@ const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({ clearInterval(interval); if (from.startsWith('z')) { saveShieldedTransaction({ + txid: operationStatus.result.txid, category: 'send', time: Date.now() / 1000, address: '(Shielded)', diff --git a/app/containers/status-pill.js b/app/containers/status-pill.js new file mode 100644 index 0000000..e98d50c --- /dev/null +++ b/app/containers/status-pill.js @@ -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); diff --git a/app/containers/transactions.js b/app/containers/transactions.js index 5fefe14..12755b8 100644 --- a/app/containers/transactions.js +++ b/app/containers/transactions.js @@ -3,7 +3,6 @@ import eres from 'eres'; import { connect } from 'react-redux'; import { BigNumber } from 'bignumber.js'; -import uuidv4 from 'uuid/v4'; import { TransactionsView } from '../views/transactions'; import { @@ -65,10 +64,10 @@ const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({ ...transactions, ...listShieldedTransactions({ count, offset: shieldedTransactionsCount }), ].map(transaction => ({ - transactionId: transaction.txid ? transaction.txid : uuidv4(), + transactionId: transaction.txid, type: transaction.category, date: new Date(transaction.time * 1000).toISOString(), - address: transaction.address, + address: transaction.address || '(Shielded)', amount: new BigNumber(transaction.amount).absoluteValue().toNumber(), fees: transaction.fee ? new BigNumber(transaction.fee).abs().toFormat(4) : 'N/A', })), diff --git a/app/redux/modules/app.js b/app/redux/modules/app.js index f8d8495..f704ef1 100644 --- a/app/redux/modules/app.js +++ b/app/redux/modules/app.js @@ -2,9 +2,17 @@ import type { Action } from '../../types/redux'; +export type State = {| + isErrorModalVisible: boolean, + error: string | null, + nodeSyncProgress: number, + nodeSyncType: 'ready' | 'syncing' | 'error', +|}; + // Actions export const SHOW_ERROR_MODAL = 'SHOW_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 }) => ({ type: SHOW_ERROR_MODAL, @@ -18,14 +26,25 @@ export const closeErrorModal = () => ({ payload: {}, }); -export type State = { - isErrorModalVisible: boolean, - error: string | null, -}; +export const updateNodeSyncStatus = ({ + nodeSyncProgress, + nodeSyncType, +}: { + nodeSyncProgress: number, + nodeSyncType: $PropertyType<State, 'nodeSyncType'>, +}) => ({ + type: UPDATE_NODE_SYNC_STATUS, + payload: { + nodeSyncProgress, + nodeSyncType, + }, +}); const initialState: State = { isErrorModalVisible: false, error: null, + nodeSyncProgress: 0, + nodeSyncType: 'syncing', }; // eslint-disable-next-line @@ -35,6 +54,12 @@ export default (state: State = initialState, action: Action) => { return { isErrorModalVisible: true, error: action.payload.error }; case HIDE_ERROR_MODAL: return { isErrorModalVisible: false, error: null }; + case UPDATE_NODE_SYNC_STATUS: + return { + ...state, + nodeSyncProgress: action.payload.nodeSyncProgress, + nodeSyncType: action.payload.nodeSyncType, + }; default: return state; } diff --git a/config/electron.js b/config/electron.js index 6dca7c2..550debe 100644 --- a/config/electron.js +++ b/config/electron.js @@ -65,9 +65,7 @@ const createWindow = () => { }, }); - getZecPrice().then((obj) => { - store.set('ZEC_DOLLAR_PRICE', String(obj.USD)); - }); + getZecPrice().then(({ USD }) => store.set('ZEC_DOLLAR_PRICE', String(USD))); mainWindow.setVisibleOnAllWorkspaces(true); registerDebugShortcut(app, mainWindow); diff --git a/package.json b/package.json index 5174fc5..89d9284 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "electron:prepare": "yarn icon:build && rm -rf dist && mkdir dist", "electron:pack": "yarn electron:prepare && electron-builder --dir", "electron:dist": "yarn electron:prepare && electron-builder", + "electron:clean-store": "electron -r @babel/register ./utils/clean-electron-store.js", "preelectron:prepare": "yarn build", "icon:build": "./node_modules/.bin/electron-icon-maker --input=build-assets/icon.png --output=./build", "docz:dev": "docz dev", @@ -25,6 +26,7 @@ "docz:deploy": "yarn docz:build && cd ./.docz/dist && now && now alias zec-docz.now.sh", "test": "jest --runInBand", "test:watch": "jest --watch", + "test:unit": "jest --testPathIgnorePatterns=e2e --testPathIgnorePatterns=setup", "e2e:serve": "node -r @babel/register ./__tests__/setup/mockAPI.js", "e2e:run": "yarn test e2e" }, diff --git a/public/flow-coverage-badge.svg b/public/flow-coverage-badge.svg index f5dabfe..b36be62 100644 --- a/public/flow-coverage-badge.svg +++ b/public/flow-coverage-badge.svg @@ -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> \ No newline at end of file +<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> \ No newline at end of file diff --git a/services/shielded-transactions.js b/services/shielded-transactions.js index f598b2e..1f57030 100644 --- a/services/shielded-transactions.js +++ b/services/shielded-transactions.js @@ -4,6 +4,7 @@ import electronStore from '../config/electron-store'; const STORE_KEY = 'SHIELDED_TRANSACTIONS'; type ShieldedTransaction = {| + txid: string, category: 'send' | 'receive', time: number, address: string, @@ -28,6 +29,7 @@ export const listShieldedTransactions = ( }; export const saveShieldedTransaction = ({ + txid, category, time, address, @@ -37,6 +39,7 @@ export const saveShieldedTransaction = ({ electronStore.set( STORE_KEY, listShieldedTransactions().concat({ + txid, category, time, address, diff --git a/services/sync-status.js b/services/sync-status.js deleted file mode 100644 index 435830f..0000000 --- a/services/sync-status.js +++ /dev/null @@ -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} />; - } - }; diff --git a/services/zec-price.js b/services/zec-price.js index 13815da..18983b1 100644 --- a/services/zec-price.js +++ b/services/zec-price.js @@ -1,8 +1,6 @@ // @flow -/* eslint-disable import/no-extraneous-dependencies */ -// $FlowFixMe -import { net } from 'electron'; +import got from 'got'; type Payload = { [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)}`; - const request = net.request(ENDPOINT); - request.on('response', (response) => { - let data = ''; - /* 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(); + got(ENDPOINT) + .then(response => resolve(JSON.parse(response.body))) + .catch(reject); }); diff --git a/utils/clean-electron-store.js b/utils/clean-electron-store.js new file mode 100644 index 0000000..21a72a2 --- /dev/null +++ b/utils/clean-electron-store.js @@ -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); +});