1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-12-24 16:08:11 +00:00

Merge pull request #8 from satoshilabs/styled-components-refactor

Styled components refactor
This commit is contained in:
Szymon Lesisz 2018-09-03 11:31:08 +02:00 committed by GitHub
commit 4e7ceef73d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
208 changed files with 4680 additions and 3818 deletions

View File

@ -16,18 +16,7 @@
"regenerator": true
}],
["module-resolver", {
"root": ["./src"],
"alias": {
"config": "./src/js/config",
"constants": "./src/js/constants",
"components": "./src/js/components",
"actions": "./src/js/actions",
"reducers": "./src/js/reducers",
"support": "./src/js/support",
"utils": "./src/js/utils",
"services": "./src/js/services",
"views": "./src/js/views"
}
"root": ["./src"]
}],
"babel-plugin-styled-components"
],

View File

@ -1 +1,4 @@
solidity
solidity
coverage
images
node_modules

View File

@ -1,6 +1,7 @@
[include]
[ignore]
.*/node_modules/fbjs/.*
.*/node_modules/rc-util/.*
.*/node_modules/react-redux/.*
.*/node_modules/redux/.*
@ -20,8 +21,6 @@
./src/flowtype/npm/web3.js
./src/flowtype/css.js
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
@ -31,13 +30,15 @@ esproposal.decorators=ignore
module.name_mapper='.*\(.less\)' -> 'CSSModule'
module.name_mapper='^\(~/\)' -> '<PROJECT_ROOT>/src/'
module.name_mapper='^universal' -> '<PROJECT_ROOT>/src/'
module.name_mapper='^config' -> '<PROJECT_ROOT>/src/js/config'
module.name_mapper='^constants' -> '<PROJECT_ROOT>/src/js/constants'
module.name_mapper='^components' -> '<PROJECT_ROOT>/src/js/components'
module.name_mapper='^actions' -> '<PROJECT_ROOT>/src/js/actions'
module.name_mapper='^reducers' -> '<PROJECT_ROOT>/src/js/reducers'
module.name_mapper='^support' -> '<PROJECT_ROOT>/src/js/support'
module.name_mapper='^utils' -> '<PROJECT_ROOT>/src/js/utils'
module.name_mapper='^services' -> '<PROJECT_ROOT>/src/js/services'
module.name_mapper='^views' -> '<PROJECT_ROOT>/src/js/views'
module.name_mapper='^flowtype' -> '<PROJECT_ROOT>/src/flowtype'
module.name_mapper='^components' -> '<PROJECT_ROOT>/src/components'
module.name_mapper='^config' -> '<PROJECT_ROOT>/src/config'
module.name_mapper='^constants' -> '<PROJECT_ROOT>/src/constants'
module.name_mapper='^utils' -> '<PROJECT_ROOT>/src/utils'
module.name_mapper='^reducers' -> '<PROJECT_ROOT>/src/reducers'
module.name_mapper='^actions' -> '<PROJECT_ROOT>/src/actions'
module.name_mapper='^views' -> '<PROJECT_ROOT>/src/views'
module.name_mapper='^data' -> '<PROJECT_ROOT>/src/data'
module.name_mapper='^services' -> '<PROJECT_ROOT>/src/services'
module.name_mapper='^support' -> '<PROJECT_ROOT>/src/support'
module.system=haste

View File

@ -8,6 +8,24 @@
],
"rules": {
"indentation": 4,
"block-no-empty": null
"declaration-empty-line-before": null,
"custom-property-empty-line-before": null,
"max-empty-lines": 1,
"block-no-empty": null,
"property-no-unknown": [
true, {
"ignoreProperties": ["composes", "font-smoothing", "font-smooth"]
}
],
"at-rule-empty-line-before": [
"always", {
"ignoreAtRules": [
"import"
]
}
],
"at-rule-no-unknown": [ true, {
ignoreAtRules: ["each"]
}]
}
}

View File

@ -2,11 +2,10 @@ module.exports = {
rootDir: './src',
collectCoverage: true,
testURL: 'http://localhost',
modulePathIgnorePatterns: [
'node_modules',
],
collectCoverageFrom: [
'js/utils/**.js',
'utils/**.js',
],
};

View File

@ -2,16 +2,7 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": ["src/*"],
"components/*": ["./src/js/components/*"],
"config/*": ["./src/js/config/*"],
"constants/*": ["./src/js/constants/*"],
"support/*": ["./src/js/support/*"],
"actions/*": ["./src/js/actions/*"],
"reducers/*": ["./src/js/reducers/*"],
"utils/*": ["./src/js/utils/*"],
"services/*": ["./src/js/services/*"],
"views/*": ["./src/js/views/*"],
"*": ["src/*"]
}
}
}

View File

@ -1,12 +1,8 @@
{
"name": "trezor-connect-react-boilerplate",
"name": "trezor-wallet",
"version": "1.0.0",
"author": "TREZOR <info@trezor.io>",
"description": "",
"repository": {
"type": "git",
"url": "https://github.com/szymonlesisz/trezor-connect-react-boilerplate.git"
},
"bugs": {
"url": "https://github.com/szymonlesisz/trezor-connect-react-boilerplate/issues"
},
@ -18,13 +14,13 @@
"dev": "webpack-dev-server --config ./webpack/config.dev.babel.js --mode development",
"dev:local": "webpack-dev-server --config ./webpack/config.dev.local.babel.js --mode development",
"build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress",
"flow": "flow check src/js",
"flow": "flow check src",
"lint": "run-s lint:*",
"lint:js": "eslint ./src/js ./webpack",
"lint:css": "stylelint './src/js/**/*.js'",
"test:*": "run-s test:*",
"lint:js": "eslint ./src ./webpack",
"lint:css": "stylelint './src/**/*.js'",
"test": "run-s test:*",
"test:unit": "jest",
"test-unit:watch": "jest -o --watchAll"
"test-unit:watch": "jest -o --watch"
},
"dependencies": {
"babel": "^6.23.0",
@ -52,8 +48,9 @@
"react-router-dom": "^4.2.2",
"react-router-redux": "next",
"react-scale-text": "^1.2.2",
"react-select": "^1.2.1",
"react-select": "2.0.0",
"react-transition-group": "^2.2.1",
"redbox-react": "^1.6.0",
"redux": "4.0.0",
"redux-logger": "^3.0.6",
"redux-raven-middleware": "^1.2.0",

View File

@ -1,7 +1,7 @@
/* @flow */
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
import type { Device } from 'trezor-connect';
import * as MODAL from 'actions/constants/modal';
import * as CONNECT from 'actions/constants/TrezorConnect';

View File

@ -33,7 +33,7 @@ import type { Config, Coin } from 'reducers/LocalStorageReducer';
import type { Token } from 'reducers/TokensReducer';
import type { State, FeeLevel } from 'reducers/SendFormReducer';
import type { Account } from 'reducers/AccountsReducer';
import type { Props } from 'components/wallet/account/send';
import type { Props } from 'views/Wallet/views/AccountSend/Container';
import * as SessionStorageActions from './SessionStorageActions';
import { estimateGas, getGasPrice, pushTx } from './Web3Actions';

View File

@ -37,20 +37,21 @@ export const load = (input: string, network: string): AsyncAction => async (disp
const tokens = getState().localStorage.tokens[network];
const value = input.toLowerCase();
const result = tokens.filter(t => t.symbol.toLowerCase().indexOf(value) >= 0
|| t.address.toLowerCase().indexOf(value) >= 0
|| t.name.toLowerCase().indexOf(value) >= 0);
|| t.address.toLowerCase().indexOf(value) >= 0
|| t.name.toLowerCase().indexOf(value) >= 0);
if (result.length > 0) {
return { options: result };
// TODO: Temporary fix for async select
// async react-select starts getting very laggy
// when options is a large list (>200 items)
return result.slice(0, 100);
}
const web3instance = getState().web3.find(w3 => w3.network === network);
if (!web3instance) return;
const info = await getTokenInfoAsync(web3instance.erc20, input);
if (info) {
return {
options: [info],
};
return [info];
}
//await resolveAfter(300000);
//await resolveAfter(3000);

View File

@ -0,0 +1,94 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import colors from 'config/colors';
import Icon from 'components/Icon';
import icons from 'config/icons';
import { FONT_SIZE } from 'config/variables';
const Wrapper = styled.div`
display: flex;
flex-direction: row;
cursor: pointer;
align-items: center;
&:hover,
&:focus {
outline: none;
}
`;
const Tick = styled.div`
`;
const IconWrapper = styled.div`
display: flex;
border-radius: 2px;
justify-content: center;
align-items: center;
color: ${props => (props.checked ? colors.WHITE : colors.GREEN_PRIMARY)};
background: ${props => (props.checked ? colors.GREEN_PRIMARY : colors.WHITE)};
border: 1px solid ${props => (props.checked ? colors.GREEN_PRIMARY : colors.DIVIDER)};
width: 24px;
height: 24px;
&:hover,
&:focus {
border: 1px solid ${colors.TEXT_PRIMARY};
background: ${props => (props.checked ? colors.TEXT_PRIMARY : colors.WHITE)};
}
`;
const Label = styled.div`
display: flex;
padding-left: 10px;
justify-content: center;
${colors.TEXT_SECONDARY};
font-size: ${FONT_SIZE.SMALL};
&:hover,
&:focus {
color: ${props => (props.checked ? colors.TEXT_PRIMARY : colors.TEXT_PRIMARY)};
}
`;
class Checkbox extends PureComponent {
handleKeyboard(e) {
if (e.keyCode === 32) {
this.props.onClick(e);
}
}
render() {
const {
checked,
children,
onClick,
} = this.props;
return (
<Wrapper
onClick={onClick}
onKeyUp={e => this.handleKeyboard(e)}
tabIndex={0}
>
<IconWrapper checked={checked}>
{checked && (
<Tick>
<Icon size={26} color={checked ? colors.WHITE : colors.GREEN_PRIMARY} icon={icons.SUCCESS} />
</Tick>
)
}
</IconWrapper>
<Label checked={checked}>{children}</Label>
</Wrapper>
);
}
}
Checkbox.propTypes = {
onClick: PropTypes.func.isRequired,
checked: PropTypes.bool,
children: PropTypes.string,
};
export default Checkbox;

View File

@ -0,0 +1,176 @@
import React, { Component } from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import Icon from 'components/Icon';
import icons from 'config/icons';
import {
getStatusColor,
getStatusName,
isDisabled,
getStatus,
getVersion,
} from 'utils/device';
import TrezorImage from 'components/images/TrezorImage';
import colors from 'config/colors';
const Wrapper = styled.div`
position: relative;
height: 64px;
width: 320px;
display: flex;
align-items: center;
background: ${colors.WHITE};
border-radius: 4px 0 0 0;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
${props => props.isOpen && css`
box-shadow: none;
`}
${props => props.isHoverable && css`
&:hover {
background: ${colors.GRAY_LIGHT};
}
`}
`;
const ClickWrapper = styled.div`
width: 100%;
display: flex;
padding-left: 25px;
height: 100%;
align-items: center;
cursor: pointer;
${props => props.disabled && css`
cursor: initial;
`}
`;
const LabelWrapper = styled.div`
flex: 1;
padding-left: 18px;
`;
const Name = styled.div`
display: block;
text-overflow: ellipsis;
overflow: hidden;
white-space: no-wrap;
font-weight: 500;
font-size: 14px;
color: ${colors.TEXT_PRIMARY};
`;
const Status = styled.div`
display: block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 12px;
color: ${colors.TEXT_SECONDARY};
`;
const Counter = styled.div`
border: 1px solid ${colors.DIVIDER};
border-radius: 50%;
color: ${colors.TEXT_SECONDARY};
width: 24px;
height: 24px;
line-height: 22px;
text-align: center;
font-size: 11px;
margin-right: 8px;
`;
const IconWrapper = styled.div`
padding-right: 25px;
display: flex;
`;
const ImageWrapper = styled.div`
position: relative;
`;
const Dot = styled.div`
border: 2px solid ${colors.WHITE};
border-radius: 50%;
position: absolute;
z-index: 10;
background: ${props => props.color};
top: -4px;
right: -3px;
width: 10px;
height: 10px;
`;
class DeviceHeader extends Component {
constructor(props) {
super(props);
this.state = {
clicked: false,
};
}
isDisabled(device, devices, transport) {
return isDisabled(device, devices, transport);
}
handleClickWrapper() {
this.setState({ clicked: true });
if (!this.props.disabled) {
this.props.onClickWrapper();
}
}
render() {
const {
isOpen, icon, device, devices, transport, isHoverable,
} = this.props;
const status = getStatus(device);
const disabled = isDisabled(device, devices, transport);
const deviceCount = devices.length;
return (
<Wrapper isOpen={isOpen} isHoverable={isHoverable}>
<ClickWrapper disabled={disabled} onClick={() => this.handleClickWrapper()}>
<ImageWrapper>
<Dot color={getStatusColor(status)} />
<TrezorImage model={getVersion(device)} />
</ImageWrapper>
<LabelWrapper>
<Name>{device.instanceLabel}</Name>
<Status>{getStatusName(status)}</Status>
</LabelWrapper>
<IconWrapper>
{icon && icon}
{!icon && deviceCount > 1 && <Counter>{deviceCount}</Counter>}
{!icon && !disabled && (
<Icon
canAnimate={this.state.clicked === true}
isActive={isOpen}
size={25}
color={colors.TEXT_SECONDARY}
icon={icons.ARROW_DOWN}
/>
)
}
</IconWrapper>
</ClickWrapper>
</Wrapper>
);
}
}
DeviceHeader.propTypes = {
device: PropTypes.object,
devices: PropTypes.array,
transport: PropTypes.object,
icon: PropTypes.element,
isHoverable: PropTypes.bool,
disabled: PropTypes.bool,
isOpen: PropTypes.bool,
onClickWrapper: PropTypes.func.isRequired,
};
export default DeviceHeader;

View File

@ -1,6 +1,7 @@
import styled from 'styled-components';
import PropTypes from 'prop-types';
import React from 'react';
import Link from 'components/Link';
import { getYear } from 'date-fns';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
@ -11,13 +12,13 @@ import * as LogActions from 'actions/LogActions';
const Wrapper = styled.div`
width: 100%;
font-size: 12px;
background: ${colors.LANDING};
color: ${colors.TEXT_SECONDARY};
padding: 22px 48px;
border-top: 1px solid ${colors.DIVIDER};
display: flex;
`;
const A = styled.a`
const StyledLink = styled(Link)`
margin: 0 6px;
margin-right: 20px;
`;
@ -28,10 +29,10 @@ const Copy = styled.div`
const Footer = ({ toggle }) => (
<Wrapper>
<Copy>© {getYear(new Date())}</Copy>
<A href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" className="satoshi green">SatoshiLabs</A>
<A href="tos.pdf" target="_blank" rel="noreferrer noopener" className="green">Terms</A>
<A onClick={toggle} className="green">Show Log</A>
<Copy>&copy; {getYear(new Date())}</Copy>
<StyledLink href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" isGreen>SatoshiLabs</StyledLink>
<StyledLink href="tos.pdf" target="_blank" rel="noreferrer noopener" isGreen>Terms</StyledLink>
<StyledLink onClick={toggle} isGreen>Show Log</StyledLink>
</Wrapper>
);

View File

@ -24,9 +24,12 @@ const LayoutWrapper = styled.div`
height: 100%;
max-width: 1170px;
margin: 0 auto;
padding: 0 32px;
display: flex;
align-items: center;
@media screen and (max-width: 1170px) {
padding: 0 25px;
}
`;
const A = styled.a`

View File

@ -4,7 +4,7 @@ import colors from 'config/colors';
const baseStyles = css`
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
color: ${colors.BLACK};
color: ${colors.TEXT_PRIMARY};
font-weight: bold;
`;

View File

@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { keyframes } from 'styled-components';
// TODO: make animation of icons better
const rotate180up = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(180deg);
}
`;
const rotate180down = keyframes`
from {
transform: rotate(180deg);
}
to {
transform: rotate(0deg);
}
`;
const SvgWrapper = styled.svg`
animation: ${props => (props.canAnimate ? (props.isActive ? rotate180up : rotate180down) : null)} 0.2s linear 1 forwards;
`;
const Path = styled.path``;
const Icon = ({
icon,
size = 32,
color = 'black',
isActive,
canAnimate,
className,
onMouseEnter,
onMouseLeave,
onFocus,
onClick,
}) => (
<SvgWrapper
className={className}
canAnimate={canAnimate}
isActive={isActive}
style={{
display: 'inline-block',
verticalAlign: 'middle',
}}
width={`${size}`}
height={`${size}`}
viewBox="0 0 1024 1024"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onFocus={onFocus}
onClick={onClick}
>
{icon.map(path => (
<Path
key={path}
isActive={isActive}
style={{ fill: color }}
d={path}
/>
))}
</SvgWrapper>
);
Icon.propTypes = {
className: PropTypes.string,
canAnimate: PropTypes.bool,
icon: PropTypes.arrayOf(PropTypes.string).isRequired,
size: PropTypes.number,
isActive: PropTypes.bool,
color: PropTypes.string,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onFocus: PropTypes.func,
onClick: PropTypes.func,
};
export default Icon;

View File

@ -0,0 +1,67 @@
import React from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import { FONT_SIZE, TRANSITION } from 'config/variables';
import colors from 'config/colors';
const A = styled.a`
text-decoration: none;
cursor: pointer;
transition: ${TRANSITION.HOVER};
font-size: ${FONT_SIZE.SMALLER};
${props => props.isGreen && css`
border-bottom: 1px solid ${colors.GREEN_PRIMARY};
`}
${props => props.isGray && css`
border-bottom: 1px solid ${colors.TEXT_SECONDARY};
`}
&,
&:visited,
&:active,
&:hover {
${props => props.isGreen && css`
color: ${colors.GREEN_PRIMARY};
`}
${props => props.isGray && css`
color: ${colors.TEXT_SECONDARY};
`}
}
&:hover {
border-color: transparent;
}
`;
const Link = ({
children, className, href, target, rel, onClick, isGreen = false, isGray = false,
}) => (
<A
className={className}
href={href}
target={target}
rel={rel}
onClick={onClick}
isGreen={isGreen}
isGray={isGray}
>{children}
</A>
);
Link.propTypes = {
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
PropTypes.array,
]).isRequired,
className: PropTypes.string,
href: PropTypes.string,
target: PropTypes.string,
rel: PropTypes.string,
onClick: PropTypes.func,
isGreen: PropTypes.bool,
isGray: PropTypes.bool,
};
export default Link;

