From 7fc6ef3c11013128ef08981ebe106b0eb7efa522 Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Tue, 2 Oct 2018 14:00:48 +0200
Subject: [PATCH 1/9] added app notifications
---
src/components/notifications/App/Container.js | 0
.../App/components/OnlineStatus/index.js | 11 +++++
.../App/components/UpdateBridge/index.js | 22 +++++++++
.../App/components/UpdateFirmware/index.js | 22 +++++++++
src/components/notifications/App/index.js | 46 +++++++++++++++++++
src/views/Wallet/index.js | 2 +
6 files changed, 103 insertions(+)
delete mode 100644 src/components/notifications/App/Container.js
create mode 100644 src/components/notifications/App/components/OnlineStatus/index.js
create mode 100644 src/components/notifications/App/components/UpdateBridge/index.js
create mode 100644 src/components/notifications/App/components/UpdateFirmware/index.js
diff --git a/src/components/notifications/App/Container.js b/src/components/notifications/App/Container.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/components/notifications/App/components/OnlineStatus/index.js b/src/components/notifications/App/components/OnlineStatus/index.js
new file mode 100644
index 00000000..1c81c0ca
--- /dev/null
+++ b/src/components/notifications/App/components/OnlineStatus/index.js
@@ -0,0 +1,11 @@
+/* @flow */
+import * as React from 'react';
+import { Notification } from 'components/Notification';
+
+import type { Props } from '../../index';
+
+export default (props: Props) => {
+ const { online } = props.wallet;
+ // if (online) return null;
+ return ();
+};
\ No newline at end of file
diff --git a/src/components/notifications/App/components/UpdateBridge/index.js b/src/components/notifications/App/components/UpdateBridge/index.js
new file mode 100644
index 00000000..45aec38d
--- /dev/null
+++ b/src/components/notifications/App/components/UpdateBridge/index.js
@@ -0,0 +1,22 @@
+/* @flow */
+import * as React from 'react';
+import { Notification } from 'components/Notification';
+
+import type { Props } from '../../index';
+
+export default (props: Props) => {
+ return (
+ {
+
+ },
+ }]
+ }
+ />
+ );
+};
\ No newline at end of file
diff --git a/src/components/notifications/App/components/UpdateFirmware/index.js b/src/components/notifications/App/components/UpdateFirmware/index.js
new file mode 100644
index 00000000..726bafe0
--- /dev/null
+++ b/src/components/notifications/App/components/UpdateFirmware/index.js
@@ -0,0 +1,22 @@
+/* @flow */
+import * as React from 'react';
+import { Notification } from 'components/Notification';
+
+import type { Props } from '../../index';
+
+export default (props: Props) => {
+ return (
+ {
+
+ },
+ }]
+ }
+ />
+ );
+};
\ No newline at end of file
diff --git a/src/components/notifications/App/index.js b/src/components/notifications/App/index.js
index e69de29b..ffce183b 100644
--- a/src/components/notifications/App/index.js
+++ b/src/components/notifications/App/index.js
@@ -0,0 +1,46 @@
+/* @flow */
+import * as React from 'react';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+
+import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
+import type { State, Dispatch } from 'flowtype';
+
+import * as NotificationActions from 'actions/NotificationActions';
+
+import OnlineStatus from './components/OnlineStatus';
+import UpdateBridge from './components/UpdateBridge';
+import UpdateFirmware from './components/UpdateFirmware';
+
+export type StateProps = {
+ connect: $ElementType;
+ wallet: $ElementType;
+ children?: React.Node;
+}
+
+export type DispatchProps = {
+ close: typeof NotificationActions.close;
+}
+
+export type Props = StateProps & DispatchProps;
+
+type OwnProps = {};
+
+const Notifications = (props: Props) => (
+
+
+
+
+
+);
+
+const mapStateToProps: MapStateToProps = (state: State): StateProps => ({
+ connect: state.connect,
+ wallet: state.wallet,
+});
+
+const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
+ close: bindActionCreators(NotificationActions.close, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Notifications);
\ No newline at end of file
diff --git a/src/views/Wallet/index.js b/src/views/Wallet/index.js
index c78a3020..67833dab 100644
--- a/src/views/Wallet/index.js
+++ b/src/views/Wallet/index.js
@@ -12,6 +12,7 @@ import type { State } from 'flowtype';
import Header from 'components/Header';
import Footer from 'components/Footer';
import ModalContainer from 'components/modals';
+import AppNotifications from 'components/notifications/App';
import ContextNotifications from 'components/notifications/Context';
import Log from 'components/Log';
@@ -84,6 +85,7 @@ const Body = styled.div`
const Wallet = (props: WalletContainerProps) => (
+
{props.wallet.selectedDevice && }
From ba7cf4d1d490e56303b16331394dc01995f834a4 Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Tue, 2 Oct 2018 14:00:48 +0200
Subject: [PATCH 2/9] added app notifications
---
src/components/notifications/App/Container.js | 0
.../App/components/OnlineStatus/index.js | 11 +++++
.../App/components/UpdateBridge/index.js | 22 +++++++++
.../App/components/UpdateFirmware/index.js | 22 +++++++++
src/components/notifications/App/index.js | 46 +++++++++++++++++++
src/views/Wallet/index.js | 2 +
6 files changed, 103 insertions(+)
delete mode 100644 src/components/notifications/App/Container.js
create mode 100644 src/components/notifications/App/components/OnlineStatus/index.js
create mode 100644 src/components/notifications/App/components/UpdateBridge/index.js
create mode 100644 src/components/notifications/App/components/UpdateFirmware/index.js
diff --git a/src/components/notifications/App/Container.js b/src/components/notifications/App/Container.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/components/notifications/App/components/OnlineStatus/index.js b/src/components/notifications/App/components/OnlineStatus/index.js
new file mode 100644
index 00000000..1c81c0ca
--- /dev/null
+++ b/src/components/notifications/App/components/OnlineStatus/index.js
@@ -0,0 +1,11 @@
+/* @flow */
+import * as React from 'react';
+import { Notification } from 'components/Notification';
+
+import type { Props } from '../../index';
+
+export default (props: Props) => {
+ const { online } = props.wallet;
+ // if (online) return null;
+ return ();
+};
\ No newline at end of file
diff --git a/src/components/notifications/App/components/UpdateBridge/index.js b/src/components/notifications/App/components/UpdateBridge/index.js
new file mode 100644
index 00000000..45aec38d
--- /dev/null
+++ b/src/components/notifications/App/components/UpdateBridge/index.js
@@ -0,0 +1,22 @@
+/* @flow */
+import * as React from 'react';
+import { Notification } from 'components/Notification';
+
+import type { Props } from '../../index';
+
+export default (props: Props) => {
+ return (
+ {
+
+ },
+ }]
+ }
+ />
+ );
+};
\ No newline at end of file
diff --git a/src/components/notifications/App/components/UpdateFirmware/index.js b/src/components/notifications/App/components/UpdateFirmware/index.js
new file mode 100644
index 00000000..726bafe0
--- /dev/null
+++ b/src/components/notifications/App/components/UpdateFirmware/index.js
@@ -0,0 +1,22 @@
+/* @flow */
+import * as React from 'react';
+import { Notification } from 'components/Notification';
+
+import type { Props } from '../../index';
+
+export default (props: Props) => {
+ return (
+ {
+
+ },
+ }]
+ }
+ />
+ );
+};
\ No newline at end of file
diff --git a/src/components/notifications/App/index.js b/src/components/notifications/App/index.js
index e69de29b..ffce183b 100644
--- a/src/components/notifications/App/index.js
+++ b/src/components/notifications/App/index.js
@@ -0,0 +1,46 @@
+/* @flow */
+import * as React from 'react';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+
+import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
+import type { State, Dispatch } from 'flowtype';
+
+import * as NotificationActions from 'actions/NotificationActions';
+
+import OnlineStatus from './components/OnlineStatus';
+import UpdateBridge from './components/UpdateBridge';
+import UpdateFirmware from './components/UpdateFirmware';
+
+export type StateProps = {
+ connect: $ElementType;
+ wallet: $ElementType;
+ children?: React.Node;
+}
+
+export type DispatchProps = {
+ close: typeof NotificationActions.close;
+}
+
+export type Props = StateProps & DispatchProps;
+
+type OwnProps = {};
+
+const Notifications = (props: Props) => (
+
+
+
+
+
+);
+
+const mapStateToProps: MapStateToProps = (state: State): StateProps => ({
+ connect: state.connect,
+ wallet: state.wallet,
+});
+
+const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
+ close: bindActionCreators(NotificationActions.close, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Notifications);
\ No newline at end of file
diff --git a/src/views/Wallet/index.js b/src/views/Wallet/index.js
index 2003d405..48eb27aa 100644
--- a/src/views/Wallet/index.js
+++ b/src/views/Wallet/index.js
@@ -12,6 +12,7 @@ import type { State } from 'flowtype';
import Header from 'components/Header';
import Footer from 'components/Footer';
import ModalContainer from 'components/modals';
+import AppNotifications from 'components/notifications/App';
import ContextNotifications from 'components/notifications/Context';
import Log from 'components/Log';
@@ -84,6 +85,7 @@ const Body = styled.div`
const Wallet = (props: WalletContainerProps) => (
+
{props.wallet.selectedDevice && }
From baa7d7d8a228860f077966fd5275b8fd50f095f3 Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Thu, 4 Oct 2018 17:08:02 +0200
Subject: [PATCH 3/9] InstallBridge page with data from trezor-connect
---
src/constants/bridge.js | 8 ---
src/reducers/TrezorConnectReducer.js | 31 +++++++--
.../Landing/components/InstallBridge/index.js | 67 +++++++++++--------
src/views/Landing/index.js | 2 +-
4 files changed, 66 insertions(+), 42 deletions(-)
delete mode 100644 src/constants/bridge.js
diff --git a/src/constants/bridge.js b/src/constants/bridge.js
deleted file mode 100644
index 7c911ebc..00000000
--- a/src/constants/bridge.js
+++ /dev/null
@@ -1,8 +0,0 @@
-export default [
- { id: 'Windows', value: 'trezor-bridge-2.0.11-win32-install.exe', label: 'Windows' },
- { id: 'macOS', value: 'trezor-bridge-2.0.11.pkg', label: 'macOS' },
- { id: 'Linux', value: 'trezor-bridge_2.0.11_amd64.deb', label: 'Linux 64-bit (deb)' },
- { id: 'Linux-rpm', value: 'trezor-bridge_2.0.11_amd64.rpm', label: 'Linux 64-bit (rpm)' },
- { id: '01', value: 'trezor-bridge_2.0.11_amd32.deb', label: 'Linux 32-bit (deb)' },
- { id: '02', value: 'trezor-bridge_2.0.11_amd32.rpm', label: 'Linux 32-bit (rpm)' },
-];
diff --git a/src/reducers/TrezorConnectReducer.js b/src/reducers/TrezorConnectReducer.js
index f8adf4dd..825d9c6b 100644
--- a/src/reducers/TrezorConnectReducer.js
+++ b/src/reducers/TrezorConnectReducer.js
@@ -9,12 +9,24 @@ export type SelectedDevice = {
instance: ?number;
}
+export type LatestBridge = {
+ version: Array;
+ directory: string;
+ packages: Array<{ name: string; url: string; signature?: string; preferred: boolean; }>;
+ changelog: string;
+}
+
export type State = {
initialized: boolean;
error: ?string;
- transport: ?{
+ transport: {
type: string;
version: string;
+ outdated: boolean;
+ bridge: LatestBridge;
+ } | {
+ type: null,
+ bridge: LatestBridge;
};
// browserState: {
// name: string;
@@ -30,7 +42,15 @@ export type State = {
const initialState: State = {
initialized: false,
error: null,
- transport: null,
+ transport: {
+ type: null,
+ bridge: {
+ version: [],
+ directory: '',
+ packages: [],
+ changelog: '',
+ },
+ },
browserState: {},
acquiringDevice: false,
};
@@ -63,9 +83,12 @@ export default function connect(state: State = initialState, action: Action): St
case TRANSPORT.ERROR:
return {
...state,
- // error: action.payload, // message is wrapped in "device" field. It's dispatched from TrezorConnect.on(DEVICE_EVENT...) in TrezorConnectService
+ // error: action.payload.error, // message is wrapped in "device" field. It's dispatched from TrezorConnect.on(DEVICE_EVENT...) in TrezorConnectService
error: 'Transport is missing',
- transport: null,
+ transport: {
+ type: null,
+ bridge: action.payload.bridge,
+ },
};
case CONNECT.START_ACQUIRING:
diff --git a/src/views/Landing/components/InstallBridge/index.js b/src/views/Landing/components/InstallBridge/index.js
index 67177f79..b9873aaa 100644
--- a/src/views/Landing/components/InstallBridge/index.js
+++ b/src/views/Landing/components/InstallBridge/index.js
@@ -4,7 +4,6 @@ import React, { Component } from 'react';
import styled from 'styled-components';
import colors from 'config/colors';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
-import installers from 'constants/bridge';
import { Select } from 'components/Select';
import Link from 'components/Link';
import Button from 'components/Button';
@@ -13,24 +12,28 @@ import P from 'components/Paragraph';
import Icon from 'components/Icon';
import ICONS from 'config/icons';
+import type { State as TrezorConnectState } from 'reducers/TrezorConnectReducer';
+
type InstallTarget = {
- id: string;
value: string;
label: string;
+ signature: ?string;
+ preferred: boolean;
}
+// { id: 'Windows', value: 'trezor-bridge-2.0.11-win32-install.exe', label: 'Windows' },
type State = {
- version: string;
- target: ?InstallTarget;
- url: string;
+ currentVersion: string;
+ latestVersion: string;
+ installers: Array;
+ target: InstallTarget;
+ uri: string;
}
// import type { Props } from './index';
type Props = {
- browserState: {
- osname: string,
- };
+ transport: $ElementType;
}
const InstallBridgeWrapper = styled.div`
@@ -85,21 +88,21 @@ export default class InstallBridge extends Component {
constructor(props: Props) {
super(props);
- const currentTarget: ?InstallTarget = installers.find(i => i.id === props.browserState.osname);
- this.state = {
- version: '2.0.12',
- url: 'https://wallet.trezor.io/data/bridge/2.0.12/',
- target: currentTarget,
- };
- }
+ const installers = props.transport.bridge.packages.map(p => ({
+ label: p.name,
+ value: p.url,
+ signature: p.signature,
+ preferred: p.preferred,
+ }));
- componentWillUpdate() {
- if (this.props.browserState.osname && !this.state.target) {
- const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname);
- this.setState({
- target: currentTarget,
- });
- }
+ const currentTarget: ?InstallTarget = installers.find(i => i.preferred === true);
+ this.state = {
+ currentVersion: props.transport.type ? `Your version ${props.transport.version}` : 'Not installed',
+ latestVersion: props.transport.bridge.version.join('.'),
+ installers,
+ target: currentTarget || installers[0],
+ uri: 'https://wallet.trezor.io/data/',
+ };
}
onChange(value: InstallTarget) {
@@ -112,20 +115,18 @@ export default class InstallBridge extends Component {
if (!this.state.target) {
return ;
}
- const { label } = this.state.target;
- const url = `${this.state.url}${this.state.target.value}`;
-
+ const url = `${this.state.uri}${this.state.target.value}`;
return (
- TREZOR Bridge.Version {this.state.version}
+ TREZOR Bridge.{this.state.currentVersion}
New communication tool to facilitate the connection between your TREZOR and your internet browser.
this.onChange(val)}
- options={installers}
+ onChange={v => this.onChange(v)}
+ options={this.state.installers}
/>
@@ -134,7 +135,7 @@ export default class InstallBridge extends Component {
color={colors.WHITE}
size={30}
/>
- Download for {label}
+ Download latest Bridge {this.state.latestVersion}
@@ -148,6 +149,14 @@ export default class InstallBridge extends Component {
>Changelog
+
+
+ No, i dont want to upgrade Bridge now,
+
+
+ Take me back to the wallet
+
+
);
}
diff --git a/src/views/Landing/index.js b/src/views/Landing/index.js
index 13ec9331..00b5facd 100644
--- a/src/views/Landing/index.js
+++ b/src/views/Landing/index.js
@@ -103,7 +103,7 @@ export default (props: Props) => {
{shouldShowUnsupportedBrowser && }
- {shouldShowInstallBridge && }
+ {shouldShowInstallBridge && }
{(shouldShowConnectDevice || shouldShowDisconnectDevice) && (
From 5a6dedb314e5cd811dab3e9b374a08e2a9ccc640 Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Thu, 4 Oct 2018 19:47:10 +0200
Subject: [PATCH 4/9] gotoBridgeUpdate link
---
src/actions/RouterActions.js | 14 +++++++++
.../App/components/UpdateBridge/index.js | 29 ++++++++++---------
src/components/notifications/App/index.js | 3 ++
3 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/src/actions/RouterActions.js b/src/actions/RouterActions.js
index 8bab4d55..a5ce0788 100644
--- a/src/actions/RouterActions.js
+++ b/src/actions/RouterActions.js
@@ -303,6 +303,20 @@ export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispat
}
};
+/*
+* Go to UpdateBridge page
+*/
+export const gotoBridgeUpdate = (): ThunkAction => (dispatch: Dispatch): void => {
+ dispatch(goto('/bridge'));
+};
+
+/*
+* Go to UpdateFirmware page
+*/
+export const gotoFirmwareUpdate = (): ThunkAction => (dispatch: Dispatch): void => {
+ dispatch(goto('/firmware-update'));
+};
+
/*
* Try to redirect to initial url
*/
diff --git a/src/components/notifications/App/components/UpdateBridge/index.js b/src/components/notifications/App/components/UpdateBridge/index.js
index 45aec38d..e418bb37 100644
--- a/src/components/notifications/App/components/UpdateBridge/index.js
+++ b/src/components/notifications/App/components/UpdateBridge/index.js
@@ -5,18 +5,19 @@ import { Notification } from 'components/Notification';
import type { Props } from '../../index';
export default (props: Props) => {
- return (
- {
-
- },
- }]
- }
- />
- );
+ if (props.connect.transport && props.connect.transport.outdated) {
+ return (
+
+ );
+ }
+ return null;
};
\ No newline at end of file
diff --git a/src/components/notifications/App/index.js b/src/components/notifications/App/index.js
index ffce183b..6167cfcf 100644
--- a/src/components/notifications/App/index.js
+++ b/src/components/notifications/App/index.js
@@ -7,6 +7,7 @@ import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from 'flowtype';
import * as NotificationActions from 'actions/NotificationActions';
+import * as RouterActions from 'actions/RouterActions';
import OnlineStatus from './components/OnlineStatus';
import UpdateBridge from './components/UpdateBridge';
@@ -20,6 +21,7 @@ export type StateProps = {
export type DispatchProps = {
close: typeof NotificationActions.close;
+ routerActions: typeof RouterActions;
}
export type Props = StateProps & DispatchProps;
@@ -41,6 +43,7 @@ const mapStateToProps: MapStateToProps = (state: St
const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
close: bindActionCreators(NotificationActions.close, dispatch),
+ routerActions: bindActionCreators(RouterActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Notifications);
\ No newline at end of file
From e9e55263a92e54ef42dc06d79cfec7049117051c Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Thu, 4 Oct 2018 22:56:50 +0200
Subject: [PATCH 5/9] update utils
---
src/reducers/utils/index.js | 5 +-
src/utils/device.js | 150 ++++++++++++++++++++----------------
2 files changed, 86 insertions(+), 69 deletions(-)
diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js
index 3d6a8de4..5de5aece 100644
--- a/src/reducers/utils/index.js
+++ b/src/reducers/utils/index.js
@@ -21,7 +21,7 @@ export const getSelectedDevice = (state: State): ?TrezorDevice => {
return state.devices.find((d) => {
if (!d.features && d.path === locationState.device) {
return true;
- } if (d.features && d.features.bootloader_mode && d.path === locationState.device) {
+ } if (d.mode === 'bootloader' && d.path === locationState.device) {
return true;
} if (d.features && d.features.device_id === locationState.device && d.instance === instance) {
return true;
@@ -30,9 +30,6 @@ export const getSelectedDevice = (state: State): ?TrezorDevice => {
});
};
-//
-export const isSelectedDevice = (current: ?TrezorDevice, device: ?TrezorDevice): boolean => !!((current && device && (current.path === device.path && current.instance === device.instance)));
-
// find device by id and state
export const findDevice = (devices: Array, deviceId: string, deviceState: string /*, instance: ?number*/): ?TrezorDevice => devices.find((d) => {
// TODO: && (instance && d.instance === instance)
diff --git a/src/utils/device.js b/src/utils/device.js
index a8b03564..94924325 100644
--- a/src/utils/device.js
+++ b/src/utils/device.js
@@ -1,64 +1,99 @@
+/* @flow */
+
import colors from 'config/colors';
-const getStatus = (device) => {
- let status = 'connected';
- if (device.features && device.features.bootloader_mode) {
- status = 'connected-bootloader';
- } else if (!device.connected) {
- status = 'disconnected';
- } else if (!device.available) {
- status = 'unavailable';
- } else if (device.type === 'acquired') {
- if (device.status === 'occupied') {
- status = 'used-in-other-window';
+import type {
+ TrezorDevice,
+ State,
+} from 'flowtype';
+
+type Transport = $ElementType<$ElementType, 'transport'>;
+
+export const getStatus = (device: TrezorDevice): string => {
+ if (!device.connected) {
+ return 'disconnected';
+ }
+ if (device.type === 'acquired') {
+ if (device.mode === 'bootloader') {
+ return 'bootloader';
}
- } else if (device.type === 'unacquired') {
- status = 'unacquired';
+ if (device.mode === 'initialize') {
+ return 'initialize';
+ }
+ if (device.firmware === 'required') {
+ return 'firmware-required';
+ }
+ if (device.status === 'occupied') {
+ return 'used-in-other-window';
+ }
+ if (device.status === 'used') {
+ return 'used-in-other-window';
+ }
+ if (device.firmware === 'outdated') {
+ return 'firmware-recommended';
+ }
+ return 'connected';
}
-
- return status;
+ if (!device.available) { // deprecated
+ return 'unavailable';
+ }
+ if (device.type === 'unacquired') {
+ return 'unacquired';
+ }
+ if (device.type === 'unreadable') {
+ return 'unreadable';
+ }
+ return 'unknown';
};
-const getStatusName = (deviceStatus) => {
- let statusName;
+export const getStatusName = (deviceStatus: string): string => {
switch (deviceStatus) {
- case 'used-in-other-window':
- statusName = 'Used in other window';
- break;
case 'connected':
- statusName = 'Connected';
- break;
- case 'connected-bootloader':
- statusName = 'Connected (bootloader mode)';
- break;
+ return 'Connected';
case 'disconnected':
- statusName = 'Disconnected';
- break;
+ return 'Disconnected';
+ case 'bootloader':
+ return 'Connected (bootloader mode)';
+ case 'initialize':
+ return 'Connected (not initialized)';
+ case 'firmware-required':
+ return 'Connected (update required)';
+ case 'firmware-recommended':
+ return 'Connected (update recommended)';
+ case 'used-in-other-window':
+ return 'Used in other window';
case 'unacquired':
- statusName = 'Used in other window';
- break;
+ return 'Used in other window';
case 'unavailable':
- statusName = 'Unavailable';
- break;
+ return 'Unavailable';
+ case 'unreadable':
+ return 'Unreadable';
default:
- statusName = 'Status unknown';
+ return 'Status unknown';
}
- return statusName;
};
-const isWebUSB = transport => !!((transport && transport.version.indexOf('webusb') >= 0));
+export const isWebUSB = (transport: Transport) => !!((transport.type && transport.version.indexOf('webusb') >= 0));
-const isDisabled = (selectedDevice, devices, transport) => {
+export const isDisabled = (selectedDevice: TrezorDevice, devices: Array, transport: Transport) => {
if (isWebUSB(transport)) return false; // always enabled if webusb
if (devices.length < 1) return true; // no devices
if (devices.length === 1) {
if (!selectedDevice.features) return true; // unacquired, unreadable
- if (selectedDevice.features.bootloader_mode || !selectedDevice.features.initialized) return true; // bootlader, not initialized
+ if (selectedDevice.mode !== 'normal') return true; // bootloader, not initialized
+ if (selectedDevice.firmware === 'required') return true; // bootloader, not initialized
}
return false; // default
};
-const getVersion = (device) => {
+export const isDeviceAccessible = (device: ?TrezorDevice): boolean => {
+ if (!device || !device.features) return false;
+ 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 getVersion = (device: TrezorDevice): string => {
let version;
if (device.features && device.features.major_version > 1) {
version = 'T';
@@ -68,38 +103,23 @@ const getVersion = (device) => {
return version;
};
-const getStatusColor = (deviceStatus) => {
- let color;
+export const getStatusColor = (deviceStatus: string): string => {
switch (deviceStatus) {
- case 'used-in-other-window':
- color = colors.WARNING_PRIMARY;
- break;
case 'connected':
- color = colors.GREEN_PRIMARY;
- break;
- case 'connected-bootloader':
- color = colors.WARNING_PRIMARY;
- break;
- case 'unacquired':
- color = colors.WARNING_PRIMARY;
- break;
+ return colors.GREEN_PRIMARY;
case 'disconnected':
- color = colors.ERROR_PRIMARY;
- break;
+ return colors.ERROR_PRIMARY;
+ case 'bootloader':
+ case 'initialize':
+ case 'firmware-recommended':
+ case 'used-in-other-window':
+ case 'unacquired':
+ return colors.WARNING_PRIMARY;
+ case 'firmware-required':
case 'unavailable':
- color = colors.ERROR_PRIMARY;
- break;
+ case 'unreadable':
+ return colors.ERROR_PRIMARY;
default:
- color = colors.TEXT_PRIMARY;
+ return colors.TEXT_PRIMARY;
}
- return color;
-};
-
-export {
- isWebUSB,
- getStatus,
- isDisabled,
- getStatusName,
- getVersion,
- getStatusColor,
};
\ No newline at end of file
From 09f819aacc1a26c46936faf656f60c36e2e862c2 Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Thu, 4 Oct 2018 22:57:03 +0200
Subject: [PATCH 6/9] update main flowtype
---
src/flowtype/index.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/flowtype/index.js b/src/flowtype/index.js
index 6c138e58..203033d1 100644
--- a/src/flowtype/index.js
+++ b/src/flowtype/index.js
@@ -38,6 +38,7 @@ import type {
Features,
DeviceStatus,
DeviceFirmwareStatus,
+ DeviceMode,
DeviceMessageType,
TransportMessageType,
BlockchainMessageType,
@@ -53,6 +54,7 @@ export type AcquiredDevice = $Exact<{
+features: Features,
+firmware: DeviceFirmwareStatus,
status: DeviceStatus,
+ +mode: DeviceMode,
state: ?string,
remember: boolean; // device should be remembered
From 566178ee3b3dee709e6a106f574e1cfed1973dd1 Mon Sep 17 00:00:00 2001
From: Szymon Lesisz
Date: Thu, 4 Oct 2018 22:59:39 +0200
Subject: [PATCH 7/9] refactor using device.mode and device.firmware
---
src/actions/RouterActions.js | 12 ++-
src/actions/TrezorConnectActions.js | 82 +++++++++----------
src/actions/WalletActions.js | 3 +-
src/components/DeviceHeader/index.js | 6 +-
.../DeviceMenu/components/DeviceList/index.js | 34 ++++----
.../DeviceMenu/components/MenuItems/index.js | 4 +-
.../components/DeviceMenu/index.js | 57 +++++++------
.../Wallet/components/LeftNavigation/index.js | 33 +++++---
8 files changed, 127 insertions(+), 104 deletions(-)
diff --git a/src/actions/RouterActions.js b/src/actions/RouterActions.js
index a5ce0788..1c1c91eb 100644
--- a/src/actions/RouterActions.js
+++ b/src/actions/RouterActions.js
@@ -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 =>
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 {
diff --git a/src/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js
index 7553edd4..b54a97a0 100644
--- a/src/actions/TrezorConnectActions.js
+++ b/src/actions/TrezorConnectActions.js
@@ -154,50 +154,50 @@ export const postInit = (): ThunkAction => (dispatch: Dispatch): void => {
export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => {
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 => {
diff --git a/src/actions/WalletActions.js b/src/actions/WalletActions.js
index 9d68a29d..a7325d25 100644
--- a/src/actions/WalletActions.js
+++ b/src/actions/WalletActions.js
@@ -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 {
@@ -123,7 +123,7 @@ const DeviceHeader = ({
{getStatusName(status)}
- {icon && !disabled && !isBootloader && icon}
+ {icon && !disabled && isAccessible && icon}
@@ -131,7 +131,7 @@ const DeviceHeader = ({
};
DeviceHeader.propTypes = {
- isBootloader: PropTypes.bool,
+ isAccessible: PropTypes.bool,
device: PropTypes.object,
icon: PropTypes.element,
isHoverable: PropTypes.bool,
diff --git a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/DeviceList/index.js b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/DeviceList/index.js
index 723e177b..e6c8baa9 100644
--- a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/DeviceList/index.js
+++ b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/DeviceList/index.js
@@ -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;
+ selectedDevice: $ElementType<$ElementType, 'selectedDevice'>;
+ onSelectDevice: $ElementType;
+ forgetDevice: $ElementType;
+};
+
+class DeviceList extends Component {
+ 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) && (
{
- 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);
\ No newline at end of file
+export default DeviceList;
\ No newline at end of file
diff --git a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js
index 7ca2c305..a52bb769 100644
--- a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js
+++ b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/components/MenuItems/index.js
@@ -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() {
diff --git a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js
index dc34884e..af12c149 100644
--- a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js
+++ b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js
@@ -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 {
- 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 {
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 {
}
}
- 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 {
forgetDevice={forgetDevice}
/>
- {isWebUSB(transport) && (
+ {deviceUtils.isWebUSB(transport) && (
Check for devices
)}
diff --git a/src/views/Wallet/components/LeftNavigation/index.js b/src/views/Wallet/components/LeftNavigation/index.js
index 2f2c87f5..db79af1f 100644
--- a/src/views/Wallet/components/LeftNavigation/index.js
+++ b/src/views/Wallet/components/LeftNavigation/index.js
@@ -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 {
@@ -119,11 +125,11 @@ class LeftNavigation extends React.PureComponent {
};
}
- 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 {
);
}
- const isDeviceInBootloader = this.props.wallet.selectedDevice.features && this.props.wallet.selectedDevice.features.bootloader_mode;
+ const { selectedDevice } = props.wallet;
+ const isDeviceAccessible = deviceUtils.isDeviceAccessible(selectedDevice);
return (
{
- 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={(
@@ -211,7 +218,7 @@ class LeftNavigation extends React.PureComponent {
/>
{this.state.shouldRenderDeviceSelection && }
- {!isDeviceInBootloader && menu}
+ {isDeviceAccessible && menu}