Browse Source

Create DeviceMonit component and use it in SendModal

master
meriadec 7 years ago
parent
commit
62466e3424
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 111
      src/components/DeviceMonit/index.js
  2. 2
      src/components/base/Button/index.js
  3. 34
      src/components/modals/Send/02-step-connect-device.js
  4. 5
      src/components/modals/Send/Footer.js
  5. 31
      src/components/modals/Send/index.js
  6. 25
      src/internals/usb/wallet/index.js
  7. 6
      src/reducers/devices.js

111
src/components/DeviceMonit/index.js

@ -0,0 +1,111 @@
// @flow
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { ipcRenderer } from 'electron'
import { sendEvent } from 'renderer/events'
import { getCurrentDevice } from 'reducers/devices'
import type { Device, Account } from 'types/common'
const mapStateToProps = state => ({
currentDevice: getCurrentDevice(state),
})
type DeviceStatus = 'unconnected' | 'connected' | 'appOpened'
type Props = {
currentDevice: Device | null,
account: Account,
onStatusChange: DeviceStatus => void,
}
type State = {
status: DeviceStatus,
}
class DeviceMonit extends PureComponent<Props, State> {
state = {
status: this.props.currentDevice ? 'connected' : 'unconnected',
}
componentDidMount() {
ipcRenderer.on('msg', this.handleMsgEvent)
if (this.props.currentDevice !== null) {
this.checkAppOpened()
}
}
componentWillReceiveProps(nextProps) {
const { status } = this.state
const { currentDevice } = this.props
const { currentDevice: nextCurrentDevice } = nextProps
if (status === 'unconnected' && !currentDevice && nextCurrentDevice) {
this.handleStatusChange('connected')
}
if (status !== 'unconnected' && !nextCurrentDevice) {
this.handleStatusChange('unconnected')
}
}
componentDidUpdate() {
const { currentDevice } = this.props
if (currentDevice !== null) {
this.checkAppOpened()
} else {
clearTimeout(this._timeout)
}
}
componentWillUnmount() {
ipcRenderer.removeListener('msg', this.handleMsgEvent)
clearTimeout(this._timeout)
}
checkAppOpened = () => {
const { currentDevice, account } = this.props
if (currentDevice === null || account.currency === null) {
return
}
sendEvent('usb', 'wallet.checkIfAppOpened', {
devicePath: currentDevice.path,
accountPath: account.path,
accountAddress: account.address,
})
}
_timeout: any = null
handleStatusChange = status => {
this.setState({ status })
this.props.onStatusChange(status)
}
handleMsgEvent = (e, { type }) => {
if (type === 'wallet.checkIfAppOpened.success') {
this.handleStatusChange('appOpened')
clearTimeout(this._timeout)
}
if (type === 'wallet.checkIfAppOpened.fail') {
this._timeout = setTimeout(this.checkAppOpened, 1e3)
}
}
render() {
const { status } = this.state
return (
<div>
<div>device connected {status !== 'unconnected' ? 'TRUE' : 'FALSE'}</div>
<div>app opened {status === 'appOpened' ? 'TRUE' : 'FALSE'}</div>
</div>
)
}
}
export default connect(mapStateToProps)(DeviceMonit)

2
src/components/base/Button/index.js

