mirror of
https://github.com/trezor/trezor-wallet
synced 2024-12-28 09:58:23 +00:00
merge
This commit is contained in:
commit
c1381823db
@ -31,6 +31,7 @@
|
||||
"ethereumjs-tx": "^1.3.3",
|
||||
"ethereumjs-units": "^0.2.0",
|
||||
"ethereumjs-util": "^5.1.4",
|
||||
"flow-webpack-plugin": "^1.2.0",
|
||||
"git-revision-webpack-plugin": "^3.0.3",
|
||||
"hdkey": "^0.8.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
@ -49,8 +50,7 @@
|
||||
"react-router-redux": "next",
|
||||
"react-scale-text": "^1.2.2",
|
||||
"react-select": "2.0.0",
|
||||
"react-sticky-el": "^1.0.20",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"react-transition-group": "^2.4.0",
|
||||
"redbox-react": "^1.6.0",
|
||||
"redux": "4.0.0",
|
||||
"redux-logger": "^3.0.6",
|
||||
|
@ -13,6 +13,7 @@ import { initialState } from 'reducers/SendFormReducer';
|
||||
import { findToken } from 'reducers/TokensReducer';
|
||||
import { findDevice, getPendingAmount, getPendingNonce } from 'reducers/utils';
|
||||
import * as stateUtils from 'reducers/utils';
|
||||
import { validateAddress } from 'utils/ethUtils';
|
||||
|
||||
import type {
|
||||
Dispatch,
|
||||
@ -345,32 +346,19 @@ export const validation = (props: Props): void => {
|
||||
if (state.untouched) return;
|
||||
// valid address
|
||||
if (state.touched.address) {
|
||||
/* if (state.address.length < 1) {
|
||||
errors.address = 'Address is not set';
|
||||
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
|
||||
errors.address = 'Address is not valid';
|
||||
} else {
|
||||
// address warning or info are set in addressValidation ThunkAction
|
||||
// do not override this
|
||||
if (state.warnings.address) {
|
||||
warnings.address = state.warnings.address;
|
||||
} else if (state.infos.address) {
|
||||
infos.address = state.infos.address;
|
||||
}
|
||||
} */
|
||||
const addressError = validateAddress(state.address);
|
||||
if (addressError) {
|
||||
errors.address = addressError;
|
||||
}
|
||||
|
||||
/* eslint (no-lonely-if) */
|
||||
if (state.address.length < 1) {
|
||||
errors.address = 'Address is not set';
|
||||
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
|
||||
errors.address = 'Address is not valid';
|
||||
} else if (state.warnings.address) {
|
||||
// address warning or info are set in addressValidation ThunkAction
|
||||
// do not override this
|
||||
// address warning or info may be set in addressValidation ThunkAction
|
||||
// do not override them
|
||||
if (state.warnings.address) {
|
||||
warnings.address = state.warnings.address;
|
||||
if (state.infos.address) {
|
||||
infos.address = state.infos.address;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.infos.address) {
|
||||
infos.address = state.infos.address;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,12 @@ class Checkbox extends PureComponent {
|
||||
<IconWrapper checked={checked}>
|
||||
{checked && (
|
||||
<Tick>
|
||||
<Icon size={26} color={checked ? colors.WHITE : colors.GREEN_PRIMARY} icon={icons.SUCCESS} />
|
||||
<Icon
|
||||
hoverColor={colors.WHITE}
|
||||
size={26}
|
||||
color={checked ? colors.WHITE : colors.GREEN_PRIMARY}
|
||||
icon={icons.SUCCESS}
|
||||
/>
|
||||
</Tick>
|
||||
)
|
||||
}
|
||||
|
@ -3,19 +3,13 @@ import RcTooltip from 'rc-tooltip';
|
||||
import colors from 'config/colors';
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FONT_SIZE } from 'config/variables';
|
||||
|
||||
const TooltipContent = styled.div`
|
||||
width: ${props => (props.isAside ? '260px' : '320px')};
|
||||
font-size: ${FONT_SIZE.SMALLEST};
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
.rc-tooltip {
|
||||
max-width: ${props => `${props.maxWidth}px` || 'auto'};
|
||||
position: absolute;
|
||||
z-index: 1070;
|
||||
display: block;
|
||||
background: red;
|
||||
visibility: visible;
|
||||
border: 1px solid ${colors.DIVIDER};
|
||||
border-radius: 3px;
|
||||
@ -178,9 +172,11 @@ class Tooltip extends Component {
|
||||
placement,
|
||||
content,
|
||||
children,
|
||||
maxWidth,
|
||||
} = this.props;
|
||||
return (
|
||||
<Wrapper
|
||||
maxWidth={maxWidth}
|
||||
className={className}
|
||||
innerRef={(node) => { this.tooltipContainerRef = node; }}
|
||||
>
|
||||
@ -188,7 +184,7 @@ class Tooltip extends Component {
|
||||
getTooltipContainer={() => this.tooltipContainerRef}
|
||||
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||
placement={placement}
|
||||
overlay={<TooltipContent>{content}</TooltipContent>}
|
||||
overlay={content}
|
||||
>
|
||||
{children}
|
||||
</RcTooltip>
|
||||
@ -204,6 +200,7 @@ Tooltip.propTypes = {
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
]),
|
||||
maxWidth: PropTypes.number,
|
||||
content: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
|
@ -5,6 +5,7 @@ import P from 'components/Paragraph';
|
||||
import Icon from 'components/Icon';
|
||||
import icons from 'config/icons';
|
||||
import { H3 } from 'components/Heading';
|
||||
import { LINE_HEIGHT } from 'config/variables';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 390px;
|
||||
@ -21,6 +22,12 @@ const Content = styled.div`
|
||||
padding: 24px 48px;
|
||||
`;
|
||||
|
||||
const StyledP = styled(P)`
|
||||
word-wrap: break-word;
|
||||
padding: 5px 0;
|
||||
line-height: ${LINE_HEIGHT.SMALL}
|
||||
`;
|
||||
|
||||
const Label = styled.div`
|
||||
padding-top: 5px;
|
||||
font-size: 10px;
|
||||
@ -49,7 +56,7 @@ const ConfirmSignTx = (props) => {
|
||||
<Label>Send</Label>
|
||||
<P>{`${amount} ${currency}` }</P>
|
||||
<Label>To</Label>
|
||||
<P>{ address }</P>
|
||||
<StyledP>{ address }</StyledP>
|
||||
<Label>Fee</Label>
|
||||
<P>{ selectedFeeLevel.label }</P>
|
||||
</Content>
|
||||
|
@ -41,5 +41,6 @@ export const TRANSITION = {
|
||||
};
|
||||
|
||||
export const LINE_HEIGHT = {
|
||||
SMALL: '1.4',
|
||||
BASE: '1.8',
|
||||
};
|
||||
|
@ -63,6 +63,44 @@ const baseStyles = () => injectGlobal`
|
||||
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
|
||||
}
|
||||
|
||||
.slide-left-enter {
|
||||
transform: translate(100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.slide-left-enter.slide-left-enter-active {
|
||||
transform: translate(0%);
|
||||
transition: transform 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.slide-left-exit {
|
||||
transform: translate(-100%);
|
||||
}
|
||||
|
||||
.slide-left-exit.slide-left-exit-active {
|
||||
transform: translate(0%);
|
||||
transition: transform 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.slide-right-enter {
|
||||
transform: translate(-100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.slide-right-enter.slide-right-enter-active {
|
||||
transform: translate(0%);
|
||||
transition: transform 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.slide-right-exit {
|
||||
transform: translate(-100%);
|
||||
}
|
||||
|
||||
.slide-right-exit.slide-right-exit-active {
|
||||
transform: translate(-200%);
|
||||
transition: transform 300ms ease-in-out;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
export default baseStyles;
|
@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import EthereumjsUtil from 'ethereumjs-util';
|
||||
|
||||
export const decimalToHex = (dec: number): string => new BigNumber(dec).toString(16);
|
||||
|
||||
@ -28,4 +29,16 @@ export const strip = (str: string): string => {
|
||||
return padLeftEven(str);
|
||||
};
|
||||
|
||||
export const calcGasPrice = (price: BigNumber, limit: string): string => price.times(limit).toString();
|
||||
export const calcGasPrice = (price: BigNumber, limit: string): string => price.times(limit).toString();
|
||||
|
||||
export const validateAddress = (address: string): ?string => {
|
||||
const hasUpperCase = new RegExp('^(.*[A-Z].*)$');
|
||||
if (address.length < 1) {
|
||||
return 'Address is not set';
|
||||
} else if (!EthereumjsUtil.isValidAddress(address)) {
|
||||
return 'Address is not valid';
|
||||
} else if (address.match(hasUpperCase) && !EthereumjsUtil.isValidChecksumAddress(address)) {
|
||||
return 'Address is not a valid checksum';
|
||||
}
|
||||
return null;
|
||||
}
|
@ -86,7 +86,7 @@ class ConnectDevice extends Component<Props> {
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ConnectTrezorWrapper>
|
||||
{this.props.showWebUsb && (
|
||||
{this.props.showWebUsb && !this.props.showDisconnect && (
|
||||
<React.Fragment>
|
||||
<P>and</P>
|
||||
<Button isWebUsb>
|
||||
|
@ -20,7 +20,6 @@ import type { Props } from '../common';
|
||||
import Row from '../Row';
|
||||
import RowCoin from '../RowCoin';
|
||||
|
||||
|
||||
const Wrapper = styled.div``;
|
||||
|
||||
const Text = styled.span`
|
||||
@ -28,6 +27,10 @@ const Text = styled.span`
|
||||
color: ${colors.TEXT_SECONDARY};
|
||||
`;
|
||||
|
||||
const TooltipContent = styled.div`
|
||||
font-size: ${FONT_SIZE.SMALLEST};
|
||||
`;
|
||||
|
||||
const RowAccountWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -115,12 +118,12 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
|
||||
const selectedCoin = config.coins.find(c => c.network === location.state.network);
|
||||
|
||||
if (!selected || !selectedCoin) return;
|
||||
|
||||
|
||||
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
|
||||
|
||||
const deviceAccounts: Accounts = findDeviceAccounts(accounts, selected, location.state.network);
|
||||
|
||||
let selectedAccounts = deviceAccounts.map((account, i) => {
|
||||
const selectedAccounts = deviceAccounts.map((account, i) => {
|
||||
// const url: string = `${baseUrl}/network/${location.state.network}/account/${i}`;
|
||||
const url: string = location.pathname.replace(/account+\/([0-9]*)/, `account/${i}`);
|
||||
|
||||
@ -158,26 +161,6 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
|
||||
);
|
||||
});
|
||||
|
||||
if (selectedAccounts.length < 1) {
|
||||
if (selected.connected) {
|
||||
const url: string = location.pathname.replace(/account+\/([0-9]*)/, 'account/0');
|
||||
selectedAccounts = (
|
||||
<NavLink
|
||||
to={url}
|
||||
>
|
||||
<Row column>
|
||||
<RowAccountWrapper
|
||||
isSelected
|
||||
borderTop
|
||||
>
|
||||
Account #1
|
||||
</RowAccountWrapper>
|
||||
</Row>
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let discoveryStatus = null;
|
||||
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
|
||||
|
||||
@ -204,7 +187,8 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
|
||||
} else {
|
||||
discoveryStatus = (
|
||||
<Tooltip
|
||||
content={<React.Fragment>To add a new account, last account must have some transactions.</React.Fragment>}
|
||||
maxWidth={300}
|
||||
content={<TooltipContent>To add a new account, last account must have some transactions.</TooltipContent>}
|
||||
placement="bottom"
|
||||
>
|
||||
<Row>
|
||||
|
@ -20,6 +20,8 @@ const RowCoinWrapper = styled.div`
|
||||
display: block;
|
||||
font-size: ${FONT_SIZE.BASE};
|
||||
color: ${colors.TEXT_PRIMARY};
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: ${colors.GRAY_LIGHT};
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ const AsideWrapper = styled.aside`
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: 320px;
|
||||
min-width: 320px;
|
||||
overflow: hidden;
|
||||
background: ${colors.MAIN};
|
||||
border-right: 1px solid ${colors.DIVIDER};
|
||||
|
@ -24,15 +24,15 @@ const TransitionContentWrapper = styled.div`
|
||||
`;
|
||||
|
||||
const Footer = styled.div`
|
||||
position: fixed;
|
||||
position: relative;
|
||||
width: 320px;
|
||||
bottom: 0;
|
||||
background: ${colors.MAIN};
|
||||
border-right: 1px solid ${colors.DIVIDER};
|
||||
`;
|
||||
|
||||
const Body = styled.div`
|
||||
overflow: auto;
|
||||
background: ${colors.LANDING};
|
||||
width: 320px;
|
||||
`;
|
||||
|
||||
const Help = styled.div`
|
||||
@ -58,6 +58,25 @@ const A = styled.a`
|
||||
}
|
||||
`;
|
||||
|
||||
const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => (
|
||||
<TransitionGroupWrapper component="div" className="transition-container">
|
||||
<CSSTransition
|
||||
key={props.animationType}
|
||||
onExit={() => { window.dispatchEvent(new Event('resize')); }}
|
||||
onExited={() => window.dispatchEvent(new Event('resize'))}
|
||||
in
|
||||
out
|
||||
classNames={props.animationType}
|
||||
appear={false}
|
||||
timeout={300}
|
||||
>
|
||||
<TransitionContentWrapper>
|
||||
{ props.children }
|
||||
</TransitionContentWrapper>
|
||||
</CSSTransition>
|
||||
</TransitionGroupWrapper>
|
||||
);
|
||||
|
||||
class LeftNavigation extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -96,39 +115,6 @@ class LeftNavigation extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to transition component for reuse of transitions
|
||||
getMenuTransition(children) {
|
||||
return (
|
||||
<TransitionGroupWrapper component="div" className="transition-container">
|
||||
<CSSTransition
|
||||
key={this.state.animationType}
|
||||
onEnter={() => {
|
||||
console.warn('ON ENTER');
|
||||
}}
|
||||
onEntering={() => {
|
||||
console.warn('ON ENTERING (ACTIVE)');
|
||||
}}
|
||||
onExit={() => {
|
||||
console.warn('ON EXIT');
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}}
|
||||
onExiting={() => {
|
||||
console.warn('ON EXITING (ACTIVE)');
|
||||
}}
|
||||
onExited={() => window.dispatchEvent(new Event('resize'))}
|
||||
classNames={this.state.animationType}
|
||||
appear={false}
|
||||
timeout={30000}
|
||||
in
|
||||
out
|
||||
>
|
||||
<TransitionContentWrapper>
|
||||
{children}
|
||||
</TransitionContentWrapper>
|
||||
</CSSTransition>
|
||||
</TransitionGroupWrapper>);
|
||||
}
|
||||
|
||||
shouldRenderAccounts() {
|
||||
const { selectedDevice } = this.props.wallet;
|
||||
return selectedDevice
|
||||
@ -148,6 +134,22 @@ class LeftNavigation extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props } = this;
|
||||
let menu;
|
||||
if (this.shouldRenderAccounts()) {
|
||||
menu = (
|
||||
<TransitionMenu animationType="slide-left">
|
||||
<AccountMenu {...props} />
|
||||
</TransitionMenu>
|
||||
);
|
||||
} else if (this.shouldRenderCoins()) {
|
||||
menu = (
|
||||
<TransitionMenu animationType="slide-right">
|
||||
<CoinMenu {...props} />
|
||||
</TransitionMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StickyContainer
|
||||
location={this.props.location.pathname}
|
||||
@ -163,8 +165,7 @@ class LeftNavigation extends Component {
|
||||
/>
|
||||
<Body>
|
||||
{this.state.shouldRenderDeviceSelection && <DeviceMenu {...this.props} />}
|
||||
{this.shouldRenderAccounts() && this.getMenuTransition(<AccountMenu {...this.props} />)}
|
||||
{this.shouldRenderCoins() && this.getMenuTransition(<CoinMenu {...this.props} />)}
|
||||
{menu}
|
||||
</Body>
|
||||
<Footer className="sticky-bottom">
|
||||
<Help>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import webpack from 'webpack';
|
||||
import GitRevisionPlugin from 'git-revision-webpack-plugin';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import FlowWebpackPlugin from 'flow-webpack-plugin';
|
||||
import WebpackBuildNotifierPlugin from 'webpack-build-notifier';
|
||||
|
||||
// turn on for bundle analyzing
|
||||
@ -101,6 +102,9 @@ module.exports = {
|
||||
new webpack.DefinePlugin({
|
||||
COMMITHASH: JSON.stringify(gitRevisionPlugin.commithash()),
|
||||
}),
|
||||
new FlowWebpackPlugin({
|
||||
reportingSeverity: 'warning',
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['index'],
|
||||
template: `${SRC}index.html`,
|
||||
|
37
yarn.lock
37
yarn.lock
@ -3297,6 +3297,10 @@ dom-helpers@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
|
||||
|
||||
dom-helpers@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
|
||||
@ -4310,6 +4314,10 @@ flow-parser@^0.*:
|
||||
version "0.72.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.72.0.tgz#6c8041e76ac7d0be1a71ce29c00cd1435fb6013c"
|
||||
|
||||
flow-webpack-plugin@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-webpack-plugin/-/flow-webpack-plugin-1.2.0.tgz#1958821d16135028e391cad5ee2f3a4fa78197ec"
|
||||
|
||||
flush-write-stream@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
|
||||
@ -8071,13 +8079,6 @@ prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, pr
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
prop-types@>=15.5.10, prop-types@^15.6.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||
dependencies:
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
prop-types@^15.6.1:
|
||||
version "15.6.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
|
||||
@ -8086,6 +8087,13 @@ prop-types@^15.6.1:
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
prop-types@^15.6.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||
dependencies:
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
proxy-addr@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
|
||||
@ -8418,12 +8426,6 @@ react-select@2.0.0:
|
||||
react-input-autosize "^2.2.1"
|
||||
react-transition-group "^2.2.1"
|
||||
|
||||
react-sticky-el@^1.0.20:
|
||||
version "1.0.20"
|
||||
resolved "https://registry.yarnpkg.com/react-sticky-el/-/react-sticky-el-1.0.20.tgz#b3c5e7128218633f440dc67aec239d1cd078342d"
|
||||
dependencies:
|
||||
prop-types ">=15.5.10"
|
||||
|
||||
react-transition-group@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.1.tgz#e9fb677b79e6455fd391b03823afe84849df4a10"
|
||||
@ -8435,6 +8437,15 @@ react-transition-group@^2.2.1:
|
||||
prop-types "^15.5.8"
|
||||
warning "^3.0.0"
|
||||
|
||||
react-transition-group@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a"
|
||||
dependencies:
|
||||
dom-helpers "^3.3.1"
|
||||
loose-envify "^1.3.1"
|
||||
prop-types "^15.6.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
"react@^15.4.2 || ^16.0.0":
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
|
||||
|
Loading…
Reference in New Issue
Block a user