mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-13 20:08:56 +00:00
Merge pull request #134 from trezor/feature/change-passphrase
Feature/change passphrase
This commit is contained in:
commit
f258f1b125
@ -142,7 +142,8 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => async (dis
|
||||
},
|
||||
path: coinToDiscover.bip44,
|
||||
keepSession: true, // acquire and hold session
|
||||
useEmptyPassphrase: !device.instance,
|
||||
//useEmptyPassphrase: !device.instance,
|
||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
||||
});
|
||||
|
||||
// handle TREZOR response error
|
||||
@ -264,7 +265,8 @@ const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction
|
||||
state: device.state,
|
||||
},
|
||||
keepSession: false,
|
||||
useEmptyPassphrase: !device.instance,
|
||||
// useEmptyPassphrase: !device.instance,
|
||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
||||
});
|
||||
|
||||
await dispatch(BlockchainActions.subscribe(discoveryProcess.network));
|
||||
|
@ -112,6 +112,17 @@ export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispa
|
||||
}
|
||||
};
|
||||
|
||||
export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean): ThunkAction => (dispatch: Dispatch): void => {
|
||||
dispatch({
|
||||
type: MODAL.CLOSE,
|
||||
});
|
||||
dispatch({
|
||||
type: CONNECT.RECEIVE_WALLET_TYPE,
|
||||
device,
|
||||
hidden,
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
onPinSubmit,
|
||||
onPassphraseSubmit,
|
||||
@ -121,4 +132,5 @@ export default {
|
||||
onForgetSingleDevice,
|
||||
onCancel,
|
||||
onDuplicateDevice,
|
||||
onWalletTypeRequest,
|
||||
};
|
@ -3,12 +3,8 @@
|
||||
|
||||
import * as PENDING from 'actions/constants/pendingTx';
|
||||
|
||||
import type {
|
||||
Action, ThunkAction, GetState, Dispatch,
|
||||
} from 'flowtype';
|
||||
import type { State, PendingTx } from 'reducers/PendingTxReducer';
|
||||
|
||||
|
||||
export type PendingTxAction = {
|
||||
type: typeof PENDING.FROM_STORAGE,
|
||||
payload: State
|
||||
@ -25,29 +21,4 @@ export type PendingTxAction = {
|
||||
} | {
|
||||
type: typeof PENDING.TX_TOKEN_ERROR,
|
||||
tx: PendingTx,
|
||||
}
|
||||
|
||||
export const reject = (tx: PendingTx): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
/*
|
||||
dispatch({
|
||||
type: NOTIFICATION.ADD,
|
||||
payload: {
|
||||
type: 'warning',
|
||||
title: 'Pending transaction rejected',
|
||||
message: `Transaction with id: ${tx.id} not found.`,
|
||||
cancelable: true,
|
||||
actions: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: () => {
|
||||
dispatch({
|
||||
type: PENDING.TX_RESOLVED,
|
||||
tx,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
*/
|
||||
};
|
||||
}
|
@ -67,7 +67,8 @@ export const showAddress = (path: Array<number>): AsyncAction => async (dispatch
|
||||
state: selected.state,
|
||||
},
|
||||
path,
|
||||
useEmptyPassphrase: !selected.instance,
|
||||
// useEmptyPassphrase: !selected.instance,
|
||||
useEmptyPassphrase: selected.useEmptyPassphrase,
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
|
@ -258,12 +258,12 @@ export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dis
|
||||
/*
|
||||
* Redirect to first device or landing page
|
||||
*/
|
||||
export const selectFirstAvailableDevice = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
export const selectFirstAvailableDevice = (gotoRoot: boolean = false): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const url = dispatch(getFirstAvailableDeviceUrl());
|
||||
if (url) {
|
||||
const currentParams = getState().router.location.state;
|
||||
const requestedParams = dispatch(pathToParams(url));
|
||||
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
|
||||
if (gotoRoot || currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
|
||||
dispatch(goto(url));
|
||||
}
|
||||
} else {
|
||||
|
@ -458,7 +458,8 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
|
||||
instance: selected.instance,
|
||||
state: selected.state,
|
||||
},
|
||||
useEmptyPassphrase: !selected.instance,
|
||||
// useEmptyPassphrase: !selected.instance,
|
||||
useEmptyPassphrase: selected.useEmptyPassphrase,
|
||||
path: account.addressPath,
|
||||
transaction: txData,
|
||||
});
|
||||
|
@ -6,6 +6,7 @@ import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||
import * as NOTIFICATION from 'actions/constants/notification';
|
||||
import { getDuplicateInstanceNumber } from 'reducers/utils';
|
||||
import * as RouterActions from 'actions/RouterActions';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
|
||||
import type {
|
||||
DeviceMessage,
|
||||
@ -58,7 +59,7 @@ export type TrezorConnectAction = {
|
||||
type: typeof CONNECT.FORGET,
|
||||
device: TrezorDevice
|
||||
} | {
|
||||
type: typeof CONNECT.FORGET_SINGLE,
|
||||
type: typeof CONNECT.FORGET_SINGLE | typeof CONNECT.FORGET_SILENT,
|
||||
device: TrezorDevice
|
||||
} | {
|
||||
type: typeof CONNECT.REMEMBER,
|
||||
@ -71,6 +72,13 @@ export type TrezorConnectAction = {
|
||||
payload: Array<TrezorDevice>
|
||||
} | {
|
||||
type: typeof CONNECT.START_ACQUIRING | typeof CONNECT.STOP_ACQUIRING,
|
||||
} | {
|
||||
type: typeof CONNECT.REQUEST_WALLET_TYPE,
|
||||
device: TrezorDevice
|
||||
} | {
|
||||
type: typeof CONNECT.RECEIVE_WALLET_TYPE,
|
||||
device: TrezorDevice,
|
||||
hidden: boolean,
|
||||
};
|
||||
|
||||
export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
@ -152,7 +160,19 @@ export const postInit = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
export const requestWalletType = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
const selected = getState().wallet.selectedDevice;
|
||||
if (!selected) return;
|
||||
const isDeviceReady = selected.connected && selected.features && !selected.state && selected.mode === 'normal' && selected.firmware !== 'required';
|
||||
if (!isDeviceReady) return;
|
||||
|
||||
dispatch({
|
||||
type: CONNECT.REQUEST_WALLET_TYPE,
|
||||
device: selected,
|
||||
});
|
||||
};
|
||||
|
||||
export const authorizeDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
const selected = getState().wallet.selectedDevice;
|
||||
if (!selected) return;
|
||||
const isDeviceReady = selected.connected && selected.features && !selected.state && selected.mode === 'normal' && selected.firmware !== 'required';
|
||||
@ -164,7 +184,7 @@ export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispat
|
||||
instance: selected.instance,
|
||||
state: selected.state,
|
||||
},
|
||||
useEmptyPassphrase: !selected.instance,
|
||||
useEmptyPassphrase: selected.useEmptyPassphrase,
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
@ -190,25 +210,32 @@ export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispat
|
||||
type: NOTIFICATION.CLOSE,
|
||||
payload: { devicePath: selected.path },
|
||||
});
|
||||
dispatch(getSelectedDeviceState());
|
||||
dispatch(authorizeDevice());
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
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);
|
||||
if (instances.length > 0) {
|
||||
dispatch({
|
||||
type: CONNECT.REMEMBER_REQUEST,
|
||||
device: instances[0],
|
||||
instances,
|
||||
});
|
||||
const isSelected = deviceUtils.isSelectedDevice(getState().wallet.selectedDevice, instances[0]);
|
||||
if (!isSelected && getState().modal.opened) {
|
||||
dispatch({
|
||||
type: CONNECT.FORGET_SILENT,
|
||||
device: instances[0],
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: CONNECT.REMEMBER_REQUEST,
|
||||
device: instances[0],
|
||||
instances,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dispatch(RouterActions.selectFirstAvailableDevice());
|
||||
}
|
||||
@ -231,11 +258,14 @@ export function acquire(): AsyncAction {
|
||||
type: CONNECT.START_ACQUIRING,
|
||||
});
|
||||
|
||||
// this is the only place where useEmptyPassphrase should be used every time
|
||||
// the goal here is to acquire device and get his features
|
||||
// authentication (passphrase) is not needed here yet
|
||||
const response = await TrezorConnect.getFeatures({
|
||||
device: {
|
||||
path: selected.path,
|
||||
},
|
||||
useEmptyPassphrase: !selected.instance,
|
||||
useEmptyPassphrase: true,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
@ -270,12 +300,7 @@ export const forget = (device: TrezorDevice): Action => ({
|
||||
device,
|
||||
});
|
||||
|
||||
export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
// dispatch({
|
||||
// type: CONNECT.TRY_TO_DUPLICATE,
|
||||
// device,
|
||||
// });
|
||||
|
||||
export const duplicateDeviceOld = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
const instance: number = getDuplicateInstanceNumber(getState().devices, device);
|
||||
const extended: Object = { instance };
|
||||
dispatch({
|
||||
@ -283,3 +308,10 @@ export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dis
|
||||
device: { ...device, ...extended },
|
||||
});
|
||||
};
|
||||
|
||||
export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
||||
dispatch({
|
||||
type: CONNECT.REQUEST_WALLET_TYPE,
|
||||
device,
|
||||
});
|
||||
};
|
||||
|
@ -87,6 +87,7 @@ export const clearUnavailableDevicesData = (prevState: State, device: Device): T
|
||||
const actions = [
|
||||
LOCATION_CHANGE,
|
||||
CONNECT.AUTH_DEVICE,
|
||||
CONNECT.RECEIVE_WALLET_TYPE,
|
||||
...Object.values(DEVICE).filter(v => typeof v === 'string'),
|
||||
];
|
||||
|
||||
|
@ -16,6 +16,7 @@ export const REMEMBER_REQUEST: 'connect__remember_request' = 'connect__remember_
|
||||
export const FORGET_REQUEST: 'connect__forget_request' = 'connect__forget_request';
|
||||
export const FORGET: 'connect__forget' = 'connect__forget';
|
||||
export const FORGET_SINGLE: 'connect__forget_single' = 'connect__forget_single';
|
||||
export const FORGET_SILENT: 'connect__forget_silent' = 'connect__forget_silent';
|
||||
export const DISCONNECT_REQUEST: 'connect__disconnect_request' = 'connect__disconnect_request';
|
||||
export const REMEMBER: 'connect__remember' = 'connect__remember';
|
||||
|
||||
@ -25,4 +26,7 @@ export const DUPLICATE: 'connect__duplicate' = 'connect__duplicate';
|
||||
export const DEVICE_STATE_EXCEPTION: 'connect__device_state_exception' = 'connect__device_state_exception';
|
||||
|
||||
export const START_ACQUIRING: 'connect__start_acquiring' = 'connect__start_acquiring';
|
||||
export const STOP_ACQUIRING: 'connect__stop_acquiring' = 'connect__stop_acquiring';
|
||||
export const STOP_ACQUIRING: 'connect__stop_acquiring' = 'connect__stop_acquiring';
|
||||
|
||||
export const REQUEST_WALLET_TYPE: 'connect__request_wallet_type' = 'connect__request_wallet_type';
|
||||
export const RECEIVE_WALLET_TYPE: 'connect__receive_wallet_type' = 'connect__receive_wallet_type';
|
51
src/components/images/WalletType/index.js
Normal file
51
src/components/images/WalletType/index.js
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import colors from 'config/colors';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const SvgWrapper = styled.svg`
|
||||
:hover {
|
||||
path {
|
||||
fill: ${props => props.hoverColor}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Path = styled.path`
|
||||
fill: ${props => props.color};
|
||||
`;
|
||||
|
||||
export const HIDDEN = 'M23.9,13.6l-4-11C19.8,2.3,19.4,2,19,2h-3c-0.6,0-1,0.4-1,1s0.4,1,1,1h2.3l3.3,9H14h-4H2.4l3.3-9H8 c0.6,0,1-0.4,1-1S8.6,2,8,2H5C4.6,2,4.2,2.3,4.1,2.7l-4,11C0,13.7,0,13.9,0,14c0,0,0,0,0,0v0c0,0,0,0,0,0v5c0,1.7,1.3,3,3,3h5 c1.7,0,3-1.3,3-3v-4h2v4c0,1.7,1.3,3,3,3h5c1.7,0,3-1.3,3-3v-5c0,0,0,0,0,0v0c0,0,0,0,0,0C24,13.9,24,13.7,23.9,13.6z';
|
||||
export const STANDARD = 'M23,4H4H3C2.449,4,2,3.551,2,3s0.449-1,1-1h15v1h2V1c0-0.552-0.448-1-1-1H3C1.343,0,0,1.343,0,3v17 c0,2.209,1.791,4,4,4h19c0.552,0,1-0.448,1-1V5C24,4.448,23.552,4,23,4z M18,16c-1.105,0-2-0.895-2-2c0-1.105,0.895-2,2-2 s2,0.895,2,2C20,15.105,19.105,16,18,16z';
|
||||
|
||||
const Icon = ({
|
||||
type = 'standard',
|
||||
size = 24,
|
||||
color = colors.TEXT_SECONDARY,
|
||||
hoverColor,
|
||||
onClick,
|
||||
}) => (
|
||||
<SvgWrapper
|
||||
hoverColor={hoverColor}
|
||||
width={`${size}`}
|
||||
height={`${size}`}
|
||||
viewBox="-12 -12 48 48"
|
||||
onClick={onClick}
|
||||
>
|
||||
<Path
|
||||
key={type}
|
||||
color={color}
|
||||
d={type === 'hidden' ? HIDDEN : STANDARD}
|
||||
/>
|
||||
</SvgWrapper>
|
||||
);
|
||||
|
||||
Icon.propTypes = {
|
||||
type: PropTypes.string,
|
||||
size: PropTypes.number,
|
||||
color: PropTypes.string,
|
||||
hoverColor: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Icon;
|
139
src/components/modals/device/WalletType/index.js
Normal file
139
src/components/modals/device/WalletType/index.js
Normal file
@ -0,0 +1,139 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { H3 } from 'components/Heading';
|
||||
import P from 'components/Paragraph';
|
||||
import Button from 'components/Button';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
import Icon from 'components/Icon';
|
||||
import Link from 'components/Link';
|
||||
import colors from 'config/colors';
|
||||
import icons from 'config/icons';
|
||||
import WalletTypeIcon from 'components/images/WalletType';
|
||||
|
||||
import type { Props } from 'components/modals/index';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 360px;
|
||||
padding: 24px 48px;
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin: 0 0 10px 0;
|
||||
`;
|
||||
|
||||
const StyledTooltip = styled(Tooltip)`
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 1px;
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(Icon)`
|
||||
position: relative;
|
||||
top: -1px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 0;
|
||||
`;
|
||||
|
||||
const Span = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const Divider = styled.div`
|
||||
margin: 20px 0;
|
||||
border-top: 1px solid ${colors.DIVIDER};
|
||||
`;
|
||||
|
||||
class WalletType extends Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.keyboardHandler, false);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.keyboardHandler, false);
|
||||
}
|
||||
|
||||
keyboardHandler(event: KeyboardEvent): void {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
this.changeType(false);
|
||||
}
|
||||
}
|
||||
|
||||
keyboardHandler: (event: KeyboardEvent) => void;
|
||||
|
||||
changeType(hidden: boolean) {
|
||||
const { modal } = this.props;
|
||||
if (!modal.opened) return;
|
||||
this.props.modalActions.onWalletTypeRequest(modal.device, hidden);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.modal.opened) return null;
|
||||
const { device } = this.props.modal;
|
||||
const { onCancel } = this.props.modalActions;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{ device.state && (
|
||||
<StyledLink onClick={onCancel}>
|
||||
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
</StyledLink>
|
||||
)}
|
||||
<H3>RequestWalletType for { device.instanceLabel }?</H3>
|
||||
<Row>
|
||||
<Span>
|
||||
<WalletTypeIcon type="standard" size={24} color={colors.TEXT_SECONDARY} />
|
||||
Standard Wallet
|
||||
</Span>
|
||||
<P isSmaller>Continue to access your standard wallet.</P>
|
||||
<StyledButton onClick={() => this.changeType(false)}>Go to your standard wallet</StyledButton>
|
||||
<Divider />
|
||||
<Span>
|
||||
<WalletTypeIcon type="hidden" size={24} color={colors.TEXT_SECONDARY} />
|
||||
Hidden Wallet
|
||||
<StyledTooltip
|
||||
maxWidth={285}
|
||||
placement="top"
|
||||
content="Passphrase is an optional feature of the Trezor device that is recommended for advanced users only. It is a word or a sentence of your choice. Its main purpose is to access a hidden wallet."
|
||||
readMoreLink="https://wiki.trezor.io/Passphrase"
|
||||
>
|
||||
<StyledIcon
|
||||
icon={icons.HELP}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
size={24}
|
||||
/>
|
||||
</StyledTooltip>
|
||||
</Span>
|
||||
<P isSmaller>You will be asked to enter your passphrase to unlock your hidden wallet.</P>
|
||||
<StyledButton isWhite onClick={() => this.changeType(true)}>Go to your hidden wallet</StyledButton>
|
||||
</Row>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WalletType;
|
@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
import React, { Component } from 'react';
|
||||
import * as React from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
@ -29,6 +29,7 @@ import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddres
|
||||
import ForgetDevice from 'components/modals/device/Forget';
|
||||
import RememberDevice from 'components/modals/device/Remember';
|
||||
import DuplicateDevice from 'components/modals/device/Duplicate';
|
||||
import WalletType from 'components/modals/device/WalletType';
|
||||
|
||||
type OwnProps = { }
|
||||
|
||||
@ -51,13 +52,13 @@ type DispatchProps = {
|
||||
|
||||
export type Props = StateProps & DispatchProps;
|
||||
|
||||
const Fade = ({ children, ...props }) => (
|
||||
const Fade = (props: { children: React.Node}) => (
|
||||
<CSSTransition
|
||||
{...props}
|
||||
timeout={1000}
|
||||
classNames="fade"
|
||||
>
|
||||
{ children }
|
||||
{ props.children }
|
||||
</CSSTransition>
|
||||
);
|
||||
|
||||
@ -84,7 +85,7 @@ const ModalWindow = styled.div`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
class Modal extends Component<Props> {
|
||||
class Modal extends React.Component<Props> {
|
||||
render() {
|
||||
if (!this.props.modal.opened) return null;
|
||||
|
||||
@ -123,6 +124,10 @@ class Modal extends Component<Props> {
|
||||
component = (<DuplicateDevice {...this.props} />);
|
||||
break;
|
||||
|
||||
case CONNECT.REQUEST_WALLET_TYPE:
|
||||
component = (<WalletType {...this.props} />);
|
||||
break;
|
||||
|
||||
default:
|
||||
component = null;
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ class Passphrase extends Component<Props, State> {
|
||||
return (
|
||||
<Wrapper>
|
||||
<H2>Enter {this.state.deviceLabel} passphrase</H2>
|
||||
<P isSmaller>Note that passphrase is case-sensitive.</P>
|
||||
<P isSmaller>Note that passphrase is case-sensitive. If you enter a wrong passphrase, you will not unlock the desired hidden wallet.</P>
|
||||
<Row>
|
||||
<Label>Passphrase</Label>
|
||||
<Input
|
||||
@ -228,7 +228,7 @@ class Passphrase extends Component<Props, State> {
|
||||
</Row>
|
||||
{!this.state.shouldShowSingleInput && (
|
||||
<Row>
|
||||
<Label>Re-enter passphrase</Label>
|
||||
<Label>Confirm passphrase</Label>
|
||||
<Input
|
||||
name="passphraseCheckInputValue"
|
||||
type={this.state.isPassphraseHidden ? 'password' : 'text'}
|
||||
@ -263,12 +263,12 @@ class Passphrase extends Component<Props, State> {
|
||||
</Row>
|
||||
|
||||
<Footer>
|
||||
<P isSmaller>If you want to access your default account</P>
|
||||
<P isSmaller>
|
||||
Changed your mind?
|
||||
<LinkButton
|
||||
isGreen
|
||||
onClick={() => this.submitPassphrase(true)}
|
||||
>Leave passphrase blank
|
||||
>Go to your standard wallet
|
||||
</LinkButton>
|
||||
</P>
|
||||
</Footer>
|
||||
|
@ -1,9 +1,14 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import colors from 'config/colors';
|
||||
import icons from 'config/icons';
|
||||
import styled from 'styled-components';
|
||||
import { H3 } from 'components/Heading';
|
||||
import P from 'components/Paragraph';
|
||||
|
||||
import type { Props } from 'components/modals/index';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 360px;
|
||||
@ -12,7 +17,7 @@ const Wrapper = styled.div`
|
||||
|
||||
const Header = styled.div``;
|
||||
|
||||
const Confirmation = (props) => {
|
||||
const Confirmation = (props: Props) => {
|
||||
if (!props.modal.opened) return null;
|
||||
const { device } = props.modal;
|
||||
|
||||
@ -21,6 +26,7 @@ const Confirmation = (props) => {
|
||||
<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>
|
||||
);
|
||||
|
@ -56,6 +56,7 @@ export type AcquiredDevice = $Exact<{
|
||||
status: DeviceStatus,
|
||||
+mode: DeviceMode,
|
||||
state: ?string,
|
||||
useEmptyPassphrase: boolean,
|
||||
|
||||
remember: boolean; // device should be remembered
|
||||
connected: boolean; // device is connected
|
||||
@ -73,6 +74,7 @@ export type UnknownDevice = $Exact<{
|
||||
+label: string,
|
||||
+features: null,
|
||||
state: ?string,
|
||||
useEmptyPassphrase: boolean,
|
||||
|
||||
remember: boolean; // device should be remembered
|
||||
connected: boolean; // device is connected
|
||||
|
@ -92,6 +92,8 @@ export default (state: State = initialState, action: Action): State => {
|
||||
|
||||
case CONNECT.FORGET:
|
||||
case CONNECT.FORGET_SINGLE:
|
||||
case CONNECT.FORGET_SILENT:
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
return removeAccounts(state, action.device);
|
||||
|
||||
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA:
|
||||
|
@ -39,6 +39,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T
|
||||
instanceName: typeof upcoming.instanceName === 'string' ? upcoming.instanceName : current.instanceName,
|
||||
state: current.state,
|
||||
ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts,
|
||||
useEmptyPassphrase: typeof upcoming.useEmptyPassphrase === 'boolean' ? upcoming.useEmptyPassphrase : current.useEmptyPassphrase,
|
||||
};
|
||||
|
||||
if (upcoming.type === 'acquired') {
|
||||
@ -89,6 +90,7 @@ const addDevice = (state: State, device: Device): State => {
|
||||
instanceLabel: device.label,
|
||||
instanceName: null,
|
||||
ts: new Date().getTime(),
|
||||
useEmptyPassphrase: true,
|
||||
};
|
||||
|
||||
|
||||
@ -260,6 +262,24 @@ const onSelectedDevice = (state: State, device: ?TrezorDevice): State => {
|
||||
return otherDevices.concat([extended]);
|
||||
};
|
||||
|
||||
const onChangeWalletType = (state: State, device: TrezorDevice, hidden: boolean): State => {
|
||||
const affectedDevices: State = state.filter(d => d.path === device.path || (d.features && device.features && d.features.device_id === device.features.device_id));
|
||||
const otherDevices: State = state.filter(d => affectedDevices.indexOf(d) === -1);
|
||||
if (affectedDevices.length > 0) {
|
||||
const changedDevices = affectedDevices.map((d) => { // eslint-disable-line arrow-body-style
|
||||
return d.type === 'acquired' ? {
|
||||
...d,
|
||||
remember: false,
|
||||
state: null,
|
||||
useEmptyPassphrase: !hidden,
|
||||
ts: new Date().getTime(),
|
||||
} : d;
|
||||
});
|
||||
return otherDevices.concat(changedDevices);
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export default function devices(state: State = initialState, action: Action): State {
|
||||
switch (action.type) {
|
||||
case CONNECT.DEVICE_FROM_STORAGE:
|
||||
@ -278,6 +298,7 @@ export default function devices(state: State = initialState, action: Action): St
|
||||
case CONNECT.FORGET:
|
||||
return forgetDevice(state, action.device);
|
||||
case CONNECT.FORGET_SINGLE:
|
||||
case CONNECT.FORGET_SILENT:
|
||||
return forgetSingleDevice(state, action.device);
|
||||
|
||||
case DEVICE.CONNECT:
|
||||
@ -296,6 +317,9 @@ export default function devices(state: State = initialState, action: Action): St
|
||||
case WALLET.SET_SELECTED_DEVICE:
|
||||
return onSelectedDevice(state, action.device);
|
||||
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
return onChangeWalletType(state, action.device, action.hidden);
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -185,6 +185,8 @@ export default function discovery(state: State = initialState, action: Action):
|
||||
});
|
||||
case CONNECT.FORGET:
|
||||
case CONNECT.FORGET_SINGLE:
|
||||
case CONNECT.FORGET_SILENT:
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
return forgetDiscovery(state, action.device);
|
||||
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA:
|
||||
return clear(state, action.devices);
|
||||
|
@ -32,6 +32,13 @@ export default function modal(state: State = initialState, action: Action): Stat
|
||||
windowType: action.type,
|
||||
};
|
||||
|
||||
case CONNECT.REQUEST_WALLET_TYPE:
|
||||
return {
|
||||
opened: true,
|
||||
device: action.device,
|
||||
windowType: action.type,
|
||||
};
|
||||
|
||||
case CONNECT.REMEMBER_REQUEST:
|
||||
return {
|
||||
opened: true,
|
||||
|
@ -1,20 +1,22 @@
|
||||
/* @flow */
|
||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||
import * as PENDING from 'actions/constants/pendingTx';
|
||||
import * as SEND from 'actions/constants/send';
|
||||
|
||||
import type { Action } from 'flowtype';
|
||||
import type { TrezorDevice, Action } from 'flowtype';
|
||||
import type { SendTxAction } from 'actions/SendFormActions';
|
||||
|
||||
export type PendingTx = {
|
||||
+type: 'send' | 'recv';
|
||||
+type: 'send' | 'receive';
|
||||
+id: string;
|
||||
+network: string;
|
||||
+address: string;
|
||||
+deviceState: string;
|
||||
+currency: string;
|
||||
+amount: string;
|
||||
+total: string;
|
||||
+tx: any;
|
||||
+nonce: number;
|
||||
+address: string;
|
||||
rejected: boolean;
|
||||
}
|
||||
|
||||
@ -28,12 +30,14 @@ const add = (state: State, action: SendTxAction): State => {
|
||||
type: 'send',
|
||||
id: action.txid,
|
||||
network: action.account.network,
|
||||
address: action.account.address,
|
||||
deviceState: action.account.deviceState,
|
||||
|
||||
currency: action.selectedCurrency,
|
||||
amount: action.amount,
|
||||
total: action.total,
|
||||
tx: action.tx,
|
||||
nonce: action.nonce,
|
||||
address: action.account.address,
|
||||
rejected: false,
|
||||
});
|
||||
return newState;
|
||||
@ -47,6 +51,8 @@ const addFromBloockbokNotifiaction = (state: State, payload: any): State => {
|
||||
};
|
||||
*/
|
||||
|
||||
const clear = (state: State, device: TrezorDevice): State => state.filter(tx => tx.deviceState !== device.state);
|
||||
|
||||
const remove = (state: State, id: string): State => state.filter(tx => tx.id !== id);
|
||||
|
||||
const reject = (state: State, id: string): State => state.map((tx) => {
|
||||
@ -61,6 +67,12 @@ export default function pending(state: State = initialState, action: Action): St
|
||||
case SEND.TX_COMPLETE:
|
||||
return add(state, action);
|
||||
|
||||
case CONNECT.FORGET:
|
||||
case CONNECT.FORGET_SINGLE:
|
||||
case CONNECT.FORGET_SILENT:
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
return clear(state, action.device);
|
||||
|
||||
// case PENDING.ADD:
|
||||
// return add(state, action.payload);
|
||||
case PENDING.TX_RESOLVED:
|
||||
|
@ -71,6 +71,8 @@ export default (state: State = initialState, action: Action): State => {
|
||||
|
||||
case CONNECT.FORGET:
|
||||
case CONNECT.FORGET_SINGLE:
|
||||
case CONNECT.FORGET_SILENT:
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
return forget(state, action.device);
|
||||
|
||||
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA:
|
||||
|
@ -102,6 +102,8 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar
|
||||
|
||||
case CONNECT.FORGET:
|
||||
case CONNECT.FORGET_SINGLE:
|
||||
case CONNECT.FORGET_SILENT:
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
case DEVICE.CHANGED:
|
||||
case DEVICE.DISCONNECT:
|
||||
case CONNECT.AUTH_DEVICE:
|
||||
|
@ -11,6 +11,7 @@ import * as TrezorConnectActions from 'actions/TrezorConnectActions';
|
||||
import * as SelectedAccountActions from 'actions/SelectedAccountActions';
|
||||
import * as SendFormActionActions from 'actions/SendFormActions';
|
||||
import * as DiscoveryActions from 'actions/DiscoveryActions';
|
||||
import * as RouterActions from 'actions/RouterActions';
|
||||
|
||||
import type {
|
||||
Middleware,
|
||||
@ -49,10 +50,9 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
||||
api.dispatch(LocalStorageActions.loadData());
|
||||
break;
|
||||
case WALLET.SET_SELECTED_DEVICE:
|
||||
if (action.device) {
|
||||
// try to authorize device
|
||||
api.dispatch(TrezorConnectActions.getSelectedDeviceState());
|
||||
}
|
||||
// try to authorize device
|
||||
// api.dispatch(TrezorConnectActions.authorizeDevice());
|
||||
api.dispatch(TrezorConnectActions.requestWalletType());
|
||||
break;
|
||||
case DEVICE.CONNECT:
|
||||
api.dispatch(WalletActions.clearUnavailableDevicesData(prevState, action.device));
|
||||
@ -102,10 +102,19 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
||||
// if "selectedAccount" didn't change observe send form props changes
|
||||
api.dispatch(SendFormActionActions.observe(prevState, action));
|
||||
}
|
||||
} else if (action.type === CONNECT.AUTH_DEVICE) {
|
||||
// selected device did changed
|
||||
// try to restore discovery after device authentication
|
||||
api.dispatch(DiscoveryActions.restore());
|
||||
} else {
|
||||
switch (action.type) {
|
||||
case CONNECT.AUTH_DEVICE:
|
||||
// selected device did changed
|
||||
// try to restore discovery after device authentication
|
||||
api.dispatch(DiscoveryActions.restore());
|
||||
break;
|
||||
case CONNECT.RECEIVE_WALLET_TYPE:
|
||||
api.dispatch(RouterActions.selectFirstAvailableDevice(true));
|
||||
api.dispatch(TrezorConnectActions.authorizeDevice());
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// even if "selectedDevice" didn't change because it was updated on DEVICE.CHANGED before DEVICE.CONNECT action
|
||||
|
@ -4,7 +4,7 @@ import colors from 'config/colors';
|
||||
const tooltipStyles = css`
|
||||
.rc-tooltip {
|
||||
position: absolute;
|
||||
z-index: 1070;
|
||||
z-index: 10070;
|
||||
visibility: visible;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
|
@ -58,14 +58,14 @@ class MenuItems extends Component {
|
||||
if (!this.showDeviceMenu()) return null;
|
||||
return (
|
||||
<Wrapper>
|
||||
<Item onClick={() => this.onDeviceMenuClick('settings', this.props.device)}>
|
||||
{/* <Item onClick={() => this.onDeviceMenuClick('settings', this.props.device)}>
|
||||
<Icon
|
||||
icon={icons.COG}
|
||||
size={25}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
/>
|
||||
<Label>Device settings</Label>
|
||||
</Item>
|
||||
</Item> */}
|
||||
<Item onClick={() => this.onDeviceMenuClick('forget', this.props.device)}>
|
||||
<Icon
|
||||
icon={icons.EJECT}
|
||||
@ -81,7 +81,7 @@ class MenuItems extends Component {
|
||||
size={25}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
/>
|
||||
<Label>Create hidden wallet</Label>
|
||||
<Label>Change wallet type</Label>
|
||||
</Item>
|
||||
)}
|
||||
{this.showRenewSession() && (
|
||||
|
@ -4,6 +4,7 @@ import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import colors from 'config/colors';
|
||||
import Icon from 'components/Icon';
|
||||
import WalletTypeIcon from 'components/images/WalletType';
|
||||
import icons from 'config/icons';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import styled from 'styled-components';
|
||||
@ -197,11 +198,12 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
this.handleOpen();
|
||||
}
|
||||
}}
|
||||
device={this.props.wallet.selectedDevice}
|
||||
device={selectedDevice}
|
||||
disabled={!isDeviceAccessible && this.props.devices.length === 1}
|
||||
isOpen={this.props.wallet.dropdownOpened}
|
||||
icon={(
|
||||
<React.Fragment>
|
||||
<WalletTypeIcon type={selectedDevice && !selectedDevice.useEmptyPassphrase ? 'hidden' : 'standard'} size={25} color={colors.TEXT_SECONDARY} />
|
||||
{this.props.devices.length > 1 && (
|
||||
<Counter>{this.props.devices.length}</Counter>
|
||||
)}
|
||||
|
Loading…
Reference in New Issue
Block a user