1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-12-30 19:00:53 +00:00
This commit is contained in:
Vladimir Volek 2018-11-05 14:57:23 +01:00
commit 53f7a8ecef
31 changed files with 496 additions and 295 deletions

View File

@ -1,4 +1,4 @@
image: node:8 image: node:9.3
cache: cache:
paths: paths:

View File

@ -10,19 +10,34 @@ build-%:
# usage: # usage:
# make stage-beta # make stage-beta
# make stage-stable # make stage-stable
stage-%: sync-stage-%:
./scripts/s3sync.sh stage $* ./scripts/s3sync.sh stage $*
# s3sync with beta.mytrezor.com # s3sync with beta.mytrezor.com
# Upload build/beta only # Upload build/beta only
# usage: # usage:
# make beta # make beta
beta: sync-beta:
./scripts/s3sync.sh beta beta ./scripts/s3sync.sh beta beta
# s3sync with wallet.mytrezor.com # s3sync with wallet.mytrezor.com
# Upload build/stable only # Upload build/stable only
# usage: # usage:
# make stable # make stable
stable: sync-stable:
./scripts/s3sync.sh stable stable ./scripts/s3sync.sh stable stable
.DEFAULT_GOAL:= default
default:
@echo "Build:"
@echo "git checkout to desired branch (beta|stable)"
@echo " make build-beta"
@echo " make build-stable"
@echo "Sync:"
@echo "s3 sync desired build to server (beta.mytrezor.com|wallet.mytrezor.com)"
@echo " make sync-beta"
@echo " make sync-stable"
@echo "Staging:"
@echo "s3 sync desired build to stage server (stage.mytrezor.com)"
@echo " make sync-stage-beta"
@echo " make sync-stage-stable"

View File

@ -52,6 +52,7 @@
"react": "^16.4.2", "react": "^16.4.2",
"react-dom": "^16.2.0", "react-dom": "^16.2.0",
"react-hot-loader": "^4.3.4", "react-hot-loader": "^4.3.4",
"react-json-view": "^1.19.1",
"react-qr-svg": "^2.1.0", "react-qr-svg": "^2.1.0",
"react-redux": "^5.0.7", "react-redux": "^5.0.7",
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",

View File