View File

@ -0,0 +1,81 @@
import React from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import Paragraph from 'components/Paragraph';
import { FONT_SIZE } from 'config/variables';
import { DASH, GREEN_COLOR } from 'config/animations';
import colors from 'config/colors';
const Wrapper = styled.div`
position: relative;
width: ${props => `${props.size}px`};
height: ${props => `${props.size}px`};
display: flex;
justify-content: center;
align-items: center;
`;
const SvgWrapper = styled.svg`
position: absolute;
width: 100%;
height: 100%;
animation: rotate 2s linear infinite;
transform-origin: center center;
`;
const CircleWrapper = styled.circle`
${props => props.isRoute && css`
stroke: ${colors.GRAY_LIGHT};
`}
${props => props.isPath && css`
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
animation: ${DASH} 1.5s ease-in-out infinite, ${GREEN_COLOR} 6s ease-in-out infinite;
stroke-linecap: round;
`};
`;
const StyledParagraph = styled(Paragraph)`
font-size: ${props => (props.isSmallText ? FONT_SIZE.SMALLER : FONT_SIZE.BASE)};
color: ${props => (props.isWhiteText ? colors.WHITE : colors.TEXT_PRIMARY)};
`;
const Loader = ({
className, text, isWhiteText = false, isSmallText, size = 100,
}) => (
<Wrapper className={className} size={size}>
<StyledParagraph isSmallText={isSmallText} isWhiteText={isWhiteText}>{text}</StyledParagraph>
<SvgWrapper viewBox="25 25 50 50">
<CircleWrapper
cx="50"
cy="50"
r="20"
fill="none"
stroke=""
strokeWidth="1"
strokeMiterlimit="10"
isRoute
/>
<CircleWrapper
cx="50"
cy="50"
r="20"
fill="none"
strokeWidth="1"
strokeMiterlimit="10"
isPath
/>
</SvgWrapper>
</Wrapper>
);
Loader.propTypes = {
isWhiteText: PropTypes.bool,
isSmallText: PropTypes.bool,
className: PropTypes.string,
text: PropTypes.string,
size: PropTypes.number,
};
export default Loader;

View File

@ -0,0 +1,83 @@
/* @flow */
import React from 'react';
import styled from 'styled-components';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import colors from 'config/colors';
import { H2 } from 'components/Heading';
import Icon from 'components/Icon';
import Paragraph from 'components/Paragraph';
import * as LogActions from 'actions/LogActions';
import icons from 'config/icons';
import type { State, Dispatch } from 'flowtype';
type Props = {
log: $ElementType<State, 'log'>,
toggle: typeof LogActions.toggle
}
const Wrapper = styled.div`
position: relative;
color: ${colors.INFO_PRIMARY};
background: ${colors.INFO_SECONDARY};
padding: 24px 48px;
display: flex;
flex-direction: column;
text-align: left;
`;
const Click = styled.div`
cursor: pointer;
position: absolute;
top: 8px;
right: 0;
padding: 12px;
color: inherit;
transition: opacity 0.3s;
&:active,
&:hover {
opacity: 0.6;
color: inherit;
}
`;
const Textarea = styled.textarea`
width: 100%;
height: 200px;
min-height: 200px;
resize: vertical;
font-size: 10px;
&:focus {
box-shadow: none;
}
`;
const StyledParagraph = styled(Paragraph)`
margin: 10px 0;
`;
const Log = (props: Props): ?React$Element<string> => {
if (!props.log.opened) return null;
return (
<Wrapper>
<Click onClick={props.toggle}>
<Icon size={25} color={colors.INFO_PRIMARY} icon={icons.CLOSE} />
</Click>
<H2>Log</H2>
<StyledParagraph>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</StyledParagraph>
<Textarea value={JSON.stringify(props.log.entries)} readOnly />
</Wrapper>
);
};
export default connect(
(state: State) => ({
log: state.log,
}),
(dispatch: Dispatch) => ({
toggle: bindActionCreators(LogActions.toggle, dispatch),
}),
)(Log);

View File

