From f4117c1aef0b1694cffd208138b5ba43be393c66 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Thu, 16 Aug 2018 13:14:13 +0200 Subject: [PATCH] Refactored /account folder to new folder structure --- .eslintrc | 1 + src/js/components/common/Button.js | 59 ----- src/js/components/modal/index.js | 2 + .../Account/components/Receive/Container.js | 45 ++++ .../views/Account/components/Receive/index.js | 101 +++++++++ .../components/SelectedAccount/index.js | 114 ++++++++++ .../Account/components/Send/Container.js | 47 ++++ .../Send/components/AdvancedForm.js | 191 ++++++++++++++++ .../Send/components/CoinSelectOption.js | 51 +++++ .../components/Send/components/FeeSelect.js | 61 ++++++ .../Send/components/PendingTransactions.js | 95 ++++++++ .../views/Account/components/Send/index.js | 205 ++++++++++++++++++ .../views/Account/components/Sign/index.js | 66 ++++++ .../Account/components/Summary/Container.js | 52 +++++ .../Summary/components/Details/index.js | 75 +++++++ .../Summary/components/Tokens/index.js | 54 +++++ .../views/Account/components/Summary/index.js | 120 ++++++++++ .../views/Account/components/Tabs/index.js | 105 +++++++++ src/js/views/Landing/Container.js | 1 - .../views/Landing/components/InstallBridge.js | 12 +- 20 files changed, 1391 insertions(+), 66 deletions(-) delete mode 100644 src/js/components/common/Button.js create mode 100644 src/js/views/Device/views/Account/components/Receive/Container.js create mode 100644 src/js/views/Device/views/Account/components/Receive/index.js create mode 100644 src/js/views/Device/views/Account/components/SelectedAccount/index.js create mode 100644 src/js/views/Device/views/Account/components/Send/Container.js create mode 100644 src/js/views/Device/views/Account/components/Send/components/AdvancedForm.js create mode 100644 src/js/views/Device/views/Account/components/Send/components/CoinSelectOption.js create mode 100644 src/js/views/Device/views/Account/components/Send/components/FeeSelect.js create mode 100644 src/js/views/Device/views/Account/components/Send/components/PendingTransactions.js create mode 100644 src/js/views/Device/views/Account/components/Send/index.js create mode 100644 src/js/views/Device/views/Account/components/Sign/index.js create mode 100644 src/js/views/Device/views/Account/components/Summary/Container.js create mode 100644 src/js/views/Device/views/Account/components/Summary/components/Details/index.js create mode 100644 src/js/views/Device/views/Account/components/Summary/components/Tokens/index.js create mode 100644 src/js/views/Device/views/Account/components/Summary/index.js create mode 100644 src/js/views/Device/views/Account/components/Tabs/index.js diff --git a/.eslintrc b/.eslintrc index 0c77d1ce..6bc0340c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,6 +10,7 @@ }, "rules": { "react/require-default-props": 0, + "react/forbid-prop-types": 0, "react/destructuring-assignment": 0, "react/jsx-one-expression-per-line": 0, "react/jsx-indent": [2, 4], diff --git a/src/js/components/common/Button.js b/src/js/components/common/Button.js deleted file mode 100644 index 6c96c199..00000000 --- a/src/js/components/common/Button.js +++ /dev/null @@ -1,59 +0,0 @@ -import styled, { css } from 'styled-components'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import colors from 'config/colors'; - -const Wrapper = styled.button` - padding: 12px 24px; - border-radius: 3px; - font-size: 14px; - font-weight: 300; - cursor: pointer; - background: ${colors.GREEN_PRIMARY}; - color: ${colors.WHITE}; - border: 0; - - &:hover { - background: ${colors.GREEN_SECONDARY}; - } - - &:active { - background: ${colors.GREEN_TERTIARY}; - } - - ${props => props.disabled && css` - pointer-events: none; - color: ${colors.TEXT_SECONDARY}; - background: ${colors.GRAY_LIGHT}; - `} -`; - -const Button = ({ - text, onClick, disabled, blue, white, -}) => ( - {text} - -); - -Button.propTypes = { - onClick: PropTypes.func, - disabled: PropTypes.bool, - blue: PropTypes.bool, - white: PropTypes.bool, - text: PropTypes.string.isRequired, -}; - -Button.defaultProps = { - onClick: () => {}, - disabled: false, - blue: false, - white: false, -}; - -export default Button; diff --git a/src/js/components/modal/index.js b/src/js/components/modal/index.js index f565e441..47984166 100644 --- a/src/js/components/modal/index.js +++ b/src/js/components/modal/index.js @@ -103,6 +103,8 @@ class Modal extends Component { case CONNECT.TRY_TO_DUPLICATE: component = (); break; + + default: null; } let ch = null; diff --git a/src/js/views/Device/views/Account/components/Receive/Container.js b/src/js/views/Device/views/Account/components/Receive/Container.js new file mode 100644 index 00000000..6285193f --- /dev/null +++ b/src/js/views/Device/views/Account/components/Receive/Container.js @@ -0,0 +1,45 @@ +/* @flow */ + + +import React, { Component, PropTypes } from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +import { default as ReceiveActions } from 'actions/ReceiveActions'; +import * as TokenActions from 'actions/TokenActions'; +import type { MapStateToProps, MapDispatchToProps } from 'react-redux'; +import type { State, Dispatch } from 'flowtype'; +import Receive from './Receive'; + +import type { + StateProps as BaseStateProps, + DispatchProps as BaseDispatchProps, +} from '../SelectedAccount'; + +type OwnProps = { } + +type StateProps = BaseStateProps & { + receive: $ElementType, + modal: $ElementType, +} + +type DispatchProps = BaseDispatchProps & { + showAddress: typeof ReceiveActions.showAddress +}; + +export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; + +const mapStateToProps: MapStateToProps = (state: State, own: OwnProps): StateProps => ({ + className: 'receive', + selectedAccount: state.selectedAccount, + wallet: state.wallet, + + receive: state.receive, + modal: state.modal, +}); + +const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Receive); \ No newline at end of file diff --git a/src/js/views/Device/views/Account/components/Receive/index.js b/src/js/views/Device/views/Account/components/Receive/index.js new file mode 100644 index 00000000..4873496c --- /dev/null +++ b/src/js/views/Device/views/Account/components/Receive/index.js @@ -0,0 +1,101 @@ +/* @flow */ +import React, { Component } from 'react'; +import styled from 'styled-components'; +import { H2 } from 'components/common/Heading'; + +import Tooltip from 'rc-tooltip'; +import { QRCode } from 'react-qr-svg'; + +import { Notification } from 'components/common/Notification'; +import SelectedAccount from '../SelectedAccount'; + +import type { Props } from './index'; + +const Wrapper = styled.div``; +const StyledH2 = styled(H2)` + padding: 20px 48px; +`; + +const Receive = (props: Props) => { + const device = props.wallet.selectedDevice; + const { + account, + network, + discovery, + } = props.selectedAccount; + + if (!device || !account || !discovery) return null; + + const { + addressVerified, + addressUnverified, + } = props.receive; + + let qrCode = null; + let address = `${account.address.substring(0, 20)}...`; + let className = 'address hidden'; + let button = ( + + ); + + if (addressVerified || addressUnverified) { + qrCode = ( + + ); + address = account.address; + className = addressUnverified ? 'address unverified' : 'address'; + + const tooltip = addressUnverified + ? (
Unverified address.
{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }
) + : (
{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }
); + + button = ( + } + overlay={tooltip} + placement="bottomRight" + > + + + ); + } + + let ver = null; + if (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address') { + className = 'address verifying'; + address = account.address; + ver = (
Confirm address on TREZOR
); + button = (
{ account.network } account #{ (account.index + 1) }
); + } + + return ( + + Receive Ethereum or tokens +
+ { ver } +
+ { address } +
+ { button } +
+ { qrCode } +
+ ); +}; + +export default (props: Props) => ( + + + +); \ No newline at end of file diff --git a/src/js/views/Device/views/Account/components/SelectedAccount/index.js b/src/js/views/Device/views/Account/components/SelectedAccount/index.js new file mode 100644 index 00000000..1fdd6c92 --- /dev/null +++ b/src/js/views/Device/views/Account/components/SelectedAccount/index.js @@ -0,0 +1,114 @@ +/* @flow */ + + +import * as React from 'react'; +import { Notification } from 'components/common/Notification'; + +import type { + State, TrezorDevice, Action, ThunkAction, +} from 'flowtype'; +import type { Account } from 'reducers/AccountsReducer'; +import type { Discovery } from 'reducers/DiscoveryReducer'; + +export type StateProps = { + className: string; + selectedAccount: $ElementType, + wallet: $ElementType, + children?: React.Node +} + +export type DispatchProps = { + +} + +export type Props = StateProps & DispatchProps; + +const SelectedAccount = (props: Props) => { + const device = props.wallet.selectedDevice; + if (!device || !device.state) { + return (
); + } + + const accountState = props.selectedAccount; + + const { + account, + discovery, + } = accountState; + + // account not found (yet). checking why... + if (!account) { + if (!discovery || discovery.waitingForDevice) { + if (device.connected) { + // case 1: device is connected but discovery not started yet (probably waiting for auth) + if (device.available) { + return ( +
+ +
+ ); + } + // case 2: device is unavailable (created with different passphrase settings) account cannot be accessed + return ( +
+ +
+ ); + } + // case 3: device is disconnected + return ( +
+ +
+ ); + } if (discovery.waitingForBackend) { + // case 4: backend is not working + return ( +
+ +
+ ); + } if (discovery.completed) { + // case 5: account not found and discovery is completed + return ( +
+ +
+ ); + } + // case 6: discovery is not completed yet + return ( +
+ +
+ ); + } + + let notification: ?React$Element = null; + if (!device.connected) { + notification = ; + } else if (!device.available) { + notification = ; + } + + if (discovery && !discovery.completed && !notification) { + notification = ; + } + + return ( +
+ { notification } + { props.children } +
+ ); +}; + +export default SelectedAccount; \ No newline at end of file diff --git a/src/js/views/Device/views/Account/components/Send/Container.js b/src/js/views/Device/views/Account/components/Send/Container.js new file mode 100644 index 00000000..c37f2556 --- /dev/null +++ b/src/js/views/Device/views/Account/components/Send/Container.js @@ -0,0 +1,47 @@ +/* @flow */ + + +import * as React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +import { default as SendFormActions } from 'actions/SendFormActions'; +import * as SessionStorageActions from 'actions/SessionStorageActions'; +import type { MapStateToProps, MapDispatchToProps } from 'react-redux'; +import type { State, Dispatch } from 'flowtype'; +import SendForm from './SendForm'; + +import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount'; + +type OwnProps = { } + +export type StateProps = BaseStateProps & { + sendForm: $ElementType, + fiat: $ElementType, + localStorage: $ElementType, + children?: React.Node; +} + +export type DispatchProps = BaseDispatchProps & { + sendFormActions: typeof SendFormActions, + saveSessionStorage: typeof SessionStorageActions.save +} + +export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; + +const mapStateToProps: MapStateToProps = (state: State, own: OwnProps): StateProps => ({ + className: 'send-from', + selectedAccount: state.selectedAccount, + wallet: state.wallet, + + sendForm: state.sendForm, + fiat: state.fiat, + localStorage: state.localStorage, +}); + +const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + sendFormActions: bindActionCreators(SendFormActions, dispatch), + saveSessionStorage: bindActionCreators(SessionStorageActions.save, dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(SendForm); \ No newline at end of file diff --git a/src/js/views/Device/views/Account/components/Send/components/AdvancedForm.js b/src/js/views/Device/views/Account/components/Send/components/AdvancedForm.js new file mode 100644 index 00000000..978f9268 --- /dev/null +++ b/src/js/views/Device/views/Account/components/Send/components/AdvancedForm.js @@ -0,0 +1,191 @@ +/* @flow */ + + +import React from 'react'; +import Tooltip from 'rc-tooltip'; +import type { Props as BaseProps } from './index'; + +type Props = { + selectedAccount: $ElementType, + sendForm: $ElementType, + sendFormActions: $ElementType, + children?: $ElementType, +}; + +const AdvancedForm = (props: Props) => { + const { + account, + network, + } = props.selectedAccount; + + const { + networkSymbol, + currency, + gasPrice, + gasLimit, + recommendedGasPrice, + calculatingGasLimit, + nonce, + data, + errors, + warnings, + infos, + advanced, + } = props.sendForm; + + const { + toggleAdvanced, + onGasPriceChange, + onGasLimitChange, + onNonceChange, + onDataChange, + } = props.sendFormActions; + + if (!advanced) { + return ( +
+ Advanced settings + { props.children } +
+ ); + } + + const nonceTooltip = ( +
+ TODO
+
+ ); + + let gasLimitTooltipCurrency: string; + let gasLimitTooltipValue: string; + if (networkSymbol !== currency) { + gasLimitTooltipCurrency = 'tokens'; + gasLimitTooltipValue = network.defaultGasLimitTokens; + } else { + gasLimitTooltipCurrency = networkSymbol; + gasLimitTooltipValue = network.defaultGasLimit; + } + + const gasLimitTooltip = ( +
+ Gas limit is the amount of gas to send with your transaction.
+ TX fee = gas price * gas limit & is paid to miners for including your TX in a block.
+ Increasing this number will not get your TX mined faster.
+ Default value for sending { gasLimitTooltipCurrency } is { gasLimitTooltipValue } +
+ ); + + const gasPriceTooltip = ( +
+ Gas Price is the amount you pay per unit of gas.
+ TX fee = gas price * gas limit & is paid to miners for including your TX in a block.
+ Higher the gas price = faster transaction, but more expensive. Recommended is { recommendedGasPrice } GWEI.
+ Read more +
+ ); + + const dataTooltip = ( +
+ Data is usually used when you send transactions to contracts. +
+ ); + + + return ( +
+ Advanced settings +
+ {/*
+
} + overlay={ nonceTooltip } + placement="top"> + + + + onNonceChange(event.target.value) } /> + { errors.nonce ? ({ errors.nonce }) : null } + { warnings.nonce ? ({ warnings.nonce }) : null } +
*/} +
+ + 0} + onChange={event => onGasLimitChange(event.target.value)} + /> + { errors.gasLimit ? ({ errors.gasLimit }) : null } + { warnings.gasLimit ? ({ warnings.gasLimit }) : null } + { calculatingGasLimit ? (Calculating...) : null } +
+
+ + onGasPriceChange(event.target.value)} + /> + { errors.gasPrice ? ({ errors.gasPrice }) : null } +
+
+ +
+ +