mirror of
https://github.com/trezor/trezor-wallet
synced 2024-12-02 21:28:12 +00:00
resolve conflict
This commit is contained in:
commit
2e70fa0bed
@ -54,6 +54,7 @@
|
||||
"react-dom": "^16.6.3",
|
||||
"react-hot-loader": "^4.6.2",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-qr-reader": "^2.1.2",
|
||||
"react-qr-svg": "^2.1.0",
|
||||
"react-redux": "^6.0.0",
|
||||
"react-router": "^4.3.1",
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable import/no-named-as-default-member */
|
||||
/* @flow */
|
||||
|
||||
import TrezorConnect, { UI } from 'trezor-connect';
|
||||
@ -9,6 +10,10 @@ import type {
|
||||
ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice,
|
||||
} from 'flowtype';
|
||||
import type { State } from 'reducers/ModalReducer';
|
||||
import type { parsedURI } from 'utils/cryptoUriParser';
|
||||
|
||||
import sendEthereumFormActions from './ethereum/SendFormActions';
|
||||
import sendRippleFormActions from './ripple/SendFormActions';
|
||||
|
||||
export type ModalAction = {
|
||||
type: typeof MODAL.CLOSE
|
||||
@ -16,8 +21,11 @@ export type ModalAction = {
|
||||
type: typeof MODAL.OPEN_EXTERNAL_WALLET,
|
||||
id: string,
|
||||
url: string,
|
||||
} | {
|
||||
type: typeof MODAL.OPEN_SCAN_QR,
|
||||
};
|
||||
|
||||
|
||||
export const onPinSubmit = (value: string): Action => {
|
||||
TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value });
|
||||
return {
|
||||
@ -139,6 +147,29 @@ export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dis
|
||||
});
|
||||
};
|
||||
|
||||
export const openQrModal = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
dispatch({
|
||||
type: MODAL.OPEN_SCAN_QR,
|
||||
});
|
||||
};
|
||||
|
||||
export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction => (dispatch: Dispatch): void => {
|
||||
const { address = '', amount } = parsedUri;
|
||||
switch (networkType) {
|
||||
case 'ethereum':
|
||||
dispatch(sendEthereumFormActions.onAddressChange(address));
|
||||
if (amount) dispatch(sendEthereumFormActions.onAmountChange(amount));
|
||||
break;
|
||||
case 'ripple':
|
||||
dispatch(sendRippleFormActions.onAddressChange(address));
|
||||
if (amount) dispatch(sendRippleFormActions.onAmountChange(amount));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
onPinSubmit,
|
||||
onPassphraseSubmit,
|
||||
@ -149,4 +180,6 @@ export default {
|
||||
onDuplicateDevice,
|
||||
onWalletTypeRequest,
|
||||
gotoExternalWallet,
|
||||
openQrModal,
|
||||
onQrScan,
|
||||
};
|
@ -5,3 +5,5 @@ export const OPEN_EXTERNAL_WALLET: 'modal__external_wallet' = 'modal__external_w
|
||||
export const CONTEXT_NONE: 'modal_ctx_none' = 'modal_ctx_none';
|
||||
export const CONTEXT_DEVICE: 'modal_ctx_device' = 'modal_ctx_device';
|
||||
export const CONTEXT_EXTERNAL_WALLET: 'modal_ctx_external-wallet' = 'modal_ctx_external-wallet';
|
||||
export const OPEN_SCAN_QR: 'modal__open_scan_qr' = 'modal__open_scan_qr';
|
||||
export const CONTEXT_SCAN_QR: 'modal__ctx_scan_qr' = 'modal__ctx_scan_qr';
|
||||
|
@ -22,7 +22,7 @@ const Wrapper = styled.div`
|
||||
position: relative;
|
||||
color: ${colors.INFO_PRIMARY};
|
||||
background: ${colors.INFO_SECONDARY};
|
||||
padding: 24px 48px;
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
@ -32,9 +32,10 @@ const Wrapper = styled.div`
|
||||
const Click = styled.div`
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 12px;
|
||||
padding-right: inherit;
|
||||
padding-top: inherit;
|
||||
color: inherit;
|
||||
transition: opacity 0.3s;
|
||||
|
||||
@ -61,7 +62,7 @@ const Log = (props: Props): ?React$Element<string> => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Click onClick={props.toggle}>
|
||||
<Icon size={25} color={colors.INFO_PRIMARY} icon={icons.CLOSE} />
|
||||
<Icon size={24} color={colors.INFO_PRIMARY} icon={icons.CLOSE} />
|
||||
</Click>
|
||||
<H2>Log</H2>
|
||||
<StyledParagraph isSmaller>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</StyledParagraph>
|
||||
|
@ -58,11 +58,8 @@ const Title = styled.div`
|
||||
`;
|
||||
|
||||
const CloseClick = styled.div`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding-right: inherit;
|
||||
padding-top: inherit;
|
||||
margin-left: 24px;
|
||||
align-self: flex-start;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
@ -102,15 +99,6 @@ const Notification = (props: Props): React$Element<string> => {
|
||||
<Wrapper className={props.className} type={props.type}>
|
||||
<Content>
|
||||
{props.loading && <Loader size={50} /> }
|
||||
{props.cancelable && (
|
||||
<CloseClick onClick={() => close()}>
|
||||
<Icon
|
||||
color={getPrimaryColor(props.type)}
|
||||
icon={icons.CLOSE}
|
||||
size={20}
|
||||
/>
|
||||
</CloseClick>
|
||||
)}
|
||||
<Body>
|
||||
<IconWrapper>
|
||||
<StyledIcon
|
||||
@ -138,6 +126,15 @@ const Notification = (props: Props): React$Element<string> => {
|
||||
</ActionContent>
|
||||
)}
|
||||
</AdditionalContent>
|
||||
{props.cancelable && (
|
||||
<CloseClick onClick={() => close()}>
|
||||
<Icon
|
||||
color={getPrimaryColor(props.type)}
|
||||
icon={icons.CLOSE}
|
||||
size={20}
|
||||
/>
|
||||
</CloseClick>
|
||||
)}
|
||||
</Content>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -114,6 +114,7 @@ const TopLabel = styled.span`
|
||||
`;
|
||||
|
||||
const BottomText = styled.span`
|
||||
margin-top: 10px;
|
||||
font-size: ${FONT_SIZE.SMALL};
|
||||
color: ${props => (props.color ? props.color : colors.TEXT_SECONDARY)};
|
||||
`;
|
||||
|
159
src/components/modals/QrModal/index.js
Normal file
159
src/components/modals/QrModal/index.js
Normal file
@ -0,0 +1,159 @@
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import QrReader from 'react-qr-reader';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import colors from 'config/colors';
|
||||
import icons from 'config/icons';
|
||||
|
||||
import { H2 } from 'components/Heading';
|
||||
import P from 'components/Paragraph';
|
||||
import Icon from 'components/Icon';
|
||||
import Link from 'components/Link';
|
||||
|
||||
import { parseUri } from 'utils/cryptoUriParser';
|
||||
import type { parsedURI } from 'utils/cryptoUriParser';
|
||||
import type { Props as BaseProps } from '../Container';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 90vw;
|
||||
max-width: 450px;
|
||||
padding: 30px 0px;
|
||||
`;
|
||||
|
||||
const Padding = styled.div`
|
||||
padding: 0px 48px;
|
||||
`;
|
||||
|
||||
const CloseLink = styled(Link)`
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
`;
|
||||
|
||||
const CameraPlaceholder = styled(P)`
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
`;
|
||||
|
||||
const Error = styled(P)`
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
color: ${colors.ERROR_PRIMARY};
|
||||
`;
|
||||
|
||||
const StyledQrReader = styled(QrReader)`
|
||||
padding: 10px 0;
|
||||
`;
|
||||
|
||||
// TODO fix types
|
||||
type Props = {
|
||||
onScan: (data: parsedURI) => any,
|
||||
onError?: (error: any) => any,
|
||||
onCancel?: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onCancel'>;
|
||||
}
|
||||
|
||||
type State = {
|
||||
readerLoaded: boolean,
|
||||
error: any,
|
||||
};
|
||||
|
||||
class QrModal extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
readerLoaded: false,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
onLoad = () => {
|
||||
this.setState({
|
||||
readerLoaded: true,
|
||||
});
|
||||
}
|
||||
|
||||
handleScan = (data: string) => {
|
||||
if (data) {
|
||||
try {
|
||||
const parsedUri = parseUri(data);
|
||||
if (parsedUri) {
|
||||
this.props.onScan(parsedUri);
|
||||
// reset error
|
||||
this.setState({
|
||||
error: null,
|
||||
});
|
||||
// close window
|
||||
this.handleCancel();
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleError = (err: any) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
error: err,
|
||||
});
|
||||
|
||||
if (this.props.onError) {
|
||||
this.props.onError(err);
|
||||
}
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
if (this.props.onCancel) {
|
||||
this.props.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Wrapper>
|
||||
<CloseLink onClick={this.handleCancel}>
|
||||
<Icon
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={icons.CLOSE}
|
||||
/>
|
||||
</CloseLink>
|
||||
<Padding>
|
||||
<H2>Scan an address from a QR code</H2>
|
||||
{!this.state.readerLoaded && (
|
||||
<CameraPlaceholder>
|
||||
Waiting for camera...
|
||||
</CameraPlaceholder>)
|
||||
}
|
||||
</Padding>
|
||||
<StyledQrReader
|
||||
delay={500}
|
||||
onError={this.handleError}
|
||||
onScan={this.handleScan}
|
||||
onLoad={this.onLoad}
|
||||
style={{ width: '100%' }}
|
||||
showViewFinder={false}
|
||||
/>
|
||||
<Padding>
|
||||
{this.state.error && (
|
||||
<Error>
|
||||
{this.state.error.toString()}
|
||||
</Error>
|
||||
)}
|
||||
</Padding>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QrModal.propTypes = {
|
||||
onScan: PropTypes.func.isRequired,
|
||||
onError: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
};
|
||||
|
||||
export default QrModal;
|
@ -17,7 +17,7 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
|
@ -24,13 +24,13 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
border-top: 1px solid ${colors.DIVIDER};
|
||||
background: ${colors.MAIN};
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
|
@ -31,21 +31,20 @@ const StyledLink = styled(Link)`
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 370px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const StyledP = styled(P)`
|
||||
padding: 10px 0px;
|
||||
padding: 20px 0px;
|
||||
`;
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 0 0 10px 0;
|
||||
Button + Button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
||||
@ -99,13 +98,13 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
||||
return (
|
||||
<Wrapper>
|
||||
<StyledLink onClick={onCancel}>
|
||||
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
<Icon size={24} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
</StyledLink>
|
||||
<H2>{ deviceStatus }</H2>
|
||||
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process.</StyledP>
|
||||
<Row>
|
||||
<StyledButton onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</StyledButton>
|
||||
<StyledButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</StyledButton>
|
||||
<Button onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</Button>
|
||||
<Button isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</Button>
|
||||
</Row>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -41,7 +41,7 @@ const StyledLink = styled(Link)`
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 360px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const Column = styled.div`
|
||||
@ -138,7 +138,7 @@ class DuplicateDevice extends PureComponent<Props, State> {
|
||||
return (
|
||||
<Wrapper>
|
||||
<StyledLink onClick={onCancel}>
|
||||
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
<Icon size={24} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
</StyledLink>
|
||||
<H3>Clone { device.label }?</H3>
|
||||
<StyledP isSmaller>This will create new instance of device which can be used with different passphrase</StyledP>
|
||||
|
@ -4,7 +4,7 @@ import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { H3 } from 'components/Heading';
|
||||
import { H2 } from 'components/Heading';
|
||||
import P from 'components/Paragraph';
|
||||
import Button from 'components/Button';
|
||||
|
||||
@ -19,21 +19,20 @@ type Props = {
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 360px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const StyledP = styled(P)`
|
||||
padding: 7px 0px;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 0 0 10px 0;
|
||||
padding: 20px 0px;
|
||||
`;
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
|
||||
Button + Button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
class ForgetDevice extends PureComponent<Props> {
|
||||
@ -62,11 +61,11 @@ class ForgetDevice extends PureComponent<Props> {
|
||||
render() {
|
||||
return (
|
||||
<Wrapper>
|
||||
<H3>Forget { this.props.device.instanceLabel }?</H3>
|
||||
<H2>Forget { this.props.device.instanceLabel }?</H2>
|
||||
<StyledP isSmaller>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your Trezor again.</StyledP>
|
||||
<Row>
|
||||
<StyledButton onClick={() => this.forget()}>Forget</StyledButton>
|
||||
<StyledButton isWhite onClick={this.props.onCancel}>Don't forget</StyledButton>
|
||||
<Button onClick={() => this.forget()}>Forget</Button>
|
||||
<Button isWhite onClick={this.props.onCancel}>Don't forget</Button>
|
||||
</Row>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -31,12 +31,12 @@ const ButtonContent = styled.div`
|
||||
`;
|
||||
|
||||
const StyledP = styled(P)`
|
||||
padding: 10px 0;
|
||||
padding: 20px 0;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 360px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const Text = styled.div`
|
||||
@ -46,10 +46,10 @@ const Text = styled.div`
|
||||
const Column = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 5px 0;
|
||||
Button + Button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledLoader = styled(Loader)`
|
||||
@ -128,7 +128,7 @@ class RememberDevice extends PureComponent<Props, State> {
|
||||
<H3>Forget {label}?</H3>
|
||||
<StyledP isSmaller>Would you like Trezor Wallet to forget your { devicePlural }, so that it is still visible even while disconnected?</StyledP>
|
||||
<Column>
|
||||
<StyledButton onClick={() => this.forget()}>
|
||||
<Button onClick={() => this.forget()}>
|
||||
<ButtonContent>
|
||||
<Text>Forget</Text>
|
||||
<StyledLoader
|
||||
@ -138,12 +138,12 @@ class RememberDevice extends PureComponent<Props, State> {
|
||||
text={this.state.countdown.toString()}
|
||||
/>
|
||||
</ButtonContent>
|
||||
</StyledButton>
|
||||
<StyledButton
|
||||
</Button>
|
||||
<Button
|
||||
isWhite
|
||||
onClick={() => onRememberDevice(device)}
|
||||
>Remember
|
||||
</StyledButton>
|
||||
</Button>
|
||||
</Column>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -7,7 +7,7 @@ import styled, { css } from 'styled-components';
|
||||
import icons from 'config/icons';
|
||||
import colors from 'config/colors';
|
||||
|
||||
import { H3 } from 'components/Heading';
|
||||
import { H2 } from 'components/Heading';
|
||||
import P from 'components/Paragraph';
|
||||
import Button from 'components/Button';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
@ -36,8 +36,8 @@ const Header = styled.div`
|
||||
color: ${colors.TEXT_PRIMARY};
|
||||
`;
|
||||
|
||||
const StyledHeading = styled(H3)`
|
||||
padding-top: 30px;
|
||||
const StyledHeading = styled(H2)`
|
||||
padding: 30px 48px 10px 48px;
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
@ -105,7 +105,7 @@ class WalletType extends PureComponent<Props> {
|
||||
{ device.state && (
|
||||
<StyledLink onClick={onCancel}>
|
||||
<Icon
|
||||
size={20}
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={icons.CLOSE}
|
||||
/>
|
||||
|
@ -26,7 +26,7 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 10px 0 10px 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@ -48,7 +48,7 @@ const CardanoWallet = (props: Props) => (
|
||||
<Wrapper>
|
||||
<StyledLink onClick={props.onCancel}>
|
||||
<Icon
|
||||
size={20}
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={icons.CLOSE}
|
||||
/>
|
||||
|
6
src/components/modals/external/Nem/index.js
vendored
6
src/components/modals/external/Nem/index.js
vendored
@ -22,11 +22,11 @@ type Props = {
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
max-width: 620px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 0 0 10px 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@ -46,7 +46,7 @@ const NemWallet = (props: Props) => (
|
||||
<Wrapper>
|
||||
<StyledLink onClick={props.onCancel}>
|
||||
<Icon
|
||||
size={20}
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={icons.CLOSE}
|
||||
/>
|
||||
|
@ -26,7 +26,7 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 10px 0 10px 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@ -48,7 +48,7 @@ const StellarWallet = (props: Props) => (
|
||||
<Wrapper>
|
||||
<StyledLink onClick={props.onCancel}>
|
||||
<Icon
|
||||
size={20}
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={icons.CLOSE}
|
||||
/>
|
||||
|
@ -29,6 +29,8 @@ import Nem from 'components/modals/external/Nem';
|
||||
import Cardano from 'components/modals/external/Cardano';
|
||||
import Stellar from 'components/modals/external/Stellar';
|
||||
|
||||
import QrModal from 'components/modals/QrModal';
|
||||
|
||||
import type { Props } from './Container';
|
||||
|
||||
const ModalContainer = styled.div`
|
||||
@ -166,6 +168,20 @@ const getExternalContextModal = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getQrModal = (props: Props) => {
|
||||
const { modalActions, selectedAccount } = props;
|
||||
|
||||
if (!selectedAccount.network) return null;
|
||||
const networkType = selectedAccount.network.type;
|
||||
|
||||
return (
|
||||
<QrModal
|
||||
onCancel={modalActions.onCancel}
|
||||
onScan={parsedUri => modalActions.onQrScan(parsedUri, networkType)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// modal container component
|
||||
const Modal = (props: Props) => {
|
||||
const { modal } = props;
|
||||
@ -179,6 +195,9 @@ const Modal = (props: Props) => {
|
||||
case MODAL.CONTEXT_EXTERNAL_WALLET:
|
||||
component = getExternalContextModal(props);
|
||||
break;
|
||||
case MODAL.CONTEXT_SCAN_QR:
|
||||
component = getQrModal(props);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ type State = {
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
max-width: 390px;
|
||||
`;
|
||||
|
||||
|
@ -19,7 +19,7 @@ type Props = {
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 360px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const Header = styled.div``;
|
||||
|
@ -14,7 +14,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const InvalidPin = (props: Props) => (
|
||||
|
@ -22,6 +22,7 @@ const Wrapper = styled.button`
|
||||
border: 1px solid ${colors.DIVIDER};
|
||||
background: ${colors.WHITE};
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0px;
|
||||
|
@ -25,7 +25,7 @@ type State = {
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const InputRow = styled.div`
|
||||
|
@ -73,6 +73,9 @@ export default {
|
||||
WALLET_HIDDEN: [
|
||||
'M813.472,552.96l-101.344,-281.6c-2.528,-7.68 -12.672,-15.36 -22.784,-15.36l-76,0c-15.2,0 -25.344,10.24 -25.344,25.6c0,15.36 10.144,25.6 25.344,25.6l58.272,0l83.584,230.4l-192.544,0l-101.344,0l-192.512,0l83.616,-230.4l58.272,0c15.2,0 25.344,-10.24 25.344,-25.6c0,-15.36 -10.144,-25.6 -25.344,-25.6l-76,0c-10.144,0 -20.256,7.68 -22.784,17.92l-101.344,281.6c-2.56,0 -2.56,5.12 -2.56,7.68l0,128c0,43.52 32.928,76.8 76,76.8l126.656,0c43.072,0 76,-33.28 76,-76.8l0,-102.4l50.656,0l0,102.4c0,43.52 32.928,76.8 76,76.8l126.656,0c43.072,0 76,-33.28 76,-76.8l0,-128c0.032,-2.56 0.032,-7.68 -2.496,-10.24Z',
|
||||
],
|
||||
QRCODE: [
|
||||
'M832 1024l-64 0l0 -128l64 0l0 128Zm-320 0l-64 0l0 -128l64 0l0 128Zm192 0l-128 0l0 -128l128 0l0 128Zm192 -192l64 0l0 64l64 0l0 128l-128 0l0 -192Zm-896 -192l384 0l0 384l-384 0l0 -384Zm320 320l0 -256l-256 0l0 256l256 0Zm-64 -64l-128 0l0 -128l128 0l0 128Zm512 0l-64 0l0 -64l64 0l0 64Zm-192 -128l0 128l-64 0l0 -64l-64 0l0 -64l128 0Zm128 64l-64 0l0 -64l64 0l0 64Zm192 0l-128 0l0 -64l128 0l0 64Zm-256 -64l-64 0l0 -64l64 0l0 64Zm320 -64l-64 0l0 -64l128 0l0 128l-64 0l0 -64Zm-384 0l-128 0l0 -128l128 0l0 128Zm64 -64l64 0l0 -64l128 0l0 128l-192 0l0 -64Zm-320 -128l64 0l0 -64l64 0l0 128l-128 0l0 -64Zm256 0l-64 0l0 -64l192 0l0 128l-128 0l0 -64Zm-576 -64l128 0l0 64l64 0l0 64l-192 0l0 -128Zm896 64l-128 0l0 -64l256 0l0 128l-128 0l0 -64Zm-576 0l-128 0l0 -64l128 0l0 64Zm192 -64l-64 0l0 -64l64 0l0 64Zm-512 -448l384 0l0 384l-384 0l0 -384Zm576 384l-64 0l0 -128l64 0l0 128Zm64 -384l384 0l0 384l-384 0l0 -384Zm-320 320l0 -256l-256 0l0 256l256 0Zm640 0l0 -256l-256 0l0 256l256 0Zm-704 -64l-128 0l0 -128l128 0l0 128Zm640 0l-128 0l0 -128l128 0l0 128Zm-384 -256l0 64l64 0l0 128l-64 0l0 64l-64 0l0 -256l64 0Z',
|
||||
],
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -18,6 +18,8 @@ export type State = {
|
||||
} | {
|
||||
context: typeof MODAL.CONTEXT_EXTERNAL_WALLET,
|
||||
windowType?: string;
|
||||
} | {
|
||||
context: typeof MODAL.CONTEXT_SCAN_QR,
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
@ -91,6 +93,11 @@ export default function modal(state: State = initialState, action: Action): Stat
|
||||
windowType: action.id,
|
||||
};
|
||||
|
||||
case MODAL.OPEN_SCAN_QR:
|
||||
return {
|
||||
context: MODAL.CONTEXT_SCAN_QR,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
39
src/utils/cryptoUriParser.js
Normal file
39
src/utils/cryptoUriParser.js
Normal file
@ -0,0 +1,39 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable prefer-destructuring */
|
||||
/* @flow */
|
||||
|
||||
// copy paste from mytrezor (old wallet) https://github.com/satoshilabs/mytrezor/blob/87f8a8d9ca82a27b3941c5ec0f399079903f2bfd/app/components/address-input/address-input.js
|
||||
|
||||
export type parsedURI = {
|
||||
address: string,
|
||||
amount: ?string,
|
||||
};
|
||||
|
||||
// Parse a string read from a bitcoin QR code into an object
|
||||
export const parseUri = (uri: string): ?parsedURI => {
|
||||
const str = stripPrefix(uri);
|
||||
const query: Array<string> = str.split('?');
|
||||
const values: Object = (query.length > 1) ? parseQuery(query[1]) : {};
|
||||
values.address = query[0];
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
const stripPrefix = (str: string): string => {
|
||||
if (!str.match(':')) {
|
||||
return str;
|
||||
}
|
||||
const parts = str.split(':');
|
||||
parts.shift();
|
||||
return parts.join('');
|
||||
};
|
||||
|
||||
// Parse URL query string (like 'foo=bar&baz=1337) into an object
|
||||
const parseQuery = (str: string): {} => str.split('&')
|
||||
.map(val => val.split('='))
|
||||
.reduce((vals, pair) => {
|
||||
if (pair.length > 1) {
|
||||
vals[pair[0]] = pair[1];
|
||||
}
|
||||
return vals;
|
||||
}, {});
|
@ -36,7 +36,7 @@ const ModalWindow = styled.div`
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 620px;
|
||||
padding: 24px 48px;
|
||||
padding: 30px 48px;
|
||||
`;
|
||||
|
||||
const StyledP = styled(P)`
|
||||
|
@ -9,6 +9,7 @@ import { FONT_SIZE } from 'config/variables';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
background: ${colors.WHITE};
|
||||
box-shadow: 0 3px 8px rgba(0,0,0,0.06);
|
||||
`;
|
||||
|
||||
const Item = styled.div`
|
||||
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import colors from 'config/colors';
|
||||
import { FONT_SIZE } from 'config/variables';
|
||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
@ -18,6 +18,10 @@ const Wrapper = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
const TextLeft = styled.p`
|
||||
font-weight: ${FONT_WEIGHT.MEDIUM};
|
||||
`;
|
||||
|
||||
const Divider = ({
|
||||
textLeft, textRight, hasBorder = false, className,
|
||||
}) => (
|
||||
@ -25,7 +29,7 @@ const Divider = ({
|
||||
hasBorder={hasBorder}
|
||||
className={className}
|
||||
>
|
||||
<p>{textLeft}</p>
|
||||
<TextLeft>{textLeft}</TextLeft>
|
||||
<p>{textRight}</p>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -30,6 +30,11 @@ const AsideWrapper = styled.aside.attrs(props => ({
|
||||
overflow: hidden;
|
||||
background: ${colors.MAIN};
|
||||
border-right: 1px solid ${colors.DIVIDER};
|
||||
border-top-left-radius: 4px;
|
||||
|
||||
@media screen and (max-width: 1170px) {
|
||||
border-top-left-radius: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StickyContainerWrapper = styled.div.attrs(props => ({
|
||||
|
@ -28,13 +28,14 @@ const WalletTypeIconWrapper = styled.div`
|
||||
`;
|
||||
|
||||
const Counter = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid ${colors.DIVIDER};
|
||||
border-radius: 50%;
|
||||
color: ${colors.TEXT_SECONDARY};
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
font-size: ${FONT_SIZE.COUNTER};
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
@ -66,6 +66,11 @@ const MainContent = styled.article`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
border-top-right-radius: 4px;
|
||||
|
||||
@media screen and (max-width: 1170px) {
|
||||
border-top-right-radius: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Navigation = styled.nav`
|
||||
|
@ -4,6 +4,7 @@ import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import SendFormActions from 'actions/ethereum/SendFormActions';
|
||||
import { openQrModal } from 'actions/ModalActions';
|
||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import type { State, Dispatch } from 'flowtype';
|
||||
import AccountSend from './index';
|
||||
@ -20,6 +21,7 @@ export type StateProps = {
|
||||
|
||||
export type DispatchProps = {
|
||||
sendFormActions: typeof SendFormActions,
|
||||
openQrModal: typeof openQrModal,
|
||||
}
|
||||
|
||||
export type Props = StateProps & DispatchProps;
|
||||
@ -34,6 +36,8 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
|
||||
|
||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||
sendFormActions: bindActionCreators(SendFormActions, dispatch),
|
||||
openQrModal: bindActionCreators(openQrModal, dispatch),
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountSend);
|
@ -4,6 +4,7 @@ import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import colors from 'config/colors';
|
||||
|
||||
import Link from 'components/Link';
|
||||
import Input from 'components/inputs/Input';
|
||||
import Textarea from 'components/Textarea';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
@ -117,7 +118,10 @@ const Right = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: ${FONT_SIZE.SMALL};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
// stateless component
|
||||
@ -190,8 +194,8 @@ const AdvancedForm = (props: Props) => {
|
||||
</Tooltip>
|
||||
</Left>
|
||||
{ showDefaultGasLimitButton && (
|
||||
<Right onClick={() => setDefaultGasLimit()}>
|
||||
Set default
|
||||
<Right>
|
||||
<StyledLink onClick={setDefaultGasLimit} isGreen>Set default</StyledLink>
|
||||
</Right>)
|
||||
}
|
||||
</InputLabelWrapper>
|
||||
|
@ -175,6 +175,14 @@ const AdvancedSettingsIcon = styled(Icon)`
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
const QrButton = styled(Button)`
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-left: 0px;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
`;
|
||||
|
||||
// render helpers
|
||||
const getAddressInputState = (address: string, addressErrors: string, addressWarnings: string): string => {
|
||||
let state = '';
|
||||
@ -298,6 +306,19 @@ const AccountSend = (props: Props) => {
|
||||
bottomText={errors.address || warnings.address || infos.address}
|
||||
value={address}
|
||||
onChange={event => onAddressChange(event.target.value)}
|
||||
sideAddons={[(
|
||||
<QrButton
|
||||
key="qrButton"
|
||||
isWhite
|
||||
onClick={props.openQrModal}
|
||||
>
|
||||
<Icon
|
||||
size={25}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={ICONS.QRCODE}
|
||||
/>
|
||||
</QrButton>
|
||||
)]}
|
||||
/>
|
||||
</InputRow>
|
||||
<InputRow>
|
||||
|
@ -4,6 +4,7 @@ import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import SendFormActions from 'actions/ripple/SendFormActions';
|
||||
import { openQrModal } from 'actions/ModalActions';
|
||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import type { State, Dispatch } from 'flowtype';
|
||||
import AccountSend from './index';
|
||||
@ -20,6 +21,7 @@ export type StateProps = {
|
||||
|
||||
export type DispatchProps = {
|
||||
sendFormActions: typeof SendFormActions,
|
||||
openQrModal: typeof openQrModal,
|
||||
}
|
||||
|
||||
export type Props = StateProps & DispatchProps;
|
||||
@ -34,6 +36,7 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
|
||||
|
||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||
sendFormActions: bindActionCreators(SendFormActions, dispatch),
|
||||
openQrModal: bindActionCreators(openQrModal, dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountSend);
|
@ -172,6 +172,15 @@ const AdvancedSettingsIcon = styled(Icon)`
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
const QrButton = styled(Button)`
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-left: 0px;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
`;
|
||||
|
||||
|
||||
// render helpers
|
||||
const getAddressInputState = (address: string, addressErrors: string, addressWarnings: string): string => {
|
||||
let state = '';
|
||||
@ -271,6 +280,19 @@ const AccountSend = (props: Props) => {
|
||||
bottomText={errors.address || warnings.address || infos.address}
|
||||
value={address}
|
||||
onChange={event => onAddressChange(event.target.value)}
|
||||
sideAddons={[(
|
||||
<QrButton
|
||||
key="qrButton"
|
||||
isWhite
|
||||
onClick={props.openQrModal}
|
||||
>
|
||||
<Icon
|
||||
size={25}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
icon={ICONS.QRCODE}
|
||||
/>
|
||||
</QrButton>
|
||||
)]}
|
||||
/>
|
||||
</InputRow>
|
||||
<InputRow>
|
||||
|
@ -46,7 +46,7 @@ const TokenText = styled.div`
|
||||
`;
|
||||
|
||||
const TokenName = styled(TokenText)`
|
||||
flex: 0 0 0;
|
||||
flex: 1 1 0;
|
||||
padding-right: 5px;
|
||||
`;
|
||||
|
||||
|
34
yarn.lock
34
yarn.lock
@ -5996,6 +5996,11 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
jsqr@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.1.1.tgz#a0d7f95e6c3b0bec913dfef2ca64a877f28ed05f"
|
||||
integrity sha512-FVoMU2ncTyjaOqN/vwvDnZ7jaAVvFzM3LK3vG3jvQZFWJQlAwJ1XTCOgAEKo+4Rkd6ydMXTTvqGV/4w5VunmTw==
|
||||
|
||||
jssha@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jssha/-/jssha-2.3.1.tgz#147b2125369035ca4b2f7d210dc539f009b3de9a"
|
||||
@ -7968,6 +7973,15 @@ react-lifecycles-compat@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
|
||||
react-qr-reader@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.1.2.tgz#ff3d7d377d3ffbd9d78a0947ca04221c2f180e15"
|
||||
integrity sha512-SyRrRRRS7XcIyX8x6tb+mgcMqYZw6Admf4vlnUO/Z21nJklf6WILmP4jstd1W5tNlonvuC/S8R8/doIuZBgVjA==
|
||||
dependencies:
|
||||
jsqr "^1.1.1"
|
||||
prop-types "^15.5.8"
|
||||
webrtc-adapter "^6.4.0"
|
||||
|
||||
react-qr-svg@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-svg/-/react-qr-svg-2.1.0.tgz#fceaf0e8bd367f714d89ae46e46b87c5201a99e9"
|
||||
@ -8652,6 +8666,13 @@ rsvp@^3.3.3:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
|
||||
|
||||
rtcpeerconnection-shim@^1.2.14:
|
||||
version "1.2.15"
|
||||
resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz#e7cc189a81b435324c4949aa3dfb51888684b243"
|
||||
integrity sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==
|
||||
dependencies:
|
||||
sdp "^2.6.0"
|
||||
|
||||
run-async@^2.0.0, run-async@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||
@ -8754,6 +8775,11 @@ scryptsy@^1.2.1:
|
||||
dependencies:
|
||||
pbkdf2 "^3.0.3"
|
||||
|
||||
sdp@^2.6.0, sdp@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.9.0.tgz#2eed2d9c0b26c81ff87593107895c68d6fb9a0a6"
|
||||
integrity sha512-XAVZQO4qsfzVTHorF49zCpkdxiGmPNjA8ps8RcJGtGP3QJ/A8I9/SVg/QnkAFDMXIyGbHZBBFwYBw6WdnhT96w==
|
||||
|
||||
seamless-immutable@^7.1.3:
|
||||
version "7.1.4"
|
||||
resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8"
|
||||
@ -10654,6 +10680,14 @@ webpack@^4.16.3:
|
||||
watchpack "^1.5.0"
|
||||
webpack-sources "^1.0.1"
|
||||
|
||||
webrtc-adapter@^6.4.0:
|
||||
version "6.4.8"
|
||||
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-6.4.8.tgz#eeca3f0d5b40c0e629b865ef2a936a0b658274de"
|
||||
integrity sha512-YM8yl545c/JhYcjGHgaCoA7jRK/KZuMwEDFeP2AcP0Auv5awEd+gZE0hXy9z7Ed3p9HvAXp8jdbe+4ESb1zxAw==
|
||||
dependencies:
|
||||
rtcpeerconnection-shim "^1.2.14"
|
||||
sdp "^2.9.0"
|
||||
|
||||
websocket-driver@>=0.5.1:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"
|
||||
|
Loading…
Reference in New Issue
Block a user