mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-24 09:18:09 +00:00
merge
This commit is contained in:
commit
a0cfa4bf4a
@ -112,7 +112,7 @@ export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispa
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean): ThunkAction => (dispatch: Dispatch): void => {
|
export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean, state: ?string): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: MODAL.CLOSE,
|
type: MODAL.CLOSE,
|
||||||
});
|
});
|
||||||
@ -120,6 +120,7 @@ export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean): Thun
|
|||||||
type: CONNECT.RECEIVE_WALLET_TYPE,
|
type: CONNECT.RECEIVE_WALLET_TYPE,
|
||||||
device,
|
device,
|
||||||
hidden,
|
hidden,
|
||||||
|
state,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import * as ValidationActions from 'actions/SendFormValidationActions';
|
|||||||
import { initialState } from 'reducers/SendFormReducer';
|
import { initialState } from 'reducers/SendFormReducer';
|
||||||
import { findToken } from 'reducers/TokensReducer';
|
import { findToken } from 'reducers/TokensReducer';
|
||||||
import * as reducerUtils from 'reducers/utils';
|
import * as reducerUtils from 'reducers/utils';
|
||||||
|
import * as ethUtils from 'utils/ethUtils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -397,8 +398,7 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const requestedData = state.data;
|
const requestedData = state.data;
|
||||||
const re = /^[0-9A-Fa-f]+$/g; // TODO: allow "0x" prefix
|
if (!ethUtils.isHex(requestedData)) {
|
||||||
if (!re.test(requestedData)) {
|
|
||||||
// stop "calculatingGasLimit" process
|
// stop "calculatingGasLimit" process
|
||||||
dispatch(onGasLimitChange(requestedData.length > 0 ? state.gasLimit : network.defaultGasLimit.toString()));
|
dispatch(onGasLimitChange(requestedData.length > 0 ? state.gasLimit : network.defaultGasLimit.toString()));
|
||||||
return;
|
return;
|
||||||
|
@ -6,6 +6,7 @@ import EthereumjsUnits from 'ethereumjs-units';
|
|||||||
import { findToken } from 'reducers/TokensReducer';
|
import { findToken } from 'reducers/TokensReducer';
|
||||||
import { findDevice, getPendingAmount } from 'reducers/utils';
|
import { findDevice, getPendingAmount } from 'reducers/utils';
|
||||||
import * as SEND from 'actions/constants/send';
|
import * as SEND from 'actions/constants/send';
|
||||||
|
import * as ethUtils from 'utils/ethUtils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -19,7 +20,6 @@ const NUMBER_RE: RegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?
|
|||||||
const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$');
|
const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$');
|
||||||
const ABS_RE = new RegExp('^[0-9]+$');
|
const ABS_RE = new RegExp('^[0-9]+$');
|
||||||
const ETH_18_RE = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$');
|
const ETH_18_RE = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$');
|
||||||
const HEX_RE = new RegExp('^[0-9A-Fa-f]+$');
|
|
||||||
const dynamicRegexp = (decimals: number): RegExp => {
|
const dynamicRegexp = (decimals: number): RegExp => {
|
||||||
if (decimals > 0) {
|
if (decimals > 0) {
|
||||||
return new RegExp(`^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?|\\.[0-9]{1,${decimals}})$`);
|
return new RegExp(`^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?|\\.[0-9]{1,${decimals}})$`);
|
||||||
@ -326,7 +326,7 @@ export const nonceValidation = ($state: State): PayloadAction<State> => (dispatc
|
|||||||
export const dataValidation = ($state: State): PayloadAction<State> => (): State => {
|
export const dataValidation = ($state: State): PayloadAction<State> => (): State => {
|
||||||
const state = { ...$state };
|
const state = { ...$state };
|
||||||
if (!state.touched.data || state.data.length === 0) return state;
|
if (!state.touched.data || state.data.length === 0) return state;
|
||||||
if (!HEX_RE.test(state.data)) {
|
if (!ethUtils.isHex(state.data)) {
|
||||||
state.errors.data = 'Data is not valid hexadecimal';
|
state.errors.data = 'Data is not valid hexadecimal';
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
@ -79,6 +79,7 @@ export type TrezorConnectAction = {
|
|||||||
type: typeof CONNECT.RECEIVE_WALLET_TYPE,
|
type: typeof CONNECT.RECEIVE_WALLET_TYPE,
|
||||||
device: TrezorDevice,
|
device: TrezorDevice,
|
||||||
hidden: boolean,
|
hidden: boolean,
|
||||||
|
state: ?string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
@ -5,6 +5,7 @@ import EthereumjsUnits from 'ethereumjs-units';
|
|||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { toHex } from 'web3-utils'; // eslint-disable-line import/no-extraneous-dependencies
|
import { toHex } from 'web3-utils'; // eslint-disable-line import/no-extraneous-dependencies
|
||||||
import { initWeb3 } from 'actions/Web3Actions';
|
import { initWeb3 } from 'actions/Web3Actions';
|
||||||
|
import * as ethUtils from 'utils/ethUtils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -29,10 +30,9 @@ type EthereumTxRequest = {
|
|||||||
export const prepareEthereumTx = (tx: EthereumTxRequest): PromiseAction<EthereumTransaction> => async (dispatch: Dispatch): Promise<EthereumTransaction> => {
|
export const prepareEthereumTx = (tx: EthereumTxRequest): PromiseAction<EthereumTransaction> => async (dispatch: Dispatch): Promise<EthereumTransaction> => {
|
||||||
const instance = await dispatch(initWeb3(tx.network));
|
const instance = await dispatch(initWeb3(tx.network));
|
||||||
const { token } = tx;
|
const { token } = tx;
|
||||||
let data: string = `0x${tx.data}`; // TODO: check if already prefixed
|
let data: string = ethUtils.sanitizeHex(tx.data);
|
||||||
let value: string = toHex(EthereumjsUnits.convert(tx.amount, 'ether', 'wei'));
|
let value: string = toHex(EthereumjsUnits.convert(tx.amount, 'ether', 'wei'));
|
||||||
let to: string = tx.to; // eslint-disable-line prefer-destructuring
|
let to: string = tx.to; // eslint-disable-line prefer-destructuring
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
// smart contract transaction
|
// smart contract transaction
|
||||||
const contract = instance.erc20.clone();
|
const contract = instance.erc20.clone();
|
||||||
|
@ -7,6 +7,7 @@ import EthereumjsUnits from 'ethereumjs-units';
|
|||||||
import type { EstimateGasOptions } from 'web3';
|
import type { EstimateGasOptions } from 'web3';
|
||||||
import * as WEB3 from 'actions/constants/web3';
|
import * as WEB3 from 'actions/constants/web3';
|
||||||
import * as PENDING from 'actions/constants/pendingTx';
|
import * as PENDING from 'actions/constants/pendingTx';
|
||||||
|
import * as ethUtils from 'utils/ethUtils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -272,8 +273,7 @@ export const updateGasPrice = (network: string): PromiseAction<void> => async (d
|
|||||||
|
|
||||||
export const estimateGasLimit = (network: string, $options: EstimateGasOptions): PromiseAction<string> => async (dispatch: Dispatch): Promise<string> => {
|
export const estimateGasLimit = (network: string, $options: EstimateGasOptions): PromiseAction<string> => async (dispatch: Dispatch): Promise<string> => {
|
||||||
const instance = await dispatch(initWeb3(network));
|
const instance = await dispatch(initWeb3(network));
|
||||||
// TODO: allow data starting with 0x ...
|
const data = ethUtils.sanitizeHex($options.data);
|
||||||
const data = `0x${$options.data.length % 2 === 0 ? $options.data : `0${$options.data}`}`;
|
|
||||||
const options = {
|
const options = {
|
||||||
...$options,
|
...$options,
|
||||||
to: '0x0000000000000000000000000000000000000000',
|
to: '0x0000000000000000000000000000000000000000',
|
||||||
|
@ -6,13 +6,14 @@ const baseStyles = css`
|
|||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
color: ${colors.TEXT_PRIMARY};
|
color: ${colors.TEXT_PRIMARY};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const H1 = styled.h1`
|
const H1 = styled.h1`
|
||||||
${baseStyles};
|
${baseStyles};
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
padding-bottom: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const H2 = styled.h2`
|
const H2 = styled.h2`
|
||||||
@ -23,16 +24,19 @@ const H2 = styled.h2`
|
|||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
padding-bottom: 24px
|
padding-bottom: 24px
|
||||||
`}
|
`}
|
||||||
|
padding-bottom: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const H3 = styled.h3`
|
const H3 = styled.h3`
|
||||||
${baseStyles};
|
${baseStyles};
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const H4 = styled.h4`
|
const H4 = styled.h4`
|
||||||
${baseStyles};
|
${baseStyles};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
padding-bottom: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -9,6 +9,7 @@ const Wrapper = styled.p`
|
|||||||
line-height: ${LINE_HEIGHT.BASE};
|
line-height: ${LINE_HEIGHT.BASE};
|
||||||
color: ${colors.TEXT_SECONDARY};
|
color: ${colors.TEXT_SECONDARY};
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
${props => props.isSmaller && css`
|
${props => props.isSmaller && css`
|
||||||
font-size: ${FONT_SIZE.SMALLER};
|
font-size: ${FONT_SIZE.SMALLER};
|
||||||
|
@ -1,22 +1,31 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
model: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div``;
|
const Wrapper = styled.div``;
|
||||||
|
|
||||||
const Img = styled.img`
|
const Img = styled.img`
|
||||||
width: ${props => (props.model === 'T' ? '17px' : '13px')};
|
width: ${props => (props.model === 'T' ? '17px' : '13px')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TrezorImage = ({ model }) => (
|
const TrezorImage = ({ model }: Props) => {
|
||||||
<Wrapper>
|
// $FlowIssue
|
||||||
<Img model={model} src={model === 'T' ? './images/trezor-T.png' : './images/trezor-1.png'} />
|
const src = require(`./images/trezor-${model}.png`); // eslint-disable-line
|
||||||
</Wrapper>
|
return (
|
||||||
);
|
<Wrapper>
|
||||||
|
<Img model={model} src={src} />
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
TrezorImage.propTypes = {
|
TrezorImage.propTypes = {
|
||||||
model: PropTypes.string,
|
model: PropTypes.string,
|
||||||
status: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TrezorImage;
|
export default TrezorImage;
|
||||||
|
@ -86,10 +86,10 @@ class WalletType extends Component<Props> {
|
|||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
changeType(hidden: boolean) {
|
changeType(hidden: boolean, state: ?string) {
|
||||||
const { modal } = this.props;
|
const { modal } = this.props;
|
||||||
if (!modal.opened) return;
|
if (!modal.opened) return;
|
||||||
this.props.modalActions.onWalletTypeRequest(modal.device, hidden);
|
this.props.modalActions.onWalletTypeRequest(modal.device, hidden, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -115,7 +115,7 @@ class WalletType extends Component<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)}>Go to your standard wallet</StyledButton>
|
<StyledButton onClick={() => this.changeType(true)}>Go to your standard wallet</StyledButton>
|
||||||
</Content>
|
</Content>
|
||||||
<Content>
|
<Content>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -139,7 +139,7 @@ class WalletType extends Component<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)}>Go to your hidden wallet</StyledButton>
|
<StyledButton isWhite onClick={() => this.changeType(true, device.state)}>Go to your hidden wallet</StyledButton>
|
||||||
</Content>
|
</Content>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
@ -18,6 +18,7 @@ const StyledInput = styled.input`
|
|||||||
padding: 5px 31px 10px 20px;
|
padding: 5px 31px 10px 20px;
|
||||||
color: ${colors.TEXT_PRIMARY};
|
color: ${colors.TEXT_PRIMARY};
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
border: 1px solid ${colors.DIVIDER};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledIcon = styled(Icon)`
|
const StyledIcon = styled(Icon)`
|
||||||
|
@ -40,10 +40,6 @@ const Footer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
class Pin extends Component<Props, State> {
|
class Pin extends Component<Props, State> {
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
|
||||||
|
|
||||||
state: State;
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -52,6 +48,15 @@ class Pin extends Component<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillMount(): void {
|
||||||
|
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||||
|
window.addEventListener('keydown', this.keyboardHandler, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
window.removeEventListener('keydown', this.keyboardHandler, false);
|
||||||
|
}
|
||||||
|
|
||||||
onPinAdd = (input: number): void => {
|
onPinAdd = (input: number): void => {
|
||||||
let { pin } = this.state;
|
let { pin } = this.state;
|
||||||
if (pin.length < 9) {
|
if (pin.length < 9) {
|
||||||
@ -120,18 +125,11 @@ class Pin extends Component<Props, State> {
|
|||||||
case 105:
|
case 105:
|
||||||
this.onPinAdd(9);
|
this.onPinAdd(9);
|
||||||
break;
|
break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
componentWillMount(): void {
|
|
||||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
|
||||||
window.addEventListener('keydown', this.keyboardHandler, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount(): void {
|
|
||||||
window.removeEventListener('keydown', this.keyboardHandler, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
if (!this.props.modal.opened) return null;
|
||||||
@ -165,7 +163,7 @@ class Pin extends Component<Props, State> {
|
|||||||
<StyledP isSmaller>Not sure how PIN works?
|
<StyledP isSmaller>Not sure how PIN works?
|
||||||
<StyledLink
|
<StyledLink
|
||||||
isGreen
|
isGreen
|
||||||
href="http://doc.satoshilabs.com/trezor-user/enteringyourpin.html"
|
href="https://wiki.trezor.io/User_manual:Entering_PIN"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>Learn more
|
>Learn more
|
||||||
|
@ -6,12 +6,11 @@ import type { Props } from '../../index';
|
|||||||
|
|
||||||
// There could be only one account notification
|
// There could be only one account notification
|
||||||
export default (props: Props) => {
|
export default (props: Props) => {
|
||||||
const { notification } = props.selectedAccount;
|
const { network, notification } = props.selectedAccount;
|
||||||
if (notification) {
|
if (network && notification) {
|
||||||
if (notification.type === 'backend') {
|
if (notification.type === 'backend') {
|
||||||
// special case: backend is down
|
// special case: backend is down
|
||||||
// TODO: this is a different component with "auto resolve" button
|
// TODO: this is a different component with "auto resolve" button
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Notification
|
<Notification
|
||||||
type="error"
|
type="error"
|
||||||
@ -21,14 +20,12 @@ export default (props: Props) => {
|
|||||||
[{
|
[{
|
||||||
label: 'Connect',
|
label: 'Connect',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
await props.blockchainReconnect('trop');
|
await props.blockchainReconnect(network.network);
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
// return (<Notification type="error" title={notification.title} message={notification.message} />);
|
|
||||||
}
|
}
|
||||||
return (<Notification type={notification.type} title={notification.title} message={notification.message} />);
|
return (<Notification type={notification.type} title={notification.title} message={notification.message} />);
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import baseStyles from 'support/styles';
|
import baseStyles from 'support/styles';
|
||||||
import { onBeforeUnload } from 'actions/WalletActions';
|
|
||||||
import App from 'views/index';
|
import App from 'views/index';
|
||||||
import store from './store';
|
|
||||||
|
|
||||||
const root: ?HTMLElement = document.getElementById('root');
|
const root: ?HTMLElement = document.getElementById('root');
|
||||||
if (root) {
|
if (root) {
|
||||||
@ -13,7 +11,8 @@ if (root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.onbeforeunload = () => {
|
window.onbeforeunload = () => {
|
||||||
store.dispatch(onBeforeUnload());
|
// $FlowIssue: render empty component
|
||||||
|
render(null, root);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Application life cycle starts in services/WalletService.js
|
// Application life cycle starts in services/WalletService.js
|
@ -110,7 +110,9 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
|||||||
api.dispatch(DiscoveryActions.restore());
|
api.dispatch(DiscoveryActions.restore());
|
||||||
break;
|
break;
|
||||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||||
api.dispatch(RouterActions.selectFirstAvailableDevice(true));
|
if (action.state) {
|
||||||
|
api.dispatch(RouterActions.selectFirstAvailableDevice(true));
|
||||||
|
}
|
||||||
api.dispatch(TrezorConnectActions.authorizeDevice());
|
api.dispatch(TrezorConnectActions.authorizeDevice());
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
|
@ -7,15 +7,14 @@ export const decimalToHex = (dec: number): string => new BigNumber(dec).toString
|
|||||||
|
|
||||||
export const padLeftEven = (hex: string): string => (hex.length % 2 !== 0 ? `0${hex}` : hex);
|
export const padLeftEven = (hex: string): string => (hex.length % 2 !== 0 ? `0${hex}` : hex);
|
||||||
|
|
||||||
export const sanitizeHex = ($hex: number | string): ?string => {
|
export const sanitizeHex = ($hex: string): string => {
|
||||||
if (typeof $hex !== 'string') return null;
|
const hex = $hex.toLowerCase().substring(0, 2) === '0x' ? $hex.substring(2) : $hex;
|
||||||
const hex = $hex.substring(0, 2) === '0x' ? $hex.substring(2) : $hex;
|
|
||||||
if (hex === '') return '';
|
if (hex === '') return '';
|
||||||
return `0x${padLeftEven(hex)}`;
|
return `0x${padLeftEven(hex)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hexToDecimal = (hex: number): string => {
|
export const hexToDecimal = (hex: number): string => {
|
||||||
const sanitized: ?string = sanitizeHex(hex);
|
const sanitized: ?string = sanitizeHex(hex.toString());
|
||||||
return !sanitized ? 'null' : new BigNumber(sanitized).toString();
|
return !sanitized ? 'null' : new BigNumber(sanitized).toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,3 +40,8 @@ export const validateAddress = (address: string): ?string => {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isHex = (str: string): boolean => {
|
||||||
|
const regExp = /^(0x|0X)?[0-9A-Fa-f]+$/g;
|
||||||
|
return regExp.test(str);
|
||||||
|
};
|
@ -3,9 +3,11 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||||
import { Select } from 'components/Select';
|
import { Select } from 'components/Select';
|
||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
|
import { H1 } from 'components/Heading';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
@ -30,26 +32,39 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
selectFirstAvailableDevice: typeof RouterActions.selectFirstAvailableDevice,
|
||||||
transport: $ElementType<TrezorConnectState, 'transport'>;
|
transport: $ElementType<TrezorConnectState, 'transport'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InstallBridgeWrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-top: 0px;
|
align-items: center;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TitleHeader = styled.h3`
|
const Top = styled.div`
|
||||||
font-size: ${FONT_SIZE.BIGGEST};
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
max-width: 500px;
|
||||||
margin-bottom: 24px;
|
flex: 1;
|
||||||
|
padding-top: 30px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const BridgeVersion = styled.span`
|
const Bottom = styled.div`
|
||||||
|
padding-bottom: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TitleHeader = styled(H1)`
|
||||||
|
display: flex;
|
||||||
|
font-size: ${FONT_SIZE.BIGGEST};
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Version = styled.span`
|
||||||
color: ${colors.GREEN_PRIMARY};
|
color: ${colors.GREEN_PRIMARY};
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
border: 1px solid ${colors.GREEN_PRIMARY};
|
border: 1px solid ${colors.GREEN_PRIMARY};
|
||||||
@ -68,7 +83,7 @@ const SelectWrapper = styled(Select)`
|
|||||||
width: 180px;
|
width: 180px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const DownloadBridgeWrapper = styled.div`
|
const Download = styled.div`
|
||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -81,7 +96,29 @@ const DownloadBridgeButton = styled(Button)`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default class InstallBridge extends Component<Props, State> {
|
const GoBack = styled.span`
|
||||||
|
color: ${colors.GREEN_PRIMARY};
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Ol = styled.ul`
|
||||||
|
margin: 0 auto;
|
||||||
|
color: ${colors.TEXT_SECONDARY};
|
||||||
|
font-size: ${FONT_SIZE.BASE};
|
||||||
|
padding: 10px 0 15px 25px;
|
||||||
|
text-align: left;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Li = styled.li`
|
||||||
|
text-align: justify;
|
||||||
|
`;
|
||||||
|
|
||||||
|
class InstallBridge extends Component<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -113,67 +150,68 @@ export default class InstallBridge extends Component<Props, State> {
|
|||||||
if (!target) {
|
if (!target) {
|
||||||
return <Loader text="Loading" size={100} />;
|
return <Loader text="Loading" size={100} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changelog = this.props.transport.bridge.changelog.map(entry => (
|
|
||||||
<li key={entry}>{entry}</li>
|
|
||||||
));
|
|
||||||
const url = `${this.state.uri}${target.value}`;
|
|
||||||
return (
|
return (
|
||||||
<InstallBridgeWrapper>
|
<Wrapper>
|
||||||
<TitleHeader>TREZOR Bridge.<BridgeVersion>{this.state.currentVersion}</BridgeVersion></TitleHeader>
|
<Top>
|
||||||
<P>New communication tool to facilitate the connection between your TREZOR and your internet browser.</P>
|
<TitleHeader>TREZOR Bridge<Version>{this.state.currentVersion}</Version></TitleHeader>
|
||||||
<DownloadBridgeWrapper>
|
<P>New communication tool to facilitate the connection between your TREZOR and your internet browser.</P>
|
||||||
<SelectWrapper
|
<Download>
|
||||||
isSearchable={false}
|
<SelectWrapper
|
||||||
isClearable={false}
|
isSearchable={false}
|
||||||
value={target}
|
isClearable={false}
|
||||||
onChange={v => this.onChange(v)}
|
value={target}
|
||||||
options={this.state.installers}
|
onChange={v => this.onChange(v)}
|
||||||
/>
|
options={this.state.installers}
|
||||||
<Link href={url}>
|
/>
|
||||||
<DownloadBridgeButton>
|
<Link href={`${this.state.uri}${target.value}`}>
|
||||||
<Icon
|
<DownloadBridgeButton>
|
||||||
icon={ICONS.DOWNLOAD}
|
<Icon
|
||||||
color={colors.WHITE}
|
icon={ICONS.DOWNLOAD}
|
||||||
size={30}
|
color={colors.WHITE}
|
||||||
/>
|
size={30}
|
||||||
|
/>
|
||||||
Download latest Bridge {this.state.latestVersion}
|
Download latest Bridge {this.state.latestVersion}
|
||||||
</DownloadBridgeButton>
|
</DownloadBridgeButton>
|
||||||
</Link>
|
</Link>
|
||||||
</DownloadBridgeWrapper>
|
</Download>
|
||||||
{target.signature && (
|
<Ol>
|
||||||
<P>
|
{this.props.transport.bridge.changelog.map(entry => (
|
||||||
|
<Li key={entry}>{entry}</Li>
|
||||||
|
))}
|
||||||
|
</Ol>
|
||||||
|
<P isSmaller>
|
||||||
|
<LearnMoreText>Learn more about latest versions in</LearnMoreText>
|
||||||
<Link
|
<Link
|
||||||
href={this.state.uri + target.signature}
|
href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
isGreen
|
isGreen
|
||||||
>Check PGP signature
|
>Changelog
|
||||||
</Link>
|
</Link>
|
||||||
</P>
|
</P>
|
||||||
)}
|
<P>
|
||||||
<P>
|
{target.signature && (
|
||||||
{ changelog }
|
<Link
|
||||||
<LearnMoreText>Learn more about latest versions in</LearnMoreText>
|
href={this.state.uri + target.signature}
|
||||||
<Link
|
target="_blank"
|
||||||
href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md"
|
rel="noreferrer noopener"
|
||||||
target="_blank"
|
isGreen
|
||||||
rel="noreferrer noopener"
|
>Check PGP signature
|
||||||
isGreen
|
</Link>
|
||||||
>Changelog
|
)}
|
||||||
</Link>
|
</P>
|
||||||
</P>
|
</Top>
|
||||||
{this.props.transport.type && (
|
<Bottom>
|
||||||
<React.Fragment>
|
{this.props.transport.type && (
|
||||||
<P>
|
<P>
|
||||||
No, i dont want to upgrade Bridge now,
|
No, i dont want to upgrade Bridge now<br />
|
||||||
|
Take me <GoBack onClick={() => this.props.selectFirstAvailableDevice()}>back to the wallet</GoBack>
|
||||||
</P>
|
</P>
|
||||||
<P>
|
)}
|
||||||
Take me <Link href="#/">back to the wallet</Link>
|
</Bottom>
|
||||||
</P>
|
</Wrapper>
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
</InstallBridgeWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default InstallBridge;
|
@ -36,7 +36,6 @@ const LandingWrapper = styled.div`
|
|||||||
const LandingContent = styled.div`
|
const LandingContent = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -85,6 +84,7 @@ export default (props: Props) => {
|
|||||||
const shouldShowUnsupportedBrowser = browserState.supported === false;
|
const shouldShowUnsupportedBrowser = browserState.supported === false;
|
||||||
|
|
||||||
const isLoading = !shouldShowInitializationError && !shouldShowInstallBridge && !shouldShowConnectDevice && !shouldShowUnsupportedBrowser && !localStorageError;
|
const isLoading = !shouldShowInitializationError && !shouldShowInstallBridge && !shouldShowConnectDevice && !shouldShowUnsupportedBrowser && !localStorageError;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LandingWrapper>
|
<LandingWrapper>
|
||||||
{isLoading && <LandingLoader text="Loading" size={100} />}
|
{isLoading && <LandingLoader text="Loading" size={100} />}
|
||||||
@ -103,7 +103,7 @@ export default (props: Props) => {
|
|||||||
<Log />
|
<Log />
|
||||||
<LandingContent>
|
<LandingContent>
|
||||||
{shouldShowUnsupportedBrowser && <BrowserNotSupported />}
|
{shouldShowUnsupportedBrowser && <BrowserNotSupported />}
|
||||||
{shouldShowInstallBridge && <InstallBridge transport={transport} />}
|
{shouldShowInstallBridge && <InstallBridge selectFirstAvailableDevice={props.selectFirstAvailableDevice} transport={transport} />}
|
||||||
|
|
||||||
{(shouldShowConnectDevice || shouldShowDisconnectDevice) && (
|
{(shouldShowConnectDevice || shouldShowDisconnectDevice) && (
|
||||||
<div>
|
<div>
|
||||||
|
Loading…
Reference in New Issue
Block a user