mirror of
https://github.com/trezor/trezor-wallet
synced 2024-12-25 00:18:07 +00:00
refactor using device.mode and device.firmware
This commit is contained in:
parent
09f819aacc
commit
566178ee3b
@ -2,6 +2,7 @@
|
||||
|
||||
import { push, LOCATION_CHANGE } from 'react-router-redux';
|
||||
import { routes } from 'support/routes';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
|
||||
import type {
|
||||
RouterLocationState,
|
||||
@ -59,6 +60,11 @@ export const paramsValidation = (params: RouterLocationState): PayloadAction<boo
|
||||
}
|
||||
|
||||
if (!device) return false;
|
||||
|
||||
if (!deviceUtils.isDeviceAccessible(device)) {
|
||||
// TODO: there should be no access to deep links if device has incorrect mode/firmware
|
||||
// if (params.hasOwnProperty('network') || params.hasOwnProperty('account')) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// validate requested network
|
||||
@ -177,10 +183,12 @@ const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction<?string> =>
|
||||
let url: ?string;
|
||||
if (!device.features) {
|
||||
url = `/device/${device.path}/${device.type === 'unreadable' ? 'unreadable' : 'acquire'}`;
|
||||
} else if (device.features.bootloader_mode) {
|
||||
} else if (device.mode === 'bootloader') { // device in bootloader doesn't have device_id
|
||||
url = `/device/${device.path}/bootloader`;
|
||||
} else if (!device.features.initialized) {
|
||||
} else if (device.mode === 'initialize') {
|
||||
url = `/device/${device.features.device_id}/initialize`;
|
||||
} else if (device.firmware === 'required') {
|
||||
url = `/device/${device.features.device_id}/firmware-update`;
|
||||
} else if (typeof device.instance === 'number') {
|
||||
url = `/device/${device.features.device_id}:${device.instance}`;
|
||||
} else {
|
||||
|
@ -154,50 +154,50 @@ export const postInit = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
|
||||
export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
const selected = getState().wallet.selectedDevice;
|
||||
if (selected
|
||||
&& selected.connected
|
||||
&& (selected.features && !selected.features.bootloader_mode && selected.features.initialized)
|
||||
&& !selected.state) {
|
||||
const response = await TrezorConnect.getDeviceState({
|
||||
device: {
|
||||
path: selected.path,
|
||||
instance: selected.instance,
|
||||
state: selected.state,
|
||||
},
|
||||
useEmptyPassphrase: !selected.instance,
|
||||
});
|
||||
if (!selected) return;
|
||||
const isDeviceReady = selected.connected && selected.features && !selected.state && selected.mode === 'normal' && selected.firmware !== 'required';
|
||||
if (!isDeviceReady) return;
|
||||
|
||||
if (response && response.success) {
|
||||
dispatch({
|
||||
type: CONNECT.AUTH_DEVICE,
|
||||
device: selected,
|
||||
state: response.payload.state,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: NOTIFICATION.ADD,
|
||||
payload: {
|
||||
devicePath: selected.path,
|
||||
type: 'error',
|
||||
title: 'Authentication error',
|
||||
message: response.payload.error,
|
||||
cancelable: false,
|
||||
actions: [
|
||||
{
|
||||
label: 'Try again',
|
||||
callback: () => {
|
||||
dispatch({
|
||||
type: NOTIFICATION.CLOSE,
|
||||
payload: { devicePath: selected.path },
|
||||
});
|
||||
dispatch(getSelectedDeviceState());
|
||||
},
|
||||
const response = await TrezorConnect.getDeviceState({
|
||||
device: {
|
||||
path: selected.path,
|
||||
instance: selected.instance,
|
||||
state: selected.state,
|
||||
},
|
||||
useEmptyPassphrase: !selected.instance,
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
dispatch({
|
||||
type: CONNECT.AUTH_DEVICE,
|
||||
device: selected,
|
||||
state: response.payload.state,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: NOTIFICATION.ADD,
|
||||
payload: {
|
||||
devicePath: selected.path,
|
||||
type: 'error',
|
||||
title: 'Authentication error',
|
||||
message: response.payload.error,
|
||||
cancelable: false,
|
||||
actions: [
|
||||
{
|
||||
label: 'Try again',
|
||||
callback: () => {
|
||||
dispatch({
|
||||
type: NOTIFICATION.CLOSE,
|
||||
payload: { devicePath: selected.path },
|
||||
});
|
||||
dispatch(getSelectedDeviceState());
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||
|
@ -5,6 +5,7 @@ import { DEVICE } from 'trezor-connect';
|
||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||
import * as WALLET from 'actions/constants/wallet';
|
||||
import * as reducerUtils from 'reducers/utils';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
|
||||
import type {
|
||||
Device,
|
||||
@ -104,7 +105,7 @@ export const observe = (prevState: State, action: Action): PayloadAction<boolean
|
||||
|
||||
// handle devices state change (from trezor-connect events or location change)
|
||||
if (locationChanged || selectedDeviceChanged) {
|
||||
if (device && reducerUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
|
||||
if (device && deviceUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
|
||||
dispatch({
|
||||
type: WALLET.UPDATE_SELECTED_DEVICE,
|
||||
device,
|
||||
|
@ -98,7 +98,7 @@ const DeviceHeader = ({
|
||||
device,
|
||||
isHoverable = true,
|
||||
onClickWrapper,
|
||||
isBootloader = false,
|
||||
isAccessible = true,
|
||||
disabled = false,
|
||||
isSelected = false,
|
||||
}) => {
|
||||
@ -123,7 +123,7 @@ const DeviceHeader = ({
|
||||
<Status>{getStatusName(status)}</Status>
|
||||
</LabelWrapper>
|
||||
<IconWrapper>
|
||||
{icon && !disabled && !isBootloader && icon}
|
||||
{icon && !disabled && isAccessible && icon}
|
||||
</IconWrapper>
|
||||
</ClickWrapper>
|
||||
</Wrapper>
|
||||
@ -131,7 +131,7 @@ const DeviceHeader = ({
|
||||
};
|
||||
|
||||
DeviceHeader.propTypes = {
|
||||
isBootloader: PropTypes.bool,
|
||||
isAccessible: PropTypes.bool,
|
||||
device: PropTypes.object,
|
||||
icon: PropTypes.element,
|
||||
isHoverable: PropTypes.bool,
|
||||
|
@ -1,24 +1,33 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import Icon from 'components/Icon';
|
||||
import DeviceHeader from 'components/DeviceHeader';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
import icons from 'config/icons';
|
||||
import colors from 'config/colors';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import type { TrezorDevice } from 'flowtype';
|
||||
import type { Props as CommonProps } from '../../../common';
|
||||
|
||||
|
||||
const Wrapper = styled.div``;
|
||||
const IconClick = styled.div``;
|
||||
|
||||
class DeviceList extends Component {
|
||||
sortByInstance(a, b) {
|
||||
type Props = {
|
||||
devices: $ElementType<CommonProps, 'devices'>;
|
||||
selectedDevice: $ElementType<$ElementType<CommonProps, 'wallet'>, 'selectedDevice'>;
|
||||
onSelectDevice: $ElementType<CommonProps, 'onSelectDevice'>;
|
||||
forgetDevice: $ElementType<CommonProps, 'forgetDevice'>;
|
||||
};
|
||||
|
||||
class DeviceList extends Component<Props> {
|
||||
sortByInstance(a: TrezorDevice, b: TrezorDevice) {
|
||||
if (!a.instance || !b.instance) return -1;
|
||||
return a.instance > b.instance ? 1 : -1;
|
||||
}
|
||||
|
||||
redirectToBootloader(selectedDevice) {
|
||||
this.props.history.push(`/device/${selectedDevice.features.device_id}/bootloader`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
devices, selectedDevice, onSelectDevice, forgetDevice,
|
||||
@ -28,16 +37,11 @@ class DeviceList extends Component {
|
||||
{devices
|
||||
.sort(this.sortByInstance)
|
||||
.map(device => (
|
||||
device !== selectedDevice && (
|
||||
!deviceUtils.isSelectedDevice(selectedDevice, device) && (
|
||||
<DeviceHeader
|
||||
key={device.state || device.path}
|
||||
isBootloader={device.features && device.features.bootloader_mode}
|
||||
isAccessible={deviceUtils.isDeviceAccessible(device)}
|
||||
onClickWrapper={() => {
|
||||
if (device.features) {
|
||||
if (device.features.bootloader_mode) {
|
||||
this.redirectToBootloader(selectedDevice);
|
||||
}
|
||||
}
|
||||
onSelectDevice(device);
|
||||
}}
|
||||
onClickIcon={() => forgetDevice(device)}
|
||||
@ -69,4 +73,4 @@ class DeviceList extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(DeviceList);
|
||||
export default DeviceList;
|
@ -42,8 +42,8 @@ class MenuItems extends Component {
|
||||
}
|
||||
|
||||
showDeviceMenu() {
|
||||
const device = this.props.device;
|
||||
return device && device.features && !device.features.bootloader_mode && device.features.initialized;
|
||||
const { device } = this.props;
|
||||
return device && device.mode === 'normal';
|
||||
}
|
||||
|
||||
showClone() {
|
||||
|
@ -4,7 +4,7 @@ import styled from 'styled-components';
|
||||
import TrezorConnect from 'trezor-connect';
|
||||
import type { TrezorDevice } from 'flowtype';
|
||||
import Button from 'components/Button';
|
||||
import { isWebUSB } from 'utils/device';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
import MenuItems from './components/MenuItems';
|
||||
import DeviceList from './components/DeviceList';
|
||||
|
||||
@ -28,10 +28,6 @@ type DeviceMenuItem = {
|
||||
}
|
||||
|
||||
class DeviceMenu extends Component<Props> {
|
||||
mouseDownHandler: (event: MouseEvent) => void;
|
||||
|
||||
blurHandler: (event: FocusEvent) => void;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.mouseDownHandler = this.mouseDownHandler.bind(this);
|
||||
@ -42,12 +38,33 @@ class DeviceMenu extends Component<Props> {
|
||||
window.addEventListener('mousedown', this.mouseDownHandler, false);
|
||||
// window.addEventListener('blur', this.blurHandler, false);
|
||||
const { transport } = this.props.connect;
|
||||
if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
|
||||
if (transport.type && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { transport } = this.props.connect;
|
||||
if (isWebUSB(transport)) TrezorConnect.renderWebUSBButton();
|
||||
if (deviceUtils.isWebUSB(transport)) TrezorConnect.renderWebUSBButton();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
window.removeEventListener('mousedown', this.mouseDownHandler, false);
|
||||
}
|
||||
|
||||
onDeviceMenuClick(item: DeviceMenuItem, device: TrezorDevice): void {
|
||||
if (item.type === 'reload') {
|
||||
this.props.acquireDevice();
|
||||
} else if (item.type === 'forget') {
|
||||
this.props.forgetDevice(device);
|
||||
} else if (item.type === 'clone') {
|
||||
this.props.duplicateDevice(device);
|
||||
} else if (item.type === 'settings') {
|
||||
this.props.toggleDeviceDropdown(false);
|
||||
this.props.gotoDeviceSettings(device);
|
||||
}
|
||||
}
|
||||
|
||||
blurHandler(): void {
|
||||
this.props.toggleDeviceDropdown(false);
|
||||
}
|
||||
|
||||
mouseDownHandler(event: MouseEvent): void {
|
||||
@ -67,34 +84,16 @@ class DeviceMenu extends Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
blurHandler(): void {
|
||||
this.props.toggleDeviceDropdown(false);
|
||||
}
|
||||
mouseDownHandler: (event: MouseEvent) => void;
|
||||
|
||||
onDeviceMenuClick(item: DeviceMenuItem, device: TrezorDevice): void {
|
||||
if (item.type === 'reload') {
|
||||
this.props.acquireDevice();
|
||||
} else if (item.type === 'forget') {
|
||||
this.props.forgetDevice(device);
|
||||
} else if (item.type === 'clone') {
|
||||
this.props.duplicateDevice(device);
|
||||
} else if (item.type === 'settings') {
|
||||
this.props.toggleDeviceDropdown(false);
|
||||
this.props.gotoDeviceSettings(device);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
window.removeEventListener('mousedown', this.mouseDownHandler, false);
|
||||
}
|
||||
blurHandler: (event: FocusEvent) => void;
|
||||
|
||||
showDivider() {
|
||||
return this.props.devices.length > 1;
|
||||
}
|
||||
|
||||
showMenuItems() {
|
||||
const { selectedDevice } = this.props.wallet;
|
||||
return selectedDevice && selectedDevice.features;
|
||||
return deviceUtils.isDeviceAccessible(this.props.wallet.selectedDevice);
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -113,7 +112,7 @@ class DeviceMenu extends Component<Props> {
|
||||
forgetDevice={forgetDevice}
|
||||
/>
|
||||
<ButtonWrapper>
|
||||
{isWebUSB(transport) && (
|
||||
{deviceUtils.isWebUSB(transport) && (
|
||||
<StyledButton isWebUsb>Check for devices</StyledButton>
|
||||
)}
|
||||
</ButtonWrapper>
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import colors from 'config/colors';
|
||||
@ -6,10 +8,13 @@ import icons from 'config/icons';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import styled from 'styled-components';
|
||||
import DeviceHeader from 'components/DeviceHeader';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
|
||||
import AccountMenu from './components/AccountMenu';
|
||||
import CoinMenu from './components/CoinMenu';
|
||||
import DeviceMenu from './components/DeviceMenu';
|
||||
import StickyContainer from './components/StickyContainer';
|
||||
|
||||
import type { Props } from './components/common';
|
||||
|
||||
const Header = styled(DeviceHeader)`
|
||||
@ -105,6 +110,7 @@ const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGro
|
||||
type State = {
|
||||
animationType: ?string;
|
||||
shouldRenderDeviceSelection: boolean;
|
||||
clicked: boolean;
|
||||
}
|
||||
|
||||
class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
@ -119,11 +125,11 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
const { dropdownOpened, selectedDevice } = nextProps.wallet;
|
||||
const hasNetwork = nextProps.location.state && nextProps.location.state.network;
|
||||
const hasFeatures = selectedDevice && selectedDevice.features;
|
||||
const deviceReady = hasFeatures && !selectedDevice.features.bootloader_mode && selectedDevice.features.initialized;
|
||||
const { location } = nextProps.router;
|
||||
const hasNetwork = location && location.state.network;
|
||||
const deviceReady = selectedDevice && selectedDevice.features && selectedDevice.mode === 'normal';
|
||||
if (dropdownOpened) {
|
||||
this.setState({ shouldRenderDeviceSelection: true });
|
||||
} else if (hasNetwork) {
|
||||
@ -176,22 +182,23 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
const isDeviceInBootloader = this.props.wallet.selectedDevice.features && this.props.wallet.selectedDevice.features.bootloader_mode;
|
||||
const { selectedDevice } = props.wallet;
|
||||
const isDeviceAccessible = deviceUtils.isDeviceAccessible(selectedDevice);
|
||||
return (
|
||||
<StickyContainer
|
||||
location={this.props.location.pathname}
|
||||
location={props.router.location.pathname}
|
||||
deviceSelection={this.props.wallet.dropdownOpened}
|
||||
>
|
||||
<Header
|
||||
isSelected
|
||||
isHoverable={false}
|
||||
onClickWrapper={() => {
|
||||
if (!isDeviceInBootloader || this.props.devices.length > 1) {
|
||||
if (isDeviceAccessible || this.props.devices.length > 1) {
|
||||
this.handleOpen();
|
||||
}
|
||||
}}
|
||||
device={this.props.wallet.selectedDevice}
|
||||
disabled={isDeviceInBootloader && this.props.devices.length === 1}
|
||||
disabled={!isDeviceAccessible && this.props.devices.length === 1}
|
||||
isOpen={this.props.wallet.dropdownOpened}
|
||||
icon={(
|
||||
<React.Fragment>
|
||||
@ -211,7 +218,7 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
<Body>
|
||||
{this.state.shouldRenderDeviceSelection && <DeviceMenu {...this.props} />}
|
||||
{!isDeviceInBootloader && menu}
|
||||
{isDeviceAccessible && menu}
|
||||
</Body>
|
||||
<Footer key="sticky-footer">
|
||||
<Help>
|
||||
@ -241,8 +248,12 @@ LeftNavigation.propTypes = {
|
||||
pending: PropTypes.array,
|
||||
|
||||
toggleDeviceDropdown: PropTypes.func,
|
||||
selectedDevice: PropTypes.object,
|
||||
addAccount: PropTypes.func,
|
||||
acquireDevice: PropTypes.func,
|
||||
forgetDevice: PropTypes.func,
|
||||
duplicateDevice: PropTypes.func,
|
||||
gotoDeviceSettings: PropTypes.func,
|
||||
onSelectDevice: PropTypes.func,
|
||||
};
|
||||
|
||||
|
||||
export default LeftNavigation;
|
||||
|
Loading…
Reference in New Issue
Block a user