mirror of
https://github.com/trezor/trezor-wallet
synced 2024-12-28 18:08:08 +00:00
Merge pull request #174 from trezor/feature/modal-context
Feature/modal context
This commit is contained in:
commit
8b332b4763
@ -13,8 +13,9 @@ import type { State } from 'reducers/ModalReducer';
|
|||||||
export type ModalAction = {
|
export type ModalAction = {
|
||||||
type: typeof MODAL.CLOSE
|
type: typeof MODAL.CLOSE
|
||||||
} | {
|
} | {
|
||||||
type: typeof MODAL.REMEMBER,
|
type: typeof MODAL.OPEN_EXTERNAL_WALLET,
|
||||||
device: TrezorDevice
|
id: string,
|
||||||
|
url: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onPinSubmit = (value: string): Action => {
|
export const onPinSubmit = (value: string): Action => {
|
||||||
@ -38,13 +39,6 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (di
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// export const askForRemember = (device: TrezorDevice): Action => {
|
|
||||||
// return {
|
|
||||||
// type: MODAL.REMEMBER,
|
|
||||||
// device
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
export const onRememberDevice = (device: TrezorDevice): Action => ({
|
export const onRememberDevice = (device: TrezorDevice): Action => ({
|
||||||
type: CONNECT.REMEMBER,
|
type: CONNECT.REMEMBER,
|
||||||
device,
|
device,
|
||||||
@ -77,9 +71,9 @@ export const onRememberRequest = (prevState: State): ThunkAction => (dispatch: D
|
|||||||
const state: State = getState().modal;
|
const state: State = getState().modal;
|
||||||
// handle case where forget modal is already opened
|
// handle case where forget modal is already opened
|
||||||
// TODO: 2 modals at once (two devices disconnected in the same time)
|
// TODO: 2 modals at once (two devices disconnected in the same time)
|
||||||
if (prevState.opened && prevState.windowType === CONNECT.REMEMBER_REQUEST) {
|
if (prevState.context === MODAL.CONTEXT_DEVICE && prevState.windowType === CONNECT.REMEMBER_REQUEST) {
|
||||||
// forget current (new)
|
// forget current (new)
|
||||||
if (state.opened) {
|
if (state.context === MODAL.CONTEXT_DEVICE) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.FORGET,
|
type: CONNECT.FORGET,
|
||||||
device: state.device,
|
device: state.device,
|
||||||
@ -98,7 +92,7 @@ export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispa
|
|||||||
// interrupt process of remembering device (force forget)
|
// interrupt process of remembering device (force forget)
|
||||||
// TODO: the same for disconnect more than 1 device at once
|
// TODO: the same for disconnect more than 1 device at once
|
||||||
const { modal } = getState();
|
const { modal } = getState();
|
||||||
if (modal.opened && modal.windowType === CONNECT.REMEMBER_REQUEST) {
|
if (modal.context === MODAL.CONTEXT_DEVICE && modal.windowType === CONNECT.REMEMBER_REQUEST) {
|
||||||
if (device.features && modal.device && modal.device.features && modal.device.features.device_id === device.features.device_id) {
|
if (device.features && modal.device && modal.device.features && modal.device.features.device_id === device.features.device_id) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: MODAL.CLOSE,
|
type: MODAL.CLOSE,
|
||||||
@ -124,6 +118,16 @@ export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean, state
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
|
console.warn('OPEN', id, url);
|
||||||
|
dispatch({
|
||||||
|
type: MODAL.OPEN_EXTERNAL_WALLET,
|
||||||
|
id,
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onPinSubmit,
|
onPinSubmit,
|
||||||
onPassphraseSubmit,
|
onPassphraseSubmit,
|
||||||
@ -134,4 +138,5 @@ export default {
|
|||||||
onCancel,
|
onCancel,
|
||||||
onDuplicateDevice,
|
onDuplicateDevice,
|
||||||
onWalletTypeRequest,
|
onWalletTypeRequest,
|
||||||
|
gotoExternalWallet,
|
||||||
};
|
};
|
@ -1,6 +1,7 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import { push, LOCATION_CHANGE } from 'react-router-redux';
|
import { push, LOCATION_CHANGE } from 'react-router-redux';
|
||||||
|
import { CONTEXT_NONE } from 'actions/constants/modal';
|
||||||
import { routes } from 'support/routes';
|
import { routes } from 'support/routes';
|
||||||
import * as deviceUtils from 'utils/device';
|
import * as deviceUtils from 'utils/device';
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ export const getValidUrl = (action: RouterAction): PayloadAction<string> => (dis
|
|||||||
|
|
||||||
// Modal is opened
|
// Modal is opened
|
||||||
// redirect to previous url
|
// redirect to previous url
|
||||||
if (getState().modal.opened) {
|
if (getState().modal.context !== CONTEXT_NONE) {
|
||||||
// Corner case: modal is opened and currentParams are still valid
|
// Corner case: modal is opened and currentParams are still valid
|
||||||
// example 1 (valid blocking): url changed while passphrase modal opened but device is still connected (we want user to finish this action)
|
// example 1 (valid blocking): url changed while passphrase modal opened but device is still connected (we want user to finish this action)
|
||||||
// example 2 (invalid blocking): url changes while passphrase modal opened because device disconnect
|
// example 2 (invalid blocking): url changes while passphrase modal opened because device disconnect
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import TrezorConnect, {
|
import TrezorConnect, {
|
||||||
DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT, BLOCKCHAIN_EVENT,
|
DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT, BLOCKCHAIN_EVENT,
|
||||||
} from 'trezor-connect';
|
} from 'trezor-connect';
|
||||||
|
import { CONTEXT_NONE } from 'actions/constants/modal';
|
||||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||||
import * as NOTIFICATION from 'actions/constants/notification';
|
import * as NOTIFICATION from 'actions/constants/notification';
|
||||||
import { getDuplicateInstanceNumber } from 'reducers/utils';
|
import { getDuplicateInstanceNumber } from 'reducers/utils';
|
||||||
@ -236,8 +237,8 @@ export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch
|
|||||||
if (device.features) {
|
if (device.features) {
|
||||||
const instances = getState().devices.filter(d => d.features && device.features && d.state && !d.remember && d.features.device_id === device.features.device_id);
|
const instances = getState().devices.filter(d => d.features && device.features && d.state && !d.remember && d.features.device_id === device.features.device_id);
|
||||||
if (instances.length > 0) {
|
if (instances.length > 0) {
|
||||||
const isSelected = deviceUtils.isSelectedDevice(getState().wallet.selectedDevice, instances[0]);
|
const isSelected = deviceUtils.isSelectedDevice(getState().wallet.selectedDevice, device);
|
||||||
if (!isSelected && getState().modal.opened) {
|
if (!isSelected && getState().modal.context !== CONTEXT_NONE) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.FORGET_SILENT,
|
type: CONNECT.FORGET_SILENT,
|
||||||
device: instances[0],
|
device: instances[0],
|
||||||
|
@ -1,17 +1,7 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
|
|
||||||
export const ON_PASSPHRASE_CHANGE: 'action__on_passphrase_change' = 'action__on_passphrase_change';
|
|
||||||
export const ON_PASSPHRASE_SHOW: 'action__on_passphrase_show' = 'action__on_passphrase_show';
|
|
||||||
export const ON_PASSPHRASE_HIDE: 'action__on_passphrase_hide' = 'action__on_passphrase_hide';
|
|
||||||
export const ON_PASSPHRASE_SAVE: 'action__on_passphrase_save' = 'action__on_passphrase_save';
|
|
||||||
export const ON_PASSPHRASE_FORGET: 'action__on_passphrase_forget' = 'action__on_passphrase_forget';
|
|
||||||
export const ON_PASSPHRASE_FOCUS: 'action__on_passphrase_focus' = 'action__on_passphrase_focus';
|
|
||||||
export const ON_PASSPHRASE_BLUR: 'action__on_passphrase_blur' = 'action__on_passphrase_blur';
|
|
||||||
export const ON_PASSPHRASE_SUBMIT: 'action__on_passphrase_submit' = 'action__on_passphrase_submit';
|
|
||||||
|
|
||||||
export const FORGET: 'modal__forget' = 'modal__forget';
|
|
||||||
export const REMEMBER: 'modal__remember' = 'modal__remember';
|
|
||||||
export const ON_FORGET: 'modal__on_forget' = 'modal__on_forget';
|
|
||||||
export const ON_REMEMBER: 'modal__on_remember' = 'modal__on_remember';
|
|
||||||
export const CLOSE: 'modal__close' = 'modal__close';
|
export const CLOSE: 'modal__close' = 'modal__close';
|
||||||
|
export const OPEN_EXTERNAL_WALLET: 'modal__external_wallet' = 'modal__external_wallet';
|
||||||
|
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';
|
||||||
|
@ -62,7 +62,7 @@ class Link extends PureComponent {
|
|||||||
<A
|
<A
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
href={this.props.href}
|
href={this.props.href}
|
||||||
target="_blank"
|
target={this.props.target || '_blank'}
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
isGreen={this.props.isGreen}
|
isGreen={this.props.isGreen}
|
||||||
@ -84,6 +84,7 @@ Link.propTypes = {
|
|||||||
]).isRequired,
|
]).isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
|
target: PropTypes.string,
|
||||||
to: PropTypes.string,
|
to: PropTypes.string,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
isGreen: PropTypes.bool,
|
isGreen: PropTypes.bool,
|
||||||
|
55
src/components/modals/Container.js
Normal file
55
src/components/modals/Container.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* @flow */
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import ModalActions from 'actions/ModalActions';
|
||||||
|
import ReceiveActions from 'actions/ReceiveActions';
|
||||||
|
|
||||||
|
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||||
|
import type { State, Dispatch } from 'flowtype';
|
||||||
|
|
||||||
|
import Modal from './index';
|
||||||
|
|
||||||
|
type OwnProps = { }
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
modal: $ElementType<State, 'modal'>,
|
||||||
|
accounts: $ElementType<State, 'accounts'>,
|
||||||
|
devices: $ElementType<State, 'devices'>,
|
||||||
|
connect: $ElementType<State, 'connect'>,
|
||||||
|
selectedAccount: $ElementType<State, 'selectedAccount'>,
|
||||||
|
sendForm: $ElementType<State, 'sendForm'>,
|
||||||
|
receive: $ElementType<State, 'receive'>,
|
||||||
|
localStorage: $ElementType<State, 'localStorage'>,
|
||||||
|
wallet: $ElementType<State, 'wallet'>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DispatchProps = {
|
||||||
|
modalActions: typeof ModalActions,
|
||||||
|
receiveActions: typeof ReceiveActions,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Props = StateProps & DispatchProps;
|
||||||
|
|
||||||
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
|
||||||
|
modal: state.modal,
|
||||||
|
accounts: state.accounts,
|
||||||
|
devices: state.devices,
|
||||||
|
connect: state.connect,
|
||||||
|
selectedAccount: state.selectedAccount,
|
||||||
|
sendForm: state.sendForm,
|
||||||
|
receive: state.receive,
|
||||||
|
localStorage: state.localStorage,
|
||||||
|
wallet: state.wallet,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
|
modalActions: bindActionCreators(ModalActions, dispatch),
|
||||||
|
receiveActions: bindActionCreators(ReceiveActions, dispatch),
|
||||||
|
});
|
||||||
|
|
||||||
|
// export default connect(mapStateToProps, mapDispatchToProps)(Modal);
|
||||||
|
export default withRouter(
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)(Modal),
|
||||||
|
);
|
@ -1,13 +1,16 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import H3 from 'components/Heading';
|
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
import P from 'components/Paragraph';
|
|
||||||
import { FONT_SIZE } from 'config/variables';
|
import { FONT_SIZE } from 'config/variables';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import H3 from 'components/Heading';
|
||||||
|
import P from 'components/Paragraph';
|
||||||
|
|
||||||
|
import type { Props } from '../../Container';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 390px;
|
width: 390px;
|
||||||
@ -49,4 +52,8 @@ const ConfirmAddress = (props: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConfirmAddress.propTypes = {
|
||||||
|
selectedAccount: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default ConfirmAddress;
|
export default ConfirmAddress;
|
@ -1,15 +1,24 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import colors from 'config/colors';
|
|
||||||
import P from 'components/Paragraph';
|
|
||||||
import Icon from 'components/Icon';
|
|
||||||
import icons from 'config/icons';
|
import icons from 'config/icons';
|
||||||
import { H3 } from 'components/Heading';
|
import colors from 'config/colors';
|
||||||
import { LINE_HEIGHT } from 'config/variables';
|
import { LINE_HEIGHT } from 'config/variables';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import P from 'components/Paragraph';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import { H3 } from 'components/Heading';
|
||||||
|
|
||||||
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
sendForm: $ElementType<BaseProps, 'sendForm'>;
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 390px;
|
width: 390px;
|
||||||
@ -39,9 +48,6 @@ const Label = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ConfirmSignTx = (props: Props) => {
|
const ConfirmSignTx = (props: Props) => {
|
||||||
if (!props.modal.opened) return null;
|
|
||||||
const { device } = props.modal;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
amount,
|
amount,
|
||||||
address,
|
address,
|
||||||
@ -53,7 +59,7 @@ const ConfirmSignTx = (props: Props) => {
|
|||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Header>
|
<Header>
|
||||||
<Icon icon={icons.T1} size={60} color={colors.TEXT_SECONDARY} />
|
<Icon icon={icons.T1} size={60} color={colors.TEXT_SECONDARY} />
|
||||||
<H3>Confirm transaction on { device.label } device</H3>
|
<H3>Confirm transaction on { props.device.label } device</H3>
|
||||||
<P isSmaller>Details are shown on display</P>
|
<P isSmaller>Details are shown on display</P>
|
||||||
</Header>
|
</Header>
|
||||||
<Content>
|
<Content>
|
||||||
@ -68,4 +74,9 @@ const ConfirmSignTx = (props: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConfirmSignTx.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
sendForm: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default ConfirmSignTx;
|
export default ConfirmSignTx;
|
@ -1,15 +1,27 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import icons from 'config/icons';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
|
||||||
import { H2 } from 'components/Heading';
|
import { H2 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
import styled from 'styled-components';
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import colors from 'config/colors';
|
|
||||||
import icons from 'config/icons';
|
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
account: $ElementType<$ElementType<BaseProps, 'selectedAccount'>, 'account'>;
|
||||||
|
showAddress: $ElementType<$ElementType<BaseProps, 'receiveActions'>, 'showAddress'>;
|
||||||
|
showUnverifiedAddress: $ElementType<$ElementType<BaseProps, 'receiveActions'>, 'showUnverifiedAddress'>;
|
||||||
|
onCancel: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onCancel'>;
|
||||||
|
}
|
||||||
|
|
||||||
const StyledLink = styled(Link)`
|
const StyledLink = styled(Link)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -56,29 +68,20 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
verifyAddress() {
|
verifyAddress() {
|
||||||
if (!this.props.modal.opened) return;
|
const { account, onCancel, showAddress } = this.props;
|
||||||
const { account } = this.props.selectedAccount;
|
|
||||||
if (!account) return;
|
if (!account) return;
|
||||||
this.props.modalActions.onCancel();
|
onCancel();
|
||||||
this.props.receiveActions.showAddress(account.addressPath);
|
showAddress(account.addressPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
showUnverifiedAddress() {
|
showUnverifiedAddress() {
|
||||||
if (!this.props.modal.opened) return;
|
const { onCancel, showUnverifiedAddress } = this.props;
|
||||||
|
onCancel();
|
||||||
this.props.modalActions.onCancel();
|
showUnverifiedAddress();
|
||||||
this.props.receiveActions.showUnverifiedAddress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
const { device, account, onCancel } = this.props;
|
||||||
const {
|
|
||||||
device,
|
|
||||||
} = this.props.modal;
|
|
||||||
|
|
||||||
const {
|
|
||||||
onCancel,
|
|
||||||
} = this.props.modalActions;
|
|
||||||
|
|
||||||
let deviceStatus: string;
|
let deviceStatus: string;
|
||||||
let claim: string;
|
let claim: string;
|
||||||
@ -101,7 +104,7 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
|||||||
<H2>{ deviceStatus }</H2>
|
<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>
|
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</StyledP>
|
||||||
<Row>
|
<Row>
|
||||||
<StyledButton onClick={() => (!this.props.selectedAccount.account ? this.verifyAddress() : 'false')}>Try again</StyledButton>
|
<StyledButton onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</StyledButton>
|
||||||
<StyledButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</StyledButton>
|
<StyledButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</StyledButton>
|
||||||
</Row>
|
</Row>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
@ -109,4 +112,12 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfirmUnverifiedAddress.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
account: PropTypes.object.isRequired,
|
||||||
|
showAddress: PropTypes.func.isRequired,
|
||||||
|
showUnverifiedAddress: PropTypes.func.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default ConfirmUnverifiedAddress;
|
export default ConfirmUnverifiedAddress;
|
@ -1,18 +1,30 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import icons from 'config/icons';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
import { FONT_SIZE } from 'config/variables';
|
||||||
|
|
||||||
import { H3 } from 'components/Heading';
|
import { H3 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
import Input from 'components/inputs/Input';
|
import Input from 'components/inputs/Input';
|
||||||
import { getDuplicateInstanceNumber } from 'reducers/utils';
|
|
||||||
import { FONT_SIZE } from 'config/variables';
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import icons from 'config/icons';
|
|
||||||
import colors from 'config/colors';
|
|
||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
|
|
||||||
import type { Props } from 'components/modals/index';
|
import { getDuplicateInstanceNumber } from 'reducers/utils';
|
||||||
|
|
||||||
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
devices: $ElementType<BaseProps, 'devices'>;
|
||||||
|
onDuplicateDevice: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onDuplicateDevice'>;
|
||||||
|
onCancel: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onCancel'>;
|
||||||
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
defaultName: string;
|
defaultName: string;
|
||||||
@ -62,17 +74,14 @@ const ErrorMessage = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default class DuplicateDevice extends PureComponent<Props, State> {
|
class DuplicateDevice extends PureComponent<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const device = props.modal.opened ? props.modal.device : null;
|
const instance = getDuplicateInstanceNumber(props.devices, props.device);
|
||||||
if (!device) return;
|
|
||||||
|
|
||||||
const instance = getDuplicateInstanceNumber(props.devices, device);
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
defaultName: `${device.label} (${instance.toString()})`,
|
defaultName: `${props.device.label} (${instance.toString()})`,
|
||||||
instance,
|
instance,
|
||||||
instanceName: null,
|
instanceName: null,
|
||||||
isUsed: false,
|
isUsed: false,
|
||||||
@ -114,16 +123,12 @@ export default class DuplicateDevice extends PureComponent<Props, State> {
|
|||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
if (!this.props.modal.opened) return;
|
|
||||||
const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance };
|
const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance };
|
||||||
this.props.modalActions.onDuplicateDevice({ ...this.props.modal.device, ...extended });
|
this.props.onDuplicateDevice({ ...this.props.device, ...extended });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
const { device, onCancel } = this.props;
|
||||||
|
|
||||||
const { device } = this.props.modal;
|
|
||||||
const { onCancel } = this.props.modalActions;
|
|
||||||
const {
|
const {
|
||||||
defaultName,
|
defaultName,
|
||||||
instanceName,
|
instanceName,
|
||||||
@ -167,4 +172,13 @@ export default class DuplicateDevice extends PureComponent<Props, State> {
|
|||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DuplicateDevice.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
devices: PropTypes.array.isRequired,
|
||||||
|
onDuplicateDevice: PropTypes.func.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DuplicateDevice;
|
@ -1,12 +1,21 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { H3 } from 'components/Heading';
|
import { H3 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
onForgetSingleDevice: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onForgetSingleDevice'>;
|
||||||
|
onCancel: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onCancel'>;
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 360px;
|
width: 360px;
|
||||||
@ -47,26 +56,27 @@ class ForgetDevice extends PureComponent<Props> {
|
|||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
forget() {
|
forget() {
|
||||||
if (this.props.modal.opened) {
|
this.props.onForgetSingleDevice(this.props.device);
|
||||||
this.props.modalActions.onForgetSingleDevice(this.props.modal.device);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
|
||||||
const { device } = this.props.modal;
|
|
||||||
const { onCancel } = this.props.modalActions;
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<H3>Forget { device.instanceLabel }?</H3>
|
<H3>Forget { this.props.device.instanceLabel }?</H3>
|
||||||
<StyledP isSmaller>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your TREZOR again.</StyledP>
|
<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>
|
<Row>
|
||||||
<StyledButton onClick={() => this.forget()}>Forget</StyledButton>
|
<StyledButton onClick={() => this.forget()}>Forget</StyledButton>
|
||||||
<StyledButton isWhite onClick={onCancel}>Don't forget</StyledButton>
|
<StyledButton isWhite onClick={this.props.onCancel}>Don't forget</StyledButton>
|
||||||
</Row>
|
</Row>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForgetDevice.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
onForgetSingleDevice: PropTypes.func.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default ForgetDevice;
|
export default ForgetDevice;
|
@ -1,12 +1,22 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { H3 } from 'components/Heading';
|
import { H3 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
instances: ?Array<TrezorDevice>;
|
||||||
|
onRememberDevice: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onRememberDevice'>;
|
||||||
|
onForgetDevice: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onForgetDevice'>;
|
||||||
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
countdown: number;
|
countdown: number;
|
||||||
@ -47,7 +57,7 @@ const StyledLoader = styled(Loader)`
|
|||||||
left: 200px;
|
left: 200px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default class RememberDevice extends PureComponent<Props, State> {
|
class RememberDevice extends PureComponent<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -62,9 +72,7 @@ export default class RememberDevice extends PureComponent<Props, State> {
|
|||||||
// TODO: possible race condition,
|
// TODO: possible race condition,
|
||||||
// device could be already connected but it didn't emit Device.CONNECT event yet
|
// device could be already connected but it didn't emit Device.CONNECT event yet
|
||||||
window.clearInterval(this.state.ticker);
|
window.clearInterval(this.state.ticker);
|
||||||
if (this.props.modal.opened) {
|
this.props.onForgetDevice(this.props.device);
|
||||||
this.props.modalActions.onForgetDevice(this.props.modal.device);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.setState(previousState => ({
|
this.setState(previousState => ({
|
||||||
countdown: previousState.countdown - 1,
|
countdown: previousState.countdown - 1,
|
||||||
@ -98,15 +106,11 @@ export default class RememberDevice extends PureComponent<Props, State> {
|
|||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
forget() {
|
forget() {
|
||||||
if (this.props.modal.opened) {
|
this.props.onForgetDevice(this.props.device);
|
||||||
this.props.modalActions.onForgetDevice(this.props.modal.device);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
const { device, instances, onRememberDevice } = this.props;
|
||||||
const { device, instances } = this.props.modal;
|
|
||||||
const { onRememberDevice } = this.props.modalActions;
|
|
||||||
|
|
||||||
let { label } = device;
|
let { label } = device;
|
||||||
const devicePlural: string = instances && instances.length > 1 ? 'devices or to remember them' : 'device or to remember it';
|
const devicePlural: string = instances && instances.length > 1 ? 'devices or to remember them' : 'device or to remember it';
|
||||||
@ -144,4 +148,13 @@ export default class RememberDevice extends PureComponent<Props, State> {
|
|||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RememberDevice.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
instances: PropTypes.array.isRequired,
|
||||||
|
onRememberDevice: PropTypes.func.isRequired,
|
||||||
|
onForgetDevice: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RememberDevice;
|
@ -1,18 +1,28 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
import icons from 'config/icons';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
|
||||||
import { H3 } from 'components/Heading';
|
import { H3 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
import Tooltip from 'components/Tooltip';
|
import Tooltip from 'components/Tooltip';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
import colors from 'config/colors';
|
|
||||||
import icons from 'config/icons';
|
|
||||||
import WalletTypeIcon from 'components/images/WalletType';
|
import WalletTypeIcon from 'components/images/WalletType';
|
||||||
|
|
||||||
import type { Props } from 'components/modals/index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
onWalletTypeRequest: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onWalletTypeRequest'>;
|
||||||
|
onCancel: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onCancel'>;
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 360px;
|
width: 360px;
|
||||||
@ -88,15 +98,11 @@ class WalletType extends PureComponent<Props> {
|
|||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
changeType(hidden: boolean, state: ?string) {
|
changeType(hidden: boolean, state: ?string) {
|
||||||
const { modal } = this.props;
|
this.props.onWalletTypeRequest(this.props.device, hidden, state);
|
||||||
if (!modal.opened) return;
|
|
||||||
this.props.modalActions.onWalletTypeRequest(modal.device, hidden, state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
const { device, onCancel } = this.props;
|
||||||
const { device } = this.props.modal;
|
|
||||||
const { onCancel } = this.props.modalActions;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@ -109,7 +115,7 @@ class WalletType extends PureComponent<Props> {
|
|||||||
/>
|
/>
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
)}
|
)}
|
||||||
<StyledHeading>Change wallet type for { device.instanceLabel }</StyledHeading>
|
<StyledHeading>{ device.state ? 'Change' : 'Select' } wallet type for { device.instanceLabel }</StyledHeading>
|
||||||
<Content isTop>
|
<Content isTop>
|
||||||
<Header>
|
<Header>
|
||||||
<WalletTypeIcon type="standard" size={32} color={colors.TEXT_PRIMARY} />
|
<WalletTypeIcon type="standard" size={32} color={colors.TEXT_PRIMARY} />
|
||||||
@ -147,4 +153,10 @@ class WalletType extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletType.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
onWalletTypeRequest: PropTypes.func.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default WalletType;
|
export default WalletType;
|
BIN
src/components/modals/external/NemWallet/images/nem-download.png
vendored
Normal file
BIN
src/components/modals/external/NemWallet/images/nem-download.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
68
src/components/modals/external/NemWallet/index.js
vendored
Normal file
68
src/components/modals/external/NemWallet/index.js
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
import icons from 'config/icons';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import Link from 'components/Link';
|
||||||
|
import Button from 'components/Button';
|
||||||
|
import { H3, H4 } from 'components/Heading';
|
||||||
|
import P from 'components/Paragraph';
|
||||||
|
import coins from 'constants/coins';
|
||||||
|
|
||||||
|
import NemImage from './images/nem-download.png';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onCancel: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onCancel'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
max-width: 620px;
|
||||||
|
padding: 24px 48px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledButton = styled(Button)`
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledLink = styled(Link)`
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Img = styled.img`
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const NemWallet = (props: Props) => (
|
||||||
|
<Wrapper>
|
||||||
|
<StyledLink onClick={props.onCancel}>
|
||||||
|
<Icon
|
||||||
|
size={20}
|
||||||
|
color={colors.TEXT_SECONDARY}
|
||||||
|
icon={icons.CLOSE}
|
||||||
|
/>
|
||||||
|
</StyledLink>
|
||||||
|
<H3>NEM Wallet</H3>
|
||||||
|
<P isSmaller>We have partnered up with the NEM Foundation to provide you with a full-fledged NEM Wallet.</P>
|
||||||
|
<H4>Make sure you download the Universal Client for TREZOR support.</H4>
|
||||||
|
<Img src={NemImage} />
|
||||||
|
<Link href={coins.find(i => i.id === 'xem').url}>
|
||||||
|
<StyledButton>Go to nem.io</StyledButton>
|
||||||
|
</Link>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
NemWallet.propTypes = {
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NemWallet;
|
@ -1,64 +1,39 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { withRouter } from 'react-router-dom';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import colors from 'config/colors';
|
|
||||||
import { CSSTransition } from 'react-transition-group';
|
import { CSSTransition } from 'react-transition-group';
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
|
||||||
import { UI } from 'trezor-connect';
|
import { UI } from 'trezor-connect';
|
||||||
|
import * as MODAL from 'actions/constants/modal';
|
||||||
import ModalActions from 'actions/ModalActions';
|
|
||||||
import ReceiveActions from 'actions/ReceiveActions';
|
|
||||||
|
|
||||||
import * as RECEIVE from 'actions/constants/receive';
|
import * as RECEIVE from 'actions/constants/receive';
|
||||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
|
||||||
import type { State, Dispatch } from 'flowtype';
|
|
||||||
|
|
||||||
|
// device context
|
||||||
import Pin from 'components/modals/pin/Pin';
|
import Pin from 'components/modals/pin/Pin';
|
||||||
import InvalidPin from 'components/modals/pin/Invalid';
|
import InvalidPin from 'components/modals/pin/Invalid';
|
||||||
|
|
||||||
import Passphrase from 'components/modals/passphrase/Passphrase';
|
import Passphrase from 'components/modals/passphrase/Passphrase';
|
||||||
import PassphraseType from 'components/modals/passphrase/Type';
|
import PassphraseType from 'components/modals/passphrase/Type';
|
||||||
|
|
||||||
import ConfirmSignTx from 'components/modals/confirm/SignTx';
|
import ConfirmSignTx from 'components/modals/confirm/SignTx';
|
||||||
import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddress';
|
import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddress';
|
||||||
|
|
||||||
import ForgetDevice from 'components/modals/device/Forget';
|
import ForgetDevice from 'components/modals/device/Forget';
|
||||||
import RememberDevice from 'components/modals/device/Remember';
|
import RememberDevice from 'components/modals/device/Remember';
|
||||||
import DuplicateDevice from 'components/modals/device/Duplicate';
|
import DuplicateDevice from 'components/modals/device/Duplicate';
|
||||||
import WalletType from 'components/modals/device/WalletType';
|
import WalletType from 'components/modals/device/WalletType';
|
||||||
|
|
||||||
type OwnProps = { }
|
// external context
|
||||||
|
import NemWallet from 'components/modals/external/NemWallet';
|
||||||
|
|
||||||
type StateProps = {
|
import type { Props } from './Container';
|
||||||
modal: $ElementType<State, 'modal'>,
|
|
||||||
accounts: $ElementType<State, 'accounts'>,
|
|
||||||
devices: $ElementType<State, 'devices'>,
|
|
||||||
connect: $ElementType<State, 'connect'>,
|
|
||||||
selectedAccount: $ElementType<State, 'selectedAccount'>,
|
|
||||||
sendForm: $ElementType<State, 'sendForm'>,
|
|
||||||
receive: $ElementType<State, 'receive'>,
|
|
||||||
localStorage: $ElementType<State, 'localStorage'>,
|
|
||||||
wallet: $ElementType<State, 'wallet'>,
|
|
||||||
}
|
|
||||||
|
|
||||||
type DispatchProps = {
|
|
||||||
modalActions: typeof ModalActions,
|
|
||||||
receiveActions: typeof ReceiveActions,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Props = StateProps & DispatchProps;
|
|
||||||
|
|
||||||
const Fade = (props: { children: React.Node}) => (
|
const Fade = (props: { children: React.Node}) => (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
{...props}
|
{...props}
|
||||||
timeout={1000}
|
timeout={1000}
|
||||||
classNames="fade"
|
classNames="fade"
|
||||||
>
|
>{ props.children }
|
||||||
{ props.children }
|
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -85,87 +60,124 @@ const ModalWindow = styled.div`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class Modal extends React.PureComponent<Props> {
|
// get modal component with device context
|
||||||
render() {
|
const getDeviceContextModal = (props: Props) => {
|
||||||
if (!this.props.modal.opened) return null;
|
const { modal, modalActions } = props;
|
||||||
|
if (modal.context !== MODAL.CONTEXT_DEVICE) return null;
|
||||||
|
|
||||||
const { opened, windowType } = this.props.modal;
|
switch (modal.windowType) {
|
||||||
let component = null;
|
case UI.REQUEST_PIN:
|
||||||
switch (windowType) {
|
return (
|
||||||
case UI.REQUEST_PIN:
|
<Pin
|
||||||
component = (<Pin {...this.props} />);
|
device={modal.device}
|
||||||
break;
|
onPinSubmit={modalActions.onPinSubmit}
|
||||||
case UI.INVALID_PIN:
|
/>);
|
||||||
component = (<InvalidPin {...this.props} />);
|
|
||||||
break;
|
|
||||||
case UI.REQUEST_PASSPHRASE:
|
|
||||||
component = (<Passphrase {...this.props} />);
|
|
||||||
break;
|
|
||||||
case 'ButtonRequest_SignTx':
|
|
||||||
component = (<ConfirmSignTx {...this.props} />);
|
|
||||||
break;
|
|
||||||
case 'ButtonRequest_PassphraseType':
|
|
||||||
component = (<PassphraseType {...this.props} />);
|
|
||||||
break;
|
|
||||||
case RECEIVE.REQUEST_UNVERIFIED:
|
|
||||||
component = (<ConfirmUnverifiedAddress {...this.props} />);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONNECT.REMEMBER_REQUEST:
|
case UI.INVALID_PIN:
|
||||||
component = (<RememberDevice {...this.props} />);
|
return <InvalidPin device={modal.device} />;
|
||||||
break;
|
|
||||||
|
|
||||||
case CONNECT.FORGET_REQUEST:
|
case UI.REQUEST_PASSPHRASE:
|
||||||
component = (<ForgetDevice {...this.props} />);
|
return (
|
||||||
break;
|
<Passphrase
|
||||||
|
device={modal.device}
|
||||||
|
selectedDevice={props.wallet.selectedDevice}
|
||||||
|
onPassphraseSubmit={modalActions.onPassphraseSubmit}
|
||||||
|
/>);
|
||||||
|
|
||||||
case CONNECT.TRY_TO_DUPLICATE:
|
case 'ButtonRequest_PassphraseType':
|
||||||
component = (<DuplicateDevice {...this.props} />);
|
return <PassphraseType device={modal.device} />;
|
||||||
break;
|
|
||||||
|
|
||||||
case CONNECT.REQUEST_WALLET_TYPE:
|
case 'ButtonRequest_SignTx':
|
||||||
component = (<WalletType {...this.props} />);
|
return <ConfirmSignTx device={modal.device} sendForm={props.sendForm} />;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
case RECEIVE.REQUEST_UNVERIFIED:
|
||||||
component = null;
|
return (
|
||||||
}
|
<ConfirmUnverifiedAddress
|
||||||
|
device={modal.device}
|
||||||
|
account={props.selectedAccount.account}
|
||||||
|
onCancel={modalActions.onCancel}
|
||||||
|
showAddress={props.receiveActions.showAddress}
|
||||||
|
showUnverifiedAddress={props.receiveActions.showUnverifiedAddress}
|
||||||
|
/>);
|
||||||
|
|
||||||
let ch = null;
|
case CONNECT.REMEMBER_REQUEST:
|
||||||
if (opened) {
|
return (
|
||||||
ch = (
|
<RememberDevice
|
||||||
<Fade key="1">
|
device={modal.device}
|
||||||
<ModalContainer>
|
instances={modal.instances}
|
||||||
<ModalWindow>
|
onRememberDevice={modalActions.onRememberDevice}
|
||||||
{ component }
|
onForgetDevice={modalActions.onForgetDevice}
|
||||||
</ModalWindow>
|
/>);
|
||||||
</ModalContainer>
|
|
||||||
</Fade>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ch;
|
case CONNECT.FORGET_REQUEST:
|
||||||
|
return (
|
||||||
|
<ForgetDevice
|
||||||
|
device={modal.device}
|
||||||
|
onForgetSingleDevice={modalActions.onForgetSingleDevice}
|
||||||
|
onCancel={modalActions.onCancel}
|
||||||
|
/>);
|
||||||
|
|
||||||
|
case CONNECT.TRY_TO_DUPLICATE:
|
||||||
|
return (
|
||||||
|
<DuplicateDevice
|
||||||
|
device={modal.device}
|
||||||
|
devices={props.devices}
|
||||||
|
onDuplicateDevice={modalActions.onDuplicateDevice}
|
||||||
|
onCancel={modalActions.onCancel}
|
||||||
|
/>);
|
||||||
|
|
||||||
|
case CONNECT.REQUEST_WALLET_TYPE:
|
||||||
|
return (
|
||||||
|
<WalletType
|
||||||
|
device={modal.device}
|
||||||
|
onWalletTypeRequest={modalActions.onWalletTypeRequest}
|
||||||
|
onCancel={modalActions.onCancel}
|
||||||
|
/>);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
|
// get modal component with external context
|
||||||
modal: state.modal,
|
const getExternalContextModal = (props: Props) => {
|
||||||
accounts: state.accounts,
|
const { modal, modalActions } = props;
|
||||||
devices: state.devices,
|
if (modal.context !== MODAL.CONTEXT_EXTERNAL_WALLET) return null;
|
||||||
connect: state.connect,
|
|
||||||
selectedAccount: state.selectedAccount,
|
|
||||||
sendForm: state.sendForm,
|
|
||||||
receive: state.receive,
|
|
||||||
localStorage: state.localStorage,
|
|
||||||
wallet: state.wallet,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
switch (modal.windowType) {
|
||||||
modalActions: bindActionCreators(ModalActions, dispatch),
|
case 'xem':
|
||||||
receiveActions: bindActionCreators(ReceiveActions, dispatch),
|
return (<NemWallet onCancel={modalActions.onCancel} />);
|
||||||
});
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// export default connect(mapStateToProps, mapDispatchToProps)(Modal);
|
// modal container component
|
||||||
export default withRouter(
|
const Modal = (props: Props) => {
|
||||||
connect(mapStateToProps, mapDispatchToProps)(Modal),
|
const { modal } = props;
|
||||||
);
|
if (modal.context === MODAL.CONTEXT_NONE) return null;
|
||||||
|
|
||||||
|
let component = null;
|
||||||
|
switch (modal.context) {
|
||||||
|
case MODAL.CONTEXT_DEVICE:
|
||||||
|
component = getDeviceContextModal(props);
|
||||||
|
break;
|
||||||
|
case MODAL.CONTEXT_EXTERNAL_WALLET:
|
||||||
|
component = getExternalContextModal(props);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fade key="modal-fade">
|
||||||
|
<ModalContainer>
|
||||||
|
<ModalWindow>
|
||||||
|
{ component }
|
||||||
|
</ModalWindow>
|
||||||
|
</ModalContainer>
|
||||||
|
</Fade>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
import { FONT_SIZE, TRANSITION } from 'config/variables';
|
import { FONT_SIZE, TRANSITION } from 'config/variables';
|
||||||
|
|
||||||
import { H2 } from 'components/Heading';
|
import { H2 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
import Checkbox from 'components/Checkbox';
|
import Checkbox from 'components/Checkbox';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
import Input from 'components/inputs/Input';
|
import Input from 'components/inputs/Input';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
selectedDevice: ?TrezorDevice;
|
||||||
|
onPassphraseSubmit: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onPassphraseSubmit'>;
|
||||||
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
deviceLabel: string,
|
deviceLabel: string,
|
||||||
@ -79,15 +89,10 @@ const LinkButton = styled(Button)`
|
|||||||
class Passphrase extends PureComponent<Props, State> {
|
class Passphrase extends PureComponent<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
const { device, selectedDevice } = props;
|
||||||
const device = props.modal.opened ? props.modal.device : null;
|
|
||||||
if (!device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this device is already known
|
// Check if this device is already known
|
||||||
// if device is already known then only one input is presented
|
// if device is already known then only one input is presented
|
||||||
const { selectedDevice } = props.wallet;
|
|
||||||
let deviceLabel = device.label;
|
let deviceLabel = device.label;
|
||||||
let shouldShowSingleInput = false;
|
let shouldShowSingleInput = false;
|
||||||
if (selectedDevice && selectedDevice.path === device.path) {
|
if (selectedDevice && selectedDevice.path === device.path) {
|
||||||
@ -105,8 +110,6 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
state: State;
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.passphraseInput.focus();
|
this.passphraseInput.focus();
|
||||||
|
|
||||||
@ -180,7 +183,7 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitPassphrase(shouldLeavePassphraseBlank: boolean = false) {
|
submitPassphrase(shouldLeavePassphraseBlank: boolean = false) {
|
||||||
const { onPassphraseSubmit } = this.props.modalActions;
|
const { onPassphraseSubmit } = this.props;
|
||||||
const passphrase = this.state.passphraseInputValue;
|
const passphrase = this.state.passphraseInputValue;
|
||||||
|
|
||||||
// Reset state so same passphrase isn't filled when the modal will be visible again
|
// Reset state so same passphrase isn't filled when the modal will be visible again
|
||||||
@ -197,7 +200,6 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
handleKeyPress(event: KeyboardEvent) {
|
handleKeyPress(event: KeyboardEvent) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.warn('ENTER', this.state);
|
|
||||||
if (this.state.doPassphraseInputsMatch) {
|
if (this.state.doPassphraseInputsMatch) {
|
||||||
this.submitPassphrase();
|
this.submitPassphrase();
|
||||||
}
|
}
|
||||||
@ -205,10 +207,6 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<H2>Enter {this.state.deviceLabel} passphrase</H2>
|
<H2>Enter {this.state.deviceLabel} passphrase</H2>
|
||||||
@ -277,4 +275,10 @@ class Passphrase extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Passphrase.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
selectedDevice: PropTypes.object.isRequired,
|
||||||
|
onPassphraseSubmit: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default Passphrase;
|
export default Passphrase;
|
@ -1,14 +1,21 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'components/Icon';
|
import PropTypes from 'prop-types';
|
||||||
import colors from 'config/colors';
|
|
||||||
import icons from 'config/icons';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import icons from 'config/icons';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
|
||||||
|
import Icon from 'components/Icon';
|
||||||
import { H3 } from 'components/Heading';
|
import { H3 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
|
|
||||||
import type { Props } from 'components/modals/index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 360px;
|
width: 360px;
|
||||||
@ -17,19 +24,18 @@ const Wrapper = styled.div`
|
|||||||
|
|
||||||
const Header = styled.div``;
|
const Header = styled.div``;
|
||||||
|
|
||||||
const Confirmation = (props: Props) => {
|
const PassphraseType = (props: Props) => (
|
||||||
if (!props.modal.opened) return null;
|
<Wrapper>
|
||||||
const { device } = props.modal;
|
<Header>
|
||||||
|
<Icon icon={icons.T1} size={60} color={colors.TEXT_SECONDARY} />
|
||||||
|
<H3>Complete the action on { props.device.label } device</H3>
|
||||||
|
<P isSmaller>If you enter a wrong passphrase, you will not unlock the desired hidden wallet.</P>
|
||||||
|
</Header>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
PassphraseType.propTypes = {
|
||||||
<Wrapper>
|
device: PropTypes.object.isRequired,
|
||||||
<Header>
|
|
||||||
<Icon icon={icons.T1} size={60} color={colors.TEXT_SECONDARY} />
|
|
||||||
<H3>Complete the action on { device.label } device</H3>
|
|
||||||
<P isSmaller>If you enter a wrong passphrase, you will not unlock the desired hidden wallet.</P>
|
|
||||||
</Header>
|
|
||||||
</Wrapper>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Confirmation;
|
export default PassphraseType;
|
@ -1,26 +1,31 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { H3 } from 'components/Heading';
|
import { H3 } from 'components/Heading';
|
||||||
import P from 'components/Paragraph';
|
import P from 'components/Paragraph';
|
||||||
|
|
||||||
import type { Props } from '../../index';
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
padding: 24px 48px;
|
padding: 24px 48px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InvalidPin = (props: Props) => {
|
const InvalidPin = (props: Props) => (
|
||||||
if (!props.modal.opened) return null;
|
<Wrapper>
|
||||||
|
<H3>Entered PIN for { props.device.label } is not correct</H3>
|
||||||
|
<P isSmaller>Retrying...</P>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
|
||||||
const { device } = props.modal;
|
InvalidPin.propTypes = {
|
||||||
return (
|
device: PropTypes.object.isRequired,
|
||||||
<Wrapper>
|
|
||||||
<H3>Entered PIN for { device.label } is not correct</H3>
|
|
||||||
<P isSmaller>Retrying...</P>
|
|
||||||
</Wrapper>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InvalidPin;
|
export default InvalidPin;
|
@ -1,14 +1,24 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import P from 'components/Paragraph';
|
|
||||||
import { H2 } from 'components/Heading';
|
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import Link from 'components/Link';
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import P from 'components/Paragraph';
|
||||||
|
import { H2 } from 'components/Heading';
|
||||||
|
import Link from 'components/Link';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
|
|
||||||
|
import type { TrezorDevice } from 'flowtype';
|
||||||
|
|
||||||
import PinButton from './components/Button';
|
import PinButton from './components/Button';
|
||||||
import PinInput from './components/Input';
|
import PinInput from './components/Input';
|
||||||
import type { Props } from '../../index';
|
|
||||||
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
device: TrezorDevice;
|
||||||
|
onPinSubmit: $ElementType<$ElementType<BaseProps, 'modalActions'>, 'onPinSubmit'>;
|
||||||
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
pin: string;
|
pin: string;
|
||||||
@ -74,7 +84,7 @@ class Pin extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keyboardHandler(event: KeyboardEvent): void {
|
keyboardHandler(event: KeyboardEvent): void {
|
||||||
const { onPinSubmit } = this.props.modalActions;
|
const { onPinSubmit } = this.props;
|
||||||
const { pin } = this.state;
|
const { pin } = this.state;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -132,9 +142,7 @@ class Pin extends PureComponent<Props, State> {
|
|||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
const { device, onPinSubmit } = this.props;
|
||||||
const { onPinSubmit } = this.props.modalActions;
|
|
||||||
const { device } = this.props.modal;
|
|
||||||
const { pin } = this.state;
|
const { pin } = this.state;
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@ -173,4 +181,9 @@ class Pin extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pin.propTypes = {
|
||||||
|
device: PropTypes.object.isRequired,
|
||||||
|
onPinSubmit: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default Pin;
|
export default Pin;
|
@ -48,5 +48,6 @@ export default [
|
|||||||
id: 'xem',
|
id: 'xem',
|
||||||
coinName: 'NEM',
|
coinName: 'NEM',
|
||||||
url: 'https://nem.io/downloads/',
|
url: 'https://nem.io/downloads/',
|
||||||
|
external: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
@ -9,92 +9,79 @@ import * as CONNECT from 'actions/constants/TrezorConnect';
|
|||||||
import type { Action, TrezorDevice } from 'flowtype';
|
import type { Action, TrezorDevice } from 'flowtype';
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
opened: false;
|
context: typeof MODAL.CONTEXT_NONE;
|
||||||
} | {
|
} | {
|
||||||
opened: true;
|
context: typeof MODAL.CONTEXT_DEVICE,
|
||||||
device: TrezorDevice;
|
device: TrezorDevice;
|
||||||
instances?: Array<TrezorDevice>;
|
instances?: Array<TrezorDevice>;
|
||||||
windowType?: string;
|
windowType?: string;
|
||||||
|
} | {
|
||||||
|
context: typeof MODAL.CONTEXT_EXTERNAL_WALLET,
|
||||||
|
windowType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
opened: false,
|
context: MODAL.CONTEXT_NONE,
|
||||||
// instances: null,
|
|
||||||
// windowType: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function modal(state: State = initialState, action: Action): State {
|
export default function modal(state: State = initialState, action: Action): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case RECEIVE.REQUEST_UNVERIFIED:
|
case RECEIVE.REQUEST_UNVERIFIED:
|
||||||
return {
|
case CONNECT.FORGET_REQUEST:
|
||||||
opened: true,
|
case CONNECT.TRY_TO_DUPLICATE:
|
||||||
device: action.device,
|
|
||||||
windowType: action.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
case CONNECT.REQUEST_WALLET_TYPE:
|
case CONNECT.REQUEST_WALLET_TYPE:
|
||||||
return {
|
return {
|
||||||
opened: true,
|
context: MODAL.CONTEXT_DEVICE,
|
||||||
device: action.device,
|
device: action.device,
|
||||||
windowType: action.type,
|
windowType: action.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
case CONNECT.REMEMBER_REQUEST:
|
case CONNECT.REMEMBER_REQUEST:
|
||||||
return {
|
return {
|
||||||
opened: true,
|
context: MODAL.CONTEXT_DEVICE,
|
||||||
device: action.device,
|
device: action.device,
|
||||||
instances: action.instances,
|
instances: action.instances,
|
||||||
windowType: action.type,
|
windowType: action.type,
|
||||||
};
|
};
|
||||||
case CONNECT.FORGET_REQUEST:
|
|
||||||
return {
|
|
||||||
opened: true,
|
|
||||||
device: action.device,
|
|
||||||
windowType: action.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
case CONNECT.TRY_TO_DUPLICATE:
|
|
||||||
return {
|
|
||||||
opened: true,
|
|
||||||
device: action.device,
|
|
||||||
windowType: action.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// device acquired
|
||||||
|
// close modal
|
||||||
case DEVICE.CHANGED:
|
case DEVICE.CHANGED:
|
||||||
if (state.opened && action.device.path === state.device.path && action.device.status === 'occupied') {
|
if (state.context === MODAL.CONTEXT_DEVICE && action.device.path === state.device.path && action.device.status === 'occupied') {
|
||||||
return initialState;
|
return initialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
|
// device connected
|
||||||
|
// close modal if modal context is not 'device'
|
||||||
|
case DEVICE.CONNECT:
|
||||||
|
case DEVICE.CONNECT_UNACQUIRED:
|
||||||
|
if (state.context !== MODAL.CONTEXT_DEVICE) {
|
||||||
|
return initialState;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
|
||||||
|
// device with context assigned to modal was disconnected
|
||||||
|
// close modal
|
||||||
case DEVICE.DISCONNECT:
|
case DEVICE.DISCONNECT:
|
||||||
if (state.opened && action.device.path === state.device.path) {
|
if (state.context === MODAL.CONTEXT_DEVICE && action.device.path === state.device.path) {
|
||||||
return initialState;
|
return initialState;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
// case DEVICE.CONNECT :
|
|
||||||
// case DEVICE.CONNECT_UNACQUIRED :
|
|
||||||
// if (state.opened && state.windowType === CONNECT.TRY_TO_FORGET) {
|
|
||||||
// return {
|
|
||||||
// ...initialState,
|
|
||||||
// passphraseCached: state.passphraseCached
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return state;
|
|
||||||
|
|
||||||
case UI.REQUEST_PIN:
|
case UI.REQUEST_PIN:
|
||||||
case UI.INVALID_PIN:
|
case UI.INVALID_PIN:
|
||||||
case UI.REQUEST_PASSPHRASE:
|
case UI.REQUEST_PASSPHRASE:
|
||||||
return {
|
return {
|
||||||
opened: true,
|
context: MODAL.CONTEXT_DEVICE,
|
||||||
device: action.payload.device,
|
device: action.payload.device,
|
||||||
windowType: action.type,
|
windowType: action.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
case UI.REQUEST_BUTTON:
|
case UI.REQUEST_BUTTON:
|
||||||
return {
|
return {
|
||||||
opened: true,
|
context: MODAL.CONTEXT_DEVICE,
|
||||||
device: action.payload.device,
|
device: action.payload.device,
|
||||||
windowType: action.payload.code,
|
windowType: action.payload.code,
|
||||||
};
|
};
|
||||||
@ -106,6 +93,12 @@ export default function modal(state: State = initialState, action: Action): Stat
|
|||||||
case CONNECT.REMEMBER:
|
case CONNECT.REMEMBER:
|
||||||
return initialState;
|
return initialState;
|
||||||
|
|
||||||
|
case MODAL.OPEN_EXTERNAL_WALLET:
|
||||||
|
return {
|
||||||
|
context: MODAL.CONTEXT_EXTERNAL_WALLET,
|
||||||
|
windowType: action.id,
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
|
|
||||||
|
import type { Device } from 'trezor-connect';
|
||||||
import type {
|
import type {
|
||||||
TrezorDevice,
|
TrezorDevice,
|
||||||
State,
|
State,
|
||||||
@ -91,7 +92,7 @@ export const isDeviceAccessible = (device: ?TrezorDevice): boolean => {
|
|||||||
return device.mode === 'normal' && device.firmware !== 'required';
|
return device.mode === 'normal' && device.firmware !== 'required';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isSelectedDevice = (current: ?TrezorDevice, device: ?TrezorDevice): boolean => !!((current && device && (current.path === device.path && current.instance === device.instance)));
|
export const isSelectedDevice = (selected: ?TrezorDevice, device: ?(TrezorDevice | Device)): boolean => !!((selected && device && (selected.path === device.path && (device.ts && selected.instance === device.instance))));
|
||||||
|
|
||||||
export const getVersion = (device: TrezorDevice): string => {
|
export const getVersion = (device: TrezorDevice): string => {
|
||||||
let version;
|
let version;
|
||||||
|
@ -7,6 +7,7 @@ import { withRouter } from 'react-router-dom';
|
|||||||
import * as TrezorConnectActions from 'actions/TrezorConnectActions';
|
import * as TrezorConnectActions from 'actions/TrezorConnectActions';
|
||||||
import * as DiscoveryActions from 'actions/DiscoveryActions';
|
import * as DiscoveryActions from 'actions/DiscoveryActions';
|
||||||
import * as RouterActions from 'actions/RouterActions';
|
import * as RouterActions from 'actions/RouterActions';
|
||||||
|
import * as ModalActions from 'actions/ModalActions';
|
||||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||||
import type { State, Dispatch } from 'flowtype';
|
import type { State, Dispatch } from 'flowtype';
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps>
|
|||||||
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
|
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
|
||||||
gotoDeviceSettings: bindActionCreators(RouterActions.gotoDeviceSettings, dispatch),
|
gotoDeviceSettings: bindActionCreators(RouterActions.gotoDeviceSettings, dispatch),
|
||||||
onSelectDevice: bindActionCreators(RouterActions.selectDevice, dispatch),
|
onSelectDevice: bindActionCreators(RouterActions.selectDevice, dispatch),
|
||||||
|
gotoExternalWallet: bindActionCreators(ModalActions.gotoExternalWallet, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import coins from 'constants/coins';
|
import coins from 'constants/coins';
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
@ -5,12 +7,19 @@ import ICONS from 'config/icons';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import Link from 'components/Link';
|
||||||
import Divider from '../Divider';
|
import Divider from '../Divider';
|
||||||
import RowCoin from '../RowCoin';
|
import RowCoin from '../RowCoin';
|
||||||
|
|
||||||
|
import type { Props } from '../common';
|
||||||
|
|
||||||
const Wrapper = styled.div``;
|
const Wrapper = styled.div``;
|
||||||
|
|
||||||
class CoinMenu extends PureComponent {
|
const ExternalWallet = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
class CoinMenu extends PureComponent<Props> {
|
||||||
getBaseUrl() {
|
getBaseUrl() {
|
||||||
const { selectedDevice } = this.props.wallet;
|
const { selectedDevice } = this.props.wallet;
|
||||||
let baseUrl = '';
|
let baseUrl = '';
|
||||||
@ -24,6 +33,27 @@ class CoinMenu extends PureComponent {
|
|||||||
return baseUrl;
|
return baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOtherCoins() {
|
||||||
|
return coins.map((coin) => {
|
||||||
|
const row = (
|
||||||
|
<RowCoin
|
||||||
|
coin={{
|
||||||
|
name: coin.coinName,
|
||||||
|
id: coin.id,
|
||||||
|
}}
|
||||||
|
iconRight={{
|
||||||
|
type: ICONS.SKIP,
|
||||||
|
color: colors.TEXT_SECONDARY,
|
||||||
|
size: 27,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (coin.external) return <ExternalWallet key={coin.id} onClick={() => this.props.gotoExternalWallet(coin.id, coin.url)}>{row}</ExternalWallet>;
|
||||||
|
return <Link key={coin.id} href={coin.url} target="_top">{row}</Link>;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { config } = this.props.localStorage;
|
const { config } = this.props.localStorage;
|
||||||
return (
|
return (
|
||||||
@ -46,31 +76,16 @@ class CoinMenu extends PureComponent {
|
|||||||
textRight="(You will be redirected)"
|
textRight="(You will be redirected)"
|
||||||
hasBorder
|
hasBorder
|
||||||
/>
|
/>
|
||||||
{coins.map(coin => (
|
{this.getOtherCoins()}
|
||||||
<a key={coin.id} href={coin.url}>
|
|
||||||
<RowCoin
|
|
||||||
coin={{
|
|
||||||
name: coin.coinName,
|
|
||||||
id: coin.id,
|
|
||||||
}}
|
|
||||||
iconRight={{
|
|
||||||
type: ICONS.SKIP,
|
|
||||||
color: colors.TEXT_SECONDARY,
|
|
||||||
size: 27,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoinMenu.propTypes = {
|
CoinMenu.propTypes = {
|
||||||
config: PropTypes.object,
|
localStorage: PropTypes.object.isRequired,
|
||||||
wallet: PropTypes.object,
|
wallet: PropTypes.object.isRequired,
|
||||||
selectedDevice: PropTypes.object,
|
gotoExternalWallet: PropTypes.func.isRequired,
|
||||||
localStorage: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CoinMenu;
|
export default CoinMenu;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import * as TrezorConnectActions from 'actions/TrezorConnectActions';
|
import * as TrezorConnectActions from 'actions/TrezorConnectActions';
|
||||||
import * as DiscoveryActions from 'actions/DiscoveryActions';
|
import * as DiscoveryActions from 'actions/DiscoveryActions';
|
||||||
import * as RouterActions from 'actions/RouterActions';
|
import * as RouterActions from 'actions/RouterActions';
|
||||||
|
import * as ModalActions from 'actions/ModalActions';
|
||||||
import { toggleDeviceDropdown } from 'actions/WalletActions';
|
import { toggleDeviceDropdown } from 'actions/WalletActions';
|
||||||
import type { State } from 'flowtype';
|
import type { State } from 'flowtype';
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ export type DispatchProps = {
|
|||||||
duplicateDevice: typeof TrezorConnectActions.duplicateDevice,
|
duplicateDevice: typeof TrezorConnectActions.duplicateDevice,
|
||||||
gotoDeviceSettings: typeof RouterActions.gotoDeviceSettings,
|
gotoDeviceSettings: typeof RouterActions.gotoDeviceSettings,
|
||||||
onSelectDevice: typeof RouterActions.selectDevice,
|
onSelectDevice: typeof RouterActions.selectDevice,
|
||||||
|
gotoExternalWallet: typeof ModalActions.gotoExternalWallet,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Props = StateProps & DispatchProps;
|
export type Props = StateProps & DispatchProps;
|
@ -11,7 +11,7 @@ import type { State } from 'flowtype';
|
|||||||
|
|
||||||
import Header from 'components/Header';
|
import Header from 'components/Header';
|
||||||
import Footer from 'components/Footer';
|
import Footer from 'components/Footer';
|
||||||
import ModalContainer from 'components/modals';
|
import ModalContainer from 'components/modals/Container';
|
||||||
import AppNotifications from 'components/notifications/App';
|
import AppNotifications from 'components/notifications/App';
|
||||||
import ContextNotifications from 'components/notifications/Context';
|
import ContextNotifications from 'components/notifications/Context';
|
||||||
|
|
||||||
|
@ -148,14 +148,13 @@ const AccountReceive = (props: Props) => {
|
|||||||
addressUnverified,
|
addressUnverified,
|
||||||
} = props.receive;
|
} = props.receive;
|
||||||
|
|
||||||
|
const isAddressVerifying = props.modal.context === 'device' && props.modal.windowType === 'ButtonRequest_Address';
|
||||||
|
const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified;
|
||||||
|
|
||||||
let address = `${account.address.substring(0, 20)}...`;
|
let address = `${account.address.substring(0, 20)}...`;
|
||||||
if (addressVerified
|
if (addressVerified || addressUnverified || isAddressVerifying) {
|
||||||
|| addressUnverified
|
|
||||||
|| (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address')) {
|
|
||||||
({ address } = account);
|
({ address } = account);
|
||||||
}
|
}
|
||||||
const isAddressVerifying = props.modal.opened && props.modal.windowType === 'ButtonRequest_Address';
|
|
||||||
const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Content>
|
<Content>
|
||||||
|
Loading…
Reference in New Issue
Block a user