@ -0,0 +1,180 @@
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import styled, { css } from 'styled-components';
import colors from 'config/colors';
import NotificationButton from 'components/buttons/NotificationButton';
import Icon from 'components/Icon';
import icons from 'config/icons';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
import * as NOTIFICATION from 'actions/constants/notification';
import * as NotificationActions from 'actions/NotificationActions';
import Loader from 'components/Loader';
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;
${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<string> => {
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
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 (
<Wrapper type={props.className}>
{props.loading && <Loader size={50} /> }
{props.cancelable && (
<CloseClick onClick={() => close()}>
<Icon
color={getIconColor(props.className)}
icon={icons.CLOSE}
size={20}
/>
</CloseClick>
)}
<Body>
<MessageContent>
<StyledIcon
color={getIconColor(props.className)}
icon={icons[props.className.toUpperCase()]}
/>
<Texts>
<Title>{ props.title }</Title>
{ props.message && (
<Message>
<p dangerouslySetInnerHTML={{ __html: props.message }} />
</Message>
) }
</Texts>
</MessageContent>
</Body>
<AdditionalContent>
{props.actions && props.actions.length > 0 && (
<ActionContent>
{props.actions.map(action => (
<NotificationButton
key={action.label}
type={props.className}
text={action.label}
onClick={() => { close(); action.callback(); }}
>{action.label}
</NotificationButton>
))}
</ActionContent>
)}
</AdditionalContent>
</Wrapper>
);
};
export const NotificationGroup = (props) => {
const { notifications, close } = props;
return notifications.map((n, i) => (
<Notification
key={i}
className={n.type}
title={n.title}
message={n.message}
cancelable={n.cancelable}
actions={n.actions}
close={close}
/>
));
};
export default connect(
state => ({
notifications: state.notifications,
}),
dispatch => ({
close: bindActionCreators(NotificationActions.close, dispatch),
}),
)(NotificationGroup);

View File

@ -0,0 +1,35 @@
import React from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import colors from 'config/colors';
import { FONT_SIZE, LINE_HEIGHT } from 'config/variables';
const Wrapper = styled.p`
font-size: ${FONT_SIZE.BASE};
line-height: ${LINE_HEIGHT.BASE};
color: ${colors.TEXT_SECONDARY};
padding: 0;
${props => props.isSmaller && css`
font-size: ${FONT_SIZE.SMALLER};
`}
`;
const P = ({ children, className, isSmaller = false }) => (
<Wrapper
className={className}
isSmaller={isSmaller}
>{children}
</Wrapper>
);
P.propTypes = {
className: PropTypes.string,
isSmaller: PropTypes.bool,
children: PropTypes.oneOfType([
PropTypes.array,
PropTypes.string,
]),
};
export default P;

View File

@ -0,0 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactSelect from 'react-select';
import ReactAsyncSelect from 'react-select/lib/Async';
import colors from 'config/colors';
const styles = isSearchable => ({
singleValue: base => ({
...base,
width: '100%',
color: colors.TEXT_SECONDARY,
}),
control: (base, { isDisabled }) => ({
...base,
minHeight: 'initial',
height: '100%',
borderRadius: '2px',
borderColor: colors.DIVIDER,
boxShadow: 'none',
background: isDisabled ? colors.LANDING : colors.WHITE,
'&:hover': {
cursor: isSearchable ? 'text' : 'pointer',
borderColor: colors.DIVIDER,
},
}),
indicatorSeparator: () => ({
display: 'none',
}),
dropdownIndicator: (base, { isDisabled }) => ({
...base,
display: (isSearchable || isDisabled) ? 'none' : 'block',
color: colors.TEXT_SECONDARY,
path: '',
'&:hover': {
color: colors.TEXT_SECONDARY,
},
}),
menu: base => ({
...base,
margin: 0,
boxShadow: 'none',
}),
menuList: base => ({
...base,
padding: 0,
boxShadow: 'none',
background: colors.WHITE,
borderLeft: `1px solid ${colors.DIVIDER}`,
borderRight: `1px solid ${colors.DIVIDER}`,
borderBottom: `1px solid ${colors.DIVIDER}`,
}),
option: (base, { isSelected }) => ({
...base,
color: colors.TEXT_SECONDARY,
background: isSelected ? colors.LANDING : colors.WHITE,
borderRadius: 0,
'&:hover': {
cursor: 'pointer',
background: colors.LANDING,
},
}),
});
const propTypes = {
isAsync: PropTypes.bool,
isSearchable: PropTypes.bool,
};
const Select = props => <ReactSelect styles={styles(props.isSearchable)} {...props} />;
const AsyncSelect = props => <ReactAsyncSelect styles={styles(props.isSearchable)} {...props} />;
Select.propTypes = propTypes;
AsyncSelect.propTypes = propTypes;
export {
Select,
AsyncSelect,
};

View File

@ -0,0 +1,121 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import colors from 'config/colors';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
const Wrapper = styled.div`
width: 100%;
`;
const disabledColor = colors.TEXT_PRIMARY;
const TextArea = styled.textarea`
width: 100%;
padding: 5px 10px;
box-sizing: border-box;
min-height: 25px;
border: ${props => (props.isError ? `1px solid ${colors.ERROR_PRIMARY}` : `1px solid ${colors.TEXT_PRIMARY}`)};
resize: none;
outline: none;
background: transparent;
font-weight: ${FONT_WEIGHT.BASE};
font-size: ${FONT_SIZE.BASE};
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
/* placeholder styles do not work correctly when groupped into one block */
&::-webkit-input-placeholder {
color: ${colors.LIGHT_GRAY_1};
opacity: 1;
}
&::-moz-placeholder {
color: ${colors.LIGHT_GRAY_1};
opacity: 1;
}
&:-moz-placeholder {
color: ${colors.LIGHT_GRAY_1};
opacity: 1;
}
&:-ms-input-placeholder {
color: ${colors.LIGHT_GRAY_1};
opacity: 1;
}
&:disabled {
border: 1px solid ${disabledColor};
cursor: not-allowed;
&::-webkit-input-placeholder {
color: ${disabledColor};
opacity: 1;
}
&::-moz-placeholder {
color: ${disabledColor};
opacity: 1;
}
&:-moz-placeholder {
color: ${disabledColor};
opacity: 1;
}
&:-ms-input-placeholder {
color: ${disabledColor};
opacity: 1;
}
}
&:hover:not(:disabled),
&:focus:not(:disabled) {
border: 1px solid ${colors.TEXT_SECONDARY};
}
`;
const Textarea = ({
className,
placeholder = '',
value = '',
customStyle = {},
onFocus,
onBlur,
disabled,
onChange,
isError,
}) => (
<Wrapper>
<TextArea
className={className}
disabled={disabled}
style={customStyle}
onFocus={onFocus}
onBlur={onBlur}
value={value}
placeholder={placeholder}
onChange={e => onChange(e.target.value)}
isError={isError}
/>
</Wrapper>
);
Textarea.propTypes = {
className: PropTypes.string,
isError: PropTypes.bool,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onChange: PropTypes.func,
customStyle: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.string,
disabled: PropTypes.bool,
};
export default Textarea;

View File

@ -0,0 +1,26 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { FONT_SIZE } from 'config/variables';
const Wrapper = styled.div`
width: ${props => (props.isAside ? '260px' : '320px')};
font-size: ${FONT_SIZE.SMALLEST};
`;
const TooltipContent = ({
children, isAside = false,
}) => (
<Wrapper
isAside={isAside}
>
{children}
</Wrapper>
);
TooltipContent.propTypes = {
children: PropTypes.node,
isAside: PropTypes.bool,
};
export default TooltipContent;

View File

@ -0,0 +1,172 @@
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: ${props => (props.icon ? '4px 24px 4px 15px' : '11px 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.isDisabled && css`
pointer-events: none;
color: ${colors.TEXT_SECONDARY};
background: ${colors.GRAY_LIGHT};
`}
${props => props.isWhite && css`
background: ${colors.WHITE};
color: ${colors.TEXT_SECONDARY};
border: 1px solid ${colors.DIVIDER};
&:hover {
color: ${colors.TEXT_PRIMARY};
background: ${colors.DIVIDER};
}
&:active {
color: ${colors.TEXT_PRIMARY};
background: ${colors.DIVIDER};
}
`}
${props => props.isTransparent && css`
background: transparent;
border: 0px;
color: ${colors.TEXT_SECONDARY};
&:hover,
&:active {
color: ${colors.TEXT_PRIMARY};
background: transparent;
}
`}
${props => props.isWebUsb && css`
position: relative;
padding: 12px 24px 12px 40px;
background: transparent;
color: ${colors.GREEN_PRIMARY};
border: 1px solid ${colors.GREEN_PRIMARY};
transition: ${TRANSITION.HOVER};
&:before,
&:after {
content: '';
position: absolute;
background: ${colors.GREEN_PRIMARY};
top: 0;
bottom: 0;
margin: auto;
transition: ${TRANSITION.HOVER};
}
&:before {
width: 12px;
height: 2px;
left: 18px;
}
&:after {
width: 2px;
height: 12px;
left: 23px;
}
&:hover {
background: ${colors.GREEN_PRIMARY};
color: ${colors.WHITE};
&:before,
&:after {
background: ${colors.WHITE};
}
}
iframe {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
`}
`;
const IconWrapper = styled.span`
${props => ((props.hasChildren && !props.isRight) ? 'margin: 0 8px 0 -4px;' : '')};
${props => ((props.hasChildren && props.isRight) ? 'margin: 0 -4px 0 8px;' : '')};
`;
const Button = ({
children,
className,
icon,
onClick = () => { },
isDisabled = false,
isWhite = false,
isWebUsb = false,
isTransparent = false,
hasIconRight = false,
}) => (
<Wrapper
className={className}
icon={icon}
onClick={onClick}
isDisabled={isDisabled}
isWhite={isWhite}
isWebUsb={isWebUsb}
isTransparent={isTransparent}
>
{hasIconRight && children}
{icon && (
<IconWrapper
hasChildren={!!children}
isRight={hasIconRight}
>
<Icon
icon={icon.type}
color={icon.color}
size={icon.size}
isActive={icon.isActive}
canAnimate={icon.canAnimate}
/>
</IconWrapper>
)}
{!hasIconRight && children}
</Wrapper>
);
Button.propTypes = {
children: PropTypes.element.isRequired,
className: PropTypes.string,
onClick: PropTypes.func,
isDisabled: PropTypes.bool,
isWhite: PropTypes.bool,
isWebUsb: PropTypes.bool,
isTransparent: PropTypes.bool,
icon: PropTypes.shape({
type: PropTypes.arrayOf(PropTypes.string).isRequired,
color: PropTypes.string,
size: PropTypes.number,
isActive: PropTypes.bool,
canAnimate: PropTypes.bool,
}),
hasIconRight: PropTypes.bool,
};
export default Button;

View File

@ -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 = ({
children, className, icon, onClick = () => { }, type = null,
}) => (
<Wrapper
className={className}
icon={icon}
onClick={onClick}
type={type}
>
{icon && (
<IconWrapper>
<Icon
icon={icon.type}
color={icon.color}
size={icon.size}
/>
</IconWrapper>
)}
{children}
</Wrapper>
);
NotificationButton.propTypes = {
children: PropTypes.element.isRequired,
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,
}),
};
export default NotificationButton;

View File

@ -0,0 +1,52 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import colors from 'config/colors';
const Wrapper = styled.button`
width: 80px;
height: 80px;
margin-top: 15px;
margin-left: 10px;
font-size: 22px;
font-weight: 600;
color: ${colors.TEXT_PRIMARY};
border: 1px solid ${colors.DIVIDER};
background: ${colors.WHITE};
transition: all 0.3s;
&:first-child {
margin-left: 0px;
}
&:hover {
color: ${colors.TEXT_PRIMARY};
background-color: ${colors.WHITE};
border-color: ${colors.TEXT_SECONDARY};
}
&:active {
color: ${colors.TEXT_PRIMARY};
background: ${colors.DIVIDER};
border-color: ${colors.DIVIDER};
}
`;
const PinButton = ({
children, className, onClick,
}) => (
<Wrapper
className={className}
onClick={onClick}
>
{children}
</Wrapper>
);
PinButton.propTypes = {
className: PropTypes.string,
children: PropTypes.string.isRequired,
onClick: PropTypes.func,
};
export default PinButton;

View File

@ -0,0 +1,41 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { ICON_SIZE } from 'config/variables';
const Logo = styled.div`
height: ${ICON_SIZE.BASE};
width: ${ICON_SIZE.BASE};
margin-right: 5px;
background-repeat: no-repeat;
background-position: center;
background-size: auto ${ICON_SIZE.BASE};
background-image: url('${props => props.coinImg}');
`;
const CoinLogo = ({
className, coinNetwork, coinImg,
}) => {
let coinImgName = coinNetwork;
if (coinImgName === 'ethereum') {
coinImgName = 'eth';
} else if (coinImgName === 'ethereum-classic') {
coinImgName = 'etc';
}
const coinImgUrl = `../images/${coinImgName}-logo.png`;
return (
<Logo
className={className}
coinImg={coinImgName ? coinImgUrl : coinImg}
/>
);
};
CoinLogo.propTypes = {
className: PropTypes.string,
coinImg: PropTypes.string,
coinNetwork: PropTypes.string,
};
export default CoinLogo;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,22 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
const Wrapper = styled.div``;
const Img = styled.img`
width: ${props => (props.model === 'T' ? '17px' : '13px')};
`;
const TrezorImage = ({ model }) => (
<Wrapper>
<Img model={model} src={model === 'T' ? './images/trezor-T.png' : './images/trezor-1.png'} />
</Wrapper>
);
TrezorImage.propTypes = {
model: PropTypes.string,
status: PropTypes.string,
};
export default TrezorImage;

View File

@ -0,0 +1,159 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import colors from 'config/colors';
import ICONS from 'config/icons';
import Icon from 'components/Icon';
import { FONT_SIZE, FONT_WEIGHT, TRANSITION } from 'config/variables';
const Wrapper = styled.div`
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
`;
const InputWrapper = styled.div`
display: flex;
`;
const InputIconWrapper = styled.div`
flex: 1;
position: relative;
display: inline-block;
`;
const InputLabel = styled.span`
padding-bottom: 4px;
color: ${colors.TEXT_SECONDARY};
`;
const StyledInput = styled.input`
width: 100%;
padding: 6px 12px;
line-height: 1.42857143;
font-size: ${FONT_SIZE.SMALL};
font-weight: ${FONT_WEIGHT.SMALLEST};
color: ${colors.TEXT_PRIMARY};
border-radius: 2px;
${props => props.hasAddon && css`
border-top-right-radius: 0;
border-bottom-right-radius: 0;
`}
border: 1px solid ${colors.DIVIDER};
border-color: ${props => props.borderColor};
background-color: ${colors.WHITE};
transition: ${TRANSITION.HOVER};
&:disabled {
pointer-events: none;
background: ${colors.GRAY_LIGHT};
color: ${colors.TEXT_SECONDARY};
}
`;
const StyledIcon = styled(Icon)`
position: absolute;
left: auto;
right: 10px;
`;
const BottomText = styled.span`
margin-top: 10px;
font-size: ${FONT_SIZE.SMALLER};
color: ${props => (props.color ? props.color : colors.TEXT_SECONDARY)};
`;
class Input extends Component {
getIcon(inputState) {
let icon = [];
if (inputState === 'success') {
icon = ICONS.CHECKED;
} else if (inputState === 'warning') {
icon = ICONS.WARNING;
} else if (inputState === 'error') {
icon = ICONS.ERROR;
}
return icon;
}
getColor(inputState) {
let color = '';
if (inputState === 'success') {
color = colors.SUCCESS_PRIMARY;
} else if (inputState === 'warning') {
color = colors.WARNING_PRIMARY;
} else if (inputState === 'error') {
color = colors.ERROR_PRIMARY;
}
return color;
}
render() {
return (
<Wrapper
className={this.props.className}
>
{this.props.inputLabel && (
<InputLabel>{this.props.inputLabel}</InputLabel>
)}
<InputWrapper>
<InputIconWrapper>
{this.props.state && (
<StyledIcon
icon={this.getIcon(this.props.state)}
color={this.getColor(this.props.state)}
/>
)}
<StyledInput
hasAddon={!!this.props.sideAddons}
type={this.props.type}
placeholder={this.props.placeholder}
autoComplete={this.props.autoComplete}
autoCorrect={this.props.autoCorrect}
autoCapitalize={this.props.autoCapitalize}
spellCheck={this.props.spellCheck}
value={this.props.value}
onChange={this.props.onChange}
borderColor={this.getColor(this.props.state)}
disabled={this.props.isDisabled}
/>
</InputIconWrapper>
{this.props.sideAddons && this.props.sideAddons.map(sideAddon => sideAddon)}
</InputWrapper>
{this.props.bottomText && (
<BottomText
color={this.getColor(this.props.state)}
>
{this.props.bottomText}
</BottomText>
)}
</Wrapper>
);
}
}
Input.propTypes = {
className: PropTypes.string,
placeholder: PropTypes.string,
type: PropTypes.string,
autoComplete: PropTypes.string,
autoCorrect: PropTypes.string,
autoCapitalize: PropTypes.string,
spellCheck: PropTypes.string,
value: PropTypes.string.isRequired,
onChange: PropTypes.func,
state: PropTypes.string,
bottomText: PropTypes.string,
inputLabel: PropTypes.string,
sideAddons: PropTypes.arrayOf(PropTypes.node),
isDisabled: PropTypes.bool,
};
Input.defaultProps = {
type: 'text',
};
export default Input;

View File

@ -0,0 +1,54 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import colors from 'config/colors';
import Icon from 'components/Icon';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
import icons from 'config/icons';
const Wrapper = styled.div`
position: relative;
`;
const StyledInput = styled.input`
letter-spacing: 7px;
width: 100%;
font-weight: ${FONT_WEIGHT.BIGGER};
font-size: ${FONT_SIZE.BIGGER};
padding: 5px 31px 10px 20px;
color: ${colors.TEXT_PRIMARY};
background: transparent;
`;
const StyledIcon = styled(Icon)`
position: absolute;
top: 10px;
right: 15px;
cursor: pointer;
`;
const Input = ({
onChange,
onDeleteClick,
value,
}) => (
<Wrapper>
<StyledInput
disabled
type="password"
maxLength="9"
autoComplete="off"
value={value}
onChange={onChange}
/>
<StyledIcon onClick={() => onDeleteClick()} color={colors.TEXT_PRIMARY} icon={icons.BACK} />
</Wrapper>
);
Input.propTypes = {
onDeleteClick: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func,
};
export default Input;

View File

@ -0,0 +1,48 @@
import styled from 'styled-components';
import H3 from 'components/Heading';
import colors from 'config/colors';
import P from 'components/Paragraph';
import { FONT_SIZE } from 'config/variables';
import React from 'react';
const Wrapper = styled.div`
width: 390px;
`;
const Header = styled.div`
padding: 24px 48px;
`;
const Content = styled.div`
border-top: 1px solid ${colors.DIVIDER};
background: ${colors.MAIN};
padding: 24px 48px;
`;
const Label = styled.div`
font-size: ${FONT_SIZE.SMALLER};
color: ${colors.TEXT_SECONDARY};
`;
const ConfirmAddress = (props) => {
const {
account,
network,
} = props.selectedAccount;
if (!account || !network) return null;
return (
<Wrapper>
<Header>
<H3>Confirm address on TREZOR</H3>
<P>Please compare your address on device with address shown bellow.</P>
</Header>
<Content>
<P>{ account.address }</P>
<Label>{ network.symbol } account #{ (account.index + 1) }</Label>
</Content>
</Wrapper>
);
};
export default ConfirmAddress;

View File

@ -0,0 +1,60 @@
import React from 'react';
import colors from 'config/colors';
import styled from 'styled-components';
import P from 'components/Paragraph';
import Icon from 'components/Icon';
import icons from 'config/icons';
import { H3 } from 'components/Heading';
const Wrapper = styled.div`
width: 390px;
padding: 12px 10px;
`;
const Header = styled.div`
padding: 24px 48px;
`;
const Content = styled.div`
border-top: 1px solid ${colors.DIVIDER};
background: ${colors.MAIN};
padding: 24px 48px;
`;
const Label = styled.div`
padding-top: 5px;
font-size: 10px;
color: ${colors.TEXT_SECONDARY};
`;
const ConfirmSignTx = (props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
const {
amount,
address,
currency,
selectedFeeLevel,
} = props.sendForm;
return (
<Wrapper>
<Header>
<Icon icon={icons.T1} size={60} color={colors.TEXT_SECONDARY} />
<H3>Confirm transaction on { device.label } device</H3>
<P isSmaller>Details are shown on display</P>
</Header>
<Content>
<Label>Send</Label>
<P>{`${amount} ${currency}` }</P>
<Label>To</Label>
<P>{ address }</P>
<Label>Fee</Label>
<P>{ selectedFeeLevel.label }</P>
</Content>
</Wrapper>
);
};
export default ConfirmSignTx;

View File

@ -1,34 +1,43 @@
/* @flow */
import React, { Component } from 'react';
import { H2 } from 'components/Heading';
import P from 'components/Paragraph';
import styled from 'styled-components';
import Icon from 'components/Icon';
import colors from 'config/colors';
import icons from 'config/icons';
import Button from 'components/buttons/Button';
import Link from 'components/Link';
import { findAccount } from 'reducers/AccountsReducer';
import type { Props } from './index';
const ConfirmAddress = (props: Props) => {
const {
account,
network,
} = props.selectedAccount;
if (!account || !network) return null;
const StyledLink = styled(Link)`
position: absolute;
right: 15px;
top: 15px;
`;
return (
<div className="confirm-address">
<div className="header">
<h3>Confirm address on TREZOR</h3>
<p>Please compare your address on device with address shown bellow.</p>
</div>
<div className="content">
<p>{ account.address }</p>
<label>{ network.symbol } account #{ (account.index + 1) }</label>
</div>
</div>
);
};
export default ConfirmAddress;
const Wrapper = styled.div`
width: 370px;
padding: 24px 48px;
`;
export class ConfirmUnverifiedAddress extends Component<Props> {
const StyledP = styled(P)`
padding: 10px 0px;
`;
const Row = styled.div`
display: flex;
flex-direction: column;
padding: 10px 0;
`;
const StyledButton = styled(Button)`
margin: 0 0 10px 0;
`;
class ConfirmUnverifiedAddress extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void {
@ -89,13 +98,19 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
}
return (
<div className="confirm-address-unverified">
<button className="close-modal transparent" onClick={onCancel} />
<h3>{ deviceStatus }</h3>
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</p>
<button onClick={event => this.verifyAddress()}>Try again</button>
<button className="white" onClick={event => this.showUnverifiedAddress()}>Show unverified address</button>
</div>
<Wrapper>
<StyledLink onClick={onCancel}>
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
</StyledLink>
<H2>{ deviceStatus }</H2>
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</StyledP>
<Row>
<StyledButton onClick={() => this.verifyAddress()}>Try again</StyledButton>
<StyledButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</StyledButton>
</Row>
</Wrapper>
);
}
}
export default ConfirmUnverifiedAddress;

View File

@ -1,9 +1,18 @@
/* @flow */
import React, { Component } from 'react';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import Button from 'components/buttons/Button';
import Input from 'components/inputs/Input';
import { getDuplicateInstanceNumber } from 'reducers/utils';
import type { Props } from './index';
import { FONT_SIZE } from 'config/variables';
import Icon from 'components/Icon';
import icons from 'config/icons';
import colors from 'config/colors';
import Link from 'components/Link';
import { Props } from './index';
type State = {
defaultName: string;
@ -12,6 +21,47 @@ type State = {
isUsed: boolean;
}
const StyledLink = styled(Link)`
position: absolute;
right: 15px;
top: 15px;
`;
const Wrapper = styled.div`
width: 360px;
padding: 24px 48px;
`;
const Column = styled.div`
display: flex;
padding: 10px 0;
flex-direction: column;
`;
const StyledP = styled(P)`
padding: 10px 0px;
`;
const StyledButton = styled(Button)`
margin: 0 0 10px 0;
`;
const Label = styled.div`
display: flex;
text-align: left;
font-size: ${FONT_SIZE.SMALLER};
flex-direction: column;
padding-bottom: 5px;
`;
const ErrorMessage = styled.div`
color: ${colors.ERROR_PRIMARY};
font-size: ${FONT_SIZE.SMALLER};
padding-top: 5px;
text-align: center;
width: 100%;
`;
export default class DuplicateDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
@ -83,29 +133,40 @@ export default class DuplicateDevice extends Component<Props, State> {
} = this.state;
return (
<div className="duplicate">
<button className="close-modal transparent" onClick={onCancel} />
<h3>Clone { device.label }?</h3>
<p>This will create new instance of device which can be used with different passphrase</p>
<div className="row">
<label>Instance name</label>
<input
<Wrapper>
<StyledLink onClick={onCancel}>
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
</StyledLink>
<H3>Clone { device.label }?</H3>
<StyledP isSmaller>This will create new instance of device which can be used with different passphrase</StyledP>
<Column>
<Label>Instance name</Label>
<Input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
className={isUsed ? 'not-valid' : null}
placeholder={defaultName}
ref={(element) => { this.input = element; }}
innerRef={(element) => { this.input = element; }}
onChange={event => this.onNameChange(event.currentTarget.value)}
defaultValue={instanceName}
value={instanceName}
/>
{ isUsed ? <span className="error">Instance name is already in use</span> : null }
</div>
<button disabled={isUsed} onClick={event => this.submit()}>Create new instance</button>
<button className="white" onClick={onCancel}>Cancel</button>
</div>
{ isUsed && <ErrorMessage>Instance name is already in use</ErrorMessage> }
</Column>
<Column>
<StyledButton
disabled={isUsed}
onClick={() => this.submit()}
>Create new instance
</StyledButton>
<StyledButton
isWhite
onClick={onCancel}
>Cancel
</StyledButton>
</Column>
</Wrapper>
);
}
}

