Browse Source

Merge pull request #23 from loeck/master

Add simple call for get bitcoinAddress on Account page
master
Meriadec Pillet 7 years ago
committed by GitHub
parent
commit
9e085b436a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      README.md
  2. 2
      package.json
  3. 26
      src/actions/devices.js
  4. 9
      src/actions/wallets.js
  5. 66
      src/components/AccountPage.js
  6. 12
      src/components/AppRegionDrag.js
  7. 15
      src/components/SideBar/index.js
  8. 12
      src/components/TopBar.js
  9. 26
      src/components/Wrapper.js
  10. 6
      src/internals/usb/index.js
  11. 8
      src/internals/usb/wallet.js
  12. 8
      src/reducers/devices.js
  13. 6
      src/reducers/index.js
  14. 20
      src/reducers/wallets.js
  15. 31
      src/renderer/events.js
  16. 4
      src/renderer/index.js
  17. 1
      src/styles/global.js

26
README.md

@ -1,32 +1,38 @@
### requirements # Ledger Wallet Desktop
#### Linux ## Requirements
``` * nodejs v8.x (https://nodejs.org/en/)
libicns * yarn latest (https://yarnpkg.com/fr/docs/install)
graphicsmagick
```
install dependencies ## Installation
``` ```
yarn yarn
``` ```
launch development version ## Development
``` ```
yarn start yarn start
``` ```
build, but not package for distribution ## Build
> Not package for distribution
``` ```
yarn dist:dir yarn dist:dir
``` ```
build and package everything > Package everything
``` ```
yarn dist yarn dist
``` ```
## Release
```
yarn release
```

2
package.json