@ -25,7 +25,19 @@ export const onPinSubmit = (value: string): Action => {
}; };
}; };
export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (dispatch: Dispatch): Promise<void> => { export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { modal } = getState();
if (modal.context !== MODAL.CONTEXT_DEVICE) return;
if (passphrase === '') {
// set standard wallet type if passphrase is blank
dispatch({
type: CONNECT.UPDATE_WALLET_TYPE,
device: modal.device,
hidden: false,
});
}
await TrezorConnect.uiResponse({ await TrezorConnect.uiResponse({
type: UI.RECEIVE_PASSPHRASE, type: UI.RECEIVE_PASSPHRASE,
payload: { payload: {
@ -106,15 +118,16 @@ export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispa
} }
}; };
export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean, state: ?string): ThunkAction => (dispatch: Dispatch): void => { export const onWalletTypeRequest = (hidden: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const { modal } = getState();
if (modal.context !== MODAL.CONTEXT_DEVICE) return;
dispatch({ dispatch({
type: MODAL.CLOSE, type: MODAL.CLOSE,
}); });
dispatch({ dispatch({
type: CONNECT.RECEIVE_WALLET_TYPE, type: CONNECT.RECEIVE_WALLET_TYPE,
device, device: modal.device,
hidden, hidden,
state,
}); });
}; };
@ -129,7 +142,6 @@ export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dis
export default { export default {
onPinSubmit, onPinSubmit,
onPassphraseSubmit, onPassphraseSubmit,
// askForRemember,
onRememberDevice, onRememberDevice,
onForgetDevice, onForgetDevice,
onForgetSingleDevice, onForgetSingleDevice,

View File

@ -64,8 +64,8 @@ export const paramsValidation = (params: RouterLocationState): PayloadAction<boo
if (!device) return false; if (!device) return false;
if (!deviceUtils.isDeviceAccessible(device)) { if (!deviceUtils.isDeviceAccessible(device)) {
// TODO: there should be no access to deep links if device has incorrect mode/firmware // no access to deep links if device has incorrect mode/firmware
// if (params.hasOwnProperty('network') || params.hasOwnProperty('account')) return false; if (params.hasOwnProperty('network') || params.hasOwnProperty('account')) return false;
} }
} }
@ -190,6 +190,8 @@ const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction<?string> =>
url = `/device/${device.path}/bootloader`; url = `/device/${device.path}/bootloader`;
} else if (device.mode === 'initialize') { } else if (device.mode === 'initialize') {
url = `/device/${device.features.device_id}/initialize`; url = `/device/${device.features.device_id}/initialize`;
} else if (device.mode === 'seedless') {
url = `/device/${device.features.device_id}/seedless`;
} else if (device.firmware === 'required') { } else if (device.firmware === 'required') {
url = `/device/${device.features.device_id}/firmware-update`; url = `/device/${device.features.device_id}/firmware-update`;
} else if (typeof device.instance === 'number') { } else if (typeof device.instance === 'number') {

View File

@ -74,6 +74,9 @@ export const verify = (
): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => { ): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
dispatch({ type: SIGN_VERIFY.VERIFY_PROGRESS, isVerifyProgress: true });
const response = await TrezorConnect.ethereumVerifyMessage({ const response = await TrezorConnect.ethereumVerifyMessage({
device: { device: {
path: selected.path, path: selected.path,
@ -87,6 +90,8 @@ export const verify = (
useEmptyPassphrase: selected.useEmptyPassphrase, useEmptyPassphrase: selected.useEmptyPassphrase,
}); });
dispatch({ type: SIGN_VERIFY.VERIFY_PROGRESS, isVerifyProgress: false });
if (response && response.success) { if (response && response.success) {
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,

View File

@ -78,10 +78,9 @@ export type TrezorConnectAction = {
type: typeof CONNECT.REQUEST_WALLET_TYPE, type: typeof CONNECT.REQUEST_WALLET_TYPE,
device: TrezorDevice device: TrezorDevice
} | { } | {
type: typeof CONNECT.RECEIVE_WALLET_TYPE, type: typeof CONNECT.RECEIVE_WALLET_TYPE | typeof CONNECT.UPDATE_WALLET_TYPE,
device: TrezorDevice, device: TrezorDevice,
hidden: boolean, hidden: boolean,
state: ?string,
}; };
declare var LOCAL: ?string; declare var LOCAL: ?string;

View File

@ -30,3 +30,4 @@ export const STOP_ACQUIRING: 'connect__stop_acquiring' = 'connect__stop_acquirin
export const REQUEST_WALLET_TYPE: 'connect__request_wallet_type' = 'connect__request_wallet_type'; export const REQUEST_WALLET_TYPE: 'connect__request_wallet_type' = 'connect__request_wallet_type';
export const RECEIVE_WALLET_TYPE: 'connect__receive_wallet_type' = 'connect__receive_wallet_type'; export const RECEIVE_WALLET_TYPE: 'connect__receive_wallet_type' = 'connect__receive_wallet_type';
export const UPDATE_WALLET_TYPE: 'connect__update_wallet_type' = 'connect__update_wallet_type';

View File

@ -5,6 +5,7 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import colors from 'config/colors'; import colors from 'config/colors';
import { H2 } from 'components/Heading'; import { H2 } from 'components/Heading';
import ReactJson from 'react-json-view';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import P from 'components/Paragraph'; import P from 'components/Paragraph';
@ -44,22 +45,17 @@ const Click = styled.div`
} }
`; `;
const Textarea = styled.textarea`
width: 100%;
height: 200px;
min-height: 200px;
resize: vertical;
font-size: 10px;
&:focus {
box-shadow: none;
}
`;
const StyledParagraph = styled(P)` const StyledParagraph = styled(P)`
margin: 10px 0; margin: 10px 0;
`; `;
const LogWrapper = styled.div`
background: white;
padding: 25px;
height: 300px;
overflow: scroll;
`;
const Log = (props: Props): ?React$Element<string> => { const Log = (props: Props): ?React$Element<string> => {
if (!props.log.opened) return null; if (!props.log.opened) return null;
return ( return (
@ -69,7 +65,9 @@ const Log = (props: Props): ?React$Element<string> => {
</Click> </Click>
<H2>Log</H2> <H2>Log</H2>
<StyledParagraph isSmaller>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</StyledParagraph> <StyledParagraph isSmaller>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</StyledParagraph>
<Textarea value={JSON.stringify(props.log.entries)} readOnly /> <LogWrapper>
<ReactJson src={props.log.entries} />
</LogWrapper>
</Wrapper> </Wrapper>
); );
}; };

View File

@ -1,10 +1,12 @@
/* @flow */ /* @flow */
import * as React from 'react'; import * as React from 'react';
import styled, { css } from 'styled-components'; import styled from 'styled-components';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import colors from 'config/colors'; import colors from 'config/colors';
import { getPrimaryColor, getSecondaryColor } from 'utils/notification';
import Loader from 'components/Loader';
import { TRANSITION } from 'config/variables'; import { TRANSITION } from 'config/variables';
type Props = { type Props = {
@ -15,59 +17,39 @@ type Props = {
size: number; size: number;
}; };
onClick: () => void; onClick: () => void;
isLoading?: boolean;
children: React.Node; children: React.Node;
}; };
const LoaderContent = styled.div`
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
cursor: default;
background: ${props => getSecondaryColor(props.type)};
`;
const Wrapper = styled.button` const Wrapper = styled.button`
padding: 12px 58px; padding: 12px 58px;
border-radius: 3px; border-radius: 3px;
background: transparent; background: transparent;
font-size: 14px; font-size: 14px;
position: relative;
font-weight: 300; font-weight: 300;
cursor: pointer; cursor: pointer;
color: ${colors.WHITE}; color: ${props => getPrimaryColor(props.type)};
border: 0; border: 1px solid ${props => getPrimaryColor(props.type)};
transition: ${TRANSITION.HOVER}; transition: ${TRANSITION.HOVER};
${props => props.type === 'info' && css`
border: 1px solid ${colors.INFO_PRIMARY};
color: ${colors.INFO_PRIMARY};
&:hover { &:hover {
color: ${colors.WHITE}; color: ${colors.WHITE};
background: ${colors.INFO_PRIMARY}; background: ${props => getPrimaryColor(props.type)};
} }
`}
${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` const IconWrapper = styled.span`
@ -75,13 +57,18 @@ const IconWrapper = styled.span`
`; `;
const NotificationButton = ({ const NotificationButton = ({
type, icon, onClick, children, type, icon, onClick, children, isLoading,
}: Props) => ( }: Props) => (
<Wrapper <Wrapper
icon={icon} icon={icon}
onClick={onClick} onClick={onClick}
type={type} type={type}
> >
{isLoading && (
<LoaderContent type={type}>
<Loader size={30} />
</LoaderContent>
)}
{icon && ( {icon && (
<IconWrapper> <IconWrapper>
<Icon <Icon
@ -102,6 +89,7 @@ NotificationButton.propTypes = {
color: PropTypes.string, color: PropTypes.string,
size: PropTypes.number, size: PropTypes.number,
}), }),
isLoading: PropTypes.bool,
onClick: PropTypes.func, onClick: PropTypes.func,
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
}; };

View File

@ -1,9 +1,7 @@
/* @flow */ /* @flow */
import * as React from 'react'; import * as React from 'react';
import styled, { css } from 'styled-components'; import styled from 'styled-components';
import colors from 'config/colors'; import { getPrimaryColor, getSecondaryColor, getIcon } from 'utils/notification';
import { getColor, getIcon } from 'utils/notification';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import icons from 'config/icons'; import icons from 'config/icons';
import { FONT_WEIGHT, FONT_SIZE } from 'config/variables'; import { FONT_WEIGHT, FONT_SIZE } from 'config/variables';
@ -28,34 +26,14 @@ type Props = {
const Wrapper = styled.div` const Wrapper = styled.div`
width: 100%; width: 100%;
position: relative; position: relative;
color: ${colors.TEXT_PRIMARY};
background: ${colors.TEXT_SECONDARY};
padding: 24px 48px 9px 24px; padding: 24px 48px 9px 24px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
text-align: left; text-align: left;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
color: ${props => getPrimaryColor(props.type)};
${props => props.type === 'info' && css` background: ${props => getSecondaryColor(props.type)};
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` const Body = styled.div`
@ -119,7 +97,7 @@ const Notification = (props: Props): React$Element<string> => {
{props.cancelable && ( {props.cancelable && (
<CloseClick onClick={() => close()}> <CloseClick onClick={() => close()}>
<Icon <Icon
color={getColor(props.type)} color={getPrimaryColor(props.type)}
icon={icons.CLOSE} icon={icons.CLOSE}
size={20} size={20}
/> />
@ -128,7 +106,7 @@ const Notification = (props: Props): React$Element<string> => {
<Body> <Body>
<IconWrapper> <IconWrapper>
<StyledIcon <StyledIcon
color={getColor(props.type)} color={getPrimaryColor(props.type)}
icon={getIcon(props.type)} icon={getIcon(props.type)}
/> />
</IconWrapper> </IconWrapper>

View File

@ -25,6 +25,7 @@ const InputIconWrapper = styled.div`
flex: 1; flex: 1;
position: relative; position: relative;
display: inline-block; display: inline-block;
background: white;
`; `;
const TopLabel = styled.span` const TopLabel = styled.span`
@ -43,10 +44,12 @@ const StyledInput = styled.input`
color: ${props => (props.color ? props.color : colors.TEXT)}; color: ${props => (props.color ? props.color : colors.TEXT)};
border-radius: 2px; border-radius: 2px;
${props => props.hasAddon && css` ${props => props.hasAddon && css`
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
`} `}
border: 1px solid ${colors.DIVIDER}; border: 1px solid ${colors.DIVIDER};
border-color: ${props => props.borderColor}; border-color: ${props => props.borderColor};
@ -58,6 +61,15 @@ const StyledInput = styled.input`
background: ${colors.GRAY_LIGHT}; background: ${colors.GRAY_LIGHT};
color: ${colors.TEXT_SECONDARY}; color: ${colors.TEXT_SECONDARY};
} }
${props => props.trezorAction && css`
z-index: 10001;
position: relative; /* bigger than modal container */
border-color: ${colors.WHITE};
border-width: 2px;
transform: translate(-1px, -1px);
background: ${colors.DIVIDER};
`};
`; `;
const StyledIcon = styled(Icon)` const StyledIcon = styled(Icon)`
@ -73,6 +85,49 @@ const BottomText = styled.span`
color: ${props => (props.color ? props.color : colors.TEXT_SECONDARY)}; color: ${props => (props.color ? props.color : colors.TEXT_SECONDARY)};
`; `;
const Overlay = styled.div`
${props => props.isPartiallyHidden && css`
bottom: 0;
border: 1px solid ${colors.DIVIDER};
border-radius: 2px;
position: absolute;
width: 100%;
height: 100%;
background-image: linear-gradient(to right,
rgba(0,0,0, 0) 0%,
rgba(255,255,255, 1) 220px
);
`}
`;
const TrezorAction = styled.div`
display: ${props => (props.action ? 'flex' : 'none')};
align-items: center;
height: 37px;
margin: 0px 10px;
padding: 0 14px 0 5px;
position: absolute;
top: 45px;
background: black;
color: ${colors.WHITE};
border-radius: 5px;
line-height: 37px;
z-index: 10001;
transform: translate(-1px, -1px);
`;
const ArrowUp = styled.div`
position: absolute;
top: -9px;
left: 12px;
width: 0;
height: 0;
border-left: 9px solid transparent;
border-right: 9px solid transparent;
border-bottom: 9px solid black;
z-index: 10001;
`;
class Input extends PureComponent { class Input extends PureComponent {
getIcon(inputState) { getIcon(inputState) {
let icon = []; let icon = [];
@ -114,8 +169,11 @@ class Input extends PureComponent {
color={this.getColor(this.props.state)} color={this.getColor(this.props.state)}
/> />
)} )}
<Overlay isPartiallyHidden={this.props.isPartiallyHidden} />
{this.props.icon}
<StyledInput <StyledInput
isSmallText={this.props.isSmallText} isSmallText={this.props.isSmallText}
trezorAction={this.props.trezorAction}
hasIcon={this.getIcon(this.props.state).length > 0} hasIcon={this.getIcon(this.props.state).length > 0}
innerRef={this.props.innerRef} innerRef={this.props.innerRef}
hasAddon={!!this.props.sideAddons} hasAddon={!!this.props.sideAddons}
@ -133,6 +191,9 @@ class Input extends PureComponent {
name={this.props.name} name={this.props.name}
data-lpignore="true" data-lpignore="true"
/> />
<TrezorAction action={this.props.trezorAction}>
<ArrowUp />{this.props.trezorAction}
</TrezorAction>
</InputIconWrapper> </InputIconWrapper>
{this.props.sideAddons && this.props.sideAddons.map(sideAddon => sideAddon)} {this.props.sideAddons && this.props.sideAddons.map(sideAddon => sideAddon)}
</InputWrapper> </InputWrapper>
@ -156,16 +217,19 @@ Input.propTypes = {
autocomplete: PropTypes.string, autocomplete: PropTypes.string,
autocorrect: PropTypes.string, autocorrect: PropTypes.string,
autocapitalize: PropTypes.string, autocapitalize: PropTypes.string,
icon: PropTypes.node,
spellCheck: PropTypes.string, spellCheck: PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
state: PropTypes.string, state: PropTypes.string,
bottomText: PropTypes.string, bottomText: PropTypes.string,
topLabel: PropTypes.node, topLabel: PropTypes.node,
trezorAction: PropTypes.node,
sideAddons: PropTypes.arrayOf(PropTypes.node), sideAddons: PropTypes.arrayOf(PropTypes.node),
isDisabled: PropTypes.bool, isDisabled: PropTypes.bool,
name: PropTypes.string, name: PropTypes.string,
isSmallText: PropTypes.string, isSmallText: PropTypes.string,
isPartiallyHidden: PropTypes.bool,
}; };
Input.defaultProps = { Input.defaultProps = {

View File

@ -91,18 +91,14 @@ class WalletType extends PureComponent<Props> {
keyboardHandler(event: KeyboardEvent): void { keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) { if (event.keyCode === 13) {
event.preventDefault(); event.preventDefault();
this.changeType(false); this.props.onWalletTypeRequest(false);
} }
} }
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
changeType(hidden: boolean, state: ?string) {
this.props.onWalletTypeRequest(this.props.device, hidden, state);
}
render() { render() {
const { device, onCancel } = this.props; const { device, onCancel, onWalletTypeRequest } = this.props;
return ( return (
<Wrapper> <Wrapper>
@ -122,7 +118,7 @@ class WalletType extends PureComponent<Props> {
Standard Wallet Standard Wallet
</Header> </Header>
<P isSmaller>Continue to access your standard wallet.</P> <P isSmaller>Continue to access your standard wallet.</P>
<StyledButton onClick={() => this.changeType(false, device.state)}>Go to your standard wallet</StyledButton> <StyledButton onClick={() => onWalletTypeRequest(false)}>Go to your standard wallet</StyledButton>
</Content> </Content>
<Content> <Content>
<Tooltip <Tooltip
@ -146,7 +142,7 @@ class WalletType extends PureComponent<Props> {
Hidden Wallet Hidden Wallet
</Header> </Header>
<P isSmaller>You will be asked to enter your passphrase to unlock your hidden wallet.</P> <P isSmaller>You will be asked to enter your passphrase to unlock your hidden wallet.</P>
<StyledButton isWhite onClick={() => this.changeType(true, device.state)}>Go to your hidden wallet</StyledButton> <StyledButton isWhite onClick={() => onWalletTypeRequest(true)}>Go to your hidden wallet</StyledButton>
</Content> </Content>
</Wrapper> </Wrapper>
); );

View File

@ -161,24 +161,18 @@ class Passphrase extends PureComponent<Props, State> {
} }
handleCheckboxClick() { handleCheckboxClick() {
// If passphrase was visible and now shouldn't be --> delete the value of passphraseCheckInputValue let match = false;
// doPassphraseInputsMatch
// - if passphrase was visible and now shouldn't be --> doPassphraseInputsMatch = false
// - because passphraseCheckInputValue will be empty string
// - if passphrase wasn't visibe and now should be --> doPassphraseInputsMatch = true
// - because passphraseCheckInputValue will be same as passphraseInputValue
let doInputsMatch = false;
if (this.state.shouldShowSingleInput || this.state.passphraseInputValue === this.state.passphraseCheckInputValue) { if (this.state.shouldShowSingleInput || this.state.passphraseInputValue === this.state.passphraseCheckInputValue) {
doInputsMatch = true; match = true;
} else { } else {
doInputsMatch = !!this.state.isPassphraseHidden; match = !!this.state.isPassphraseHidden;
} }
this.setState(previousState => ({ this.setState(previousState => ({
isPassphraseHidden: !previousState.isPassphraseHidden, isPassphraseHidden: !previousState.isPassphraseHidden,
passphraseInputValue: previousState.isPassphraseHidden ? previousState.passphraseInputValue : '', passphraseInputValue: previousState.passphraseInputValue,
passphraseCheckInputValue: previousState.isPassphraseHidden ? previousState.passphraseInputValue : '', passphraseCheckInputValue: previousState.passphraseCheckInputValue,
doPassphraseInputsMatch: doInputsMatch, doPassphraseInputsMatch: match,
})); }));
} }
@ -239,11 +233,9 @@ class Passphrase extends PureComponent<Props, State> {
/> />
</Row> </Row>
)} )}
{!this.state.doPassphraseInputsMatch && ( {!this.state.doPassphraseInputsMatch && (
<PassphraseError>Passphrases do not match</PassphraseError> <PassphraseError>Passphrases do not match</PassphraseError>
)} )}
<Row> <Row>
<Checkbox <Checkbox
isChecked={!this.state.isPassphraseHidden} isChecked={!this.state.isPassphraseHidden}
@ -259,10 +251,8 @@ class Passphrase extends PureComponent<Props, State> {
>Enter >Enter
</Button> </Button>
</Row> </Row>
<Footer> <Footer>
<P isSmaller> <P isSmaller>Changed your mind? &nbsp;
Changed your mind? &nbsp;
<LinkButton <LinkButton
isGreen isGreen
onClick={() => this.submitPassphrase(true)} onClick={() => this.submitPassphrase(true)}

View File

@ -5,7 +5,7 @@ import Icon from 'components/Icon';
import ICONS from 'config/icons'; import ICONS from 'config/icons';
import colors from 'config/colors'; import colors from 'config/colors';
import Notification from 'components/Notification'; import Notification from 'components/Notification';
import { getIcon, getColor } from 'utils/notification'; import { getIcon, getPrimaryColor } from 'utils/notification';
const Wrapper = styled.div``; const Wrapper = styled.div``;
@ -65,7 +65,7 @@ class Group extends PureComponent {
render() { render() {
const { type, groupNotifications, close } = this.props; const { type, groupNotifications, close } = this.props;
const color = getColor(type); const color = getPrimaryColor(type);
return ( return (
<Wrapper> <Wrapper>
{groupNotifications.length > 1 && ( {groupNotifications.length > 1 && (

View File

@ -54,7 +54,6 @@ class NotificationsGroup extends PureComponent {
// key: 5, // key: 5,
// title: 'this is a title of warning notification as', // title: 'this is a title of warning notification as',
// type: 'success', // type: 'success',
// message: <Link href="link" isGreen>See transaction detail</Link>,
// }, // },
// { // {
// key: 6, // key: 6,

View File

@ -0,0 +1,70 @@
import React from 'react';
import RcTooltip from 'rc-tooltip';
import colors from 'config/colors';
import Link from 'components/Link';
import styled from 'styled-components';
import PropTypes from 'prop-types';
const Wrapper = styled.div``;
const Content = styled.div`
max-width: ${props => `${props.maxWidth}px` || 'auto'};
`;
const ContentWrapper = styled.div`
display: block;
`;
const ReadMore = styled.div`
margin-top: 15px;
padding: 10px 0 5px 0;
text-align: center;
width: 100%;
color: ${colors.WHITE};
border-top: 1px solid ${colors.TEXT_SECONDARY};
`;
const Tooltip = ({
maxWidth,
className,
placement,
content,
readMoreLink,
children,
}) => (
<Wrapper className={className}>
<RcTooltip
arrowContent={<div className="rc-tooltip-arrow-inner" />}
placement={placement}
overlay={() => (
<ContentWrapper>
<Content maxWidth={maxWidth}>{content}</Content>
{readMoreLink && (
<Link href={readMoreLink}>
<ReadMore>Read more</ReadMore>
</Link>
)
}
</ContentWrapper>)}
>
{children}
</RcTooltip>
</Wrapper>
);
Tooltip.propTypes = {
className: PropTypes.string,
placement: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
]),
maxWidth: PropTypes.number,
content: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
]),
readMoreLink: PropTypes.string,
};
export default Tooltip;

View File

@ -33,6 +33,6 @@
</div> </div>
</div> </div>
<![endif]--> <![endif]-->
<div id="root"></div> <div id="trezor-wallet-root"></div>
</body> </body>
</html> </html>

View File

@ -4,7 +4,7 @@ import { render } from 'react-dom';
import baseStyles from 'support/styles'; import baseStyles from 'support/styles';
import App from 'views/index'; import App from 'views/index';
const root: ?HTMLElement = document.getElementById('root'); const root: ?HTMLElement = document.getElementById('trezor-wallet-root');
if (root) { if (root) {
baseStyles(); baseStyles();
render(<App />, root); render(<App />, root);

View File

@ -317,6 +317,7 @@ export default function devices(state: State = initialState, action: Action): St
return onSelectedDevice(state, action.device); return onSelectedDevice(state, action.device);
case CONNECT.RECEIVE_WALLET_TYPE: case CONNECT.RECEIVE_WALLET_TYPE:
case CONNECT.UPDATE_WALLET_TYPE:
return onChangeWalletType(state, action.device, action.hidden); return onChangeWalletType(state, action.device, action.hidden);
default: default:

View File

@ -26,6 +26,12 @@ export default (state: State = initialState, action: Action): State => {
signature: action.signature, signature: action.signature,
}; };
case SIGN_VERIFY.VERIFY_PROGRESS:
return {
...state,
isVerifyProgress: action.isVerifyProgress,
};
case SIGN_VERIFY.CLEAR: case SIGN_VERIFY.CLEAR:
return { return {
...initialState, ...initialState,

View File

@ -105,22 +105,22 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
api.dispatch(SendFormActionActions.observe(prevState, action)); api.dispatch(SendFormActionActions.observe(prevState, action));
} }
} else { } else {
switch (action.type) { // no changes in common values
case CONNECT.AUTH_DEVICE: if (action.type === CONNECT.RECEIVE_WALLET_TYPE) {
// selected device did changed if (action.device.state) {
// try to restore discovery after device authentication // redirect to root view (Dashboard) if device was authenticated before
api.dispatch(DiscoveryActions.restore());
break;
case CONNECT.RECEIVE_WALLET_TYPE:
if (action.state) {
api.dispatch(RouterActions.selectFirstAvailableDevice(true)); api.dispatch(RouterActions.selectFirstAvailableDevice(true));
} }
api.dispatch(TrezorConnectActions.authorizeDevice()); api.dispatch(TrezorConnectActions.authorizeDevice());
break; }
default: break; if (action.type === CONNECT.AUTH_DEVICE) {
// selected device did changed
// try to restore discovery after device authentication
api.dispatch(DiscoveryActions.restore());
} }
} }
// even if "selectedDevice" didn't change because it was updated on DEVICE.CHANGED before DEVICE.CONNECT action // even if "selectedDevice" didn't change because it was updated on DEVICE.CHANGED before DEVICE.CONNECT action
// try to restore discovery // try to restore discovery
if (action.type === DEVICE.CONNECT) { if (action.type === DEVICE.CONNECT) {

View File

@ -47,6 +47,11 @@ export const routes: Array<Route> = [
pattern: '/device/:device/initialize', pattern: '/device/:device/initialize',
fields: ['device', 'initialize'], fields: ['device', 'initialize'],
}, },
{
name: 'wallet-seedless',
pattern: '/device/:device/seedless',
fields: ['device', 'seedless'],
},
{ {
name: 'wallet-firmware-update', name: 'wallet-firmware-update',
pattern: '/device/:device/firmware-update', pattern: '/device/:device/firmware-update',

View File

@ -44,7 +44,7 @@ const baseStyles = () => injectGlobal`
outline: 0; outline: 0;
} }
#root { #trezor-wallet-root {
height: 100%; height: 100%;
} }

View File

@ -21,6 +21,9 @@ export const getStatus = (device: TrezorDevice): string => {
if (device.mode === 'initialize') { if (device.mode === 'initialize') {
return 'initialize'; return 'initialize';
} }
if (device.mode === 'seedless') {
return 'seedless';
}
if (device.firmware === 'required') { if (device.firmware === 'required') {
return 'firmware-required'; return 'firmware-required';
} }
@ -57,6 +60,8 @@ export const getStatusName = (deviceStatus: string): string => {
return 'Connected (bootloader mode)'; return 'Connected (bootloader mode)';
case 'initialize': case 'initialize':
return 'Connected (not initialized)'; return 'Connected (not initialized)';
case 'seedless':
return 'Connected (seedless mode)';
case 'firmware-required': case 'firmware-required':
return 'Connected (update required)'; return 'Connected (update required)';
case 'firmware-recommended': case 'firmware-recommended':
@ -81,8 +86,8 @@ export const isDisabled = (selectedDevice: TrezorDevice, devices: Array<TrezorDe
if (devices.length < 1) return true; // no devices if (devices.length < 1) return true; // no devices
if (devices.length === 1) { if (devices.length === 1) {
if (!selectedDevice.features) return true; // unacquired, unreadable if (!selectedDevice.features) return true; // unacquired, unreadable
if (selectedDevice.mode !== 'normal') return true; // bootloader, not initialized if (selectedDevice.mode !== 'normal') return true; // bootloader, not initialized, seedless
if (selectedDevice.firmware === 'required') return true; // bootloader, not initialized if (selectedDevice.firmware === 'required') return true;
} }
return false; // default return false; // default
}; };
@ -112,6 +117,7 @@ export const getStatusColor = (deviceStatus: string): string => {
return colors.ERROR_PRIMARY; return colors.ERROR_PRIMARY;
case 'bootloader': case 'bootloader':
case 'initialize': case 'initialize':
case 'seedless':
case 'firmware-recommended': case 'firmware-recommended':
case 'used-in-other-window': case 'used-in-other-window':
case 'unacquired': case 'unacquired':

View File

@ -1,7 +1,7 @@
import colors from 'config/colors'; import colors from 'config/colors';
import icons from 'config/icons'; import icons from 'config/icons';
const getColor = (type) => { const getPrimaryColor = (type) => {
let color; let color;
switch (type) { switch (type) {
case 'info': case 'info':
@ -23,9 +23,32 @@ const getColor = (type) => {
return color; return color;
}; };
const getSecondaryColor = (type) => {
let color;
switch (type) {
case 'info':
color = colors.INFO_SECONDARY;
break;
case 'error':
color = colors.ERROR_SECONDARY;
break;
case 'warning':
color = colors.WARNING_SECONDARY;
break;
case 'success':
color = colors.SUCCESS_SECONDARY;
break;
default:
color = null;
}
return color;
};
const getIcon = type => icons[type.toUpperCase()]; const getIcon = type => icons[type.toUpperCase()];
export { export {
getColor, getPrimaryColor,
getSecondaryColor,
getIcon, getIcon,
}; };

View File

@ -1,14 +1,21 @@
/* @flow */ /* @flow */
import React from 'react'; import React from 'react';
import { QRCode } from 'react-qr-svg'; import { QRCode } from 'react-qr-svg';
<<<<<<< HEAD
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
=======
import styled from 'styled-components';
import media from 'styled-media-query';
import { H2 } from 'components/Heading';
>>>>>>> master
import Button from 'components/Button'; import Button from 'components/Button';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import Tooltip from 'components/Tooltip'; import Tooltip from 'components/Tooltip';
import Input from 'components/inputs/Input';
import ICONS from 'config/icons'; import ICONS from 'config/icons';
import colors from 'config/colors'; import colors from 'config/colors';
import { FONT_SIZE, FONT_WEIGHT, FONT_FAMILY } from 'config/variables';
import { CONTEXT_DEVICE } from 'actions/constants/modal'; import { CONTEXT_DEVICE } from 'actions/constants/modal';
import Content from 'views/Wallet/components/Content'; import Content from 'views/Wallet/components/Content';
@ -23,10 +30,9 @@ const Label = styled.div`
`; `;
const AddressWrapper = styled.div` const AddressWrapper = styled.div`
position: relative;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: ${props => (props.isShowingQrCode ? 'column' : 'row')}; flex-direction: row;
`; `;
const StyledQRCode = styled(QRCode)` const StyledQRCode = styled(QRCode)`
@ -35,87 +41,21 @@ const StyledQRCode = styled(QRCode)`
border: 1px solid ${colors.BODY}; border: 1px solid ${colors.BODY};
`; `;
const ValueWrapper = styled.div`
font-size: ${FONT_SIZE.SMALL};
height: 40px;
font-weight: ${FONT_WEIGHT.SMALLEST};
line-height: 1.42857143;
font-family: ${FONT_FAMILY.MONOSPACE};
color: ${colors.TEXT_PRIMARY};
border: 1px solid ${colors.DIVIDER};
border-radius: 3px;
padding: 10px 12px;
padding-right: 38px;
position: relative;
flex: 1;
user-select: all;
${props => props.isHidden && css`
padding-right: 6px;
user-select: none;
border-radius: 3px 0px 0px 3px;
&:after {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background: linear-gradient(to right,
rgba(255,255,255, 0) 0%,
rgba(255,255,255, 1) 220px
);
pointer-events: none; /* so the text is still selectable */
}
`};
${props => props.isVerifying && css`
z-index: 10001; /* bigger than modal container */
border-color: ${colors.WHITE};
border-width: 2px;
transform: translate(-1px, -1px);
background: ${colors.DIVIDER};
`};
`;
const ArrowUp = styled.div`
position: absolute;
top: 35px;
left: 70px;
width: 0;
height: 0;
border-left: 9px solid transparent;
border-right: 9px solid transparent;
border-bottom: 9px solid black;
z-index: 10001;
`;
const AddressInfoText = styled.div`
display: flex;
align-items: center;
height: 37px;
margin: 0px 2px;
padding: 0 14px 0 5px;
position: absolute;
top: 45px;
background: black;
color: ${colors.WHITE};
border-radius: 5px;
line-height: 37px;
z-index: 10001;
transform: translate(-1px, -1px);
`;
const ShowAddressButton = styled(Button)` const ShowAddressButton = styled(Button)`
padding-top: 0; min-width: 195px;
padding-bottom: 0; padding: 0;
padding-left: 10px; white-space: nowrap;
display: flex; display: flex;
height: 40px;
align-items: center; align-items: center;
justify-content: center;
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
${media.lessThan('795px')`
margin-top: 10px;
`}
`; `;
const ShowAddressIcon = styled(Icon)` const ShowAddressIcon = styled(Icon)`
@ -128,9 +68,28 @@ const EyeButton = styled(Button)`
z-index: 10001; z-index: 10001;
padding: 0; padding: 0;
width: 30px; width: 30px;
background: white;
top: 5px; top: 5px;
position: absolute; position: absolute;
right: 10px; right: 10px;
&:hover {
background: white;
}
`;
const Row = styled.div`
display: flex;
width: 100%;
${media.lessThan('795px')`
flex-direction: column;
`}
`;
const QrWrapper = styled.div`
display: flex;
flex-direction: column;
`; `;
const AccountReceive = (props: Props) => { const AccountReceive = (props: Props) => {
@ -159,14 +118,24 @@ const AccountReceive = (props: Props) => {
return ( return (
<Content> <Content>
<React.Fragment> <React.Fragment>
<Title>Receive Ethereum or tokens</Title> <H2>Receive Ethereum or tokens</H2>
<AddressWrapper <AddressWrapper isShowingQrCode={addressVerified || addressUnverified}>
isShowingQrCode={addressVerified || addressUnverified} <Label>Address</Label>
> <Row>
{isAddressVerifying && ( <Input
<AddressInfoText>Confirm address on Trezor</AddressInfoText> type="text"
)} value={address}
{((addressVerified || addressUnverified) && !isAddressVerifying) && ( isPartiallyHidden={isAddressHidden}
trezorAction={isAddressVerifying ? (
<React.Fragment>
<Icon
icon={ICONS.T1}
color={colors.WHITE}
/>
Check address on your Trezor
</React.Fragment>
) : null}
icon={((addressVerified || addressUnverified) && !isAddressVerifying) && (
<Tooltip <Tooltip
placement="left" placement="left"
content={( content={(
@ -177,39 +146,23 @@ const AccountReceive = (props: Props) => {
/> />
)} )}
> >
<EyeButton <EyeButton onClick={() => props.showAddress(account.addressPath)}>
isTransparent
onClick={() => props.showAddress(account.addressPath)}
>
<Icon <Icon
icon={addressUnverified ? ICONS.EYE_CROSSED : ICONS.EYE} icon={addressUnverified ? ICONS.EYE_CROSSED : ICONS.EYE}
color={addressUnverified ? colors.ERROR_PRIMARY : colors.TEXT_PRIMARY} color={addressUnverified ? colors.ERROR_PRIMARY : colors.TEXT_PRIMARY}
/> />
</EyeButton> </EyeButton>
</Tooltip> </Tooltip>
)} )}
<ValueWrapper
isHidden={isAddressHidden}
isVerifying={isAddressVerifying}
>
{address}
</ValueWrapper>
{isAddressVerifying && (
<React.Fragment>
<ArrowUp />
<AddressInfoText>
<Icon
icon={ICONS.T1}
color={colors.WHITE}
/> />
Check address on your Trezor {!(addressVerified || addressUnverified) && (
</AddressInfoText> <ShowAddressButton onClick={() => props.showAddress(account.addressPath)} isDisabled={device.connected && !discovery.completed}>
</React.Fragment> <ShowAddressIcon icon={ICONS.EYE} color={colors.WHITE} />Show full address
</ShowAddressButton>
)} )}
{(addressVerified || addressUnverified) && ( </Row>
<React.Fragment> {(addressVerified || addressUnverified) && !isAddressVerifying && (
<QrWrapper>
<Label>QR code</Label> <Label>QR code</Label>
<StyledQRCode <StyledQRCode
bgColor="#FFFFFF" bgColor="#FFFFFF"
@ -218,12 +171,7 @@ const AccountReceive = (props: Props) => {
style={{ width: 150 }} style={{ width: 150 }}
value={account.address} value={account.address}
/> />
</React.Fragment> </QrWrapper>
)}
{!(addressVerified || addressUnverified) && (
<ShowAddressButton onClick={() => props.showAddress(account.addressPath)} isDisabled={device.connected && !discovery.completed}>
<ShowAddressIcon icon={ICONS.EYE} color={colors.WHITE} />Show full address
</ShowAddressButton>
)} )}
</AddressWrapper> </AddressWrapper>
</React.Fragment> </React.Fragment>

View File

@ -13,7 +13,8 @@ type OwnProps = {}
export type StateProps = { export type StateProps = {
selectedAccount: $ElementType<State, 'selectedAccount'>, selectedAccount: $ElementType<State, 'selectedAccount'>,
signature: string, signature: string,
isSignProgress: boolean isSignProgress: boolean,
isVerifyProgress: boolean
} }
export type DispatchProps = { export type DispatchProps = {
@ -26,6 +27,7 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
selectedAccount: state.selectedAccount, selectedAccount: state.selectedAccount,
signature: state.signVerifyReducer.signature, signature: state.signVerifyReducer.signature,
isSignProgress: state.signVerifyReducer.isSignProgress, isSignProgress: state.signVerifyReducer.isSignProgress,
isVerifyProgress: state.signVerifyReducer.isVerifyProgress,
}); });
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({

View File

@ -0,0 +1,37 @@
import styled from 'styled-components';
import { H2 } from 'components/Heading';
import Button from 'components/Button';
import Paragraph from 'components/Paragraph';
import React from 'react';
import { connect } from 'react-redux';
const Wrapper = styled.div`
display: flex;
flex: 1;
justify-content: center;
align-items: center;
`;
const Row = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const StyledParagraph = styled(Paragraph)`
margin: 10px 50px;
display: block;
text-align: center;
`;
const Seedless = () => (
<Wrapper>
<Row>
<H2>Device is in seedless mode</H2>
<StyledParagraph>It&apos;s not suitable to use this service.</StyledParagraph>
</Row>
</Wrapper>
);
export default connect(null, null)(Seedless);

View File

@ -26,6 +26,7 @@ import WalletSettings from 'views/Wallet/views/WalletSettings';
import WalletBootloader from 'views/Wallet/views/Bootloader'; import WalletBootloader from 'views/Wallet/views/Bootloader';
import WalletFirmwareUpdate from 'views/Wallet/views/FirmwareUpdate'; import WalletFirmwareUpdate from 'views/Wallet/views/FirmwareUpdate';
import WalletInitialize from 'views/Wallet/views/Initialize'; import WalletInitialize from 'views/Wallet/views/Initialize';
import WalletSeedless from 'views/Wallet/views/Seedless';
import WalletAcquire from 'views/Wallet/views/Acquire'; import WalletAcquire from 'views/Wallet/views/Acquire';
import WalletUnreadableDevice from 'views/Wallet/views/UnreadableDevice'; import WalletUnreadableDevice from 'views/Wallet/views/UnreadableDevice';
@ -47,6 +48,7 @@ const App = () => (
<Route exact path={getPattern('wallet-unreadable')} component={WalletUnreadableDevice} /> <Route exact path={getPattern('wallet-unreadable')} component={WalletUnreadableDevice} />
<Route exact path={getPattern('wallet-bootloader')} component={WalletBootloader} /> <Route exact path={getPattern('wallet-bootloader')} component={WalletBootloader} />
<Route exact path={getPattern('wallet-initialize')} component={WalletInitialize} /> <Route exact path={getPattern('wallet-initialize')} component={WalletInitialize} />
<Route exact path={getPattern('wallet-seedless')} component={WalletSeedless} />
<Route exact path={getPattern('wallet-firmware-update')} component={WalletFirmwareUpdate} /> <Route exact path={getPattern('wallet-firmware-update')} component={WalletFirmwareUpdate} />
<Route exact path={getPattern('wallet-device-settings')} component={WalletDeviceSettings} /> <Route exact path={getPattern('wallet-device-settings')} component={WalletDeviceSettings} />
<Route exact path={getPattern('wallet-account-summary')} component={AccountSummary} /> <Route exact path={getPattern('wallet-account-summary')} component={AccountSummary} />

View File

@ -1690,6 +1690,10 @@ balanced-match@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
base16@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
base64-js@0.0.8: base64-js@0.0.8:
version "0.0.8" version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
@ -4149,6 +4153,24 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "^2.0.0" bser "^2.0.0"
fbemitter@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-2.1.1.tgz#523e14fdaf5248805bb02f62efc33be703f51865"
dependencies:
fbjs "^0.8.4"
fbjs@^0.8.0, fbjs@^0.8.4, fbjs@^0.8.5:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
fbjs@^0.8.16: fbjs@^0.8.16:
version "0.8.16" version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
@ -4161,18 +4183,6 @@ fbjs@^0.8.16:
setimmediate "^1.0.5" setimmediate "^1.0.5"
ua-parser-js "^0.7.9" ua-parser-js "^0.7.9"
fbjs@^0.8.5:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
fd-slicer@~1.1.0: fd-slicer@~1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
@ -4342,6 +4352,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.1" inherits "^2.0.1"
readable-stream "^2.0.4" readable-stream "^2.0.4"
flux@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/flux/-/flux-3.1.3.tgz#d23bed515a79a22d933ab53ab4ada19d05b2f08a"
dependencies:
fbemitter "^2.0.0"
fbjs "^0.8.0"
follow-redirects@^1.0.0: follow-redirects@^1.0.0:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.4.1.tgz#d8120f4518190f55aac65bb6fc7b85fcd666d6aa" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.4.1.tgz#d8120f4518190f55aac65bb6fc7b85fcd666d6aa"
@ -6390,10 +6407,18 @@ lodash.camelcase@^4.3.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
lodash.curry@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
lodash.debounce@^4.0.8: lodash.debounce@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
lodash.flow@^3.3.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
lodash.isarguments@^3.0.0: lodash.isarguments@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@ -8183,6 +8208,10 @@ punycode@^2.1.0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
pure-color@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
q@^1.1.2: q@^1.1.2:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -8347,6 +8376,15 @@ rc@^1.1.7:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
react-base16-styling@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c"
dependencies:
base16 "^1.0.0"
lodash.curry "^4.0.1"
lodash.flow "^3.3.0"
pure-color "^1.2.0"
"react-dom@^15.4.2 || ^16.0.0", react-dom@^16.2.0: "react-dom@^15.4.2 || ^16.0.0", react-dom@^16.2.0:
version "16.2.0" version "16.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
@ -8377,6 +8415,15 @@ react-is@^16.3.1:
version "16.4.1" version "16.4.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
react-json-view@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.19.1.tgz#95d8e59e024f08a25e5dc8f076ae304eed97cf5c"
dependencies:
flux "^3.1.3"
react-base16-styling "^0.6.0"
react-lifecycles-compat "^3.0.4"
react-textarea-autosize "^6.1.0"
react-lifecycles-compat@^3.0.4: react-lifecycles-compat@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@ -8453,9 +8500,15 @@ react-select@2.0.0:
react-input-autosize "^2.2.1" react-input-autosize "^2.2.1"
react-transition-group "^2.2.1" react-transition-group "^2.2.1"
<<<<<<< HEAD
react-textarea-autosize@^7.0.4: react-textarea-autosize@^7.0.4:
version "7.0.4" version "7.0.4"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.0.4.tgz#4e4be649b544a88713e7b5043f76950f35d3d503" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.0.4.tgz#4e4be649b544a88713e7b5043f76950f35d3d503"
=======
react-textarea-autosize@^6.1.0:
version "6.1.0"
resolved "http://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz#df91387f8a8f22020b77e3833c09829d706a09a5"
>>>>>>> master
dependencies: dependencies:
prop-types "^15.6.0" prop-types "^15.6.0"