View File

@ -0,0 +1,66 @@
import React, { Component } from 'react';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import Button from 'components/buttons/Button';
const Wrapper = styled.div`
width: 360px;
padding: 24px 48px;
`;
const StyledP = styled(P)`
padding: 14px 0px;
`;
const StyledButton = styled(Button)`
margin: 0 0 10px 0;
`;
const Row = styled.div`
display: flex;
flex-direction: column;
padding: 10px 0;
`;
class ForgetDevice extends Component {
componentDidMount() {
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
}
componentWillUnmount() {
window.removeEventListener('keydown', this.keyboardHandler, false);
}
keyboardHandler(event) {
if (event.keyCode === 13) {
event.preventDefault();
this.forget();
}
}
forget() {
if (this.props.modal.opened) {
this.props.modalActions.onForgetSingleDevice(this.props.modal.device);
}
}
render() {
if (!this.props.modal.opened) return null;
const { device } = this.props.modal;
const { onCancel } = this.props.modalActions;
return (
<Wrapper>
<H3>Forget { device.instanceLabel }?</H3>
<StyledP isSmaller>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your TREZOR again.</StyledP>
<Row>
<StyledButton onClick={() => this.forget()}>Forget</StyledButton>
<StyledButton isWhite onClick={onCancel}>Don't forget</StyledButton>
</Row>
</Wrapper>
);
}
}
export default ForgetDevice;

View File

