–fix(git): merge conflicts

This commit is contained in:
georgelima 2019-02-18 10:55:25 -03:00
commit eff884f0b4
14 changed files with 313 additions and 2348 deletions

View File

@ -30,6 +30,7 @@ describe('Transactions Actions', () => {
const payload = {
list: [],
zecPrice: 0,
hasNextPage: false,
};
store.dispatch(loadTransactionsSuccess(payload));

View File

@ -14,7 +14,7 @@ describe('<LoadingScreen />', () => {
test('should render status pill correctly', () => {
const { queryByTestId } = render(
<ThemeProvider theme={appTheme}>
<LoadingScreen progress={83.0} />
<LoadingScreen progress={83.0} message='ZEC Wallet Starting' />
</ThemeProvider>,
);

View File

@ -3,8 +3,6 @@
import React, { PureComponent } from 'react';
import styled, { withTheme } from 'styled-components';
import { Transition, animated } from 'react-spring';
// eslint-disable-next-line import/no-extraneous-dependencies
import { ipcRenderer } from 'electron';
import CircleProgressComponent from 'react-circle';
import { TextComponent } from './text';
@ -40,11 +38,11 @@ const Logo = styled.img`
type Props = {
progress: number,
theme: AppTheme,
message: string,
};
type State = {
start: boolean,
message: string,
};
const TIME_DELAY_ANIM = 100;
@ -56,19 +54,11 @@ export class Component extends PureComponent<Props, State> {
setTimeout(() => {
this.setState(() => ({ start: true }));
}, TIME_DELAY_ANIM);
ipcRenderer.on('zcashd-params-download', (event: Object, message: string) => {
this.setState(() => ({ message }));
});
}
componentWillUnmount() {
ipcRenderer.removeAllListeners('zcashd-log');
}
render() {
const { start, message } = this.state;
const { progress, theme } = this.props;
const { start } = this.state;
const { progress, message, theme } = this.props;
return (
<Wrapper data-testid='LoadingScreen'>

View File

@ -188,7 +188,13 @@ export const TransactionDetailsComponent = ({
<InfoRow>
<ColumnComponent width='100%'>
<Label value='TRANSACTION ID' />
<TransactionId onClick={() => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)}>
<TransactionId
onClick={
from !== '(Shielded)'
? () => openExternal(ZCASH_EXPLORER_BASE_URL + transactionId)
: () => {}
}
>
<Ellipsis value={transactionId} />
</TransactionId>
</ColumnComponent>

View File

@ -11,6 +11,7 @@ type Props = {};
type State = {
isRunning: boolean,
progress: number,
message: string,
};
/* eslint-disable max-len */
@ -22,6 +23,7 @@ export const withDaemonStatusCheck = <PassedProps: {}>(
state = {
isRunning: false,
progress: 0,
message: 'ZEC Wallet Starting',
};
componentDidMount() {
@ -53,21 +55,23 @@ export const withDaemonStatusCheck = <PassedProps: {}>(
}
}
})
.catch(() => {
.catch((error) => {
const statusMessage = error.message === 'Something went wrong' ? 'ZEC Wallet Starting' : error.message;
this.setState((state) => {
const newProgress = state.progress > 70 ? state.progress + 2.5 : state.progress + 5;
return { progress: newProgress > 95 ? 95 : newProgress };
return { progress: newProgress > 95 ? 95 : newProgress, message: statusMessage };
});
});
};
render() {
const { isRunning, progress } = this.state;
const { isRunning, progress, message } = this.state;
if (isRunning) {
return <WrappedComponent {...this.props} {...this.state} />;
}
return <LoadingScreen progress={progress} />;
return <LoadingScreen progress={progress} message={message} />;
}
};

View File

@ -19,6 +19,10 @@ export const withDeepLink = (
if (arg) this.redirect(arg);
remote.app.on('open-url', (event, url) => {
this.redirect(url);
});
ipcRenderer.on('on-deep-link', (event: Object, message: string) => {
this.redirect(message);
});

View File

@ -2,16 +2,15 @@
import eres from 'eres';
import { connect } from 'react-redux';
import flow from 'lodash.flow';
import groupBy from 'lodash.groupby';
import dateFns from 'date-fns';
import { BigNumber } from 'bignumber.js';
import uuidv4 from 'uuid/v4';
import { TransactionsView } from '../views/transactions';
import {
loadTransactions,
loadTransactionsSuccess,
loadTransactionsError,
resetTransactionsList,
} from '../redux/modules/transactions';
import rpc from '../../services/api';
import { listShieldedTransactions } from '../../services/shielded-transactions';
@ -21,45 +20,64 @@ import { sortByDescend } from '../utils/sort-by-descend';
import type { AppState } from '../types/app-state';
import type { Dispatch } from '../types/redux';
import type { Transaction } from '../components/transaction-item';
const mapStateToProps = ({ transactions }: AppState) => ({
transactions: transactions.list,
isLoading: transactions.isLoading,
error: transactions.error,
zecPrice: transactions.zecPrice,
hasNextPage: transactions.hasNextPage,
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
getTransactions: async () => {
export type MapStateToProps = {
transactions: Transaction[],
isLoading: boolean,
error: string | null,
zecPrice: number,
hasNextPage: boolean,
};
export type MapDispatchToProps = {|
getTransactions: ({
offset: number,
count: number,
shieldedTransactionsCount: number,
}) => Promise<void>,
resetTransactionsList: () => void,
|};
const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
resetTransactionsList: () => dispatch(resetTransactionsList()),
getTransactions: async ({ offset, count, shieldedTransactionsCount }) => {
dispatch(loadTransactions());
const [transactionsErr, transactions = []] = await eres(rpc.listtransactions('', 200));
const [transactionsErr, transactions = []] = await eres(
rpc.listtransactions('', count, offset),
);
if (transactionsErr) {
return dispatch(loadTransactionsError({ error: transactionsErr.message }));
}
const formattedTransactions = flow([
arr => arr.map(transaction => ({
transactionId: transaction.txid,
const formattedTransactions = sortByDescend('date')(
[
...transactions,
...listShieldedTransactions({ count, offset: shieldedTransactionsCount }),
].map(transaction => ({
transactionId: transaction.txid ? transaction.txid : uuidv4(),
type: transaction.category,
date: new Date(transaction.time * 1000).toISOString(),
address: transaction.address,
amount: new BigNumber(transaction.amount).absoluteValue().toNumber(),
})),
arr => groupBy(arr, obj => dateFns.format(obj.date, 'MMM DD, YYYY')),
obj => Object.keys(obj).map(day => ({
day,
jsDay: new Date(day),
list: sortByDescend('date')(obj[day]),
})),
sortByDescend('jsDay'),
])([...transactions, ...listShieldedTransactions()]);
);
dispatch(
loadTransactionsSuccess({
list: formattedTransactions,
zecPrice: new BigNumber(store.get('ZEC_DOLLAR_PRICE')).toNumber(),
hasNextPage: Boolean(formattedTransactions.length),
}),
);
},

View File

@ -1,5 +1,5 @@
// @flow
import uniqBy from 'lodash.uniqby';
import type { Action } from '../../types/redux';
import type { Transaction } from '../../components/transaction-item';
@ -7,6 +7,7 @@ import type { Transaction } from '../../components/transaction-item';
export const LOAD_TRANSACTIONS = 'LOAD_TRANSACTIONS';
export const LOAD_TRANSACTIONS_SUCCESS = 'LOAD_TRANSACTIONS_SUCCESS';
export const LOAD_TRANSACTIONS_ERROR = 'LOAD_TRANSACTIONS_ERROR';
export const RESET_TRANSACTIONS_LIST = 'RESET_TRANSACTIONS_LIST';
export type TransactionsList = { day: string, list: Transaction[] }[];
@ -18,14 +19,17 @@ export const loadTransactions = () => ({
export const loadTransactionsSuccess = ({
list,
zecPrice,
hasNextPage,
}: {
list: TransactionsList,
list: Transaction[],
zecPrice: number,
hasNextPage: boolean,
}) => ({
type: LOAD_TRANSACTIONS_SUCCESS,
payload: {
list,
zecPrice,
hasNextPage,
},
});
@ -34,11 +38,17 @@ export const loadTransactionsError = ({ error }: { error: string }) => ({
payload: { error },
});
export const resetTransactionsList = () => ({
type: RESET_TRANSACTIONS_LIST,
payload: {},
});
export type State = {
isLoading: boolean,
error: string | null,
list: TransactionsList,
list: Transaction[],
zecPrice: number,
hasNextPage: boolean,
};
const initialState = {
@ -46,6 +56,7 @@ const initialState = {
list: [],
error: null,
isLoading: false,
hasNextPage: true,
};
// eslint-disable-next-line
@ -61,6 +72,7 @@ export default (state: State = initialState, action: Action) => {
return {
...state,
...action.payload,
list: uniqBy(state.list.concat(action.payload.list), tr => tr.transactionId + tr.type),
isLoading: false,
error: null,
};
@ -70,6 +82,13 @@ export default (state: State = initialState, action: Action) => {
isLoading: false,
error: action.payload.error,
};
case RESET_TRANSACTIONS_LIST:
return {
...state,
isLoading: false,
error: null,
list: [],
};
default:
return state;
}

View File

@ -1,48 +1,198 @@
// @flow
import React, { PureComponent, Fragment } from 'react';
import React, { PureComponent, Fragment, type Element } from 'react';
import { InfiniteLoader, AutoSizer, List } from 'react-virtualized';
import styled from 'styled-components';
import dateFns from 'date-fns';
import { TransactionDailyComponent } from '../components/transaction-daily';
import { TransactionItemComponent } from '../components/transaction-item';
import { TextComponent } from '../components/text';
import { EmptyTransactionsComponent } from '../components/empty-transactions';
import type { TransactionsList } from '../redux/modules/transactions';
import type { MapDispatchToProps, MapStateToProps } from '../containers/transactions';
type Props = {
error: string | null,
transactions: TransactionsList,
zecPrice: number,
getTransactions: () => void,
};
type Props = MapDispatchToProps & MapStateToProps;
const PAGE_SIZE = 15;
const ROW_HEIGHT = 60;
const ROW_HEIGHT_WITH_HEADER = 88;
const Day = styled(TextComponent)`
text-transform: uppercase;
color: ${props => props.theme.colors.transactionsDate};
font-size: ${props => `${props.theme.fontSize.regular * 0.9}em`};
font-weight: ${props => String(props.theme.fontWeight.bold)};
margin-top: 10px;
margin-bottom: 5px;
`;
const RoundedTransactionWrapper = styled.div`
overflow: hidden;
${props => (props.roundPosition === 'top'
? `
border-top-left-radius: ${props.theme.boxBorderRadius};
border-top-right-radius: ${props.theme.boxBorderRadius};`
: `border-bottom-left-radius: ${props.theme.boxBorderRadius};
border-bottom-right-radius: ${props.theme.boxBorderRadius};`)}
`;
export class TransactionsView extends PureComponent<Props> {
componentDidMount() {
// eslint-disable-next-line
this.props.getTransactions();
const { getTransactions, resetTransactionsList } = this.props;
resetTransactionsList();
getTransactions({ count: PAGE_SIZE, offset: 0, shieldedTransactionsCount: 0 });
}
isRowLoaded = ({ index }: { index: number }) => {
const { hasNextPage, transactions } = this.props;
const transactionsSize = transactions.length;
return !hasNextPage || index < transactionsSize;
};
renderTransactionWrapper = ({
index,
transactionDate,
previousTransactionDate,
nextTransactionDate,
component,
}: {|
index: number,
transactionDate: Date,
previousTransactionDate: ?Date,
nextTransactionDate: ?Date,
component: Element<*>,
|}) => {
if (
index === 0
|| (previousTransactionDate && !dateFns.isSameDay(transactionDate, previousTransactionDate))
) {
return <RoundedTransactionWrapper roundPosition='top'>{component}</RoundedTransactionWrapper>;
}
if (
nextTransactionDate
&& (nextTransactionDate && !dateFns.isSameDay(transactionDate, nextTransactionDate))
) {
return (
<RoundedTransactionWrapper roundPosition='bottom'>{component}</RoundedTransactionWrapper>
);
}
return component;
};
renderTransactions = ({ index }: { index: number }) => {
const { transactions, zecPrice } = this.props;
const transaction = transactions[index];
const previousTransaction = transactions[index - 1];
const nextTransaction = transactions[index + 1];
const transactionItem = this.renderTransactionWrapper({
transactionDate: new Date(transaction.date),
previousTransactionDate: previousTransaction ? new Date(previousTransaction.date) : null,
nextTransactionDate: nextTransaction ? new Date(nextTransaction.date) : null,
component: (
<TransactionItemComponent
address={transaction.address}
amount={transaction.amount}
date={transaction.date}
transactionId={transaction.transactionId}
type={transaction.type}
zecPrice={zecPrice}
/>
),
index,
});
if (
index === 0
|| (previousTransaction
&& !dateFns.isSameDay(new Date(previousTransaction.date), new Date(transaction.date)))
) {
return (
<Fragment>
<Day value={dateFns.format(new Date(transaction.date), 'MMM DD, YYYY')} />
{transactionItem}
</Fragment>
);
}
return transactionItem;
};
renderRow = ({ index, key, style }: { index: number, key: string, style: Object }) => (
<div key={key} style={style}>
{this.isRowLoaded({ index }) ? this.renderTransactions({ index }) : 'Loading...'}
</div>
);
getRowHeight = ({ index }: { index: number }) => {
const { transactions } = this.props;
const transaction = transactions[index];
if (
index === 0
|| !dateFns.isSameDay(new Date(transactions[index - 1].date), new Date(transaction.date))
) {
return ROW_HEIGHT_WITH_HEADER;
}
return ROW_HEIGHT;
};
loadNextPage = () => {
const { transactions, getTransactions } = this.props;
const shieldedTransactionsCount = transactions.filter(
transaction => transaction.address === '(Shielded)',
).length;
getTransactions({ count: PAGE_SIZE, offset: transactions.length, shieldedTransactionsCount });
};
loadMoreRows = async () => {
const { isLoading } = this.props;
return isLoading ? Promise.resolve([]) : this.loadNextPage();
};
render() {
const { error, transactions, zecPrice } = this.props;
const { error, transactions, hasNextPage } = this.props;
const transactionsSize = transactions.length;
const isRowLoaded = ({ index }) => !hasNextPage || index < transactionsSize;
const rowCount = transactionsSize ? transactionsSize + 1 : transactionsSize;
if (error) {
return <TextComponent value={error} />;
}
return (
<Fragment>
{transactions.length === 0 ? (
<EmptyTransactionsComponent />
) : (
transactions.map(({ day, list }) => (
<TransactionDailyComponent
transactionsDate={day}
transactions={list}
zecPrice={zecPrice}
key={day}
/>
))
<InfiniteLoader
isRowLoaded={isRowLoaded}
loadMoreRows={this.loadMoreRows}
rowCount={rowCount}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ width, height }) => (
<List
noRowsRenderer={EmptyTransactionsComponent}
ref={registerChild}
onRowsRendered={onRowsRendered}
rowRenderer={this.renderRow}
rowHeight={this.getRowHeight}
rowCount={transactionsSize}
width={width}
height={height - 20}
/>
)}
</AutoSizer>
)}
</Fragment>
</InfiniteLoader>
);
}
}

View File

@ -2,6 +2,17 @@
set -eu
# Create default zcash.conf
if [[ "$OSTYPE" == "darwin"* ]]; then
if [ ! -e "$HOME/Library/Application Support/Zcash/zcash.conf" ] ; then
echo "server=1" > "$HOME/Library/Application Support/Zcash/zcash.conf"
fi
else
if [ ! -e "$HOME/.zcash/zcash.conf" ] ; then
echo "server=1" > "$HOME/.zcash/zcash.conf"
fi
fi
if [[ "$OSTYPE" == "darwin"* ]]; then
PARAMS_DIR="$HOME/Library/Application Support/ZcashParams"
else

View File

@ -0,0 +1,3 @@
declare module 'lodash.uniqby' {
declare module.exports: <T>(arr: T[], (T) => $Values<T>) => T[];
}

View File

@ -5,7 +5,7 @@
"main": "config/main.js",
"license": "MIT",
"scripts": {
"start": "concurrently \"cross-env BROWSER=none yarn dev\" \"wait-on http://0.0.0.0:8080 && yarn electron:dev\"",
"start": "yarn check --integrity && concurrently \"cross-env BROWSER=none yarn dev\" \"wait-on http://0.0.0.0:8080 && yarn electron:dev\"",
"dev": "webpack-dev-server --config config/webpack-dev.config.js --mode development --open --hot",
"build": "rm -rf build && webpack --config config/webpack-prod.config.js --mode production --env.NODE_ENV=production",
"lint:precommit": "eslint ./app/",
@ -108,6 +108,7 @@
"history": "^4.7.2",
"lodash.flow": "^3.5.0",
"lodash.groupby": "^4.6.0",
"lodash.uniqby": "^4.7.0",
"p-queue": "^3.0.0",
"process-exists": "^3.1.0",
"qrcode.react": "^0.8.0",
@ -119,6 +120,7 @@
"react-redux": "^5.0.7",
"react-router-dom": "^4.2.2",
"react-spring": "^7.2.10",
"react-virtualized": "^9.21.0",
"redux": "^4.0.1",
"redux-thunk": "^2.2.0",
"styled-components": "^4.1.1",

View File

@ -12,7 +12,20 @@ type ShieldedTransaction = {|
|};
// eslint-disable-next-line
export const listShieldedTransactions = (): Array<ShieldedTransaction> => electronStore.has(STORE_KEY) ? electronStore.get(STORE_KEY) : [];
export const listShieldedTransactions = (
pagination: ?{
offset: number,
count: number,
},
): Array<ShieldedTransaction> => {
const transactions = electronStore.has(STORE_KEY) ? electronStore.get(STORE_KEY) : [];
if (!pagination) return transactions;
const { offset = 0, count = 10 } = pagination;
return transactions.slice(offset - 1, offset + count);
};
export const saveShieldedTransaction = ({
category,

2308
yarn.lock

File diff suppressed because it is too large Load Diff