@ -27,7 +27,7 @@ const Base = styled.button.attrs({
outline: none;
&:hover {
background: ${p => (p.primary ? lighten(p.theme.colors.wallet, 0.05) : '')};
background: ${p => (p.disabled ? '' : p.primary ? lighten(p.theme.colors.wallet, 0.05) : '')};
}
&:active {

34
src/components/modals/Send/02-step-connect-device.js

@ -1,7 +1,37 @@
// @flow
import React from 'react'
function StepConnectDevice() {
return <div>step connect device</div>
import DeviceMonit from 'components/DeviceMonit'
import type { Account } from 'types/common'
type Props = {
account: Account | null,
isDeviceReady: boolean,
onChange: Function,
}
function StepConnectDevice(props: Props) {
const { account, isDeviceReady, onChange } = props
const setReady = onChange('isDeviceReady')
if (!account) {
return null
}
return (
<div>
<DeviceMonit
account={account}
onStatusChange={status => {
if (status === 'appOpened' && !isDeviceReady) {
setReady(true)
}
if (status !== 'appOpened' && isDeviceReady) {
setReady(false)
}
}}
/>
</div>
)
}
export default StepConnectDevice

5
src/components/modals/Send/Footer.js

@ -17,9 +17,10 @@ type Props = {
account: Account,
amount: DoubleVal,
onNext: Function,
canNext: boolean,
}
function Footer({ account, amount, t, onNext }: Props) {
function Footer({ account, amount, t, onNext, canNext }: Props) {
return (
<ModalFooter horizontal align="center">
<Box grow>
@ -42,7 +43,7 @@ function Footer({ account, amount, t, onNext }: Props) {
</Box>
</Box>
</Box>
<Button primary onClick={onNext}>
<Button primary onClick={onNext} disabled={!canNext}>
{'Next'}
</Button>
</ModalFooter>

31
src/components/modals/Send/index.js

@ -25,6 +25,7 @@ type Props = {
type State = {
stepIndex: number,
isDeviceReady: boolean,
amount: DoubleVal,
account: Account | null,
recipientAddress: string,
@ -40,6 +41,7 @@ const GET_STEPS = t => [
const INITIAL_STATE = {
stepIndex: 0,
isDeviceReady: false,
account: null,
recipientAddress: '',
amount: {
@ -54,6 +56,24 @@ class SendModal extends PureComponent<Props, State> {
_steps = GET_STEPS(this.props.t)
canNext = account => {
const { stepIndex } = this.state
// informations
if (stepIndex === 0) {
const { amount, recipientAddress } = this.state
return !!amount.left && !!recipientAddress && !!account
}
// connect device
if (stepIndex === 1) {
const { isDeviceReady } = this.state
return !!isDeviceReady
}
return false
}
handleReset = () => this.setState(INITIAL_STATE)
handleNextStep = () => {
@ -90,6 +110,7 @@ class SendModal extends PureComponent<Props, State> {
onHide={this.handleReset}
render={({ data, onClose }) => {
const acc = account || get(data, 'account', null)
const canNext = this.canNext(acc)
return (
<ModalBody onClose={onClose}>
<ModalTitle>{t('send:title')}</ModalTitle>
@ -97,7 +118,15 @@ class SendModal extends PureComponent<Props, State> {
<Breadcrumb mb={6} mt={2} currentStep={stepIndex} items={this._steps} />
{this.renderStep(acc)}
</ModalContent>
{acc && <Footer onNext={this.handleNextStep} account={acc} amount={amount} t={t} />}
{acc && (
<Footer
canNext={canNext}
onNext={this.handleNextStep}
account={acc}
amount={amount}
t={t}
/>
)}
</ModalBody>
)
}}

25
src/internals/usb/wallet/index.js

@ -1,6 +1,7 @@
// @flow
import CommNodeHid from '@ledgerhq/hw-transport-node-hid'
import Btc from '@ledgerhq/hw-app-btc'
import getAllAccounts, { verifyAddress } from './accounts'
@ -57,4 +58,28 @@ export default (sendEvent: Function) => ({
sendEvent('wallet.verifyAddress.fail')
}
},
checkIfAppOpened: async ({
devicePath,
accountPath,
accountAddress,
segwit = true,
}: {
devicePath: string,
accountPath: string,
accountAddress: string,
segwit: boolean,
}) => {
try {
const transport = await CommNodeHid.open(devicePath)
const btc = new Btc(transport)
const { bitcoinAddress } = await btc.getWalletPublicKey(accountPath, false, segwit)
if (bitcoinAddress === accountAddress) {
sendEvent('wallet.checkIfAppOpened.success')
} else {
throw new Error('Address is different')
}
} catch (err) {
sendEvent('wallet.checkIfAppOpened.fail')
}
},
})

6
src/reducers/devices.js

@ -15,10 +15,8 @@ const state: DevicesState = {
}
function setCurrentDevice(state) {
return {
...state,
currentDevice: state.devices.length === 1 ? state.devices[0] : state.currentDevice,
}
const currentDevice = state.devices.length ? state.devices[state.devices.length - 1] : null
return { ...state, currentDevice }
}
const handlers: Object = {

Loading…
Cancel
Save