@ -1,8 +1,10 @@
/* @flow */
import React, { Component } from 'react';
import Loader from 'components/LoaderCircle';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import Loader from 'components/Loader';
import Button from 'components/buttons/Button';
import type { Props } from './index';
@ -11,6 +13,40 @@ type State = {
ticker?: number;
}
const ButtonContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
`;
const StyledP = styled(P)`
padding: 10px 0;
`;
const Wrapper = styled.div`
width: 360px;
padding: 24px 48px;
`;
const Text = styled.div`
padding-right: 10px;
`;
const Column = styled.div`
display: flex;
flex-direction: column;
`;
const StyledButton = styled(Button)`
margin: 5px 0;
`;
const StyledLoader = styled(Loader)`
position: absolute;
left: 200px;
`;
export default class RememberDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
@ -86,52 +122,28 @@ export default class RememberDevice extends Component<Props, State> {
});
}
return (
<div className="remember">
<h3>Forget {label}?</h3>
<p>Would you like TREZOR Wallet to forget your { devicePlural }, so that it is still visible even while disconnected?</p>
<button onClick={event => this.forget()}><span>Forget <Loader size="28" label={this.state.countdown.toString()} /></span></button>
<button className="white" onClick={event => onRememberDevice(device)}>Remember</button>
</div>
<Wrapper>
<H3>Forget {label}?</H3>
<StyledP isSmaller>Would you like TREZOR Wallet to forget your { devicePlural }, so that it is still visible even while disconnected?</StyledP>
<Column>
<StyledButton onClick={() => this.forget()}>
<ButtonContent>
<Text>Forget</Text>
<StyledLoader
isSmallText
isWhiteText
size={28}
text={this.state.countdown.toString()}
/>
</ButtonContent>
</StyledButton>
<StyledButton
isWhite
onClick={() => onRememberDevice(device)}
>Remember
</StyledButton>
</Column>
</Wrapper>
);
}
}
export class ForgetDevice extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) {
event.preventDefault();
this.forget();
}
}
componentDidMount(): void {
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
}
componentWillUnmount(): void {
window.removeEventListener('keydown', this.keyboardHandler, false);
}
forget() {
if (this.props.modal.opened) {
this.props.modalActions.onForgetSingleDevice(this.props.modal.device);
}
}
render() {
if (!this.props.modal.opened) return null;
const { device } = this.props.modal;
const { onCancel } = this.props.modalActions;
return (
<div className="remember">
<h3>Forget { device.instanceLabel } ?</h3>
<p>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your TREZOR again.</p>
<button onClick={event => this.forget()}>Forget</button>
<button className="white" onClick={onCancel}>Don't forget</button>
</div>
);
}
}
}

View File

@ -1,12 +1,11 @@
/* @flow */
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { CSSTransition, Transition } from 'react-transition-group';
import styled from 'styled-components';
import colors from 'config/colors';
import { CSSTransition } from 'react-transition-group';
import { UI } from 'trezor-connect';
@ -14,19 +13,22 @@ import { default as ModalActions } from 'actions/ModalActions';
import { default as ReceiveActions } from 'actions/ReceiveActions';
import * as RECEIVE from 'actions/constants/receive';
import * as MODAL from 'actions/constants/modal';
import * as CONNECT from 'actions/constants/TrezorConnect';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from 'flowtype';
import Pin from './Pin';
import InvalidPin from './InvalidPin';
import Passphrase from './Passphrase';
import PassphraseType from './PassphraseType';
import ConfirmSignTx from './ConfirmSignTx';
import ConfirmAddress, { ConfirmUnverifiedAddress } from './ConfirmAddress';
import RememberDevice, { ForgetDevice } from './RememberDevice';
import DuplicateDevice from './DuplicateDevice';
import Pin from 'components/modals/pin/Pin';
import InvalidPin from 'components/modals/pin/Invalid';
import Passphrase from 'components/modals/passphrase/Passphrase';
import PassphraseType from 'components/modals/passphrase/Type';
import ConfirmSignTx from 'components/modals/confirm/SignTx';
import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddress';
import ForgetDevice from 'components/modals/device/Forget';
import RememberDevice from 'components/modals/device/Remember';
import DuplicateDevice from 'components/modals/device/Duplicate';
type OwnProps = { }
@ -51,7 +53,6 @@ export type Props = StateProps & DispatchProps;
const duration = 300;
const Fade = ({ children, ...props }) => (
<CSSTransition
{...props}
@ -62,6 +63,29 @@ const Fade = ({ children, ...props }) => (
</CSSTransition>
);
const ModalContainer = styled.div`
position: fixed;
z-index: 10000;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
background: rgba(0, 0, 0, 0.35);
display: flex;
flex-direction: column;
align-items: center;
overflow: auto;
padding: 20px;
`;
const ModalWindow = styled.div`
margin: auto;
position: relative;
border-radius: 4px;
background-color: ${colors.WHITE};
text-align: center;
`;
class Modal extends Component<Props> {
render() {
if (!this.props.modal.opened) return null;
@ -82,9 +106,6 @@ class Modal extends Component<Props> {
case 'ButtonRequest_SignTx':
component = (<ConfirmSignTx {...this.props} />);
break;
// case "ButtonRequest_Address" :
// component = (<ConfirmAddress { ...this.props } />)
// break;
case 'ButtonRequest_PassphraseType':
component = (<PassphraseType {...this.props} />);
break;
@ -111,11 +132,11 @@ class Modal extends Component<Props> {
if (opened) {
ch = (
<Fade key="1">
<div className="modal-container">
<div className="modal-window">
<ModalContainer>
<ModalWindow>
{ component }
</div>
</div>
</ModalWindow>
</ModalContainer>
</Fade>
);
}

View File

@ -1,11 +1,47 @@
/* @flow */
import React, { Component } from 'react';
import raf from 'raf';
import colors from 'config/colors';
import P from 'components/Paragraph';
import { FONT_SIZE } from 'config/variables';
import { H2 } from 'components/Heading';
import Link from 'components/Link';
import Checkbox from 'components/Checkbox';
import Button from 'components/buttons/Button';
import Input from 'components/inputs/Input';
import styled from 'styled-components';
import type { Props } from './index';
const Wrapper = styled.div`
padding: 24px 48px;
max-width: 390px;
`;
const Label = styled.div`
${colors.TEXT_SECONDARY};
font-size: ${FONT_SIZE.SMALL};
padding-bottom: 5px;
`;
const PassphraseError = styled.div``;
const Row = styled.div`
position: relative;
text-align: left;
padding-top: 24px;
display: flex;
flex-direction: column;
`;
const Footer = styled.div`
display: flex;
padding-top: 10px;
align-items: center;
flex-direction: column;
justify-content: center;
`;
type State = {
deviceLabel: string;
singleInput: boolean;
@ -233,18 +269,16 @@ export default class PinModal extends Component<Props, State> {
//let passphraseInputType: string = visible || passphraseFocused ? "text" : "password";
//let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password";
const showPassphraseCheckboxFn: Function = visible ? this.onPassphraseHide : this.onPassphraseShow;
console.log('passphraseInputType', passphraseInputType);
return (
<div className="passphrase">
{/* <button className="close-modal transparent" onClick={ event => this.submit(true) }></button> */}
<h3>Enter { deviceLabel } passphrase</h3>
<p>Note that passphrase is case-sensitive.</p>
<div className="row">
<label>Passphrase</label>
<input
ref={(element) => { this.passphraseInput = element; }}
<Wrapper>
{/* ?<H2>Enter { deviceLabel } passphrase</H2> */}
{/* <P isSmaller>Note that passphrase is case-sensitive.</P> */}
<Row>
<Label>Passphrase</Label>
<Input
innerRef={(element) => { this.passphraseInput = element; }}
onChange={event => this.onPassphraseChange('passphrase', event.currentTarget.value)}
type={passphraseInputType}
autoComplete="off"
@ -252,17 +286,16 @@ export default class PinModal extends Component<Props, State> {
autoCapitalize="off"
spellCheck="false"
data-lpignore="true"
onFocus={event => this.onPassphraseFocus('passphrase')}
onBlur={event => this.onPassphraseBlur('passphrase')}
onFocus={() => this.onPassphraseFocus('passphrase')}
onBlur={() => this.onPassphraseBlur('passphrase')}
tabIndex="1"
/>
</div>
{ singleInput ? null : (
<div className="row">
<label>Re-enter passphrase</label>
<input
ref={(element) => { this.passphraseRevisionInput = element; }}
</Row>
{!singleInput && (
<Row>
<Label>Re-enter passphrase</Label>
<Input
innerRef={(element) => { this.passphraseRevisionInput = element; }}
onChange={event => this.onPassphraseChange('revision', event.currentTarget.value)}
type={passphraseRevisionInputType}
autoComplete="off"
@ -270,39 +303,26 @@ export default class PinModal extends Component<Props, State> {
autoCapitalize="off"
spellCheck="false"
data-lpignore="true"
onFocus={event => this.onPassphraseFocus('revision')}
onBlur={event => this.onPassphraseBlur('revision')}
onFocus={() => this.onPassphraseFocus('revision')}
onBlur={() => this.onPassphraseBlur('revision')}
tabIndex="2"
/>
{ !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null }
</div>
{!match && passphraseRevisionTouched && <PassphraseError className="error">Passphrases do not match</PassphraseError> }
</Row>
) }
<div className="row">
<label className="custom-checkbox">
<input type="checkbox" tabIndex="3" onChange={showPassphraseCheckboxFn} checked={visible} />
<span className="indicator" />
Show passphrase
</label>
{/* <label className="custom-checkbox">
<input type="checkbox" className="save_passphrase" tabIndex="4" onChange={ savePassphraseCheckboxFn } checked={ passphraseCached } />
<span className="indicator"></span>
<span>Save passphrase for current session (i)</span>
</label> */}
</div>
<div>
<button type="button" className="submit" tabIndex="4" disabled={!match} onClick={event => this.submit()}>Enter</button>
</div>
<div>
<p>If you want to access your default account</p>
<p><a className="green" onClick={event => this.submit(true)}>Leave passphrase blank</a></p>
</div>
</div>
<Row>
<Checkbox onClick={showPassphraseCheckboxFn} checked={visible}>Show passphrase</Checkbox>
</Row>
<Row>
<Button type="button" tabIndex="4" disabled={!match} onClick={event => this.submit()}>Enter</Button>
</Row>
<Footer>
<P isSmaller>If you want to access your default account</P>
<P isSmaller>
<Link isGreen onClick={() => this.submit(true)}>Leave passphrase blank</Link>
</P>
</Footer>
</Wrapper>
);
}
}

View File

@ -0,0 +1,29 @@
import React from 'react';
import Icon from 'components/Icon';
import colors from 'config/colors';
import icons from 'config/icons';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
const Wrapper = styled.div`
width: 360px;
padding: 24px 48px;
`;
const Header = styled.div``;
const Confirmation = (props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
return (
<Wrapper>
<Header>
<Icon icon={icons.T1} size={60} color={colors.TEXT_SECONDARY} />
<H3>Complete the action on { device.label } device</H3>
</Header>
</Wrapper>
);
};
export default Confirmation;

View File

@ -0,0 +1,24 @@
import React from 'react';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import type { Props } from './index';
const Wrapper = styled.div`
padding: 24px 48px;
`;
const InvalidPin = (props: Props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
return (
<Wrapper>
<H3>Entered PIN for { device.label } is not correct</H3>
<P isSmaller>Retrying...</P>
</Wrapper>
);
};
export default InvalidPin;

View File

@ -0,0 +1,180 @@
/* @flow */
import P from 'components/Paragraph';
import { H2 } from 'components/Heading';
import React, { Component } from 'react';
import Link from 'components/Link';
import colors from 'config/colors';
import styled from 'styled-components';
import PinInput from 'components/inputs/PinInput';
import PinButton from 'components/buttons/PinButton';
import Button from 'components/buttons/Button';
import type { Props } from './index';
type State = {
pin: string;
}
const Wrapper = styled.div`
padding: 24px 48px;
`;
const InputRow = styled.div`
margin-top: 24px;
max-width: 260px;
`;
const PinRow = styled.div``;
const StyledP = styled(P)`
padding-top: 5px;
`;
const StyledLink = styled(Link)`
padding-left: 5px;
`;
const Footer = styled.div`
margin: 20px 0 10px 0;
display: flex;
flex-direction: column;
`;
class Pin extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
constructor(props: Props) {
super(props);
this.state = {
pin: '',
};
}
onPinAdd = (input: number): void => {
let pin: string = this.state.pin;
if (pin.length < 9) {
pin += input;
this.setState({
pin,
});
}
}
onPinBackspace = (): void => {
this.setState({
pin: this.state.pin.substring(0, this.state.pin.length - 1),
});
}
keyboardHandler(event: KeyboardEvent): void {
const { onPinSubmit } = this.props.modalActions;
const { pin } = this.state;
event.preventDefault();
switch (event.keyCode) {
case 13:
// enter,
onPinSubmit(pin);
break;
// backspace
case 8:
this.onPinBackspace();
break;
// numeric and numpad
case 49:
case 97:
this.onPinAdd(1);
break;
case 50:
case 98:
this.onPinAdd(2);
break;
case 51:
case 99:
this.onPinAdd(3);
break;
case 52:
case 100:
this.onPinAdd(4);
break;
case 53:
case 101:
this.onPinAdd(5);
break;
case 54:
case 102:
this.onPinAdd(6);
break;
case 55:
case 103:
this.onPinAdd(7);
break;
case 56:
case 104:
this.onPinAdd(8);
break;
case 57:
case 105:
this.onPinAdd(9);
break;
}
}
componentWillMount(): void {
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
}
componentWillUnmount(): void {
window.removeEventListener('keydown', this.keyboardHandler, false);
}
render() {
if (!this.props.modal.opened) return null;
const { onPinSubmit } = this.props.modalActions;
const { device } = this.props.modal;
const { pin } = this.state;
return (
<Wrapper>
<H2>Enter { device.label } PIN</H2>
<P isSmaller>The PIN layout is displayed on your TREZOR.</P>
<InputRow>
<PinInput value={pin} onDeleteClick={() => this.onPinBackspace()} />
</InputRow>
<PinRow>
<PinButton type="button" data-value="7" onClick={() => this.onPinAdd(7)}>&#8226; </PinButton>
<PinButton type="button" data-value="8" onClick={() => this.onPinAdd(8)}>&#8226;</PinButton>
<PinButton type="button" data-value="9" onClick={() => this.onPinAdd(9)}>&#8226;</PinButton>
</PinRow>
<PinRow>
<PinButton type="button" data-value="4" onClick={() => this.onPinAdd(4)}>&#8226; </PinButton>
<PinButton type="button" data-value="5" onClick={() => this.onPinAdd(5)}>&#8226;</PinButton>
<PinButton type="button" data-value="6" onClick={() => this.onPinAdd(6)}>&#8226;</PinButton>
</PinRow>
<PinRow>
<PinButton type="button" data-value="1" onClick={() => this.onPinAdd(1)}>&#8226; </PinButton>
<PinButton type="button" data-value="2" onClick={() => this.onPinAdd(2)}>&#8226;</PinButton>
<PinButton type="button" data-value="3" onClick={() => this.onPinAdd(3)}>&#8226;</PinButton>
</PinRow>
<Footer>
<Button type="button" onClick={() => onPinSubmit(pin)}>Enter PIN</Button>
<StyledP isSmaller>Not sure how PIN works?
<StyledLink
isGreen
href="http://doc.satoshilabs.com/trezor-user/enteringyourpin.html"
target="_blank"
rel="noreferrer noopener"
>Learn more
</StyledLink>
</StyledP>
</Footer>
</Wrapper>
);
}
}
export default Pin;

59
src/config/animations.js Normal file
View File

@ -0,0 +1,59 @@
import { keyframes } from 'styled-components';
import colors from 'config/colors';
export const ROTATE_180_UP = keyframes`
from {
transform: rotate(0deg)
}
to {
transform: rotate(180deg)
}
`;
export const ROTATE_180_DOWN = keyframes`
from {
transform: rotate(180deg)
}
to {
transform: rotate(0deg)
}
`;
export const DASH = keyframes`
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124;
}
`;
export const GREEN_COLOR = keyframes`
100%, 0% {
stroke: ${colors.GREEN_PRIMARY};
}
40% {
stroke: ${colors.GREEN_PRIMARY};
}
66% {
stroke: ${colors.GREEN_SECONDARY};
}
80%, 90% {
stroke: ${colors.GREEN_TERTIARY};
}
`;
export const PULSATE = keyframes`
0%, 100% {
opacity: 0.5;
}
50% {
opacity: 1.0;
}
`;

View File

@ -1,7 +1,8 @@
export default {
WHITE: '#FFFFFF',
BACKGROUND: '#EBEBEB',
HEADER: '#212121',
HEADER: '#1A1A1A',
BODY: '#E3E3E3',
MAIN: '#FBFBFB',
LANDING: '#F9F9F9',

View File

@ -1,9 +1,62 @@
export default {
PLUS: 'M768 512c0 22.080-17.92 40-40 40h-176v176c0 22.080-17.92 40-40 40s-40-17.92-40-40v-176h-176c-22.080 0-40-17.92-40-40s17.92-40 40-40h176v-176c0-22.080 17.92-40 40-40s40 17.92 40 40v176h176c22.080 0 40 17.92 40 40z',
ARROW_LEFT: 'M603.072 757.216l-237.44-219.616c-8.576-8.128-13.632-19.296-13.632-31.040 0.288-11.744 5.056-23.2 13.664-31.040l227.040-208.768c16.928-15.36 43.040-14.176 58.176 3.008 15.424 16.864 13.952 43.392-2.656 59.040l-193.504 177.76 203.904 188.608c8 7.52 12.768 18.080 13.344 29.216 0.608 11.456-3.264 21.696-10.688 30.112-15.456 16.896-41.568 18.080-58.208 2.72z',
CHAT: 'M580.992 256h-137.984c-103.296 0-187.008 85.952-187.008 192 0 96.608 69.536 176.32 160 189.792v130.208l128-128h36.992c103.296 0 187.008-85.952 187.008-192s-83.712-192-187.008-192z',
SKIP: 'M512 256c-141.376 0-256 114.656-256 256 0 141.408 114.624 256 256 256s256-114.592 256-256c0-141.344-114.624-256-256-256zM529.056 631.456v-68.256c-102.4-34.144-136.544 0-170.656 68.256 0-170.656 102.4-204.8 170.656-204.8v-68.256l136.544 136.544-136.544 136.512z',
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',
CHECKED: [
'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',
],
BACK: [
'M656.224 402.304l-66.848 66.176-66.848-66.176-50.144 49.6 66.912 66.176-66.912 66.176 50.176 49.632 66.848-66.176 66.848 66.176 50.112-49.632-66.816-66.176 66.816-66.176-50.144-49.6zM337.824 256h540.928c27.2 0 49.248 21.824 49.248 48.768v414.464c0 26.944-22.048 48.768-49.248 48.768h-540.608c-13.856 0-27.072-5.792-36.416-15.936l-192.896-209.664c-17.248-18.752-17.088-47.488 0.352-66.048l192.576-204.8c9.344-9.92 22.4-15.552 36.064-15.552z',
],
HELP: [
'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.080zM501.216 717.952c-27.808 0-50.496-22.464-50.496-50.048 0-28.32 22.176-50.528 50.496-50.528 27.616 0 50.048 22.656 50.048 50.528 0.032 27.168-22.88 50.048-50.048 50.048zM536.416 542.464v27.744c0 13.504-5.28 18.784-18.784 18.784h-36.224c-13.504 0-18.72-5.28-18.72-18.784v-61.984c0-15.68 16.064-20.352 30.208-24.48 3.456-1.056 7.040-2.080 10.496-3.264 18.336-6.592 29.696-14.816 29.696-35.296 0-6.656 0-26.816-32.832-26.816-20.224 0-38.624 7.776-49.6 12.416-6.208 2.624-9.28 3.904-12.384 3.904-6.336 0-12.32-5.088-13.248-10.304l-12.608-32.96c-1.824-3.776-1.824-6.784-1.824-9.216 0-24.288 75.552-37.664 100.608-37.664 63.104 0 105.504 40.672 105.504 101.152 0.032 65.44-49.12 85.952-80.288 96.768z',
],
REFRESH: [
'M347.392 473.312c17.28-82.24 90.4-142.336 173.92-142.336 31.648 0 61.92 8.704 88.576 24.416 6.656 3.936 12.8 8.672 18.944 13.504l-68.832 68.736 192 33.056-32-198.272-38.4 42.016c-5.92-5.024-12.064-9.728-18.336-14.144-40.928-28.672-89.92-44.288-141.92-44.288-121.664 0-225.12 89.312-245.984 210.368l-3.36 20.896h72.672l2.72-13.952zM676.608 550.688c-17.28 82.24-90.4 142.336-173.92 142.336-31.648 0-61.92-8.704-88.576-24.416-6.624-3.936-12.8-8.672-18.944-13.504l68.832-68.736-192-33.056 32 198.272 38.4-42.016c5.92 5.024 12.032 9.696 18.336 14.144 40.928 28.672 89.92 44.288 141.952 44.288 121.664 0 225.12-89.312 245.984-210.368l3.328-20.864h-72.672l-2.72 13.92z',
],
T1: [
'M603.2 265.6h-6.4c-25.494-5.341-54.79-8.398-84.8-8.398s-59.305 3.058-87.592 8.879l2.792-0.48h-6.72c-30.053 5.643-52.489 31.68-52.489 62.956 0 0.367 0.003 0.733 0.009 1.099l-0.001-0.055v234.88c0.075 40.921 11.238 79.22 30.643 112.071l-0.563-1.031 35.2 60.48c11.655 19.297 32.515 32.001 56.342 32.001 0.105 0 0.209 0 0.314-0.001h44.144c0.359 0.007 0.783 0.011 1.208 0.011 23.569 0 44.162-12.74 55.269-31.709l0.164-0.302 36.16-64c18.232-31.447 29.027-69.173 29.12-109.413v-232.987c0.005-0.293 0.008-0.639 0.008-0.986 0-31.391-22.599-57.503-52.416-62.954l-0.392-0.059zM629.76 563.2c-0.193 35.364-9.792 68.446-26.418 96.923l0.498-0.923-35.84 64c-6.868 11.865-19.463 19.742-33.906 19.84h-44.174c-0.073 0-0.159 0.001-0.246 0.001-14.427 0-27.041-7.762-33.894-19.338l-0.1-0.183-34.88-59.84c-16.656-28.155-26.515-62.042-26.56-98.227v-235.853c0.133-19.025 13.742-34.833 31.751-38.359l0.249-0.041h6.72c24.050-5.126 51.682-8.062 80-8.062s55.949 2.936 82.608 8.519l-2.608-0.457h6.72c18.258 3.568 31.867 19.375 32 38.386v0.014zM422.4 353.92h179.2c3.535 0 6.4 2.865 6.4 6.4v99.2c0 3.535-2.865 6.4-6.4 6.4h-179.2c-3.535 0-6.4-2.865-6.4-6.4v-99.2c0-3.535 2.865-6.4 6.4-6.4z',
],
COG: [
'M739.552 462.144h-71.328c-4.256-13.664-10.208-26.56-17.472-38.56l47.264-47.424c11.2-11.008 11.2-29.056 0-40.192l-20.064-20.032c-11.136-11.104-29.152-11.040-40.192 0l-48.128 48.032c-12.992-7.392-27.072-13.152-42.080-16.992v-62.496c0-15.68-12.672-28.48-28.448-28.48h-28.448c-15.68 0-28.416 12.8-28.416 28.48v62.464c-16.352 4.128-31.68 10.656-45.728 19.2l-40.288-40.224c-11.072-11.040-29.184-11.104-40.288 0l-20.096 20.096c-11.104 11.072-10.976 29.152 0.064 40.288l40.992 40.992c-8.672 15.136-15.168 31.648-18.88 49.152h-53.504c-15.776 0-28.544 12.736-28.544 28.48v28.416c0 15.68 12.768 28.416 28.544 28.416h57.152c5.184 17.152 12.992 32.928 23.008 47.328l-38.656 38.656c-11.136 11.136-11.136 29.216-0.064 40.288l20.064 20.096c11.2 11.040 29.248 11.040 40.32-0.032l43.232-43.2c14.528 7.232 30.336 12.48 46.944 15.2v59.488c0 15.68 12.736 28.448 28.448 28.48h28.448c15.68-0.032 28.448-12.8 28.448-28.48v-66.816c14.336-5.088 27.904-11.872 40.224-20.544l45.76 45.888c11.104 11.072 29.12 11.072 40.224 0l20.096-20.128c11.168-11.072 11.168-29.056-0.096-40.288l-50.144-50.24c6.144-12.512 10.944-25.792 13.92-39.904h67.776c15.744 0 28.448-12.672 28.48-28.448v-28.448c-0.096-15.68-12.8-28.512-28.544-28.512zM504.928 583.072c-39.264 0-71.072-31.776-71.072-71.104 0-39.264 31.808-71.040 71.072-71.040 39.296 0 71.136 31.776 71.136 71.040 0 39.328-31.84 71.104-71.136 71.104z',
],
EJECT: [
'M276 768h471.968c11.072 0 20.032-9.76 20.032-21.824v-75.968c0-12.064-8.96-21.824-20-21.824h-472c-11.040 0-20 9.76-20 21.824v75.968c0 12.064 8.96 21.824 20 21.824zM503.552 260.192l-231.232 288.128c-6.368 7.904-1.184 20.32 8.448 20.32h462.496c9.664 0 14.816-12.384 8.448-20.32l-231.232-288.128c-4.512-5.6-12.448-5.6-16.928 0z',
],
CLOSE: [
'M754.816 689.92c17.6 17.6 17.6 46.72 0 64.64-8.96 8.64-20.48 13.44-32.64 13.44s-23.68-4.8-32.32-13.44l-177.888-177.92-177.888 177.92c-16.32 16.96-47.040 17.6-64.64 0-17.92-17.92-17.92-47.040 0-64.64l178.208-177.92-178.208-177.92c-17.92-17.92-17.92-46.72 0-64.64 17.28-17.28 47.36-17.28 64.64 0l177.888 177.92 177.888-177.92c17.92-17.92 47.040-17.92 64.96 0 17.6 17.92 17.6 46.72 0 64.64l-178.24 177.92 178.24 177.92z',
],
DOWNLOAD: [
'M346.56 410.24c3.255-1.423 7.047-2.252 11.033-2.252 0.284 0 0.566 0.004 0.848 0.013l-0.041-0.001c8.323 0.531 15.657 4.371 20.77 10.206l0.030 0.034 93.44 109.44c0.378 0.735 1.131 1.229 1.999 1.229 1.237 0 2.24-1.003 2.24-2.24 0-0.209-0.029-0.412-0.083-0.605l0.004 0.016v-233.28c-0.102-0.987-0.16-2.132-0.16-3.291 0-18.733 15.187-33.92 33.92-33.92s33.92 15.187 33.92 33.92c0 1.159-0.058 2.304-0.172 3.433l0.012-0.142v236.16c-0.050 0.177-0.079 0.379-0.079 0.589 0 1.237 1.003 2.24 2.24 2.24 0.868 0 1.621-0.494 1.993-1.216l0.006-0.013 88.32-104.32c5.204-6.343 13.042-10.358 21.819-10.358 7.711 0 14.699 3.099 19.784 8.121l-0.003-0.003c6.16 5.845 9.993 14.090 9.993 23.231 0 8.17-3.062 15.625-8.101 21.28l0.028-0.032-146.56 173.44c-5.311 6.15-13.061 10.069-21.731 10.24h-0.029c-8.727-0.036-16.523-3.991-21.724-10.196l-0.036-0.044-152-178.56c-5.441-6.124-8.764-14.234-8.764-23.121 0-12.698 6.785-23.81 16.927-29.911l0.157-0.088z',
'M694.72 731.2c0.024 0.488 0.038 1.060 0.038 1.635 0 18.891-14.881 34.306-33.561 35.163l-0.077 0.003h-292.48c-18.795-1.81-33.372-17.523-33.372-36.64s14.577-34.83 33.222-36.628l0.15-0.012h292.48c18.751 0.866 33.625 16.278 33.625 35.165 0 0.463-0.009 0.923-0.027 1.381l0.002-0.066z',
],
PLUS: [
'M768 512c0 22.080-17.92 40-40 40h-176v176c0 22.080-17.92 40-40 40s-40-17.92-40-40v-176h-176c-22.080 0-40-17.92-40-40s17.92-40 40-40h176v-176c0-22.080 17.92-40 40-40s40 17.92 40 40v176h176c22.080 0 40 17.92 40 40z',
],
ARROW_UP: [
'M757.216 603.072l-219.616-237.44c-8.128-8.576-19.296-13.632-31.040-13.632-11.744 0.288-23.2 5.056-31.040 13.664l-208.768 227.040c-15.36 16.928-14.176 43.040 3.008 58.176 16.864 15.424 43.392 13.952 59.040-2.656l177.76-193.504 188.608 203.904c7.52 8 18.080 12.768 29.216 13.344 11.456 0.608 21.696-3.264 30.112-10.688 16.896-15.456 18.080-41.568 2.72-58.208z',
],
ARROW_LEFT: [
'M603.072 757.216l-237.44-219.616c-8.576-8.128-13.632-19.296-13.632-31.040 0.288-11.744 5.056-23.2 13.664-31.040l227.040-208.768c16.928-15.36 43.040-14.176 58.176 3.008 15.424 16.864 13.952 43.392-2.656 59.040l-193.504 177.76 203.904 188.608c8 7.52 12.768 18.080 13.344 29.216 0.608 11.456-3.264 21.696-10.688 30.112-15.456 16.896-41.568 18.080-58.208 2.72z',
],
ARROW_DOWN: [
'M757.216 420.928l-219.616 237.44c-8.128 8.576-19.296 13.632-31.040 13.632-11.744-0.288-23.2-5.056-31.040-13.664l-208.768-227.040c-15.36-16.928-14.176-43.040 3.008-58.176 16.864-15.424 43.392-13.952 59.040 2.656l177.76 193.504 188.608-203.904c7.52-8 18.080-12.768 29.216-13.344 11.456-0.608 21.696 3.264 30.112 10.688 16.896 15.456 18.080 41.568 2.72 58.208z',
],
CHAT: [
'M580.992 256h-137.984c-103.296 0-187.008 85.952-187.008 192 0 96.608 69.536 176.32 160 189.792v130.208l128-128h36.992c103.296 0 187.008-85.952 187.008-192s-83.712-192-187.008-192z',
],
SKIP: [
'M512 256c-141.376 0-256 114.656-256 256 0 141.408 114.624 256 256 256s256-114.592 256-256c0-141.344-114.624-256-256-256zM529.056 631.456v-68.256c-102.4-34.144-136.544 0-170.656 68.256 0-170.656 102.4-204.8 170.656-204.8v-68.256l136.544 136.544-136.544 136.512z',
],
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',
],
};
/*

39
src/config/variables.js Normal file
View File

@ -0,0 +1,39 @@
export const FONT_SIZE = {
SMALLEST: '10px',
SMALLER: '12px',
SMALL: '14px',
BASE: '16px',
BIGGER: '32px',
BIGGEST: '36px',
};
export const FONT_WEIGHT = {
SMALLEST: '300',
SMALL: '400',
BASE: '500',
BIGGER: '600',
};
export const ICON_SIZE = {
BASE: '20px',
};
export const BORDER_WIDTH = {
SELECTED: '3px',
};
export const LEFT_NAVIGATION_ROW = {
PADDING: '16px 24px',
};
const TRANSITION_TIME = {
BASE: '0.3s',
};
export const TRANSITION = {
HOVER: `background-color ${TRANSITION_TIME.BASE} ease-in-out, color ${TRANSITION_TIME.BASE} ease-in-out, border-color ${TRANSITION_TIME.BASE} ease-in-out`,
};
export const LINE_HEIGHT = {
BASE: '1.8',
};

View File

@ -1,6 +1,6 @@
export default [
{ id: 'Windows', value: 'trezor-bridge-2.0.11-win32-install.exe', label: 'Windows' },
{ id: 'macOS', value: 'trezor-bridge-2.0.11.pkg', label: 'Mac OS X' },
{ id: 'macOS', value: 'trezor-bridge-2.0.11.pkg', label: 'macOS' },
{ id: 'Linux', value: 'trezor-bridge_2.0.11_amd64.deb', label: 'Linux 64-bit (deb)' },
{ id: 'Linux-rpm', value: 'trezor-bridge_2.0.11_amd64.rpm', label: 'Linux 64-bit (rpm)' },
{ id: '01', value: 'trezor-bridge_2.0.11_amd32.deb', label: 'Linux 32-bit (deb)' },

View File

@ -5,7 +5,7 @@ import baseStyles from 'support/BaseStyles';
import { onBeforeUnload } from 'actions/WalletActions';
import 'styles/index.less';
import App from 'views/index';
import store from './store';
import store from 'support/store';
const root: ?HTMLElement = document.getElementById('root');
if (root) {

View File

@ -1,56 +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,
}) => (
<Wrapper
onClick={onClick}
disabled={disabled}
blue={blue}
white={white}
>{text}
</Wrapper>
);
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;

View File

@ -1,38 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const Icon = ({ icon, size = 32, color = 'black' }) => {
const styles = {
svg: {
display: 'inline-block',
verticalAlign: 'middle',
},
path: {
fill: color,
},
};
return (
<svg
style={styles.svg}
width={`${size}`}
height={`${size}`}
viewBox="0 0 1024 1024"
>
<path
style={styles.path}
d={icon}
/>
</svg>
);
};
Icon.propTypes = {
icon: PropTypes.string.isRequired,
size: PropTypes.number,
color: PropTypes.string,
};
export default Icon;

View File

@ -1,21 +0,0 @@
/* @flow */
import React from 'react';
export default (props: { size: string, label?: string, className?: string }): React$Element<string> => {
const className = props.className ? `loader-circle ${props.className}` : 'loader-circle';
const style = {
width: `${props.size}px`,
height: `${props.size}px`,
};
return (
<div className={className} style={style}>
<p>{ props.label }</p>
<svg className="circular" viewBox="25 25 50 50">
<circle className="route" cx="50" cy="50" r="20" fill="none" stroke="" strokeWidth="1" strokeMiterlimit="10" />
<circle className="path" cx="50" cy="50" r="20" fill="none" strokeWidth="1" strokeMiterlimit="10" />
</svg>
</div>
);
};

View File

@ -1,31 +0,0 @@
/* @flow */
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { H2 } from 'components/Heading';
import * as LogActions from 'actions/LogActions';
import type { State, Dispatch } from 'flowtype';
const Log = (props: Props): ?React$Element<string> => {
if (!props.log.opened) return null;
return (
<div className="log">
<button className="log-close transparent" onClick={props.toggle} />
<H2>Log</H2>
<p>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</p>
<textarea value={JSON.stringify(props.log.entries)} readOnly />
</div>
);
};
export default connect(
(state: State) => ({
log: state.log,
}),
(dispatch: Dispatch) => ({
toggle: bindActionCreators(LogActions.toggle, dispatch),
}),
)(Log);

View File

@ -1,87 +0,0 @@
/* @flow */
import React from 'react';
import { H2 } from 'components/Heading';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as NOTIFICATION from 'actions/constants/notification';
import * as NotificationActions from 'actions/NotificationActions';
import type { Action, State, Dispatch } from 'flowtype';
import Loader from 'components/LoaderCircle';
type Props = {
notifications: $ElementType<State, 'notifications'>,
close: (notif?: any) => Action
}
type NProps = {
key?: number;
className: string;
cancelable?: boolean;
title: string;
message?: string;
actions?: Array<any>;
close?: typeof NotificationActions.close,
loading?: boolean
}
export const Notification = (props: NProps): React$Element<string> => {
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) => (
<button key={i} onClick={(event) => { close(); a.callback(); }} className="transparent">{ a.label }</button>
)) : null;
return (
<div className={className}>
{ props.cancelable ? (
<button
className="notification-close transparent"
onClick={event => close()}
/>
) : null }
<div className="notification-body">
<H2>{ props.title }</H2>
{ props.message ? (<p dangerouslySetInnerHTML={{ __html: props.message }} />) : null }
</div>
{ props.actions && props.actions.length > 0 ? (
<div className="notification-action">
{ actionButtons }
</div>
) : null }
{ props.loading ? (
<Loader
className="info"
size="50"
/>
) : null }
</div>
);
};
export const NotificationGroup = (props: Props) => {
const { notifications, close } = props;
return notifications.map((n, i) => (
<Notification
key={i}
className={n.type}
title={n.title}
message={n.message}
cancelable={n.cancelable}
actions={n.actions}
close={close}
/>
));
};
export default connect(
(state: State) => ({
notifications: state.notifications,
}),
(dispatch: Dispatch) => ({
close: bindActionCreators(NotificationActions.close, dispatch),
}),
)(NotificationGroup);

View File

@ -1,114 +0,0 @@
/* @flow */
import React, { Component } from 'react';
import TrezorConnect from 'trezor-connect';
import type { State, TrezorDevice } from '~/flowtype';
type Props = {
transport: $PropertyType<$ElementType<State, 'connect'>, 'transport'>;
disconnectRequest: ?TrezorDevice;
}
const DisconnectDevice = (props: Props) => {
if (!props.disconnectRequest) return null;
return (
<main>
<h2 className="claim">The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
Unplug { props.disconnectRequest.label } device.
</span>
</p>
</div>
<div className="image" />
</main>
);
};
const ConnectHIDDevice = (props: Props) => (
<main>
<h2 className="claim">The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g>
</svg>
Connect TREZOR to continue
</span>
</p>
</div>
<div className="image">
<p>
<span>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></span>
</p>
</div>
</main>
);
class ConnectWebUsbDevice extends Component<Props> {
componentDidMount(): void {
TrezorConnect.renderWebUSBButton();
}
componentDidUpdate() {
TrezorConnect.renderWebUSBButton();
}
render() {
return (
<main>
<h2 className="claim">The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row webusb">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g>
</svg>
Connect TREZOR
</span>
</p>
<p className="webusb-and">and</p>
<button className="trezor-webusb-button">Check for devices</button>
</div>
<div className="image">
<p>
<span>Device not recognized? <a href="#/bridge" className="green">Try installing the TREZOR Bridge.</a></span>
<span>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></span>
</p>
</div>
</main>
);
}
}
const ConnectDevice = (props: Props) => {
const { transport, disconnectRequest } = props;
if (disconnectRequest) {
return <DisconnectDevice {...props} />;
} if (transport && transport.version.indexOf('webusb') >= 0) {
return <ConnectWebUsbDevice {...props} />;
}
return <ConnectHIDDevice {...props} />;
};
export default ConnectDevice;

View File

@ -1,37 +0,0 @@
/* @flow */
import React from 'react';
import type { Props } from './index';
const Confirmation = (props: Props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
const {
amount,
address,
currency,
total,
selectedFeeLevel,
} = props.sendForm;
return (
<div className="confirm-tx">
<div className="header">
<h3>Confirm transaction on { device.label } device</h3>
<p>Details are shown on display</p>
</div>
<div className="content">
<label>Send </label>
<p>{ `${amount} ${currency}` }</p>
<label>To</label>
<p>{ address }</p>
<label>Fee</label>
<p>{ selectedFeeLevel.label }</p>
</div>
</div>
);
};
export default Confirmation;

View File

@ -1,19 +0,0 @@
/* @flow */
import React from 'react';
import type { Props } from './index';
const InvalidPin = (props: Props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
return (
<div className="pin">
<h3>Entered PIN for { device.label } is not correct</h3>
<p>Retrying...</p>
</div>
);
};
export default InvalidPin;

View File

@ -1,20 +0,0 @@
/* @flow */
import React from 'react';
import type { Props } from './index';
const Confirmation = (props: Props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
return (
<div className="confirm-tx">
<div className="header">
<h3>Complete the action on { device.label } device</h3>
</div>
</div>
);
};
export default Confirmation;

View File

@ -1,145 +0,0 @@
/* @flow */
import React, { Component } from 'react';
import type { Props } from './index';
type State = {
pin: string;
}
export default class Pin extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
constructor(props: Props) {
super(props);
this.state = {
pin: '',
};
}
onPinAdd = (input: number): void => {
let pin: string = this.state.pin;
if (pin.length < 9) {
pin += input;
this.setState({
pin,
});
}
}
onPinBackspace = (): void => {
this.setState({
pin: this.state.pin.substring(0, this.state.pin.length - 1),
});
}
keyboardHandler(event: KeyboardEvent): void {
const { onPinSubmit } = this.props.modalActions;
const { pin } = this.state;
event.preventDefault();
switch (event.keyCode) {
case 13:
// enter,
onPinSubmit(pin);
break;
// backspace
case 8:
this.onPinBackspace();
break;
// numeric and numpad
case 49:
case 97:
this.onPinAdd(1);
break;
case 50:
case 98:
this.onPinAdd(2);
break;
case 51:
case 99:
this.onPinAdd(3);
break;
case 52:
case 100:
this.onPinAdd(4);
break;
case 53:
case 101:
this.onPinAdd(5);
break;
case 54:
case 102:
this.onPinAdd(6);
break;
case 55:
case 103:
this.onPinAdd(7);
break;
case 56:
case 104:
this.onPinAdd(8);
break;
case 57:
case 105:
this.onPinAdd(9);
break;
}
}
componentWillMount(): void {
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
}
componentWillUnmount(): void {
window.removeEventListener('keydown', this.keyboardHandler, false);
}
render() {
if (!this.props.modal.opened) return null;
const { onPinSubmit } = this.props.modalActions;
const { device } = this.props.modal;
const { pin } = this.state;
return (
<div className="pin">
{/* <button className="close-modal transparent"></button> */}
<h3>Enter { device.label } PIN</h3>
<p>The PIN layout is displayed on your TREZOR.</p>
<div className="pin-input-row">
<input type="password" autoComplete="off" maxLength="9" disabled value={pin} />
<button type="button" className="pin-backspace transparent" onClick={event => this.onPinBackspace()} />
</div>
<div className="pin-row">
<button type="button" data-value="7" onClick={event => this.onPinAdd(7)}>&#8226;</button>
<button type="button" data-value="8" onClick={event => this.onPinAdd(8)}>&#8226;</button>
<button type="button" data-value="9" onClick={event => this.onPinAdd(9)}>&#8226;</button>
</div>
<div className="pin-row">
<button type="button" data-value="4" onClick={event => this.onPinAdd(4)}>&#8226;</button>
<button type="button" data-value="5" onClick={event => this.onPinAdd(5)}>&#8226;</button>
<button type="button" data-value="6" onClick={event => this.onPinAdd(6)}>&#8226;</button>
</div>
<div className="pin-row">
<button type="button" data-value="1" onClick={event => this.onPinAdd(1)}>&#8226;</button>
<button type="button" data-value="2" onClick={event => this.onPinAdd(2)}>&#8226;</button>
<button type="button" data-value="3" onClick={event => this.onPinAdd(3)}>&#8226;</button>
</div>
<div><button className="submit" type="button" onClick={event => onPinSubmit(pin)}>Enter PIN</button></div>
<p>Not sure how PIN works? <a className="green" href="http://doc.satoshilabs.com/trezor-user/enteringyourpin.html" target="_blank" rel="noreferrer noopener">Learn more</a></p>
</div>
);
}
}

View File

@ -1,17 +0,0 @@
export const FONT_SIZE = {
SMALLER: '12px',
SMALL: '14px',
BASE: '16px',
};
export const TRANSITION_TIME = {
BASE: '0.3s',
};
export const ICON_SIZE = {
BASE: '20px',
};
export const BORDER_WIDTH = {
SELECTED: '3px',
};

View File

@ -1,26 +0,0 @@
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
console.error('error', error);
// You can also log the error to an error reporting service
// logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <div>Something went wrong.</div>;
}
return this.props.children;
}
}
export default ErrorBoundary;

View File

@ -1,32 +0,0 @@
import React from 'react';
import { H2 } from 'components/Heading';
const ConnectHIDDevice = () => (
<main>
<H2 claim>The private bank in your hands.</H2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g>
</svg>
Connect TREZOR to continue
</span>
</p>
</div>
<div className="image">
<p>
<span>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></span>
</p>
</div>
</main>
);
export default ConnectHIDDevice;

View File

@ -1,48 +0,0 @@
import React, { Component } from 'react';
import { H2 } from 'components/Heading';
import TrezorConnect from 'trezor-connect';
class ConnectWebUsbDevice extends Component<Props> {
componentDidMount() {
TrezorConnect.renderWebUSBButton();
}
componentDidUpdate() {
TrezorConnect.renderWebUSBButton();
}
render() {
return (
<main>
<H2 claim>The private bank in your hands.</H2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row webusb">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g>
</svg>
Connect TREZOR
</span>
</p>
<p className="webusb-and">and</p>
<button className="trezor-webusb-button">Check for devices</button>
</div>
<div className="image">
<p>
<span>Device not recognized? <a href="#/bridge" className="green">Try installing the TREZOR Bridge.</a></span>
<span>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></span>
</p>
</div>
</main>
);
}
}
export default ConnectWebUsbDevice;

View File

@ -1,25 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { H2 } from 'components/Heading';
const DisconnectDevice = ({ label }) => (
<main>
<H2 claim>The private bank in your hands.</H2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
Unplug { label } device.
</span>
</p>
</div>
<div className="image" />
</main>
);
DisconnectDevice.propTypes = {
label: PropTypes.string.isRequired,
};
export default DisconnectDevice;

View File

@ -1,28 +0,0 @@
/* @flow */
import React, { Component } from 'react';
import TrezorConnect from 'trezor-connect';
import { H2 } from 'components/Heading';
import type { State, TrezorDevice } from 'flowtype';
import DisconnectDevice from './components/DisconnectDevice';
import ConnectHIDDevice from './components/ConnectHIDDevice';
import ConnectWebUsbDevice from './components/ConnectWebUsbDevice';
type Props = {
transport: $PropertyType<$ElementType<State, 'connect'>, 'transport'>;
disconnectRequest: ?TrezorDevice;
}
const ConnectDevice = (props: Props) => {
const { transport, disconnectRequest } = props;
if (disconnectRequest) {
return <DisconnectDevice label={props.disconnectRequest.label} />;
} if (transport && transport.version.indexOf('webusb') >= 0) {
return <ConnectWebUsbDevice {...props} />;
}
return <ConnectHIDDevice {...props} />;
};
export default ConnectDevice;

View File

@ -1,82 +0,0 @@
/* @flow */
import installers from 'constants/bridge';
import React, { Component } from 'react';
import Select from 'react-select';
import Preloader from './Preloader';
type State = {
version: string;
target: ?InstallTarget;
url: string;
}
type InstallTarget = {
id: string;
value: string;
label: string;
}
// import type { Props } from './index';
type Props = {
browserState: {
osname: string,
};
}
export default class InstallBridge extends Component<Props, State> {
constructor(props: Props) {
super(props);
const currentTarget: ?InstallTarget = installers.find(i => i.id === props.browserState.osname);
this.state = {
version: '2.0.12',
url: 'https://wallet.trezor.io/data/bridge/2.0.12/',
target: currentTarget,
};
}
componentWillUpdate() {
if (this.props.browserState.osname && !this.state.target) {
const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname);
this.setState({
target: currentTarget,
});
}
}
onChange(value: InstallTarget) {
this.setState({
target: value,
});
}
render() {
if (!this.state.target) {
return <Preloader />;
}
const label: string = this.state.target.label;
const url = `${this.state.url}${this.state.target.value}`;
return (
<main>
<h3 className="claim">TREZOR Bridge. <span>Version 2.0.12</span></h3>
<p>New communication tool to facilitate the connection between your TREZOR and your internet browser.</p>
<div className="row">
<Select
name="installers"
className="installers"
searchable={false}
clearable={false}
value={this.state.target}
onChange={this.onChange.bind(this)}
options={installers}
/>
<a href={url} className="button">Download for { label }</a>
</div>
<p>Learn more about latest version in <a href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md" className="green" target="_blank" rel="noreferrer noopener">Changelog</a></p>
</main>
);
}
}

View File

@ -1,10 +0,0 @@
/* @flow */
import React from 'react';
export default (props: {}): React$Element<string> => (
<section className="landing">
localstorage ERROR
</section>
);

View File

@ -1,11 +0,0 @@
/* @flow */
import React from 'react';
import Loader from 'components/LoaderCircle';
export default (props: {}): React$Element<string> => (
<section className="landing">
<Loader label="Loading" size="100" />
</section>
);

View File

@ -1,10 +0,0 @@
/* @flow */
import React from 'react';
export default (props: {}): React$Element<string> => (
<section className="landing">
connect ERROR
</section>
);

View File

@ -1,81 +0,0 @@
/* @flow */
import React from 'react';
import { H2 } from 'components/Heading';
import Header from 'components/Header';
import Footer from 'components/Footer';
import Log from 'components/Log';
import Notifications, { Notification } from 'components/Notification';
import Preloader from './components/Preloader';
import ConnectDevice from './components/ConnectDevice';
import InstallBridge from './components/InstallBridge';
import LocalStorageError from './components/LocalStorageError';
import TrezorConnectError from './components/TrezorConnectError';
import type { Props } from './index';
const BrowserNotSupported = (props: {}): React$Element<string> => (
<main>
<H2>Your browser is not supported</H2>
<p>Please choose one of the supported browsers</p>
<div className="row">
<div className="chrome">
<p>Google Chrome</p>
<a className="button" href="https://www.google.com/chrome/" target="_blank" rel="noreferrer noopener">Get Chrome</a>
</div>
<div className="firefox">
<p>Mozilla Firefox</p>
<a className="button" href="https://www.mozilla.org/en-US/firefox/new/" target="_blank" rel="noreferrer noopener">Get Firefox</a>
</div>
</div>
</main>
);
export default (props: Props) => {
const { web3 } = props;
const { devices } = props;
const { browserState, transport } = props.connect;
const localStorageError = props.localStorage.error;
const connectError = props.connect.error;
let notification = null;
let body = null;
let css: string = 'app landing';
const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge');
if (localStorageError) {
notification = (
<Notification
title="Initialization error"
message="Config files are missing"
className="error"
/>
);
css += ' config-error';
} else if (browserState.supported === false) {
css += ' browser-not-supported';
body = <BrowserNotSupported />;
} else if (connectError || bridgeRoute) {
css += ' install-bridge';
body = <InstallBridge browserState={browserState} />;
} else if (props.wallet.ready && devices.length < 1) {
css += ' connect-device';
body = <ConnectDevice transport={transport} disconnectRequest={props.wallet.disconnectRequest} />;
}
if (notification || body) {
return (
<div className={css}>
<Header />
{ notification }
<Notifications />
<Log />
{ body }
<Footer />
</div>
);
}
return (<Preloader />);
};

Some files were not shown because too many files have changed in this diff Show More