From 53ae6cf18826f929888499de0a5f9e2c92d3dbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 09:08:31 +0100 Subject: [PATCH 1/9] Update dependencies --- package.json | 4 ++-- yarn.lock | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 4c1cea42..bdbcee25 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,12 @@ "history": "^4.7.2", "i18next": "^10.2.2", "i18next-node-fs-backend": "^1.0.0", - "ledger-node-js-hid": "github:loeck/ledger-node-js-hid", + "ledger-node-js-hid": "github:LedgerHQ/ledger-node-js-hid", "ledgerco": "^1.2.1", "object-path": "^0.11.4", "react": "^16.2.0", "react-dom": "^16.2.0", - "react-i18next": "^7.3.0", + "react-i18next": "^7.3.1", "react-redux": "^5.0.6", "react-router": "^4.2.0", "react-router-dom": "^4.2.2", diff --git a/yarn.lock b/yarn.lock index b709c8dd..9a2f9d12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4315,9 +4315,9 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -"ledger-node-js-hid@github:loeck/ledger-node-js-hid": +"ledger-node-js-hid@github:LedgerHQ/ledger-node-js-hid": version "0.1.0" - resolved "git+ssh://git@github.com/loeck/ledger-node-js-hid.git#58642a9bb703d0d65cb847402e84ac677710ef92" + resolved "https://codeload.github.com/LedgerHQ/ledger-node-js-hid/tar.gz/7a422b99f5305b23dd0da8ea17cefb99821aa5a4" ledgerco@^1.2.1: version "1.2.1" @@ -5685,9 +5685,9 @@ react-hot-loader@^4.0.0-beta.12: redbox-react "^1.3.6" source-map "^0.6.1" -react-i18next@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-7.3.0.tgz#53de5afb6a3de53a0a9d09be7249c4748c0fc2db" +react-i18next@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-7.3.1.tgz#b0ca03db9cc4d4067b6481d09d902a5166d620a0" dependencies: hoist-non-react-statics "2.3.1" html-parse-stringify2 "2.0.1" From 797ceac6ba70fed0e9fb35d22e1c33e9adcd5c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 14:55:28 +0100 Subject: [PATCH 2/9] Add currentDevice in reducer devices --- src/actions/devices.js | 65 +++++++++++++++++++++++++++++-------- src/components/Home.js | 16 +++++++-- src/components/Wrapper.js | 31 ++++++------------ src/i18n/en/translation.yml | 2 +- src/main/app.js | 2 +- src/main/ledger.js | 18 ++++++---- src/reducers/devices.js | 43 ++++++++++++++++++++---- src/renderer/i18n.js | 5 +++ src/renderer/initEvents.js | 16 +++++---- src/types/common.js | 5 +++ 10 files changed, 144 insertions(+), 59 deletions(-) diff --git a/src/actions/devices.js b/src/actions/devices.js index d11081ec..3a8f2894 100644 --- a/src/actions/devices.js +++ b/src/actions/devices.js @@ -2,22 +2,59 @@ // eslint-disable import/prefer-default-export -import type { Device } from 'types/common' +import type { Dispatch } from 'redux' -type devicesUpdateType = (Array) => { type: string, payload: Array } -export const devicesUpdate: devicesUpdateType = payload => ({ - type: 'DEVICES_UPDATE', - payload, -}) +import { getDevices } from 'reducers/devices' -type devicesAddType = Device => { type: string, payload: Device } -export const deviceAdd: devicesAddType = payload => ({ - type: 'DEVICE_ADD', - payload, -}) +import type { Device, Devices } from 'types/common' -type devicesRemoveType = Device => { type: string, payload: Device } -export const deviceRemove: devicesRemoveType = payload => ({ - type: 'DEVICE_REMOVE', +type deviceChooseType = (?Device) => { type: string, payload: ?Device } +export const deviceChoose: deviceChooseType = payload => ({ + type: 'DEVICE_CHOOSE', payload, }) + +type deviceChooseFirstType = () => (Dispatch, () => { devices: Devices }) => void +export const deviceChooseFirst: deviceChooseFirstType = () => (dispatch, getState) => { + const devices = getDevices(getState()) + + // If we detect only 1 device, we choose it + if (devices.length === 1) { + dispatch(deviceChoose(devices[0])) + } +} + +type devicesAddType = Device => (Dispatch) => void +export const deviceAdd: devicesAddType = payload => dispatch => { + dispatch({ + type: 'DEVICE_ADD', + payload, + }) + + dispatch(deviceChooseFirst()) +} + +type devicesRemoveType = Device => (Dispatch, () => { devices: Devices }) => void +export const deviceRemove: devicesRemoveType = payload => (dispatch, getState) => { + dispatch({ + type: 'DEVICE_REMOVE', + payload, + }) + + const devices = getDevices(getState()) + + // If we don't detect any device we reset currentDevice + if (devices.length === 0) { + dispatch(deviceChoose(null)) + } +} + +type devicesUpdateType = Devices => (Dispatch) => void +export const devicesUpdate: devicesUpdateType = payload => dispatch => { + dispatch({ + type: 'DEVICES_UPDATE', + payload, + }) + + dispatch(deviceChooseFirst()) +} diff --git a/src/components/Home.js b/src/components/Home.js index b408f4ae..e7494a57 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -5,16 +5,26 @@ import { compose } from 'redux' import { connect } from 'react-redux' import { translate } from 'react-i18next' +import type { MapStateToProps } from 'react-redux' +import type { Devices, T } from 'types/common' + +import { getDevices } from 'reducers/devices' + type Props = { - devices: Array, - t: (string, ?Object) => string, + devices: Devices, + t: T, } +const mapStateToProps: MapStateToProps<*, *, *> = state => ({ + devices: getDevices(state), +}) + class Home extends PureComponent { render() { const { devices, t } = this.props + return
{t('common.connectedDevices', { count: devices.length })}
} } -export default compose(connect(({ devices }: Props): Object => ({ devices })), translate())(Home) +export default compose(connect(mapStateToProps), translate())(Home) diff --git a/src/components/Wrapper.js b/src/components/Wrapper.js index 92025554..902fc036 100644 --- a/src/components/Wrapper.js +++ b/src/components/Wrapper.js @@ -1,34 +1,23 @@ // @flow -import React, { Fragment } from 'react' -import { compose } from 'redux' -import { connect } from 'react-redux' +import React from 'react' import { Route } from 'react-router' import { translate } from 'react-i18next' import Box from 'components/base/Box' -import Overlay from 'components/base/Overlay' import Home from 'components/Home' import SideBar from 'components/SideBar' import TopBar from 'components/TopBar' -const Wrapper = ({ devices, t }: { devices: Array, t: string => string }) => ( - - {devices.length === 0 ? ( - - {t('common.connectDevice')} - - ) : ( - - - - - - - - )} - +const Wrapper = () => ( + + + + + + + ) -export default compose(connect(({ devices }): Object => ({ devices })), translate())(Wrapper) +export default translate()(Wrapper) diff --git a/src/i18n/en/translation.yml b/src/i18n/en/translation.yml index 60aebb64..7c818448 100644 --- a/src/i18n/en/translation.yml +++ b/src/i18n/en/translation.yml @@ -1,6 +1,6 @@ common: ok: Okay cancel: Cancel - connectDevice: Please connect your device connectedDevices: You have {{count}} device connected + connectedDevices_0: You don't have device connected connectedDevices_plural: You have {{count}} devices connected diff --git a/src/main/app.js b/src/main/app.js index 3a8e84e8..59226b37 100644 --- a/src/main/app.js +++ b/src/main/app.js @@ -35,7 +35,7 @@ function createMainWindow() { return window } - +// dsq // Quit application when all windows are closed app.on('window-all-closed', () => { // On macOS it is common for applications to stay open diff --git a/src/main/ledger.js b/src/main/ledger.js index 7d403af1..34d764ef 100644 --- a/src/main/ledger.js +++ b/src/main/ledger.js @@ -41,13 +41,17 @@ const handlers = { }, all: send => send('devices.update', HID.devices().filter(isLedgerDevice)), }, - requestWalletInfos: async (send, { path, wallet }) => { - try { - const publicKey = await getWalletInfos(path, wallet) - send('receiveWalletInfos', { path, publicKey }) - } catch (err) { - send('failWalletInfos', { path, err: err.stack }) - } + wallet: { + infos: { + request: async (send, { path, wallet }) => { + try { + const publicKey = await getWalletInfos(path, wallet) + send('wallet.infos.success', { path, publicKey }) + } catch (err) { + send('wallet.infos.fail', { path, err: err.stack || err }) + } + }, + }, }, } diff --git a/src/reducers/devices.js b/src/reducers/devices.js index 817a21f3..bb3097be 100644 --- a/src/reducers/devices.js +++ b/src/reducers/devices.js @@ -2,13 +2,44 @@ import { handleActions } from 'redux-actions' -const state = [] +import type { Device, Devices } from 'types/common' -const handlers = { - DEVICES_UPDATE: (state, { payload: devices }) => devices, - DEVICE_ADD: (state, { payload: device }) => - [...state, device].filter((v, i, s) => s.findIndex(t => t.path === v.path) === i), - DEVICE_REMOVE: (state, { payload: device }) => state.filter(d => d.path !== device.path), +type stateType = { + currentDevice: ?Device, + devices: Devices, +} +const state = { + currentDevice: null, + devices: [], +} + +const handlers: Object = { + DEVICES_UPDATE: (state: stateType, { payload: devices }: { payload: Devices }) => ({ + ...state, + devices, + }), + DEVICE_ADD: (state: stateType, { payload: device }: { payload: Device }) => ({ + ...state, + devices: [...state.devices, device].filter( + (v, i, s) => s.findIndex(t => t.path === v.path) === i, + ), + }), + DEVICE_REMOVE: (state: stateType, { payload: device }: { payload: Device }) => ({ + ...state, + devices: state.devices.filter(d => d.path !== device.path), + }), + DEVICE_CHOOSE: (state: stateType, { payload: currentDevice }: { payload: Device }) => ({ + ...state, + currentDevice, + }), +} + +export function getCurrentDevice(state: Object) { + return state.devices.currentDevice +} + +export function getDevices(state: Object) { + return state.devices.devices } export default handleActions(handlers, state) diff --git a/src/renderer/i18n.js b/src/renderer/i18n.js index 82f635e3..39129a61 100644 --- a/src/renderer/i18n.js +++ b/src/renderer/i18n.js @@ -16,4 +16,9 @@ i18n.use(Backend).init({ }, }) +i18n.services.pluralResolver.addRule('en', { + numbers: [0, 1, 'plural'], + plurals: n => Number(n >= 2 ? 2 : n), +}) + export default i18n diff --git a/src/renderer/initEvents.js b/src/renderer/initEvents.js index ad8995ad..129388bb 100644 --- a/src/renderer/initEvents.js +++ b/src/renderer/initEvents.js @@ -23,7 +23,7 @@ export default (store: Object) => { update: devices => { store.dispatch(devicesUpdate(devices)) if (devices.length) { - send('requestWalletInfos', { + send('wallet.infos.request', { path: devices[0].path, wallet: 'btc', }) @@ -34,11 +34,15 @@ export default (store: Object) => { add: device => store.dispatch(deviceAdd(device)), remove: device => store.dispatch(deviceRemove(device)), }, - receiveWalletInfos: ({ path, publicKey }) => { - console.log({ path, publicKey }) - }, - failWalletInfos: ({ path, err }) => { - console.log({ path, err }) + wallet: { + infos: { + success: ({ path, publicKey }) => { + console.log({ path, publicKey }) + }, + fail: ({ path, err }) => { + console.log({ path, err }) + }, + }, }, } diff --git a/src/types/common.js b/src/types/common.js index 24f905de..80c1c1bb 100644 --- a/src/types/common.js +++ b/src/types/common.js @@ -3,4 +3,9 @@ export type Device = { vendorId: string, productId: string, + path: string, } + +export type Devices = Array + +export type T = (string, ?Object) => string From 2332855e7c97e2f4cc68b1ef60dfc3448e973224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 16:11:06 +0100 Subject: [PATCH 3/9] Add device in TopBar --- src/actions/devices.js | 10 +-- src/components/Home.js | 28 +++++---- src/components/TopBar.js | 107 +++++++++++++++++++++++++++++++-- src/components/base/Overlay.js | 4 +- 4 files changed, 124 insertions(+), 25 deletions(-) diff --git a/src/actions/devices.js b/src/actions/devices.js index 3a8f2894..6df2ebd3 100644 --- a/src/actions/devices.js +++ b/src/actions/devices.js @@ -4,11 +4,11 @@ import type { Dispatch } from 'redux' -import { getDevices } from 'reducers/devices' +import { getDevices, getCurrentDevice } from 'reducers/devices' import type { Device, Devices } from 'types/common' -type deviceChooseType = (?Device) => { type: string, payload: ?Device } +export type deviceChooseType = (?Device) => { type: string, payload: ?Device } export const deviceChoose: deviceChooseType = payload => ({ type: 'DEVICE_CHOOSE', payload, @@ -41,10 +41,10 @@ export const deviceRemove: devicesRemoveType = payload => (dispatch, getState) = payload, }) - const devices = getDevices(getState()) + const currentDevice = getCurrentDevice(getState()) - // If we don't detect any device we reset currentDevice - if (devices.length === 0) { + // If we disconnect the currentDevice we reset it + if (currentDevice.path === payload.path) { dispatch(deviceChoose(null)) } } diff --git a/src/components/Home.js b/src/components/Home.js index e7494a57..0d37bf87 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -1,30 +1,32 @@ // @flow import React, { PureComponent } from 'react' -import { compose } from 'redux' import { connect } from 'react-redux' -import { translate } from 'react-i18next' import type { MapStateToProps } from 'react-redux' -import type { Devices, T } from 'types/common' +import type { Device } from 'types/common' -import { getDevices } from 'reducers/devices' +import { getCurrentDevice } from 'reducers/devices' -type Props = { - devices: Devices, - t: T, -} +import Box from 'components/base/Box' const mapStateToProps: MapStateToProps<*, *, *> = state => ({ - devices: getDevices(state), + currentDevice: getCurrentDevice(state), }) +type Props = { + currentDevice: Device | null, +} + class Home extends PureComponent { render() { - const { devices, t } = this.props - - return
{t('common.connectedDevices', { count: devices.length })}
+ const { currentDevice } = this.props + return currentDevice !== null ? ( + + Your current device: {currentDevice.path} + + ) : null } } -export default compose(connect(mapStateToProps), translate())(Home) +export default connect(mapStateToProps)(Home) diff --git a/src/components/TopBar.js b/src/components/TopBar.js index 06ec7f34..dc2f8aa6 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -1,17 +1,112 @@ // @flow -import React, { PureComponent } from 'react' +import React, { PureComponent, Fragment } from 'react' +import { connect } from 'react-redux' + +import type { MapStateToProps, MapDispatchToProps } from 'react-redux' +import type { Device, Devices } from 'types/common' +import type { deviceChooseType } from 'actions/devices' + +import { getDevices, getCurrentDevice } from 'reducers/devices' + +import { deviceChoose } from 'actions/devices' import Box from 'components/base/Box' +import Overlay from 'components/base/Overlay' + +const mapStateToProps: MapStateToProps<*, *, *> = state => ({ + devices: getDevices(state), + currentDevice: getCurrentDevice(state), +}) + +const mapDispatchToProps: MapDispatchToProps<*, *, *> = { + deviceChoose, +} + +type Props = { + devices: Devices, + currentDevice: Device | null, + deviceChoose: deviceChooseType, +} + +type State = { + changeDevice: boolean, +} + +const hasDevices = props => props.currentDevice === null && props.devices.length > 0 + +class TopBar extends PureComponent { + state = { + changeDevice: hasDevices(this.props), + } + + componentWillReceiveProps(nextProps) { + if (hasDevices(nextProps) && this.props.currentDevice !== null) { + this.setState({ + changeDevice: true, + }) + } + } + + handleChangeDevice = () => + this.setState({ + changeDevice: true, + }) + + handleSelectDevice = device => () => { + const { deviceChoose } = this.props + + deviceChoose(device) + + this.setState({ + changeDevice: false, + }) + } -class TopBar extends PureComponent<{}> { render() { + const { devices } = this.props + const { changeDevice } = this.state + return ( - - {''} - + + {changeDevice && ( + + {devices.map(device => ( + + {device.path} + + ))} + + )} + + + + ) } } -export default TopBar +const CountDevices = ({ count, onChangeDevice } = { count: Number, onChangeDevice: Function }) => ( + + + + + {count} + +) + +const DeviceIcon = props => ( + + + +) + +export default connect(mapStateToProps, mapDispatchToProps)(TopBar) diff --git a/src/components/base/Overlay.js b/src/components/base/Overlay.js index d0e01fed..93be10ad 100644 --- a/src/components/base/Overlay.js +++ b/src/components/base/Overlay.js @@ -3,10 +3,12 @@ import React from 'react' import styled from 'styled-components' +import { rgba } from 'styles/helpers' + import Box from 'components/base/Box' const Overlay = styled(({ sticky, ...props }) => )` - background-color: ${p => p.theme.colors.night}; + background-color: ${p => rgba(p.theme.colors.night, 0.4)}; position: fixed; ` From 16aac7c82da8a82a30cb9b30f3885a97471b4638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 16:28:27 +0100 Subject: [PATCH 4/9] Change Device type --- package.json | 2 +- src/actions/devices.js | 2 +- src/components/Home.js | 2 +- src/components/TopBar.js | 2 +- src/types/common.js | 2 +- yarn.lock | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index bdbcee25..967ae22a 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "eslint-plugin-react": "^7.5.1", "flow-bin": "^0.63.1", "flow-typed": "^2.2.3", - "prettier": "^1.9.2", + "prettier": "^1.10.1", "react-hot-loader": "^4.0.0-beta.12" } } diff --git a/src/actions/devices.js b/src/actions/devices.js index 6df2ebd3..d0c51682 100644 --- a/src/actions/devices.js +++ b/src/actions/devices.js @@ -8,7 +8,7 @@ import { getDevices, getCurrentDevice } from 'reducers/devices' import type { Device, Devices } from 'types/common' -export type deviceChooseType = (?Device) => { type: string, payload: ?Device } +export type deviceChooseType = Device => { type: string, payload: Device } export const deviceChoose: deviceChooseType = payload => ({ type: 'DEVICE_CHOOSE', payload, diff --git a/src/components/Home.js b/src/components/Home.js index 0d37bf87..7c63d050 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -15,7 +15,7 @@ const mapStateToProps: MapStateToProps<*, *, *> = state => ({ }) type Props = { - currentDevice: Device | null, + currentDevice: Device, } class Home extends PureComponent { diff --git a/src/components/TopBar.js b/src/components/TopBar.js index dc2f8aa6..d9aafb4c 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -25,7 +25,7 @@ const mapDispatchToProps: MapDispatchToProps<*, *, *> = { type Props = { devices: Devices, - currentDevice: Device | null, + currentDevice: Device, deviceChoose: deviceChooseType, } diff --git a/src/types/common.js b/src/types/common.js index 80c1c1bb..2ae93e8c 100644 --- a/src/types/common.js +++ b/src/types/common.js @@ -4,7 +4,7 @@ export type Device = { vendorId: string, productId: string, path: string, -} +} | null export type Devices = Array diff --git a/yarn.lock b/yarn.lock index 9a2f9d12..e86614d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5487,9 +5487,9 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.9.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.9.2.tgz#96bc2132f7a32338e6078aeb29727178c6335827" +prettier@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.1.tgz#01423fea6957ea23618d37d339ef0e7f7c967fc6" pretty-bytes@^1.0.2: version "1.0.4" From 562cc7930455a7d438b54a02c90e864e135e56ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 18:50:10 +0100 Subject: [PATCH 5/9] Create bridge for usb side --- src/actions/devices.js | 2 +- src/components/Home.js | 2 +- src/components/TopBar.js | 22 +++++++++++++----- src/main/bridge.js | 29 ++++++++++++++++++++++++ src/main/index.js | 2 +- src/main/{ledger.js => usb.js} | 41 +++++++++++++++------------------- src/styles/global.js | 1 - src/types/common.js | 2 +- 8 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 src/main/bridge.js rename src/main/{ledger.js => usb.js} (58%) diff --git a/src/actions/devices.js b/src/actions/devices.js index d0c51682..79173a60 100644 --- a/src/actions/devices.js +++ b/src/actions/devices.js @@ -8,7 +8,7 @@ import { getDevices, getCurrentDevice } from 'reducers/devices' import type { Device, Devices } from 'types/common' -export type deviceChooseType = Device => { type: string, payload: Device } +export type deviceChooseType = (Device | null) => { type: string, payload: Device | null } export const deviceChoose: deviceChooseType = payload => ({ type: 'DEVICE_CHOOSE', payload, diff --git a/src/components/Home.js b/src/components/Home.js index 7c63d050..0d37bf87 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -15,7 +15,7 @@ const mapStateToProps: MapStateToProps<*, *, *> = state => ({ }) type Props = { - currentDevice: Device, + currentDevice: Device | null, } class Home extends PureComponent { diff --git a/src/components/TopBar.js b/src/components/TopBar.js index d9aafb4c..80ad0f9b 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -48,10 +48,15 @@ class TopBar extends PureComponent { } } - handleChangeDevice = () => - this.setState({ - changeDevice: true, - }) + handleChangeDevice = () => { + const { devices } = this.props + + if (devices.length > 0) { + this.setState({ + changeDevice: true, + }) + } + } handleSelectDevice = device => () => { const { deviceChoose } = this.props @@ -92,7 +97,14 @@ class TopBar extends PureComponent { } const CountDevices = ({ count, onChangeDevice } = { count: Number, onChangeDevice: Function }) => ( - + diff --git a/src/main/bridge.js b/src/main/bridge.js new file mode 100644 index 00000000..31bcb0a2 --- /dev/null +++ b/src/main/bridge.js @@ -0,0 +1,29 @@ +// @flow + +import { fork } from 'child_process' +import { ipcMain } from 'electron' +import { resolve } from 'path' + +ipcMain.on('msg', (event: any, payload) => { + const { type, data } = payload + + const compute = fork('./usb', { + cwd: resolve(__dirname, './'), + }) + + const send = (msgType, data) => { + event.sender.send('msg', { + type: msgType, + data, + }) + } + + compute.send([type, data]) + compute.on('message', payload => { + const [type, data, options = {}] = payload + send(type, data) + if (options.kill) { + compute.kill() + } + }) +}) diff --git a/src/main/index.js b/src/main/index.js index ac1236a2..9ad87886 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,5 +1,5 @@ // @flow require('../globals') -require('./ledger') +require('./bridge') require('./app') diff --git a/src/main/ledger.js b/src/main/usb.js similarity index 58% rename from src/main/ledger.js rename to src/main/usb.js index 34d764ef..e2ddf6ac 100644 --- a/src/main/ledger.js +++ b/src/main/usb.js @@ -1,15 +1,17 @@ -// @flow +process.title = 'ledger-wallet-desktop-usb' -import { ipcMain } from 'electron' -import { isLedgerDevice } from 'ledgerco/lib/utils' -import ledgerco, { comm_node } from 'ledgerco' -import objectPath from 'object-path' +const HID = require('ledger-node-js-hid') +const objectPath = require('object-path') +const { isLedgerDevice } = require('ledgerco/lib/utils') +const ledgerco = require('ledgerco') -import HID from 'ledger-node-js-hid' +function send(type, data, options = { kill: true }) { + process.send([type, data, options]) +} -async function getWalletInfos(path: string, wallet: string) { +async function getWalletInfos(path, wallet) { if (wallet === 'btc') { - const comm = new comm_node(new HID.HID(path), true, 0, false) + const comm = new ledgerco.comm_node(new HID.HID(path), true, 0, false) const btc = new ledgerco.btc(comm) const walletInfos = await btc.getWalletPublicKey_async("44'/0'/0'/0") return walletInfos @@ -21,7 +23,7 @@ let isListenDevices = false const handlers = { devices: { - listen: send => { + listen: () => { if (isListenDevices) { return } @@ -32,18 +34,18 @@ const handlers = { HID.listenDevices.events.on( 'add', - device => isLedgerDevice(device) && send('device.add', device), + device => isLedgerDevice(device) && send('device.add', device, { kill: false }), ) HID.listenDevices.events.on( 'remove', - device => isLedgerDevice(device) && send('device.remove', device), + device => isLedgerDevice(device) && send('device.remove', device, { kill: false }), ) }, - all: send => send('devices.update', HID.devices().filter(isLedgerDevice)), + all: () => send('devices.update', HID.devices().filter(isLedgerDevice)), }, wallet: { infos: { - request: async (send, { path, wallet }) => { + request: async ({ path, wallet }) => { try { const publicKey = await getWalletInfos(path, wallet) send('wallet.infos.success', { path, publicKey }) @@ -55,20 +57,13 @@ const handlers = { }, } -ipcMain.on('msg', (event: *, payload) => { - const { type, data } = payload +process.on('message', payload => { + const [type, data] = payload const handler = objectPath.get(handlers, type) if (!handler) { return } - const send = (msgType: string, data: *) => { - event.sender.send('msg', { - type: msgType, - data, - }) - } - - handler(send, data) + handler(data) }) diff --git a/src/styles/global.js b/src/styles/global.js index d22f3d22..7c4bb783 100644 --- a/src/styles/global.js +++ b/src/styles/global.js @@ -11,7 +11,6 @@ injectGlobal` font: inherit; color: inherit; user-select: none; - cursor: default; min-width: 0; } diff --git a/src/types/common.js b/src/types/common.js index 2ae93e8c..80c1c1bb 100644 --- a/src/types/common.js +++ b/src/types/common.js @@ -4,7 +4,7 @@ export type Device = { vendorId: string, productId: string, path: string, -} | null +} export type Devices = Array From f0e8c0fd3dcefed93ab2622b71658dcd5c76c1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 18:52:53 +0100 Subject: [PATCH 6/9] Refacto --- src/main/usb.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/usb.js b/src/main/usb.js index e2ddf6ac..7459a8a3 100644 --- a/src/main/usb.js +++ b/src/main/usb.js @@ -30,16 +30,13 @@ const handlers = { isListenDevices = true + const handleChangeDevice = (device, event) => + isLedgerDevice(device) && send(event, device, { kill: false }) + HID.listenDevices.start() - HID.listenDevices.events.on( - 'add', - device => isLedgerDevice(device) && send('device.add', device, { kill: false }), - ) - HID.listenDevices.events.on( - 'remove', - device => isLedgerDevice(device) && send('device.remove', device, { kill: false }), - ) + HID.listenDevices.events.on('add', handleChangeDevice) + HID.listenDevices.events.on('remove', handleChangeDevice) }, all: () => send('devices.update', HID.devices().filter(isLedgerDevice)), }, From 679bac65635c163663c4097606e4b986154d3599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Wed, 10 Jan 2018 18:55:53 +0100 Subject: [PATCH 7/9] Add process name --- src/main/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/index.js b/src/main/index.js index 9ad87886..773bfdd0 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,5 +1,8 @@ // @flow +process.title = 'ledger-wallet-desktop' + require('../globals') + require('./bridge') require('./app') From 335de0966a478d5e9bb1a4e7d9d0c1f4cdcf8aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Thu, 11 Jan 2018 09:18:32 +0100 Subject: [PATCH 8/9] Clean USB bridge --- package.json | 4 ++-- src/main/bridge.js | 4 ++-- src/main/index.js | 3 --- src/main/usb.js | 12 ++++++------ yarn.lock | 17 +++++++++++------ 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 967ae22a..10113d6c 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "electron-builder": "^19.49.0", - "electron-rebuild": "^1.6.0", + "electron-rebuild": "^1.6.1", "electron-webpack": "1.11.0", "eslint": "^4.13.1", "eslint-config-airbnb": "^16.1.0", @@ -69,7 +69,7 @@ "eslint-plugin-react": "^7.5.1", "flow-bin": "^0.63.1", "flow-typed": "^2.2.3", - "prettier": "^1.10.1", + "prettier": "^1.10.2", "react-hot-loader": "^4.0.0-beta.12" } } diff --git a/src/main/bridge.js b/src/main/bridge.js index 31bcb0a2..d2be2236 100644 --- a/src/main/bridge.js +++ b/src/main/bridge.js @@ -18,9 +18,9 @@ ipcMain.on('msg', (event: any, payload) => { }) } - compute.send([type, data]) + compute.send({ type, data }) compute.on('message', payload => { - const [type, data, options = {}] = payload + const { type, data, options = {} } = payload send(type, data) if (options.kill) { compute.kill() diff --git a/src/main/index.js b/src/main/index.js index 773bfdd0..9ad87886 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,8 +1,5 @@ // @flow -process.title = 'ledger-wallet-desktop' - require('../globals') - require('./bridge') require('./app') diff --git a/src/main/usb.js b/src/main/usb.js index 7459a8a3..194b61fb 100644 --- a/src/main/usb.js +++ b/src/main/usb.js @@ -6,7 +6,7 @@ const { isLedgerDevice } = require('ledgerco/lib/utils') const ledgerco = require('ledgerco') function send(type, data, options = { kill: true }) { - process.send([type, data, options]) + process.send({ type, data, options }) } async function getWalletInfos(path, wallet) { @@ -30,13 +30,13 @@ const handlers = { isListenDevices = true - const handleChangeDevice = (device, event) => - isLedgerDevice(device) && send(event, device, { kill: false }) + const handleChangeDevice = eventName => device => + isLedgerDevice(device) && send(eventName, device, { kill: false }) HID.listenDevices.start() - HID.listenDevices.events.on('add', handleChangeDevice) - HID.listenDevices.events.on('remove', handleChangeDevice) + HID.listenDevices.events.on('add', handleChangeDevice('device.add')) + HID.listenDevices.events.on('remove', handleChangeDevice('device.remove')) }, all: () => send('devices.update', HID.devices().filter(isLedgerDevice)), }, @@ -55,7 +55,7 @@ const handlers = { } process.on('message', payload => { - const [type, data] = payload + const { type, data } = payload const handler = objectPath.get(handlers, type) if (!handler) { diff --git a/yarn.lock b/yarn.lock index e86614d8..e4b8cdc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2263,6 +2263,10 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + detect-node@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" @@ -2518,12 +2522,13 @@ electron-publish@19.52.0: fs-extra-p "^4.5.0" mime "^2.1.0" -electron-rebuild@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.6.0.tgz#e8d26f4d8e9fe5388df35864b3658e5cfd4dcb7e" +electron-rebuild@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.6.1.tgz#3c7ab64db31e5e78ef76fedd7a53aec087b723c5" dependencies: colors "^1.1.2" debug "^2.6.3" + detect-libc "^1.0.3" fs-extra "^3.0.1" node-abi "^2.0.0" node-gyp "^3.6.0" @@ -5487,9 +5492,9 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.1.tgz#01423fea6957ea23618d37d339ef0e7f7c967fc6" +prettier@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93" pretty-bytes@^1.0.2: version "1.0.4" From c40f4095751282cd506ec16ff6d84911164b1b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Thu, 11 Jan 2018 09:50:42 +0100 Subject: [PATCH 9/9] Clean actions/devices --- src/actions/devices.js | 61 ++++++++++------------------------------- src/main/app.js | 2 +- src/reducers/devices.js | 35 +++++++++++++++-------- 3 files changed, 40 insertions(+), 58 deletions(-) diff --git a/src/actions/devices.js b/src/actions/devices.js index 79173a60..c5af4ad5 100644 --- a/src/actions/devices.js +++ b/src/actions/devices.js @@ -2,10 +2,6 @@ // eslint-disable import/prefer-default-export -import type { Dispatch } from 'redux' - -import { getDevices, getCurrentDevice } from 'reducers/devices' - import type { Device, Devices } from 'types/common' export type deviceChooseType = (Device | null) => { type: string, payload: Device | null } @@ -14,47 +10,20 @@ export const deviceChoose: deviceChooseType = payload => ({ payload, }) -type deviceChooseFirstType = () => (Dispatch, () => { devices: Devices }) => void -export const deviceChooseFirst: deviceChooseFirstType = () => (dispatch, getState) => { - const devices = getDevices(getState()) - - // If we detect only 1 device, we choose it - if (devices.length === 1) { - dispatch(deviceChoose(devices[0])) - } -} - -type devicesAddType = Device => (Dispatch) => void -export const deviceAdd: devicesAddType = payload => dispatch => { - dispatch({ - type: 'DEVICE_ADD', - payload, - }) - - dispatch(deviceChooseFirst()) -} - -type devicesRemoveType = Device => (Dispatch, () => { devices: Devices }) => void -export const deviceRemove: devicesRemoveType = payload => (dispatch, getState) => { - dispatch({ - type: 'DEVICE_REMOVE', - payload, - }) - - const currentDevice = getCurrentDevice(getState()) - - // If we disconnect the currentDevice we reset it - if (currentDevice.path === payload.path) { - dispatch(deviceChoose(null)) - } -} +type devicesAddType = Device => { type: string, payload: Device } +export const deviceAdd: devicesAddType = payload => ({ + type: 'DEVICE_ADD', + payload, +}) -type devicesUpdateType = Devices => (Dispatch) => void -export const devicesUpdate: devicesUpdateType = payload => dispatch => { - dispatch({ - type: 'DEVICES_UPDATE', - payload, - }) +type devicesRemoveType = Device => { type: string, payload: Device } +export const deviceRemove: devicesRemoveType = payload => ({ + type: 'DEVICE_REMOVE', + payload, +}) - dispatch(deviceChooseFirst()) -} +type devicesUpdateType = Devices => { type: string, payload: Devices } +export const devicesUpdate: devicesUpdateType = payload => ({ + type: 'DEVICES_UPDATE', + payload, +}) diff --git a/src/main/app.js b/src/main/app.js index 59226b37..3a8e84e8 100644 --- a/src/main/app.js +++ b/src/main/app.js @@ -35,7 +35,7 @@ function createMainWindow() { return window } -// dsq + // Quit application when all windows are closed app.on('window-all-closed', () => { // On macOS it is common for applications to stay open diff --git a/src/reducers/devices.js b/src/reducers/devices.js index bb3097be..d74c0c17 100644 --- a/src/reducers/devices.js +++ b/src/reducers/devices.js @@ -5,7 +5,7 @@ import { handleActions } from 'redux-actions' import type { Device, Devices } from 'types/common' type stateType = { - currentDevice: ?Device, + currentDevice: Device | null, devices: Devices, } const state = { @@ -13,19 +13,32 @@ const state = { devices: [], } -const handlers: Object = { - DEVICES_UPDATE: (state: stateType, { payload: devices }: { payload: Devices }) => ({ - ...state, - devices, - }), - DEVICE_ADD: (state: stateType, { payload: device }: { payload: Device }) => ({ +function setCurrentDevice(state) { + return { ...state, - devices: [...state.devices, device].filter( - (v, i, s) => s.findIndex(t => t.path === v.path) === i, - ), - }), + currentDevice: state.devices.length === 1 ? state.devices[0] : state.currentDevice, + } +} + +const handlers: Object = { + DEVICES_UPDATE: (state: stateType, { payload: devices }: { payload: Devices }) => + setCurrentDevice({ + ...state, + devices, + }), + DEVICE_ADD: (state: stateType, { payload: device }: { payload: Device }) => + setCurrentDevice({ + ...state, + devices: [...state.devices, device].filter( + (v, i, s) => s.findIndex(t => t.path === v.path) === i, + ), + }), DEVICE_REMOVE: (state: stateType, { payload: device }: { payload: Device }) => ({ ...state, + currentDevice: + state.currentDevice !== null && state.currentDevice.path === device.path + ? null + : state.currentDevice, devices: state.devices.filter(d => d.path !== device.path), }), DEVICE_CHOOSE: (state: stateType, { payload: currentDevice }: { payload: Device }) => ({