diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..f3f37c4e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,20 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue with the owners and core contributors of this repository. Please note we have a [Code of Conduct](https://github.com/ZcashFoundation/zepio/blob/master/CODE_OF_CONDUCT.md) - please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +Learn more about this project's [development workflow](https://github.com/ZcashFoundation/zepio/blob/master/DEVELOPMENT_WORKFLOW.md). + +## Code of Conduct + +For information on this project's Code of Conduct details, please see [this file](https://github.com/ZcashFoundation/zepio/blob/master/CODE_OF_CONDUCT.md). \ No newline at end of file diff --git a/DEVELOPMENT_WORKFLOW.md b/DEVELOPMENT_WORKFLOW.md index a7d4a0b7..2cd1adfb 100644 --- a/DEVELOPMENT_WORKFLOW.md +++ b/DEVELOPMENT_WORKFLOW.md @@ -16,7 +16,7 @@ The following lists the branching semantics to follow: ## Commit Messages -Commit messages should have semantic meanings to make it more easily possible to comb through commits. The following are commit types to follow. +Commit messages should have semantic meanings, and explain the 'why' of the change performed, to make it more easily possible to comb through commits. The following are commit types to follow. * **feature**: New feature * **hotfix**: Bug fixes @@ -45,6 +45,6 @@ docs: commit messages doc ### Title -The title of the PR should have the type shown (feature, hotfix, chore, etc..) as well as a brief description of the PR's purpose. Example +The title of the PR should have the type shown (feature, hotfix, chore, etc..) as well as a brief description of the PR's purpose. Example: `[feature] Add Infinite Scroll` diff --git a/LICENSE.md b/LICENSE.md index 423d8b5c..13b030e4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Zcash Foundation +Copyright (c) 2019 Zcash Foundation zfnd.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 389d4f10..28d8dd1b 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,125 @@ # Zepio | ZEC Wallet -Zepio is a Sapling-enabled shielded-address first Zcash wallet, featuring cross-platform applications (macOS, Windows and Linux), built-in full node with support for `mainnet` and `testnet`, as well as `dark` and `light` themes. +Zepio is a Sapling-enabled shielded-address-first Zcash wallet, featuring cross-platform applications (macOS, Windows and Linux), built-in full node with support for `mainnet` and `testnet`, as well as `dark` and `light` themes. +![Build Status](https://app.bitrise.io/app/a5bc7a8391d5501b/status.svg?token=SOuGNc3Qf9rCj3Osl-eHyQ&branch=master) ![Flow Coverage](./public/flow-coverage-badge.svg) -### [Latest Documentation at https://zepiowallet.com](https://zepiowallet.com) +### [Latest Documentation](https://zepiowallet.com) +### [Latest Release](https://github.com/ZcashFoundation/zepio/releases) + +![Zepio Wallet](https://raw.githubusercontent.com/ZcashFoundation/zepio-docs/develop/docz/assets/dashboard.png) ## Stack Information -- [Electron](https://github.com/electron/electron): desktop application builder +List of the main open source libraries and technologies used in building **Zepio**: -- [React](https://facebook.github.io/react/): UI view layer +- [zcashd](https://github.com/zcash/zcash): Zcash node daemon +- [Electron](https://github.com/electron/electron): Desktop application builder +- [React](https://facebook.github.io/react/): User interface view layer +- [Redux](http://redux.js.org/): Predictable application state container +- [Styled Components](https://www.styled-components.com/): Visual primitives for theming and styling applications +- [webpack](http://webpack.github.io/): Application module bundler (and more) +- [Babel](http://babeljs.io/): ES7/JSX transpilling +- [ESLint](http://eslint.org/): Code linting rules +- [Flow](https://flow.org): JavaScript static type checker +- [Docz](https://docz.site): Documentation builder -- [Redux](http://redux.js.org/): predictable state container +## Installing and Running From Source -- [Webpack](http://webpack.github.io/): module bundler +To run **Zepio** from source you'll need to perform the following steps: +```bash +# Ensure you have Node LTS v8+ +# https://nodejs.org/en/ -- [Webpack Development Server](https://webpack.github.io/docs/webpack-dev-server.html): development server +# Clone Codebase +git clone git@github.com:ZcashFoundation/zepio.git -- [Babel](http://babeljs.io/): ES7/JSX transpilling +# Install Dependencies +# inside of the `zepio` folder +yarn install +# or +npm install -- [ESLint](http://eslint.org/): code rules and linting +# Start Application +# webpack development server hosts the application on port +# 8080 and launches the Electron wrapper, which also hosts +# the `zcashd` node daemon process. +yarn start +# or +npm start +``` -- [React Router](https://github.com/reactjs/react-router): routing solution for react +## Building Application Locally -- [Styled Components](https://www.styled-components.com/): visual primitives for theming applications +To build the application locally follow the instructions below: +```bash +# Make sure you are inside of the main `zepio` folder + +# Run Build Script +yarn electron:distall + +# Executables and binaries available under `/dist` folder +``` -## Installation +## Flow Coverage (Static Type Checker) +For a deeper look on the static typing coverage of the application, please follow below: ```bash +# Make sure you are inside of the main `zepio` folder -yarn install +# Generate Flow Coverage Report +# this can take a couple seconds +yarn flow:report +# Browser should open with the file `index.html` opened +# Files are also available at `zepio/flow-coverage/source` ``` -## Development +## Component Library (Docz) -To run the application you simply need to run +To see Zepio's React component library, please visit https://zepio-components.now.sh. We're always looking for folks to help keep the styleguide updated. +To run the component library locally, run the following: ```bash +# Make sure you are inside of the main `zepio` folder -yarn start +# Run Docz Development Script +yarn docz:dev +# Visit http://127.0.0.1:4000/ ``` -This will kickstart the webpack development server and serve the app on port 8080, as well as launch the Electron wrapper for the application, which houses the `zcashd` daemon process. +To build the component library locally, run the following: +```bash +# Make sure you are inside of the main `zepio` folder + +# Run Build Script +yarn docz:build + +# Check `/.docz/dist` folder for built static assets +``` + +## Tests + +To run the application's tests, please run the below: +```bash +# Make sure you are inside of the main `zepio` folder + +# For Unit Tests: Run Jest Unit Test Suite +yarn test:unit + +# For E2E (end-to-end) Tests: Run Jest E2E Suite +yarn e2e:serve +# on another terminal window +yarn test e2e +``` + +## Contributing + +In order to contribute and submit PRs to improve the **Zepio** codebase, please check our [CONTRIBUTING](https://github.com/ZcashFoundation/zepio/blob/master/CONTRIBUTING.md) guide. ## License -MIT © Zcash Foundation 2019 +MIT © Zcash Foundation 2019 zfnd.org diff --git a/__tests__/actions/wallet-summary.test.js b/__tests__/actions/wallet-summary.test.js index 724c171b..f5552b77 100644 --- a/__tests__/actions/wallet-summary.test.js +++ b/__tests__/actions/wallet-summary.test.js @@ -31,6 +31,7 @@ describe('WalletSummary Actions', () => { total: 5000, transparent: 5000, shielded: 5000, + unconfirmed: 100, addresses: [], transactions: [], zecPrice: 50, diff --git a/__tests__/components/__snapshots__/transaction-item.test.js.snap b/__tests__/components/__snapshots__/transaction-item.test.js.snap index c2cb98ad..f6e8b94f 100644 --- a/__tests__/components/__snapshots__/transaction-item.test.js.snap +++ b/__tests__/components/__snapshots__/transaction-item.test.js.snap @@ -10,7 +10,7 @@ exports[` should render a transaction item correctly 1`] = ` class="sc-bdVaJa eFCRqo" >
Transaction Type Icon render() should render user daily transactions 1`] = `

2019-02-20T19:31:57.117Z

render() should render user daily transac class="sc-bdVaJa eFCRqo" >
Transaction Type Icon render() should render user daily transac class="sc-bdVaJa eFCRqo" >
Transaction Type Icon', () => { const { container } = render( , ); diff --git a/__tests__/components/transactions-daily.test.js b/__tests__/components/transactions-daily.test.js index 6efd8310..91813112 100644 --- a/__tests__/components/transactions-daily.test.js +++ b/__tests__/components/transactions-daily.test.js @@ -49,6 +49,8 @@ describe('', () => { date: '2019-02-20T19:31:57.117Z', theme: appTheme, fees: 0.001, + confirmations: 10, + confirmed: true, }, { type: 'send', @@ -59,6 +61,8 @@ describe('', () => { date: '2019-02-20T19:31:57.117Z', theme: appTheme, fees: 0.001, + confirmed: false, + confirmations: 3, }, ]} /> diff --git a/__tests__/reducers/wallet-summary.test.js b/__tests__/reducers/wallet-summary.test.js index 3c333926..edf98abf 100644 --- a/__tests__/reducers/wallet-summary.test.js +++ b/__tests__/reducers/wallet-summary.test.js @@ -14,6 +14,7 @@ describe('WalletSummary Reducer', () => { total: 0, shielded: 0, transparent: 0, + unconfirmed: 0, error: null, isLoading: false, zecPrice: 0, @@ -37,6 +38,7 @@ describe('WalletSummary Reducer', () => { total: 0, shielded: 0, transparent: 0, + unconfirmed: 0, error: null, isLoading: true, zecPrice: 0, @@ -52,6 +54,7 @@ describe('WalletSummary Reducer', () => { total: 1000, transparent: 1000, shielded: 1000, + unconfirmed: 0, }, }; const expectedState = { @@ -77,6 +80,7 @@ describe('WalletSummary Reducer', () => { total: 0, shielded: 0, transparent: 0, + unconfirmed: 0, error: action.payload.error, isLoading: false, addresses: [], diff --git a/__tests__/setup/mockAPI.js b/__tests__/setup/mockAPI.js index ff725339..57288035 100644 --- a/__tests__/setup/mockAPI.js +++ b/__tests__/setup/mockAPI.js @@ -105,6 +105,10 @@ const handler = (server) => { return res.send({ result: 't1VpYecBW4UudbGcy4ufh61eWxQCoFaUrPs', }); + case 'getunconfirmedbalance': + return res.send({ + result: 10, + }); default: return null; } diff --git a/app/assets/images/shield_dark.png b/app/assets/images/shield_dark.png new file mode 100644 index 00000000..9bd42228 Binary files /dev/null and b/app/assets/images/shield_dark.png differ diff --git a/app/assets/images/shield_dark_gray.png b/app/assets/images/shield_dark_gray.png new file mode 100644 index 00000000..90f74160 Binary files /dev/null and b/app/assets/images/shield_dark_gray.png differ diff --git a/app/assets/images/shield_light.png b/app/assets/images/shield_light.png new file mode 100644 index 00000000..fb85721e Binary files /dev/null and b/app/assets/images/shield_light.png differ diff --git a/app/assets/images/unconfirmed_dark.svg b/app/assets/images/unconfirmed_dark.svg new file mode 100644 index 00000000..bbc83833 --- /dev/null +++ b/app/assets/images/unconfirmed_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/unconfirmed_light.svg b/app/assets/images/unconfirmed_light.svg new file mode 100644 index 00000000..e88a1728 --- /dev/null +++ b/app/assets/images/unconfirmed_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/components/a-index.mdx b/app/components/a-index.mdx index 2395b8d3..0feb1f28 100644 --- a/app/components/a-index.mdx +++ b/app/components/a-index.mdx @@ -3,6 +3,8 @@ name: Home route: / --- -# UI Components +# Zepio Wallet -Use the sidebar menu to browse the styleguide of React components available for the Zepio application. +Use the sidebar menu to browse the component library of React components available for the Zepio application. + +> This is not an exhaustive list, and we're always looking for folks to help keep it updated. diff --git a/app/components/button.mdx b/app/components/button.mdx index 357115a6..86fc44de 100644 --- a/app/components/button.mdx +++ b/app/components/button.mdx @@ -5,13 +5,17 @@ name: Button import { Playground, PropsTable } from 'docz' import { Button } from './button.js' -import { DoczWrapper } from '../theme.js' +import { DoczWrapper } from '../theme/docz.js' # Button +## Properties + -## Primary +## Variations + +### Primary
@@ -26,7 +30,7 @@ import { DoczWrapper } from '../theme.js'
-## Secondary +### Secondary
@@ -42,7 +46,7 @@ import { DoczWrapper } from '../theme.js'
-## Primary Disabled +### Primary Disabled
@@ -57,7 +61,7 @@ import { DoczWrapper } from '../theme.js'
-## Secondary Disabled +### Secondary Disabled
@@ -73,7 +77,7 @@ import { DoczWrapper } from '../theme.js'
-## Link Button +### Link Button
diff --git a/app/components/confirm-dialog.mdx b/app/components/confirm-dialog.mdx index 43a2851c..41e8913d 100644 --- a/app/components/confirm-dialog.mdx +++ b/app/components/confirm-dialog.mdx @@ -6,7 +6,7 @@ import { Playground, PropsTable } from 'docz' import { Button } from './button.js' import { ConfirmDialogComponent } from './confirm-dialog.js' -import { DoczWrapper } from '../theme.js' +import { DoczWrapper } from '../theme/docz.js' # Confirm Dialog diff --git a/app/components/dropdown.mdx b/app/components/dropdown.mdx index 2f179a47..f1a4533c 100644 --- a/app/components/dropdown.mdx +++ b/app/components/dropdown.mdx @@ -6,7 +6,7 @@ import { Playground, PropsTable } from 'docz' import { Button } from './button.js' import { DropdownComponent } from './dropdown.js' -import { DoczWrapper } from '../theme.js' +import { DoczWrapper } from '../theme/docz.js' # Dropdown diff --git a/app/components/input.mdx b/app/components/input.mdx index 24a890f7..5083029b 100644 --- a/app/components/input.mdx +++ b/app/components/input.mdx @@ -5,7 +5,7 @@ name: Input import { Playground, PropsTable } from 'docz' import { InputComponent } from './input.js' -import { DoczWrapper } from '../theme.js' +import { DoczWrapper } from '../theme/docz.js' # Input diff --git a/app/components/layout.js b/app/components/layout.js index 4ef57c3e..8f05f767 100644 --- a/app/components/layout.js +++ b/app/components/layout.js @@ -14,6 +14,7 @@ const Layout = styled.div` padding-left: ${props => props.theme.layoutPaddingLeft}; padding-right: ${props => props.theme.layoutPaddingRight}; overflow: auto; + padding-bottom: 20px; ::-webkit-scrollbar { display: none; diff --git a/app/components/sidebar.mdx b/app/components/sidebar.mdx deleted file mode 100644 index 1fb0846a..00000000 --- a/app/components/sidebar.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -name: Sidebar ---- - -import { Playground, PropsTable } from 'docz' - -import { SidebarComponent } from './sidebar.js' -import { DoczWrapper } from '../theme.js' - -# Sidebar - -## General - -Sidebar component does not behave correctly inside Docz Styleguide as it is tighly coupled with the router. - -## Properties - - - -## Basic Usage - - - - {() => } - - diff --git a/app/components/status-pill.mdx b/app/components/status-pill.mdx index 02e1fc5a..3bb06eb7 100644 --- a/app/components/status-pill.mdx +++ b/app/components/status-pill.mdx @@ -5,7 +5,7 @@ name: StatusPill import { Playground, PropsTable } from 'docz' import { StatusPill } from './status-pill.js' -import { DoczWrapper } from '../theme.js' +import { DoczWrapper } from '../theme/docz.js' # StatusPill diff --git a/app/components/transaction-daily.js b/app/components/transaction-daily.js index b063351f..fdfb75df 100644 --- a/app/components/transaction-daily.js +++ b/app/components/transaction-daily.js @@ -17,7 +17,6 @@ const TransactionsWrapper = styled.div` padding: 0; margin: 0; box-sizing: border-box; - margin-bottom: 20px; `; const Day = styled(TextComponent)` @@ -38,21 +37,24 @@ export const TransactionDailyComponent = ({ transactionsDate, transactions, zecP - {transactions.map(({ - date, type, address, amount, transactionId, fees, - }) => ( - - - - ))} + {transactions.map( + ({ + date, type, address, amount, transactionId, confirmed, confirmations, + }) => ( + + + + ), + )} ); diff --git a/app/components/transaction-daily.mdx b/app/components/transaction-daily.mdx deleted file mode 100644 index c2660c16..00000000 --- a/app/components/transaction-daily.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: Transaction Daily ---- - -import { Playground, PropsTable } from 'docz' - -import { TransactionDailyComponent } from './transaction-daily.js' -import { DoczWrapper } from '../theme.js' - -# Transaction Item - -## Properties - - - -## Basic Usage - - - - {() => ( - - )} - - diff --git a/app/components/transaction-details.js b/app/components/transaction-details.js index cb020b3e..5d7467b4 100644 --- a/app/components/transaction-details.js +++ b/app/components/transaction-details.js @@ -58,6 +58,10 @@ const CloseIconImg = styled.img` margin-top: 12px; margin-right: 12px; cursor: pointer; + + &:hover { + filter: brightness(150%); + } `; const InfoRow = styled(RowComponent)` @@ -126,9 +130,10 @@ type Props = { date: string, transactionId: string, address: string, - fees: number | string, handleClose: () => void, theme: AppTheme, + confirmed: boolean, + confirmations: number, }; const Component = ({ @@ -138,15 +143,21 @@ const Component = ({ date, transactionId, address, - fees, handleClose, theme, + confirmed, + confirmations, }: Props) => { const isReceived = type === 'receive'; const receivedIcon = theme.mode === DARK ? ReceivedIconDark : ReceivedIconLight; const sentIcon = theme.mode === DARK ? SentIconDark : SentIconLight; const coinName = getCoinName(); + + let confirmationValue = 'Unconfirmed'; + if (confirmations >= 3) confirmationValue = confirmations; + if (confirmed) confirmationValue = confirmations; + return ( @@ -186,20 +197,11 @@ const Component = ({ - + diff --git a/app/components/transaction-details.mdx b/app/components/transaction-details.mdx deleted file mode 100644 index 4ea000ba..00000000 --- a/app/components/transaction-details.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Transaction Details ---- - -import { Playground, PropsTable } from 'docz' - -import { TransactionDetailsComponent } from './transaction-details.js' -import { DoczWrapper } from '../theme.js' - -# Transaction Details - -## Properties - - - -## Basic Usage - - - - {() => ( - - )} - - diff --git a/app/components/transaction-item.js b/app/components/transaction-item.js index 851a3191..c1ddd674 100644 --- a/app/components/transaction-item.js +++ b/app/components/transaction-item.js @@ -10,6 +10,8 @@ import SentIconDark from '../assets/images/transaction_sent_icon_dark.svg'; import ReceivedIconDark from '../assets/images/transaction_received_icon_dark.svg'; import SentIconLight from '../assets/images/transaction_sent_icon_light.svg'; import ReceivedIconLight from '../assets/images/transaction_received_icon_light.svg'; +import UnconfirmedLight from '../assets/images/unconfirmed_light.svg'; +import UnconfirmedDark from '../assets/images/unconfirmed_dark.svg'; import { RowComponent } from './row'; import { ColumnComponent } from './column'; @@ -70,23 +72,44 @@ const TransactionColumn = styled(ColumnComponent)` min-width: 60px; `; +const UnconfirmedStatusWrapper = styled.div` + right: 30px; + position: absolute; +`; + +const UnconfirmedStatus = styled.img` + width: 20px; + height: 20px; + opacity: 0.6; + + ${String(Wrapper)}:hover & { + opacity: 1; + } +`; + +const RelativeRowComponent = styled(RowComponent)` + position: relative; +`; + export type Transaction = { + confirmed: boolean, + confirmations: number, type: 'send' | 'receive', date: string, address: string, amount: number, - fees: number | string, zecPrice: number, transactionId: string, theme: AppTheme, }; const Component = ({ + confirmed, + confirmations, type, date, address, amount, - fees, zecPrice, transactionId, theme, @@ -106,6 +129,11 @@ const Component = ({ const receivedIcon = theme.mode === DARK ? ReceivedIconDark : ReceivedIconLight; const sentIcon = theme.mode === DARK ? SentIconDark : SentIconLight; + const unconfirmedIcon = theme.mode === DARK ? UnconfirmedLight : UnconfirmedDark; + + // TODO: style the tooltip correctly (overlay issue) + // const showUnconfirmed = !confirmed || confirmations < 1 || address === '(Shielded)'; + const showUnconfirmed = false; return ( - + - + {showUnconfirmed && ( + + + + )} + @@ -142,7 +175,8 @@ const Component = ({ amount={amount} date={date} address={address} - fees={fees} + confirmed={confirmed} + confirmations={confirmations} transactionId={transactionId} handleClose={toggleVisibility} type={type} diff --git a/app/components/transaction-item.mdx b/app/components/transaction-item.mdx deleted file mode 100644 index a07e270f..00000000 --- a/app/components/transaction-item.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: Transaction Item ---- - -import { Playground, PropsTable } from 'docz' - -import { TransactionItemComponent } from './transaction-item.js' -import { DoczWrapper } from '../theme.js' - -# Transaction Item - -## Properties - - - -## Sent - - - - {() => ( - - )} - - - -## Received - - - - {() => ( - - )} - - diff --git a/app/components/wallet-address.js b/app/components/wallet-address.js index 2d316b1f..698beb55 100644 --- a/app/components/wallet-address.js +++ b/app/components/wallet-address.js @@ -87,7 +87,7 @@ const QRCodeContainer = styled.div` background-color: ${props => props.theme.colors.qrCodeWrapperBg} border: 1px solid ${props => props.theme.colors.qrCodeWrapperBorder} border-radius: ${props => props.theme.boxBorderRadius}; - padding: 20px; + padding: 20px 20px 20px 10px; margin-bottom: 10px; width: 100%; `; @@ -164,8 +164,9 @@ const AddressDetailsLabel = styled.div` const AddressDetailsWrapper = styled.div` display: flex; + flex: 1; flex-direction: column; - padding: 10px 20px 0 20px; + padding: 15px 20px 0 20px; `; const FormButton = styled(Button)` @@ -179,7 +180,7 @@ const FormButton = styled(Button)` const Column = styled.div` display: flex; - padding-bottom: 10px; + padding-bottom: 15px; flex-direction: column; justify-content: flex-start; align-items: flex-start; diff --git a/app/components/wallet-address.mdx b/app/components/wallet-address.mdx deleted file mode 100644 index c7a7c84d..00000000 --- a/app/components/wallet-address.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Wallet Address ---- - -import { Playground, PropsTable } from 'docz' - -import { WalletAddress } from './wallet-address.js' -import { DoczWrapper } from '../theme.js' - -# Wallet Address - -## Properties - - - -## Basic usage - - - - {() => ( -
- -
- )} -
-
diff --git a/app/components/wallet-summary.js b/app/components/wallet-summary.js index 0144b2ca..1556d0c4 100644 --- a/app/components/wallet-summary.js +++ b/app/components/wallet-summary.js @@ -4,95 +4,151 @@ import React from 'react'; import styled, { withTheme } from 'styled-components'; import { TextComponent } from './text'; -import { RowComponent } from './row'; import { formatNumber } from '../utils/format-number'; import { getCoinName } from '../utils/get-coin-name'; +import { DARK } from '../constants/themes'; + +import ShieldDarkImage from '../assets/images/shield_dark.png'; +import ShieldLightImage from '../assets/images/shield_light.png'; + +const OutsideWrapper = styled.div` + margin-top: ${props => props.theme.layoutContentPaddingTop}; +`; const Wrapper = styled.div` display: flex; - flex-direction: column; + flex-direction: row; background-color: ${props => props.theme.colors.walletSummaryBg}; border: 1px solid ${props => props.theme.colors.walletSummaryBorder}; border-radius: ${props => props.theme.boxBorderRadius}; - padding: 37px 45px; - min-height: 250px; + padding: 30px 30px; position: relative; - margin-top: ${props => props.theme.layoutContentPaddingTop}; `; -const AllAddresses = styled(TextComponent)` - margin-bottom: 2.5px; - font-size: ${props => `${props.theme.fontSize.small}em`}; +const OutsideLabel = 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-bottom: 5px; `; -const ValueBox = styled.div` - margin-bottom: 15px; - margin-right: 25px; +const TotalContainer = styled.div` + min-width: 270px; `; -const Label = styled(TextComponent)` - margin-top: 10px; - margin-bottom: 5px; - margin-left: -7.5px; +const DetailContainer = styled.div` + min-width: 130px; + padding-right: 20px; `; const USDValue = styled(TextComponent)` opacity: 0.5; font-weight: ${props => String(props.theme.fontWeight.light)}; + font-size: 16px; `; -const ShieldedValue = styled(Label)` +const DefaultLabel = styled(TextComponent)` + margin-top: 5px; + margin-bottom: 0px; + color: ${props => props.theme.colors.walletSummaryTransparent}; +`; + +const MiddleLabel = styled(TextComponent)` + margin-top: 7px; + margin-bottom: 5px; + font-size: 18px; +`; + +const ShieldedValue = styled(DefaultLabel)` color: ${props => props.theme.colors.activeItem}; + padding-left: 14px; + position: relative; + + &:before { + position: absolute; + left: 0; + top: -1px; + content: ''; + background: url(${props => (props.theme.mode === DARK ? ShieldDarkImage : ShieldLightImage)}); + background-size: cover; + height: 12px; + width: 11px; + } +`; + +const UnconfirmedLabel = styled(DefaultLabel)` + color: ${props => props.theme.colors.walletSummaryUnconfirmed}; +`; + +const UnconfirmedValue = styled(MiddleLabel)` + color: ${props => props.theme.colors.walletSummaryUnconfirmed}; `; type Props = { total: number, shielded: number, transparent: number, + unconfirmed: number, zecPrice: number, theme: AppTheme, }; export const Component = ({ - total, shielded, transparent, zecPrice, theme, + total, + shielded, + transparent, + unconfirmed, + zecPrice, + theme, }: Props) => { const coinName = getCoinName(); + return ( - - - - - - - - - + + + + + + + + + - - - - - + + + + + + +
+ ); }; diff --git a/app/components/wallet-summary.mdx b/app/components/wallet-summary.mdx deleted file mode 100644 index 9c45f0dc..00000000 --- a/app/components/wallet-summary.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Wallet Summary ---- - -import { Playground, PropsTable } from 'docz' - -import { WalletSummaryComponent } from './wallet-summary.js' -import { DoczWrapper } from '../theme.js' - -# Wallet Summary - -## Properties - - - -## Basic Usage - - - - {() => ( -
- -
- )} -
-
diff --git a/app/constants/zcash-network.js b/app/constants/zcash-network.js index a874c6e7..435691fd 100644 --- a/app/constants/zcash-network.js +++ b/app/constants/zcash-network.js @@ -8,3 +8,5 @@ export const TESTNET = 'TESTNET'; export const SPROUT = 'sprout'; export const SAPLING = 'sapling'; + +export const MIN_CONFIRMATIONS_NUMBER = 12; diff --git a/app/containers/dashboard.js b/app/containers/dashboard.js index 76e37507..19ff9bbf 100644 --- a/app/containers/dashboard.js +++ b/app/containers/dashboard.js @@ -11,7 +11,7 @@ import { DashboardView } from '../views/dashboard'; import rpc from '../../services/api'; import store from '../../config/electron-store'; -import { SAPLING } from '../constants/zcash-network'; +import { SAPLING, MIN_CONFIRMATIONS_NUMBER } from '../constants/zcash-network'; import { listShieldedTransactions } from '../../services/shielded-transactions'; import { sortByDescend } from '../utils/sort-by-descend'; @@ -28,6 +28,7 @@ const mapStateToProps = ({ walletSummary }: AppState) => ({ total: walletSummary.total, shielded: walletSummary.shielded, transparent: walletSummary.transparent, + unconfirmed: walletSummary.unconfirmed, error: walletSummary.error, isLoading: walletSummary.isLoading, zecPrice: walletSummary.zecPrice, @@ -43,8 +44,9 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ const [zAddressesErr, zAddresses = []] = await eres(rpc.z_listaddresses()); const [tAddressesErr, tAddresses = []] = await eres(rpc.getaddressesbyaccount('')); const [transactionsErr, transactions] = await eres(rpc.listtransactions()); + const [unconfirmedBalanceErr, unconfirmedBalance] = await eres(rpc.getunconfirmedbalance()); - if (walletErr || zAddressesErr || tAddressesErr || transactionsErr) { + if (walletErr || zAddressesErr || tAddressesErr || transactionsErr || unconfirmedBalanceErr) { return dispatch( loadWalletSummaryError({ error: 'Something went wrong!', @@ -54,6 +56,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ const formattedTransactions: Array = flow([ arr => arr.map(transaction => ({ + confirmed: transaction.confirmations >= MIN_CONFIRMATIONS_NUMBER, + confirmations: transaction.confirmations, transactionId: transaction.txid, type: transaction.category, date: new Date(transaction.time * 1000).toISOString(), @@ -87,6 +91,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ transparent: walletSummary.transparent, total: walletSummary.total, shielded: walletSummary.private, + unconfirmed: unconfirmedBalance, addresses: [...zAddresses, ...tAddresses], transactions: formattedTransactions, zecPrice: new BigNumber(store.get('ZEC_DOLLAR_PRICE')).toNumber(), diff --git a/app/containers/transactions.js b/app/containers/transactions.js index 12755b8e..de2e1087 100644 --- a/app/containers/transactions.js +++ b/app/containers/transactions.js @@ -14,6 +14,7 @@ import { import rpc from '../../services/api'; import { listShieldedTransactions } from '../../services/shielded-transactions'; import store from '../../config/electron-store'; +import { MIN_CONFIRMATIONS_NUMBER } from '../constants/zcash-network'; import { sortByDescend } from '../utils/sort-by-descend'; @@ -64,6 +65,11 @@ const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({ ...transactions, ...listShieldedTransactions({ count, offset: shieldedTransactionsCount }), ].map(transaction => ({ + confirmations: transaction.confirmations !== undefined ? transaction.confirmations : 0, + confirmed: + transaction.confirmations !== undefined + ? transaction.confirmations >= MIN_CONFIRMATIONS_NUMBER + : true, transactionId: transaction.txid, type: transaction.category, date: new Date(transaction.time * 1000).toISOString(), diff --git a/app/redux/modules/wallet.js b/app/redux/modules/wallet.js index 8dbc8576..37b98605 100644 --- a/app/redux/modules/wallet.js +++ b/app/redux/modules/wallet.js @@ -18,6 +18,7 @@ export const loadWalletSummarySuccess = ({ total, shielded, transparent, + unconfirmed, addresses, transactions, zecPrice, @@ -25,6 +26,7 @@ export const loadWalletSummarySuccess = ({ total: number, shielded: number, transparent: number, + unconfirmed: number, addresses: string[], transactions: TransactionsList, zecPrice: number, @@ -34,6 +36,7 @@ export const loadWalletSummarySuccess = ({ total, shielded, transparent, + unconfirmed, addresses, transactions, zecPrice, @@ -49,6 +52,7 @@ export type State = { total: number, shielded: number, transparent: number, + unconfirmed: number, error: string | null, isLoading: boolean, zecPrice: number, @@ -60,6 +64,7 @@ const initialState = { total: 0, shielded: 0, transparent: 0, + unconfirmed: 0, error: null, isLoading: false, zecPrice: 0, diff --git a/app/theme/colors/dark.js b/app/theme/colors/dark.js index af36117e..46a442bb 100644 --- a/app/theme/colors/dark.js +++ b/app/theme/colors/dark.js @@ -124,6 +124,9 @@ export const DARK_COLORS = { // Wallet Summary walletSummaryBg: black, walletSummaryBorder: black, + walletSummaryUnconfirmed: silver, + walletSummaryShielded: white, + walletSummaryTransparent: white, // Wallet Address walletAddressBg: black, diff --git a/app/theme/colors/light.js b/app/theme/colors/light.js index 980eb5b4..f31be1d0 100644 --- a/app/theme/colors/light.js +++ b/app/theme/colors/light.js @@ -123,6 +123,9 @@ export const LIGHT_COLORS = { // Wallet Summary walletSummaryBg: white, walletSummaryBorder: alto, + walletSummaryUnconfirmed: shipGray, + walletSummaryShielded: black, + walletSummaryTransparent: black, // Wallet Address walletAddressBg: white, diff --git a/app/views/dashboard.js b/app/views/dashboard.js index a2cb6a90..045c4326 100644 --- a/app/views/dashboard.js +++ b/app/views/dashboard.js @@ -14,16 +14,27 @@ type Props = { total: number, shielded: number, transparent: number, + unconfirmed: number, error: string | null, zecPrice: number, addresses: string[], transactions: TransactionsList, }; +const UPDATE_INTERVAL = 5000; + export class DashboardView extends PureComponent { + interval = null; + componentDidMount() { const { getSummary } = this.props; getSummary(); + + this.interval = setInterval(() => getSummary(), UPDATE_INTERVAL); + } + + componentWillUnmount() { + clearInterval(this.interval); } render() { @@ -32,6 +43,7 @@ export class DashboardView extends PureComponent { total, shielded, transparent, + unconfirmed, zecPrice, addresses, transactions, @@ -47,6 +59,7 @@ export class DashboardView extends PureComponent { total={total} shielded={shielded} transparent={transparent} + unconfirmed={unconfirmed} zecPrice={zecPrice} addresses={addresses} /> diff --git a/app/views/receive.js b/app/views/receive.js index 21661f6e..8fbccc0b 100644 --- a/app/views/receive.js +++ b/app/views/receive.js @@ -15,10 +15,13 @@ import MenuIconDark from '../assets/images/menu_icon_dark.svg'; import MenuIconLight from '../assets/images/menu_icon_light.svg'; import PlusIconDark from '../assets/images/plus_icon_dark.svg'; import PlusIconLight from '../assets/images/plus_icon_light.svg'; +import ShieldGrayImage from '../assets/images/shield_dark_gray.png'; import type { addressType } from '../redux/modules/receive'; import type { MapStateToProps, MapDispatchToProps } from '../containers/receive'; +const TRANSPARENT_ADDRESS_SUBLABEL = 'By using transaparent addresses you are forgiving any privacy properties to your transactions.'; + const Row = styled(RowComponent)` margin-bottom: 10px; `; @@ -31,6 +34,16 @@ const Label = styled(InputLabelComponent)` margin-bottom: 5px; `; +const SubLabel = styled(InputLabelComponent)` + color: ${props => props.theme.colors.transactionsDate}; + font-size: ${props => `${props.theme.fontSize.regular * 0.9}em`}; + font-weight: ${props => String(props.theme.fontWeight.light)}; + letter-spacing: 0.5px; + margin-bottom: 10px; + margin-top: 0; + padding-top: 8px; +`; + const ActionButton = styled.button` background: none; border: none; @@ -75,6 +88,22 @@ const RevealsMain = styled.div` justify-content: flex-start; `; +const ShieldedLabel = styled(Label)` + padding-left: 14px; + position: relative; + + &:before { + position: absolute; + left: 0; + top: -1px; + content: ''; + background: url(${ShieldGrayImage}); + background-size: cover; + height: 12px; + width: 11px; + } +`; + type Props = MapDispatchToProps & MapStateToProps & { theme: AppTheme, @@ -119,7 +148,7 @@ class Component extends PureComponent { return (
-