From 1512ec6b234ec31f911e8d15fae0178eca0ca1bb Mon Sep 17 00:00:00 2001 From: meriadec Date: Thu, 22 Mar 2018 16:36:09 +0100 Subject: [PATCH] Fetch installable apps from api instead of currency list --- src/components/ManagerPage/ManagerApp.js | 11 ++--- src/components/ManagerPage/index.js | 59 ++++++++++++++++++------ src/internals/usb/manager/constants.js | 11 ----- src/internals/usb/manager/helpers.js | 45 ++++++++---------- src/internals/usb/manager/index.js | 10 ++++ src/renderer/events.js | 2 +- 6 files changed, 79 insertions(+), 59 deletions(-) diff --git a/src/components/ManagerPage/ManagerApp.js b/src/components/ManagerPage/ManagerApp.js index 4376d156..ed72d3e5 100644 --- a/src/components/ManagerPage/ManagerApp.js +++ b/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 ( - {Icon && } - {currency.name} + {name} {'Install'} {'Remove'} diff --git a/src/components/ManagerPage/index.js b/src/components/ManagerPage/index.js index 2f455686..3e9958aa 100644 --- a/src/components/ManagerPage/index.js +++ b/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 { 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 { renderList = () => ( - {CURRENCIES.map(c => ( + {this.state.appsList.map(c => ( @@ -100,9 +125,17 @@ class ManagerPage extends PureComponent { {'Connect your device'} )} - {deviceStatus === 'connected' && this.renderList()} + {deviceStatus === 'connected' && ( + + {status === 'loading' ? ( + {'Loading app list...'} + ) : ( + this.renderList() + )} + + )} ( {status === 'busy' ? ( diff --git a/src/internals/usb/manager/constants.js b/src/internals/usb/manager/constants.js index 29796025..c67191e1 100644 --- a/src/internals/usb/manager/constants.js +++ b/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], diff --git a/src/internals/usb/manager/helpers.js b/src/internals/usb/manager/helpers.js index 4b8e580a..bb31e8cc 100644 --- a/src/internals/usb/manager/helpers.js +++ b/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 { - 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 { - 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) diff --git a/src/internals/usb/manager/index.js b/src/internals/usb/manager/index.js index a4af2a75..bde6aac6 100644 --- a/src/internals/usb/manager/index.js +++ b/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 }) + } + }, }) diff --git a/src/renderer/events.js b/src/renderer/events.js index 34ac611d..e5e5fdfa 100644 --- a/src/renderer/events.js +++ b/src/renderer/events.js @@ -56,7 +56,7 @@ export function runJob({ job: string, successResponse: string, errorResponse: string, - data: any, + data?: any, }): Promise { return new Promise((resolve, reject) => { ipcRenderer.send(channel, { type: job, data })