@ -1,7 +1,7 @@
{ {
"name": "ledger-wallet-desktop", "name": "ledger-wallet-desktop",
"description": "Ledger Wallet Desktop", "description": "Ledger Wallet Desktop",
"repository": "https://github.com/meriadec/ledger-wallet-desktop-releases-DEV", "repository": "https://github.com/LedgerHQ/ledger-wallet-desktop",
"version": "0.1.0", "version": "0.1.0",
"author": "Ledger", "author": "Ledger",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",

26
src/actions/devices.js

@ -1,29 +1,29 @@
// @flow // @flow
// eslint-disable import/prefer-default-export /* eslint-disable import/prefer-default-export */
import type { Device, Devices } from 'types/common' import type { Device, Devices } from 'types/common'
export type deviceChooseType = (Device | null) => { type: string, payload: Device | null } export type SetCurrentDevice = (Device | null) => { type: string, payload: Device | null }
export const deviceChoose: deviceChooseType = payload => ({ export const setCurrentDevice: SetCurrentDevice = payload => ({
type: 'DEVICE_CHOOSE', type: 'SET_CURRENT_DEVICE',
payload, payload,
}) })
type devicesAddType = Device => { type: string, payload: Device } type AddDevice = Device => { type: string, payload: Device }
export const deviceAdd: devicesAddType = payload => ({ export const addDevice: AddDevice = payload => ({
type: 'DEVICE_ADD', type: 'ADD_DEVICE',
payload, payload,
}) })
type devicesRemoveType = Device => { type: string, payload: Device } type RemoveDevice = Device => { type: string, payload: Device }
export const deviceRemove: devicesRemoveType = payload => ({ export const removeDevice: RemoveDevice = payload => ({
type: 'DEVICE_REMOVE', type: 'REMOVE_DEVICE',
payload, payload,
}) })
type devicesUpdateType = Devices => { type: string, payload: Devices } type UpdateDevices = Devices => { type: string, payload: Devices }
export const devicesUpdate: devicesUpdateType = payload => ({ export const updateDevices: UpdateDevices = payload => ({
type: 'DEVICES_UPDATE', type: 'UPDATE_DEVICES',
payload, payload,
}) })

9
src/actions/wallets.js

@ -0,0 +1,9 @@
// @flow
/* eslint-disable import/prefer-default-export */
export type SetCurrentWalletType = (Object | null) => { type: string, payload: Object | null }
export const setCurrentWallet: SetCurrentWalletType = payload => ({
type: 'SET_CURRENT_WALLET',
payload,
})

66
src/components/AccountPage.js

@ -1,15 +1,73 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import type { MapStateToProps } from 'react-redux'
import type { Device } from 'types/common'
import { getCurrentDevice } from 'reducers/devices'
import { sendEvent } from 'renderer/events'
import Box from 'components/base/Box' import Box from 'components/base/Box'
type Props = {} const mapStateToProps: MapStateToProps<*, *, *> = state => ({
currentDevice: getCurrentDevice(state),
currentWallet: state.wallets.currentWallet,
})
type Props = {
currentDevice: Device | null,
currentWallet: Object | null,
}
type State = {
address: Object | null,
}
class AccountPage extends PureComponent<Props, State> {
state = {
address: null,
}
componentDidMount() {
this.getWalletInfos()
}
componentWillReceiveProps(nextProps) {
const { currentWallet } = nextProps
if (currentWallet !== null && !currentWallet.err) {
this.setState({
address: currentWallet.data.bitcoinAddress,
})
} else {
this._timeout = setTimeout(() => this.getWalletInfos(), 2e3)
}
}
componentWillUnmount() {
clearTimeout(this._timeout)
}
getWalletInfos() {
const { currentDevice } = this.props
if (currentDevice !== null) {
sendEvent('usb', 'wallet.infos.request', {
path: currentDevice.path,
wallet: 'btc',
})
}
}
_timeout = undefined
class AccountPage extends PureComponent<Props> {
render() { render() {
return <Box>{'account'}</Box> const { address } = this.state
return <Box>{address === null ? 'Select Bitcoin App on your Ledger' : address}</Box>
} }
} }
export default AccountPage export default connect(mapStateToProps)(AccountPage)

12
src/components/AppRegionDrag.js

@ -0,0 +1,12 @@
// @flow
import styled from 'styled-components'
export default styled.div`
-webkit-app-region: drag;
height: 40px;
left: 0;
position: absolute;
right: 0;
top: 0;
`

15
src/components/SideBar/index.js

@ -17,18 +17,19 @@ const CapsSubtitle = styled(Box).attrs({
font-weight: bold; font-weight: bold;
` `
const Container = styled(GrowScroll).attrs({ const Container = styled(Box).attrs({
flow: 4, noShrink: true,
py: 4,
})` })`
background-color: ${p => rgba(p.theme.colors[p.bg], 0.4)}; background-color: ${p => rgba(p.theme.colors[p.bg], 0.4)};
padding-top: 40px;
width: 250px;
` `
class SideBar extends PureComponent<{}> { class SideBar extends PureComponent<{}> {
render() { render() {
return ( return (
<Box noShrink style={{ width: 250 }}> <Container>
<Container bg="night"> <GrowScroll flow={4} py={4}>
<Box flow={2}> <Box flow={2}>
<CapsSubtitle>{'Menu'}</CapsSubtitle> <CapsSubtitle>{'Menu'}</CapsSubtitle>
<div> <div>
@ -55,8 +56,8 @@ class SideBar extends PureComponent<{}> {
</Item> </Item>
</div> </div>
</Box> </Box>
</Container> </GrowScroll>
</Box> </Container>
) )
} }
} }

12
src/components/TopBar.js

@ -5,11 +5,11 @@ import { connect } from 'react-redux'
import type { MapStateToProps, MapDispatchToProps } from 'react-redux' import type { MapStateToProps, MapDispatchToProps } from 'react-redux'
import type { Device, Devices } from 'types/common' import type { Device, Devices } from 'types/common'
import type { deviceChooseType } from 'actions/devices' import type { SetCurrentDevice } from 'actions/devices'
import { getDevices, getCurrentDevice } from 'reducers/devices' import { getDevices, getCurrentDevice } from 'reducers/devices'
import { deviceChoose } from 'actions/devices' import { setCurrentDevice } from 'actions/devices'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Overlay from 'components/base/Overlay' import Overlay from 'components/base/Overlay'
@ -20,13 +20,13 @@ const mapStateToProps: MapStateToProps<*, *, *> = state => ({
}) })
const mapDispatchToProps: MapDispatchToProps<*, *, *> = { const mapDispatchToProps: MapDispatchToProps<*, *, *> = {
deviceChoose, setCurrentDevice,
} }
type Props = { type Props = {
devices: Devices, devices: Devices,
currentDevice: Device, currentDevice: Device,
deviceChoose: deviceChooseType, setCurrentDevice: SetCurrentDevice,
} }
type State = { type State = {
@ -59,9 +59,9 @@ class TopBar extends PureComponent<Props, State> {
} }
handleSelectDevice = device => () => { handleSelectDevice = device => () => {
const { deviceChoose } = this.props const { setCurrentDevice } = this.props
deviceChoose(device) setCurrentDevice(device)
this.setState({ this.setState({
changeDevice: false, changeDevice: false,

26
src/components/Wrapper.js

@ -1,6 +1,6 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React, { Fragment, PureComponent } from 'react'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { Route } from 'react-router' import { Route } from 'react-router'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
@ -14,6 +14,7 @@ import SendModal from 'components/SendModal'
import ReceiveModal from 'components/ReceiveModal' import ReceiveModal from 'components/ReceiveModal'
import UpdateNotifier from 'components/UpdateNotifier' import UpdateNotifier from 'components/UpdateNotifier'
import AppRegionDrag from 'components/AppRegionDrag'
import SideBar from 'components/SideBar' import SideBar from 'components/SideBar'
import TopBar from 'components/TopBar' import TopBar from 'components/TopBar'
@ -24,20 +25,25 @@ class Wrapper extends PureComponent<{}> {
render() { render() {
return ( return (
<Box grow horizontal> <Fragment>
<SideBar /> <AppRegionDrag />
<Box shrink grow bg="cream">
<TopBar />
<Route path="/" component={DashboardPage} />
<Route path="/settings" component={SettingsPage} />
<Route path="/account/:account" component={AccountPage} />
</Box>
<SendModal /> <SendModal />
<ReceiveModal /> <ReceiveModal />
<UpdateNotifier /> <UpdateNotifier />
</Box>
<Box grow horizontal>
<SideBar />
<Box shrink grow bg="cream">
<TopBar />
<Route path="/" exact component={DashboardPage} />
<Route path="/settings" component={SettingsPage} />
<Route path="/account/:account" component={AccountPage} />
</Box>
</Box>
</Fragment>
) )
} }
} }

6
src/internals/usb/index.js

@ -7,13 +7,13 @@ import wallet from './wallet'
process.title = 'ledger-wallet-desktop-usb' process.title = 'ledger-wallet-desktop-usb'
function send(type: string, data: any, options: Object = { kill: true }) { function sendEvent(type: string, data: any, options: Object = { kill: true }) {
process.send({ type, data, options }) process.send({ type, data, options })
} }
const handlers = { const handlers = {
devices: devices(send), devices: devices(sendEvent),
wallet: wallet(send), wallet: wallet(sendEvent),
} }
process.on('message', payload => { process.on('message', payload => {

8
src/internals/usb/wallet.js

@ -13,14 +13,14 @@ async function getWalletInfos(path, wallet) {
throw new Error('invalid wallet') throw new Error('invalid wallet')
} }
export default (send: Function) => ({ export default (sendEvent: Function) => ({
infos: { infos: {
request: async ({ path, wallet }: { path: string, wallet: string }) => { request: async ({ path, wallet }: { path: string, wallet: string }) => {
try { try {
const publicKey = await getWalletInfos(path, wallet) const data = await getWalletInfos(path, wallet)
send('wallet.infos.success', { path, publicKey }) sendEvent('wallet.infos.success', { path, wallet, data })
} catch (err) { } catch (err) {
send('wallet.infos.fail', { path, err: err.stack || err }) sendEvent('wallet.infos.fail', { path, wallet, err: err.stack || err })
} }
}, },
}, },

8
src/reducers/devices.js

@ -22,19 +22,19 @@ function setCurrentDevice(state) {
} }
const handlers: Object = { const handlers: Object = {
DEVICES_UPDATE: (state: DevicesState, { payload: devices }: { payload: Devices }) => UPDATE_DEVICES: (state: DevicesState, { payload: devices }: { payload: Devices }) =>
setCurrentDevice({ setCurrentDevice({
...state, ...state,
devices, devices,
}), }),
DEVICE_ADD: (state: DevicesState, { payload: device }: { payload: Device }) => ADD_DEVICE: (state: DevicesState, { payload: device }: { payload: Device }) =>
setCurrentDevice({ setCurrentDevice({
...state, ...state,
devices: [...state.devices, device].filter( devices: [...state.devices, device].filter(
(v, i, s) => s.findIndex(t => t.path === v.path) === i, (v, i, s) => s.findIndex(t => t.path === v.path) === i,
), ),
}), }),
DEVICE_REMOVE: (state: DevicesState, { payload: device }: { payload: Device }) => ({ REMOVE_DEVICE: (state: DevicesState, { payload: device }: { payload: Device }) => ({
...state, ...state,
currentDevice: currentDevice:
state.currentDevice !== null && state.currentDevice.path === device.path state.currentDevice !== null && state.currentDevice.path === device.path
@ -42,7 +42,7 @@ const handlers: Object = {
: state.currentDevice, : state.currentDevice,
devices: state.devices.filter(d => d.path !== device.path), devices: state.devices.filter(d => d.path !== device.path),
}), }),
DEVICE_CHOOSE: (state: DevicesState, { payload: currentDevice }: { payload: Device }) => ({ SET_CURRENT_DEVICE: (state: DevicesState, { payload: currentDevice }: { payload: Device }) => ({
...state, ...state,
currentDevice, currentDevice,
}), }),

6
src/reducers/index.js

@ -8,21 +8,25 @@ import type { LocationShape } from 'react-router'
import devices from './devices' import devices from './devices'
import modals from './modals' import modals from './modals'
import update from './update' import update from './update'
import wallets from './wallets'
import type { DevicesState } from './devices' import type { DevicesState } from './devices'
import type { ModalsState } from './modals' import type { ModalsState } from './modals'
import type { UpdateState } from './update' import type { UpdateState } from './update'
import type { WalletsState } from './wallets'
export type State = { export type State = {
router: LocationShape, router: LocationShape,
devices: DevicesState, devices: DevicesState,
modals: ModalsState, modals: ModalsState,
update: UpdateState, update: UpdateState,
wallets: WalletsState,
} }
export default combineReducers({ export default combineReducers({
router,
devices, devices,
modals, modals,
router,
update, update,
wallets,
}) })

20
src/reducers/wallets.js

@ -0,0 +1,20 @@
// @flow
import { handleActions } from 'redux-actions'
export type WalletsState = {
currentWallet: Object | null,
}
const state: WalletsState = {
currentWallet: null,
}
const handlers: Object = {
SET_CURRENT_WALLET: (state: Object, { payload: currentWallet }: { payload: WalletsState }) => ({
...state,
currentWallet,
}),
}
export default handleActions(handlers, state)

31
src/renderer/initEvents.js → src/renderer/events.js

@ -3,7 +3,8 @@
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import objectPath from 'object-path' import objectPath from 'object-path'
import { devicesUpdate, deviceAdd, deviceRemove } from 'actions/devices' import { updateDevices, addDevice, removeDevice } from 'actions/devices'
import { setCurrentWallet } from 'actions/wallets'
import { setUpdateStatus } from 'reducers/update' import { setUpdateStatus } from 'reducers/update'
type MsgPayload = { type MsgPayload = {
@ -14,7 +15,7 @@ type MsgPayload = {
// wait a bit before launching update check // wait a bit before launching update check
const CHECK_UPDATE_TIMEOUT = 3e3 const CHECK_UPDATE_TIMEOUT = 3e3
function send(channel: string, msgType: string, data: *) { export function sendEvent(channel: string, msgType: string, data: any) {
ipcRenderer.send(channel, { ipcRenderer.send(channel, {
type: msgType, type: msgType,
data, data,
@ -25,27 +26,17 @@ export default (store: Object) => {
const handlers = { const handlers = {
devices: { devices: {
update: devices => { update: devices => {
store.dispatch(devicesUpdate(devices)) store.dispatch(updateDevices(devices))
if (devices.length) {
send('usb', 'wallet.infos.request', {
path: devices[0].path,
wallet: 'btc',
})
}
}, },
}, },
device: { device: {
add: device => store.dispatch(deviceAdd(device)), add: device => store.dispatch(addDevice(device)),
remove: device => store.dispatch(deviceRemove(device)), remove: device => store.dispatch(removeDevice(device)),
}, },
wallet: { wallet: {
infos: { infos: {
success: ({ path, publicKey }) => { success: ({ wallet, data }) => store.dispatch(setCurrentWallet({ wallet, data })),
console.log({ path, publicKey }) fail: ({ wallet, err }) => store.dispatch(setCurrentWallet({ wallet, err })),
},
fail: ({ path, err }) => {
console.log({ path, err })
},
}, },
}, },
updater: { updater: {
@ -70,13 +61,13 @@ export default (store: Object) => {
}) })
// First time, we get all devices // First time, we get all devices
send('usb', 'devices.all') sendEvent('usb', 'devices.all')
// Start detection when we plug/unplug devices // Start detection when we plug/unplug devices
send('usb', 'devices.listen') sendEvent('usb', 'devices.listen')
if (__PROD__) { if (__PROD__) {
// Start check of eventual updates // Start check of eventual updates
setTimeout(() => send('msg', 'updater.init'), CHECK_UPDATE_TIMEOUT) setTimeout(() => sendEvent('msg', 'updater.init'), CHECK_UPDATE_TIMEOUT)
} }
} }

4
src/renderer/index.js

@ -6,7 +6,7 @@ import { AppContainer } from 'react-hot-loader'
import createHistory from 'history/createHashHistory' import createHistory from 'history/createHashHistory'
import createStore from 'renderer/createStore' import createStore from 'renderer/createStore'
import initEvents from 'renderer/initEvents' import events from 'renderer/events'
import App from 'components/App' import App from 'components/App'
@ -16,7 +16,7 @@ const history = createHistory()
const store = createStore(history) const store = createStore(history)
const rootNode = document.getElementById('app') const rootNode = document.getElementById('app')
initEvents(store) events(store)
function r(Comp) { function r(Comp) {
if (rootNode) { if (rootNode) {

1
src/styles/global.js

@ -22,7 +22,6 @@ injectGlobal`
line-height: 1.5; line-height: 1.5;
font-size: 16px; font-size: 16px;
font-family: "Open Sans", Arial, Helvetica, sans-serif; font-family: "Open Sans", Arial, Helvetica, sans-serif;
-webkit-app-region: drag;
} }
#app { #app {

Loading…
Cancel
Save