1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-12-28 09:58:23 +00:00
This commit is contained in:
Vladimir Volek 2018-09-21 14:19:18 +02:00
commit c1381823db
15 changed files with 165 additions and 113 deletions

View File

@ -31,6 +31,7 @@
"ethereumjs-tx": "^1.3.3",
"ethereumjs-units": "^0.2.0",
"ethereumjs-util": "^5.1.4",
"flow-webpack-plugin": "^1.2.0",
"git-revision-webpack-plugin": "^3.0.3",
"hdkey": "^0.8.0",
"html-webpack-plugin": "^3.2.0",
@ -49,8 +50,7 @@
"react-router-redux": "next",
"react-scale-text": "^1.2.2",
"react-select": "2.0.0",
"react-sticky-el": "^1.0.20",
"react-transition-group": "^2.2.1",
"react-transition-group": "^2.4.0",
"redbox-react": "^1.6.0",
"redux": "4.0.0",
"redux-logger": "^3.0.6",

View File

@ -13,6 +13,7 @@ import { initialState } from 'reducers/SendFormReducer';
import { findToken } from 'reducers/TokensReducer';
import { findDevice, getPendingAmount, getPendingNonce } from 'reducers/utils';
import * as stateUtils from 'reducers/utils';
import { validateAddress } from 'utils/ethUtils';
import type {
Dispatch,
@ -345,32 +346,19 @@ export const validation = (props: Props): void => {
if (state.untouched) return;
// valid address
if (state.touched.address) {
/* if (state.address.length < 1) {
errors.address = 'Address is not set';
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
errors.address = 'Address is not valid';
} else {
// address warning or info are set in addressValidation ThunkAction
// do not override this
if (state.warnings.address) {
warnings.address = state.warnings.address;
} else if (state.infos.address) {
infos.address = state.infos.address;
}
} */
const addressError = validateAddress(state.address);
if (addressError) {
errors.address = addressError;
}
/* eslint (no-lonely-if) */
if (state.address.length < 1) {
errors.address = 'Address is not set';
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
errors.address = 'Address is not valid';
} else if (state.warnings.address) {
// address warning or info are set in addressValidation ThunkAction
// do not override this
// address warning or info may be set in addressValidation ThunkAction
// do not override them
if (state.warnings.address) {
warnings.address = state.warnings.address;
if (state.infos.address) {
infos.address = state.infos.address;
}
}
if (state.infos.address) {
infos.address = state.infos.address;
}
}

View File

@ -76,7 +76,12 @@ class Checkbox extends PureComponent {
<IconWrapper checked={checked}>
{checked && (
<Tick>
<Icon size={26} color={checked ? colors.WHITE : colors.GREEN_PRIMARY} icon={icons.SUCCESS} />
<Icon
hoverColor={colors.WHITE}
size={26}
color={checked ? colors.WHITE : colors.GREEN_PRIMARY}
icon={icons.SUCCESS}
/>
</Tick>
)
}

View File

@ -3,19 +3,13 @@ import RcTooltip from 'rc-tooltip';
import colors from 'config/colors';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { FONT_SIZE } from 'config/variables';
const TooltipContent = styled.div`
width: ${props => (props.isAside ? '260px' : '320px')};
font-size: ${FONT_SIZE.SMALLEST};
`;
const Wrapper = styled.div`
.rc-tooltip {
max-width: ${props => `${props.maxWidth}px` || 'auto'};
position: absolute;
z-index: 1070;
display: block;
background: red;
visibility: visible;
border: 1px solid ${colors.DIVIDER};
border-radius: 3px;
@ -178,9 +172,11 @@ class Tooltip extends Component {
placement,
content,
children,
maxWidth,
} = this.props;
return (
<Wrapper
maxWidth={maxWidth}
className={className}
innerRef={(node) => { this.tooltipContainerRef = node; }}
>
@ -188,7 +184,7 @@ class Tooltip extends Component {
getTooltipContainer={() => this.tooltipContainerRef}
arrowContent={<div className="rc-tooltip-arrow-inner" />}
placement={placement}
overlay={<TooltipContent>{content}</TooltipContent>}
overlay={content}
>
{children}
</RcTooltip>
@ -204,6 +200,7 @@ Tooltip.propTypes = {
PropTypes.element,
PropTypes.string,
]),
maxWidth: PropTypes.number,
content: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,

View File

@ -5,6 +5,7 @@ import P from 'components/Paragraph';
import Icon from 'components/Icon';
import icons from 'config/icons';
import { H3 } from 'components/Heading';
import { LINE_HEIGHT } from 'config/variables';
const Wrapper = styled.div`
width: 390px;
@ -21,6 +22,12 @@ const Content = styled.div`
padding: 24px 48px;
`;
const StyledP = styled(P)`
word-wrap: break-word;
padding: 5px 0;
line-height: ${LINE_HEIGHT.SMALL}
`;
const Label = styled.div`
padding-top: 5px;
font-size: 10px;
@ -49,7 +56,7 @@ const ConfirmSignTx = (props) => {
<Label>Send</Label>
<P>{`${amount} ${currency}` }</P>
<Label>To</Label>
<P>{ address }</P>
<StyledP>{ address }</StyledP>
<Label>Fee</Label>
<P>{ selectedFeeLevel.label }</P>
</Content>

View File

@ -41,5 +41,6 @@ export const TRANSITION = {
};
export const LINE_HEIGHT = {
SMALL: '1.4',
BASE: '1.8',
};

View File

@ -63,6 +63,44 @@ const baseStyles = () => injectGlobal`
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
}
.slide-left-enter {
transform: translate(100%);
pointer-events: none;
}
.slide-left-enter.slide-left-enter-active {
transform: translate(0%);
transition: transform 300ms ease-in-out;
}
.slide-left-exit {
transform: translate(-100%);
}
.slide-left-exit.slide-left-exit-active {
transform: translate(0%);
transition: transform 300ms ease-in-out;
}
.slide-right-enter {
transform: translate(-100%);
pointer-events: none;
}
.slide-right-enter.slide-right-enter-active {
transform: translate(0%);
transition: transform 300ms ease-in-out;
}
.slide-right-exit {
transform: translate(-100%);
}
.slide-right-exit.slide-right-exit-active {
transform: translate(-200%);
transition: transform 300ms ease-in-out;
}
`;
export default baseStyles;

View File

@ -1,6 +1,7 @@
/* @flow */
import BigNumber from 'bignumber.js';
import EthereumjsUtil from 'ethereumjs-util';
export const decimalToHex = (dec: number): string => new BigNumber(dec).toString(16);
@ -28,4 +29,16 @@ export const strip = (str: string): string => {
return padLeftEven(str);
};
export const calcGasPrice = (price: BigNumber, limit: string): string => price.times(limit).toString();
export const calcGasPrice = (price: BigNumber, limit: string): string => price.times(limit).toString();
export const validateAddress = (address: string): ?string => {
const hasUpperCase = new RegExp('^(.*[A-Z].*)$');
if (address.length < 1) {
return 'Address is not set';
} else if (!EthereumjsUtil.isValidAddress(address)) {
return 'Address is not valid';
} else if (address.match(hasUpperCase) && !EthereumjsUtil.isValidChecksumAddress(address)) {
return 'Address is not a valid checksum';
}
return null;
}

View File

@ -86,7 +86,7 @@ class ConnectDevice extends Component<Props> {
</React.Fragment>
)}
</ConnectTrezorWrapper>
{this.props.showWebUsb && (
{this.props.showWebUsb && !this.props.showDisconnect && (
<React.Fragment>
<P>and</P>
<Button isWebUsb>

View File

@ -20,7 +20,6 @@ import type { Props } from '../common';
import Row from '../Row';
import RowCoin from '../RowCoin';
const Wrapper = styled.div``;
const Text = styled.span`
@ -28,6 +27,10 @@ const Text = styled.span`
color: ${colors.TEXT_SECONDARY};
`;
const TooltipContent = styled.div`
font-size: ${FONT_SIZE.SMALLEST};
`;
const RowAccountWrapper = styled.div`
width: 100%;
display: flex;
@ -115,12 +118,12 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
const selectedCoin = config.coins.find(c => c.network === location.state.network);
if (!selected || !selectedCoin) return;
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
const deviceAccounts: Accounts = findDeviceAccounts(accounts, selected, location.state.network);
let selectedAccounts = deviceAccounts.map((account, i) => {
const selectedAccounts = deviceAccounts.map((account, i) => {
// const url: string = `${baseUrl}/network/${location.state.network}/account/${i}`;
const url: string = location.pathname.replace(/account+\/([0-9]*)/, `account/${i}`);
@ -158,26 +161,6 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
);
});
if (selectedAccounts.length < 1) {
if (selected.connected) {
const url: string = location.pathname.replace(/account+\/([0-9]*)/, 'account/0');
selectedAccounts = (
<NavLink
to={url}
>
<Row column>
<RowAccountWrapper
isSelected
borderTop
>
Account #1
</RowAccountWrapper>
</Row>
</NavLink>
);
}
}
let discoveryStatus = null;
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
@ -204,7 +187,8 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
} else {
discoveryStatus = (
<Tooltip
content={<React.Fragment>To add a new account, last account must have some transactions.</React.Fragment>}
maxWidth={300}
content={<TooltipContent>To add a new account, last account must have some transactions.</TooltipContent>}
placement="bottom"
>
<Row>

View File

@ -20,6 +20,8 @@ const RowCoinWrapper = styled.div`
display: block;
font-size: ${FONT_SIZE.BASE};
color: ${colors.TEXT_PRIMARY};
transition: background-color 0.3s, color 0.3s;
&:hover {
background-color: ${colors.GRAY_LIGHT};
}

View File

@ -19,6 +19,7 @@ const AsideWrapper = styled.aside`
position: relative;
top: 0;
width: 320px;
min-width: 320px;
overflow: hidden;
background: ${colors.MAIN};
border-right: 1px solid ${colors.DIVIDER};

View File

@ -24,15 +24,15 @@ const TransitionContentWrapper = styled.div`
`;
const Footer = styled.div`
position: fixed;
position: relative;
width: 320px;
bottom: 0;
background: ${colors.MAIN};
border-right: 1px solid ${colors.DIVIDER};
`;
const Body = styled.div`
overflow: auto;
background: ${colors.LANDING};
width: 320px;
`;
const Help = styled.div`
@ -58,6 +58,25 @@ const A = styled.a`
}
`;
const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => (
<TransitionGroupWrapper component="div" className="transition-container">
<CSSTransition
key={props.animationType}
onExit={() => { window.dispatchEvent(new Event('resize')); }}
onExited={() => window.dispatchEvent(new Event('resize'))}
in
out
classNames={props.animationType}
appear={false}
timeout={300}
>
<TransitionContentWrapper>
{ props.children }
</TransitionContentWrapper>
</CSSTransition>
</TransitionGroupWrapper>
);
class LeftNavigation extends Component {
constructor(props) {
super(props);
@ -96,39 +115,6 @@ class LeftNavigation extends Component {
}
}
// TODO: refactor to transition component for reuse of transitions
getMenuTransition(children) {
return (
<TransitionGroupWrapper component="div" className="transition-container">
<CSSTransition
key={this.state.animationType}
onEnter={() => {
console.warn('ON ENTER');
}}
onEntering={() => {
console.warn('ON ENTERING (ACTIVE)');
}}
onExit={() => {
console.warn('ON EXIT');
window.dispatchEvent(new Event('resize'));
}}
onExiting={() => {
console.warn('ON EXITING (ACTIVE)');
}}
onExited={() => window.dispatchEvent(new Event('resize'))}
classNames={this.state.animationType}
appear={false}
timeout={30000}
in
out
>
<TransitionContentWrapper>
{children}
</TransitionContentWrapper>
</CSSTransition>
</TransitionGroupWrapper>);
}
shouldRenderAccounts() {
const { selectedDevice } = this.props.wallet;
return selectedDevice
@ -148,6 +134,22 @@ class LeftNavigation extends Component {
}
render() {
const { props } = this;
let menu;
if (this.shouldRenderAccounts()) {
menu = (
<TransitionMenu animationType="slide-left">
<AccountMenu {...props} />
</TransitionMenu>
);
} else if (this.shouldRenderCoins()) {
menu = (
<TransitionMenu animationType="slide-right">
<CoinMenu {...props} />
</TransitionMenu>
);
}
return (
<StickyContainer
location={this.props.location.pathname}
@ -163,8 +165,7 @@ class LeftNavigation extends Component {
/>
<Body>
{this.state.shouldRenderDeviceSelection && <DeviceMenu {...this.props} />}
{this.shouldRenderAccounts() && this.getMenuTransition(<AccountMenu {...this.props} />)}
{this.shouldRenderCoins() && this.getMenuTransition(<CoinMenu {...this.props} />)}
{menu}
</Body>
<Footer className="sticky-bottom">
<Help>

View File

@ -1,6 +1,7 @@
import webpack from 'webpack';
import GitRevisionPlugin from 'git-revision-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import FlowWebpackPlugin from 'flow-webpack-plugin';
import WebpackBuildNotifierPlugin from 'webpack-build-notifier';
// turn on for bundle analyzing
@ -101,6 +102,9 @@ module.exports = {
new webpack.DefinePlugin({
COMMITHASH: JSON.stringify(gitRevisionPlugin.commithash()),
}),
new FlowWebpackPlugin({
reportingSeverity: 'warning',
}),
new HtmlWebpackPlugin({
chunks: ['index'],
template: `${SRC}index.html`,

View File

@ -3297,6 +3297,10 @@ dom-helpers@^3.2.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
dom-helpers@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
dom-serializer@0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@ -4310,6 +4314,10 @@ flow-parser@^0.*:
version "0.72.0"
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.72.0.tgz#6c8041e76ac7d0be1a71ce29c00cd1435fb6013c"
flow-webpack-plugin@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/flow-webpack-plugin/-/flow-webpack-plugin-1.2.0.tgz#1958821d16135028e391cad5ee2f3a4fa78197ec"
flush-write-stream@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
@ -8071,13 +8079,6 @@ prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, pr
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@>=15.5.10, prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@^15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
@ -8086,6 +8087,13 @@ prop-types@^15.6.1:
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
proxy-addr@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@ -8418,12 +8426,6 @@ react-select@2.0.0:
react-input-autosize "^2.2.1"
react-transition-group "^2.2.1"
react-sticky-el@^1.0.20:
version "1.0.20"
resolved "https://registry.yarnpkg.com/react-sticky-el/-/react-sticky-el-1.0.20.tgz#b3c5e7128218633f440dc67aec239d1cd078342d"
dependencies:
prop-types ">=15.5.10"
react-transition-group@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.1.tgz#e9fb677b79e6455fd391b03823afe84849df4a10"
@ -8435,6 +8437,15 @@ react-transition-group@^2.2.1:
prop-types "^15.5.8"
warning "^3.0.0"
react-transition-group@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a"
dependencies:
dom-helpers "^3.3.1"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
"react@^15.4.2 || ^16.0.0":
version "16.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"