diff --git a/README.md b/README.md index 3341504c..416973de 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ -# TREZOR Ethereum Wallet +👛 A webwallet using TREZOR as a private key storage https://wallet.trezor.io/trezor-wallet npm install / yarn - npm run dev / yarn run dev - npm run build / yarn run build \ No newline at end of file diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 15642eb1..876fe893 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -29,20 +29,8 @@ const Wrapper = styled.button` background: ${colors.GRAY_LIGHT}; `} - ${props => props.isBlue && css` - background: transparent; - border: 1px solid ${colors.INFO_PRIMARY}; - color: ${colors.INFO_PRIMARY}; - padding: 12px 58px; - - &:hover { - color: ${colors.WHITE}; - background: ${colors.INFO_PRIMARY}; - } - `} - - ${props => props.isWhite && css` - background: @color_white; + ${props => props.color === 'white' && css` + background: ${colors.WHITE}; color: ${colors.TEXT_SECONDARY}; border: 1px solid ${colors.DIVIDER}; @@ -125,14 +113,14 @@ const IconWrapper = styled.span` `; const Button = ({ - className, text, icon, onClick = () => { }, disabled, isBlue = false, isWhite = false, isWebUsb = false, isTransparent = false, + className, text, icon, onClick = () => { }, disabled, color = null, isWhite = false, isWebUsb = false, isTransparent = false, }) => ( ( , - close: (notif?: any) => Action -} +const Wrapper = styled.div` + position: relative; + color: ${colors.TEXT_PRIMARY}; + background: ${colors.TEXT_SECONDARY}; + padding: 24px 48px 24px 24px; + display: flex; + flex-direction: row; + text-align: left; -type NProps = { - key?: number; - className: string; - cancelable?: boolean; - title: string; - message?: string; - actions?: Array; - close?: typeof NotificationActions.close, - loading?: boolean -} + ${props => props.type === 'info' && css` + color: ${colors.INFO_PRIMARY}; + background: ${colors.INFO_SECONDARY}; + `} + + ${props => props.type === 'success' && css` + color: ${colors.SUCCESS_PRIMARY}; + background: ${colors.SUCCESS_SECONDARY}; + `} + + ${props => props.type === 'warning' && css` + color: ${colors.WARNING_PRIMARY}; + background: ${colors.WARNING_SECONDARY}; + `} + + ${props => props.type === 'error' && css` + color: ${colors.ERROR_PRIMARY}; + background: ${colors.ERROR_SECONDARY}; + `} +`; + +const Body = styled.div` + display: flex; + margin-right: 40px; + flex: 1; +`; + +const Title = styled.div` + padding-bottom: 5px; + font-weight: ${FONT_WEIGHT.BIGGER}; +`; + +const ActionContent = styled.div``; + +const CloseClick = styled.div` + position: absolute; + right: 0; + top: 0; + padding: 20px 10px 0 0; +`; + +const Message = styled.div` + font-size: ${FONT_SIZE.SMALLER}; +`; + +const StyledIcon = styled(Icon)` + position: relative; + top: -7px; +`; + +const MessageContent = styled.div` + height: 20px; + display: flex; +`; + +const Texts = styled.div` + display: flex; + flex-direction: column; +`; + +const AdditionalContent = styled.div``; export const Notification = (props: NProps): React$Element => { - const className = `notification ${props.className}`; const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action - const actionButtons = props.actions ? props.actions.map((a, i) => ( - - )) : null; + + const getIconColor = (type) => { + let color; + switch (type) { + case 'info': + color = colors.INFO_PRIMARY; + break; + case 'error': + color = colors.ERROR_PRIMARY; + break; + case 'warning': + color = colors.WARNING_PRIMARY; + break; + case 'success': + color = colors.SUCCESS_PRIMARY; + break; + default: + color = null; + } + + return color; + }; return ( -
- { props.cancelable ? ( -
+ + {props.loading && } + {props.cancelable && ( + close()}> + + + )} + + + + + { props.title } + { props.message && ( + +

+ + ) } + + + + + {props.actions && props.actions.length > 0 && ( + + {props.actions.map(action => ( + { close(); action.callback(); }} + /> + ))} + + )} + + ); }; -export const NotificationGroup = (props: Props) => { +export const NotificationGroup = (props) => { const { notifications, close } = props; return notifications.map((n, i) => ( { }; export default connect( - (state: State) => ({ + state => ({ notifications: state.notifications, }), - (dispatch: Dispatch) => ({ + dispatch => ({ close: bindActionCreators(NotificationActions.close, dispatch), }), )(NotificationGroup); \ No newline at end of file diff --git a/src/components/NotificationButton/index.js b/src/components/NotificationButton/index.js new file mode 100644 index 00000000..0de0e40d --- /dev/null +++ b/src/components/NotificationButton/index.js @@ -0,0 +1,98 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import PropTypes from 'prop-types'; +import Icon from 'components/Icon'; +import colors from 'config/colors'; +import { TRANSITION } from 'config/variables'; + +const Wrapper = styled.button` + padding: 12px 58px; + border-radius: 3px; + background: transparent; + font-size: 14px; + font-weight: 300; + cursor: pointer; + color: ${colors.WHITE}; + border: 0; + transition: ${TRANSITION.HOVER}; + + ${props => props.type === 'info' && css` + border: 1px solid ${colors.INFO_PRIMARY}; + color: ${colors.INFO_PRIMARY}; + + &:hover { + color: ${colors.WHITE}; + background: ${colors.INFO_PRIMARY}; + } + `} + + ${props => props.type === 'success' && css` + border: 1px solid ${colors.SUCCESS_PRIMARY}; + color: ${colors.SUCCESS_PRIMARY}; + + &:hover { + color: ${colors.WHITE}; + background: ${colors.SUCCESS_PRIMARY}; + } + `} + + ${props => props.type === 'error' && css` + border: 1px solid ${colors.ERROR_PRIMARY}; + color: ${colors.ERROR_PRIMARY}; + + &:hover { + color: ${colors.WHITE}; + background: ${colors.ERROR_PRIMARY}; + } + `} + + ${props => props.type === 'warning' && css` + border: 1px solid ${colors.WARNING_PRIMARY}; + color: ${colors.WARNING_PRIMARY}; + + &:hover { + color: ${colors.WHITE}; + background: ${colors.WARNING_PRIMARY}; + } + `} +`; + +const IconWrapper = styled.span` + margin-right: 8px; +`; + +const NotificationButton = ({ + className, text, icon, onClick = () => { }, type = null, +}) => ( + + {icon && ( + + + + )} + {text} + +); + +NotificationButton.propTypes = { + type: PropTypes.string.isRequired, + className: PropTypes.string, + onClick: PropTypes.func, + icon: PropTypes.shape({ + type: PropTypes.arrayOf(PropTypes.string).isRequired, + color: PropTypes.string, + size: PropTypes.number, + }), + text: PropTypes.string.isRequired, +}; + +export default NotificationButton; \ No newline at end of file diff --git a/src/config/icons.js b/src/config/icons.js index ebc2574d..0a4e18c9 100644 --- a/src/config/icons.js +++ b/src/config/icons.js @@ -39,6 +39,15 @@ export default { WARNING: [ 'M795.616 735.008l-264.896-465.44c-10.272-18.080-27.168-18.080-37.504 0l-264.864 465.44c-10.272 18.176-1.696 32.992 19.040 32.992h529.184c20.8 0 29.376-14.816 19.040-32.992zM549.76 673.12c0 10.464-8.48 18.976-18.912 18.976h-37.792c-10.336 0-18.912-8.512-18.912-18.976v-37.952c0-10.464 8.576-18.976 18.912-18.976h37.792c10.4 0 18.912 8.544 18.912 18.976v37.952zM549.76 559.264c0 10.464-8.48 18.976-18.912 18.976h-37.792c-10.336 0-18.912-8.512-18.912-18.976v-113.856c0-10.464 8.576-18.976 18.912-18.976h37.792c10.4 0 18.912 8.544 18.912 18.976v113.856z', ], + INFO: [ + 'M693.024 330.944c-99.968-99.936-262.080-99.936-362.048 0s-99.968 262.112 0 362.080c99.968 100 262.144 99.936 362.048 0 99.968-99.904 99.968-262.176 0-362.080zM507.904 300.192c27.008 0 48.992 21.984 48.992 49.088 0 27.296-21.984 49.472-48.992 49.472-27.264 0-49.536-22.176-49.536-49.472 0-27.552 21.728-49.088 49.536-49.088zM586.656 660.8c0 10.304-4.96 15.328-15.264 15.328h-126.464c-10.304 0-15.328-5.024-15.328-15.328v-32.256c0-10.304 5.024-15.264 15.328-15.264h23.36v-136.064h-23.872c-10.304 0-15.264-5.024-15.264-15.328v-32.224c0-10.304 4.96-15.264 15.264-15.264h88.288c10.304 0 15.264 4.96 15.264 15.264v183.648h23.424c10.304 0 15.264 4.96 15.264 15.264v32.224z', + ], + ERROR: [ + 'M693.12 330.88c-46.317-46.267-110.276-74.88-180.919-74.88-141.385 0-256 114.615-256 256s114.615 256 256 256c70.642 0 134.602-28.613 180.921-74.882l-0.002 0.002c46.387-46.337 75.081-110.377 75.081-181.12s-28.694-134.783-75.079-181.118l-0.002-0.002zM494.080 344.32h53.12c16 0 18.24 9.28 18.24 14.72v10.24l-10.88 194.56c0 14.4-8 17.28-18.88 17.28h-28.16c-10.56 0-17.28-2.88-18.88-17.92l-10.88-193.92v-10.56c-1.28-4.8 2.24-14.080 16.32-14.080zM521.28 717.76c-0.095 0.001-0.207 0.001-0.319 0.001-27.747 0-50.24-22.493-50.24-50.24s22.493-50.24 50.24-50.24c27.747 0 50.24 22.493 50.24 50.24 0 0.112 0 0.224-0.001 0.336v-0.017c0 0 0 0.001 0 0.001 0 27.634-22.311 50.057-49.903 50.239h-0.017z', + ], + SUCCESS: [ + 'M692.8 313.92l-1.92-1.92c-6.246-7.057-15.326-11.484-25.44-11.484s-19.194 4.427-25.409 11.448l-0.031 0.036-196.48 224-3.84 1.6-3.84-1.92-48.64-57.28c-7.010-7.905-17.193-12.862-28.533-12.862-21.031 0-38.080 17.049-38.080 38.080 0 7.495 2.165 14.485 5.905 20.377l-0.092-0.155 100.8 148.16c5.391 8.036 14.386 13.292 24.618 13.44h8.662c17.251-0.146 32.385-9.075 41.163-22.529l0.117-0.191 195.2-296.32c4.473-6.632 7.141-14.803 7.141-23.597 0-11.162-4.297-21.32-11.326-28.911l0.025 0.028z', + ], }; /* diff --git a/src/styles/index.less b/src/styles/index.less index 198816e3..e472047a 100644 --- a/src/styles/index.less +++ b/src/styles/index.less @@ -11,6 +11,4 @@ @import './receive.less'; @import './summary.less'; -@import './notification.less'; - @import './inputs.less'; \ No newline at end of file diff --git a/src/styles/notification.less b/src/styles/notification.less deleted file mode 100644 index dc8aab70..00000000 --- a/src/styles/notification.less +++ /dev/null @@ -1,127 +0,0 @@ -.notification { - position: relative; - color: @color_info_primary; - background: @color_info_secondary; - padding: 24px 48px 24px 80px; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; - text-align: left; - - .notification-body { - flex: 1; - margin-right: 24px; - } - - .notification-action button { - padding: 12px 58px; - } - - .notification-close { - position: absolute; - top: 8px; - right: 0; - padding: 12px; - color: inherit; - transition: opacity 0.3s; - z-index: 1; - - &:after { - .icomoon-close; - } - &:active, - &:hover { - opacity: 0.6; - color: inherit; - } - } - - h2 { - font-size: 14px; - font-weight: bold; - - padding: 0px; - &:before { - .icomoon-info; - position: absolute; - top: 17px; - left: 40px; - font-size: 32px !important; - } - } - - p { - padding: 0px; - margin: 8px 0px; - color: inherit; - } - - &.info { - .notification-action button { - border: 1px solid @color_info_primary; - color: @color_info_primary; - &:hover { - color: @color_white; - background: @color_info_primary; - } - } - } - - - - &.success { - color: @color_success_primary; - background: @color_success_secondary; - - .notification-action button { - border: 1px solid @color_success_primary; - color: @color_success_primary; - &:hover { - color: @color_white; - background: @color_success_primary; - } - } - } - - &.warning { - color: @color_warning_primary; - background: @color_warning_secondary; - h2:before { - .icomoon-warning; - } - - .notification-action button { - border: 1px solid @color_warning_primary; - color: @color_warning_primary; - &:hover { - color: @color_white; - background: @color_warning_primary; - } - } - } - - &.error { - color: @color_error_primary; - background: @color_error_secondary; - h2:before { - .icomoon-error; - } - - .notification-close { - color: @color_error_primary; - &:hover { - color: @color_error_primary; - } - } - - .notification-action button { - border: 1px solid @color_error_primary; - color: @color_error_primary; - &:hover { - color: @color_white; - background: @color_error_primary; - } - } - } -} \ No newline at end of file diff --git a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js index e1c6d43d..5c0b9d15 100644 --- a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js +++ b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js @@ -53,21 +53,35 @@ class MenuItems extends Component { return ( this.onDeviceMenuClick('settings', this.props.device)}> - + this.onDeviceMenuClick('forget', this.props.device)}> - + {this.showClone() && ( this.onDeviceMenuClick('clone', this.props.device)}> - + )} {this.showRenewSession() && ( - this.onDeviceMenuClick('reload')}> + this.onDeviceMenuClick('reload')} + > diff --git a/src/views/Wallet/views/AccountSummary/components/AccountBalance/index.js b/src/views/Wallet/views/AccountSummary/components/AccountBalance/index.js new file mode 100644 index 00000000..24bdfb9d --- /dev/null +++ b/src/views/Wallet/views/AccountSummary/components/AccountBalance/index.js @@ -0,0 +1,177 @@ +/* @flow */ +import React, { Component } from 'react'; +import styled, { css } from 'styled-components'; +import Icon from 'components/Icon'; +import colors from 'config/colors'; +import ICONS from 'config/icons'; +import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; +import BigNumber from 'bignumber.js'; + + +import type { Coin } from 'reducers/LocalStorageReducer'; +import type { Props as BaseProps } from '../../Container'; + +type Props = { + // coin: $PropertyType<$ElementType, 'coin'>, + coin: Coin, + summary: $ElementType, + balance: string, + network: string, + fiat: $ElementType, + onToggle: $ElementType +} + +const Wrapper = styled.div` + padding: 0 48px 25px; + position: relative; + display: flex; + + border-bottom: 1px solid ${colors.DIVIDER}; +`; + +const HideBalanceIconWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + + width: 40px; + height: 40px; + position: absolute; + margin-left: -20px; + left: 50%; + bottom: -20px; + + cursor: pointer; + background: ${colors.WHITE}; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04); + border-radius: 50%; + transition: all 0.3s; + &:hover { + background: ${colors.DIVIDER}; + } +`; + +const FiatValue = styled.div` + font-size: ${FONT_SIZE.BASE}; + font-weight: ${FONT_WEIGHT.BASE}; + margin: 7px 0; + color: ${colors.TEXT_PRIMARY}; +`; + +const CoinBalace = styled.div` + font-size: ${FONT_SIZE.SMALLER}; + color: ${colors.TEXT_SECONDARY}; +`; + +const Label = styled.div` + font-size: ${FONT_SIZE.SMALLER}; + color: ${colors.TEXT_SECONDARY}; +`; + +const BalanceWrapper = styled.div` + margin-right: 48px; +`; + +class AccountBalance extends Component { + constructor(props) { + super(props); + this.state = { + isHidden: false, + }; + } + + shouldHide(hide) { + this.setState({ + isHidden: hide, + }); + } + + render() { + const selectedCoin = props.coin; + const fiatRate = props.fiat.find(f => f.network === selectedCoin.network); + + const accountBalance = new BigNumber(props.balance); + const fiatRateValue = new BigNumber(fiatRate.value); + const fiat = accountBalance.times(fiatRateValue).toFixed(2); + + return ( + + ); + } +} + + +const AccountBalance = (props: Props): ?React$Element => { + if (!props.summary.details) { + return ( + + + + + + ); + } + + const selectedCoin = props.coin; + const fiatRate = props.fiat.find(f => f.network === selectedCoin.network); + + const accountBalance = new BigNumber(props.balance); + const fiatRateValue = new BigNumber(fiatRate.value); + const fiat = accountBalance.times(fiatRateValue).toFixed(2); + + return ( + + {props.summary.details && ( + + + + + + + + {fiatRate && ( + ${fiat} + )} + {props.balance} {selectedCoin.symbol} + + {fiatRate && ( + + + ${fiatRateValue.toFixed(2)} + 1.00 {selectedCoin.symbol} + + )} + + )} + + {!props.summary.details && ( + + + + )} + + ); +}; + +export default AccountBalance; \ No newline at end of file