Browse Source

Fetch installable apps from api instead of currency list

master
meriadec 7 years ago
parent
commit
1512ec6b23
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 11
      src/components/ManagerPage/ManagerApp.js
  2. 59
      src/components/ManagerPage/index.js
  3. 11
      src/internals/usb/manager/constants.js
  4. 45
      src/internals/usb/manager/helpers.js
  5. 10
      src/internals/usb/manager/index.js
  6. 2
      src/renderer/events.js

11
src/components/ManagerPage/ManagerApp.js

@ -2,9 +2,6 @@
import React from 'react'
import styled from 'styled-components'
import { getIconByCoinType } from '@ledgerhq/currencies/react'
import type { Currency } from '@ledgerhq/currencies'
import Box, { Tabbable } from 'components/base/Box'
import Text from 'components/base/Text'
@ -24,18 +21,16 @@ const ActionBtn = styled(Tabbable).attrs({
})``
type Props = {
currency: Currency,
name: string,
onInstall: Function,
onUninstall: Function,
}
export default function ManagerApp(props: Props) {
const { currency, onInstall, onUninstall } = props
const Icon = getIconByCoinType(currency.coinType)
const { name, onInstall, onUninstall } = props
return (
<Container flow={3}>
{Icon && <Icon size={24} />}
<Text>{currency.name}</Text>
<Text ff="Museo Sans|Bold">{name}</Text>
<Box horizontal flow={2}>
<ActionBtn onClick={onInstall}>{'Install'}</ActionBtn>
<ActionBtn onClick={onUninstall}>{'Remove'}</ActionBtn>

59
src/components/ManagerPage/index.js

@ -5,9 +5,7 @@ import { connect } from 'react-redux'
import styled from 'styled-components'
import { translate } from 'react-i18next'
import { compose } from 'redux'
import { listCurrencies } from '@ledgerhq/currencies'
import type { Currency } from '@ledgerhq/currencies'
import type { Device } from 'types/common'
import { runJob } from 'renderer/events'
@ -19,8 +17,6 @@ import Modal, { ModalBody } from 'components/base/Modal'
import ManagerApp from './ManagerApp'
const CURRENCIES = listCurrencies()
const List = styled(Box).attrs({
horizontal: true,
m: -1,
@ -36,25 +32,54 @@ type Props = {
device: Device,
}
type Status = 'idle' | 'busy' | 'success' | 'error'
type Status = 'loading' | 'idle' | 'busy' | 'success' | 'error'
type LedgerApp = {
name: string,
app: Object,
}
type State = {
status: Status,
error: string | null,
appsList: LedgerApp[],
}
class ManagerPage extends PureComponent<Props, State> {
state = {
status: 'idle',
status: 'loading',
error: null,
appsList: [],
}
componentDidMount() {
this.fetchList()
}
componentWillUnmount() {
this._unmounted = true
}
_unmounted = false
async fetchList() {
const appsList = await runJob({
channel: 'usb',
job: 'manager.listApps',
successResponse: 'manager.listAppsSuccess',
errorResponse: 'manager.listAppsError',
})
if (!this._unmounted) {
this.setState({ appsList, status: 'idle' })
}
}
createDeviceJobHandler = options => (currency: Currency) => async () => {
createDeviceJobHandler = options => ({ app: appParams }) => async () => {
this.setState({ status: 'busy' })
try {
const { job, successResponse, errorResponse } = options
const { device: { path: devicePath } } = this.props
const data = { appName: currency.name.toLowerCase(), devicePath }
const data = { appParams, devicePath }
await runJob({ channel: 'usb', job, successResponse, errorResponse, data })
this.setState({ status: 'success' })
} catch (err) {
@ -78,10 +103,10 @@ class ManagerPage extends PureComponent<Props, State> {
renderList = () => (
<List>
{CURRENCIES.map(c => (
{this.state.appsList.map(c => (
<ManagerApp
key={c.coinType}
currency={c}
key={c.name}
name={c.name}
onInstall={this.handleInstall(c)}
onUninstall={this.handleUninstall(c)}
/>
@ -100,9 +125,17 @@ class ManagerPage extends PureComponent<Props, State> {
<Box fontSize={8}>{'Connect your device'}</Box>
</Box>
)}
{deviceStatus === 'connected' && this.renderList()}
{deviceStatus === 'connected' && (
<Box>
{status === 'loading' ? (
<Box ff="Museo Sans|Bold">{'Loading app list...'}</Box>
) : (
this.renderList()
)}
</Box>
)}
<Modal
isOpened={status !== 'idle'}
isOpened={status !== 'idle' && status !== 'loading'}
render={() => (
<ModalBody p={6} align="center" justify="center" style={{ height: 300 }}>
{status === 'busy' ? (

11
src/internals/usb/manager/constants.js

@ -1,17 +1,6 @@
// Socket endpoint
export const BASE_SOCKET_URL = 'ws://api.ledgerwallet.com/update/install'
// Apparently params we need to add to websocket requests
//
// see https://github.com/LedgerHQ/ledger-manager-chrome
// > controllers/manager/ApplyUpdateController.scala
//
// @TODO: Get rid of them.
export const DEFAULT_SOCKET_PARAMS = {
perso: 'perso_11',
hash: '0000000000000000000000000000000000000000000000000000000000000000',
}
// List of APDUS
export const APDUS = {
GET_FIRMWARE: [0xe0, 0x01, 0x00, 0x00],

45
src/internals/usb/manager/helpers.js

@ -8,7 +8,7 @@ import noop from 'lodash/noop'
import type Transport from '@ledgerhq/hw-transport'
import type { IPCSend } from 'types/electron'
import { BASE_SOCKET_URL, DEFAULT_SOCKET_PARAMS, APDUS } from './constants'
import { BASE_SOCKET_URL, APDUS } from './constants'
type WebsocketType = {
send: (string, any) => void,
@ -22,6 +22,13 @@ type Message = {
data: any,
}
type LedgerAppParams = {
firmware: string,
firmwareKey: string,
delete: string,
deleteKey: string,
}
/**
* Generate handler which create transport with given
* `devicePath` then call action with it
@ -63,15 +70,9 @@ export function createTransportHandler(
*/
export async function installApp(
transport: Transport<*>,
{ appName }: { appName: string },
{ appParams }: { appParams: LedgerAppParams },
): Promise<void> {
log('INSTALL', `Request to install ${appName} app`)
return createSocketDialog(transport, ({ version }) => ({
firmware: `nanos/${version}/${appName}/app_latest`,
firmwareKey: `nanos/${version}/${appName}/app_latest_key`,
delete: `nanos/${version}/${appName}/app_del`,
deleteKey: `nanos/${version}/${appName}/app_del_key`,
}))
return createSocketDialog(transport, appParams)
}
/**
@ -79,15 +80,13 @@ export async function installApp(
*/
export async function uninstallApp(
transport: Transport<*>,
{ appName }: { appName: string },
{ appParams }: { appParams: LedgerAppParams },
): Promise<void> {
log('INSTALL', `Request to uninstall ${appName} app`)
return createSocketDialog(transport, ({ version }) => ({
firmware: `nanos/${version}/${appName}/app_del`,
firmwareKey: `nanos/${version}/${appName}/app_del_key`,
delete: `nanos/${version}/${appName}/app_del`,
deleteKey: `nanos/${version}/${appName}/app_del_key`,
}))
return createSocketDialog(transport, {
...appParams,
firmware: appParams.delete,
firmwareKey: appParams.deleteKey,
})
}
/**
@ -143,16 +142,10 @@ async function bulk(ws: WebsocketType, transport: Transport<*>, msg: Message) {
* Open socket connection with firmware api, and init a dialog
* with the device
*/
function createSocketDialog(transport: Transport<*>, buildParams: Function) {
function createSocketDialog(transport: Transport<*>, appParams: LedgerAppParams) {
return new Promise(async (resolve, reject) => {
try {
const { targetId, version } = await getFirmwareInfo(transport)
const fullParams = qs.stringify({
targetId,
...DEFAULT_SOCKET_PARAMS,
...buildParams({ targetId, version }),
})
const url = `${BASE_SOCKET_URL}?${fullParams}`
const url = `${BASE_SOCKET_URL}?${qs.stringify(appParams)}`
log('WS CONNECTING', url)
const ws: WebsocketType = new Websocket(url)
@ -195,7 +188,7 @@ function createSocketDialog(transport: Transport<*>, buildParams: Function) {
/**
* Retrieve targetId and firmware version from device
*/
async function getFirmwareInfo(transport: Transport<*>) {
export async function getFirmwareInfo(transport: Transport<*>) {
const res = await transport.send(...APDUS.GET_FIRMWARE)
const byteArray = [...res]
const data = byteArray.slice(0, byteArray.length - 2)

10
src/internals/usb/manager/index.js

@ -17,6 +17,7 @@
*/
import type { IPCSend } from 'types/electron'
import axios from 'axios'
import { createTransportHandler, installApp, uninstallApp } from './helpers'
export default (send: IPCSend) => ({
@ -31,4 +32,13 @@ export default (send: IPCSend) => ({
successResponse: 'device.appUninstalled',
errorResponse: 'device.appUninstallError',
}),
listApps: async () => {
try {
const { data } = await axios.get('https://api.ledgerwallet.com/update/applications')
send('manager.listAppsSuccess', data['nanos-1.4'])
} catch (err) {
send('manager.listAppsError', { message: err.message, stack: err.stack })
}
},
})

2
src/renderer/events.js

@ -56,7 +56,7 @@ export function runJob({
job: string,
successResponse: string,
errorResponse: string,
data: any,
data?: any,
}): Promise<void> {
return new Promise((resolve, reject) => {
ipcRenderer.send(channel, { type: job, data })

Loading…
Cancel
Save