Browse Source

Merge pull request #679 from LedgerHQ/develop

Prepare for beta.2
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
8f5ae0420a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 91
      src/components/ManagerPage/AppSearchBar.js
  2. 33
      src/components/ManagerPage/AppsList.js
  3. 14
      src/components/ManagerPage/Dashboard.js
  4. 4
      src/components/ManagerPage/UpdateFirmwareButton.js
  5. 32
      src/components/RenderError.js
  6. 2
      src/components/ThrowBlock.js
  7. 1
      src/config/cryptocurrencies.js
  8. 20
      src/helpers/apps/installApp.js
  9. 14
      src/helpers/apps/uninstallApp.js
  10. 16
      src/logger.js
  11. 3
      src/renderer/init.js
  12. 4
      src/sentry/browser.js
  13. 4
      src/sentry/node.js
  14. 10
      static/i18n/en/errors.yml

91
src/components/ManagerPage/AppSearchBar.js

@ -1,14 +1,12 @@
// @flow // @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { color, fontSize, space } from 'styled-system'
import fontFamily from 'styles/styled/fontFamily'
import type { LedgerScriptParams } from 'helpers/common' import React, { PureComponent, Fragment } from 'react'
import { ff } from 'styles/helpers' import type { LedgerScriptParams } from 'helpers/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Space from 'components/base/Space'
import Input from 'components/base/Input'
import Search from 'components/base/Search' import Search from 'components/base/Search'
import SearchIcon from 'icons/Search' import SearchIcon from 'icons/Search'
@ -24,53 +22,13 @@ type State = {
focused: boolean, focused: boolean,
} }
const SearchBarWrapper = styled(Box).attrs({
horizontal: true,
borderRadius: 4,
})`
height: 42px;
width: 100%;
margin: 0 0 20px 0;
background-color: white;
padding: 0 13px;
${p =>
p.focused
? `
border: 1px solid #6490f1;
`
: 'border: 1px solid white;'};
`
const Input = styled.input.attrs({
ff: 'Open Sans|SemiBold',
color: 'dark',
mx: 3,
fontSize: 4,
})`
${space};
${fontFamily};
${fontSize};
${color};
border: 0;
flex: 1;
outline: none;
background: transparent;
&::placeholder {
color: ${p => p.theme.colors.fog};
${() => ff('Open Sans|Regular')};
}
`
class AppSearchBar extends PureComponent<Props, State> { class AppSearchBar extends PureComponent<Props, State> {
state = { state = {
query: '', query: '',
focused: false, focused: false,
} }
handleChange = (e: any) => this.setState({ query: e.target.value }) handleChange = (query: string) => this.setState({ query })
handleFocus = (bool: boolean) => () => this.setState({ focused: bool }) handleFocus = (bool: boolean) => () => this.setState({ focused: bool })
@ -88,20 +46,29 @@ class AppSearchBar extends PureComponent<Props, State> {
const color = focused ? 'black' : 'grey' const color = focused ? 'black' : 'grey'
return ( return (
<Box> <Fragment>
<SearchBarWrapper align="center" focused={focused}> <Input
<SearchIcon size={16} style={{ color }} /> innerRef={c => (this.input = c)}
<Input type="text"
innerRef={c => (this.input = c)} value={query}
type="text" onChange={this.handleChange}
value={query} onFocus={this.handleFocus(true)}
onChange={this.handleChange} onBlur={this.handleFocus(false)}
onFocus={this.handleFocus(true)} placeholder={'Search app'}
onBlur={this.handleFocus(false)} renderLeft={
placeholder={'Search app'} <Box pl={3} justify="center">
/> <SearchIcon size={16} style={{ color }} />
{!!query && <CrossIcon size={16} cursor="pointer" onClick={this.reset} />} </Box>
</SearchBarWrapper> }
renderRight={
query ? (
<Box justify="center" cursor="pointer" onClick={this.reset} px={3}>
<CrossIcon size={16} />
</Box>
) : null
}
/>
<Space of={30} />
<Search <Search
fuseOptions={{ fuseOptions={{
threshold: 0.5, threshold: 0.5,
@ -111,7 +78,7 @@ class AppSearchBar extends PureComponent<Props, State> {
items={list} items={list}
render={items => children(items)} render={items => children(items)}
/> />
</Box> </Fragment>
) )
} }
} }

33
src/components/ManagerPage/AppsList.js

@ -21,7 +21,7 @@ import Spinner from 'components/base/Spinner'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import TranslatedError from 'components/TranslatedError' import TranslatedError from 'components/TranslatedError'
import ExclamationCircle from 'icons/ExclamationCircle' import IconInfoCircle from 'icons/InfoCircle'
import ExclamationCircleThin from 'icons/ExclamationCircleThin' import ExclamationCircleThin from 'icons/ExclamationCircleThin'
import Update from 'icons/Update' import Update from 'icons/Update'
import Trash from 'icons/Trash' import Trash from 'icons/Trash'
@ -37,8 +37,6 @@ const List = styled(Box).attrs({
flex-wrap: wrap; flex-wrap: wrap;
` `
let CACHED_APPS = null
const ICONS_FALLBACK = { const ICONS_FALLBACK = {
bitcoin_testnet: 'bitcoin', bitcoin_testnet: 'bitcoin',
} }
@ -85,8 +83,7 @@ class AppsList extends PureComponent<Props, State> {
async fetchAppList() { async fetchAppList() {
try { try {
const { targetId, version } = this.props const { targetId, version } = this.props
const appsList = CACHED_APPS || (await listApps.send({ targetId, version }).toPromise()) const appsList = await listApps.send({ targetId, version }).toPromise()
CACHED_APPS = appsList
if (!this._unmounted) { if (!this._unmounted) {
this.setState({ appsList, status: 'idle', appsLoaded: true }) this.setState({ appsList, status: 'idle', appsLoaded: true })
} }
@ -249,19 +246,19 @@ class AppsList extends PureComponent<Props, State> {
return ( return (
<Box flow={6}> <Box flow={6}>
<Box> <Box>
<Box mb={4} color="dark" ff="Museo Sans" fontSize={5} flow={2} horizontal> <Box mb={4} color="dark" ff="Museo Sans" fontSize={5} flow={2} horizontal align="center">
<span>{t('app:manager.apps.all')}</span> <span style={{ lineHeight: 1 }}>{t('app:manager.apps.all')}</span>
<span> <Tooltip
<Tooltip render={() => (
render={() => ( <Box ff="Open Sans|SemiBold" fontSize={2}>
<Box ff="Open Sans|SemiBold" fontSize={2}> {t('app:manager.apps.help')}
{t('app:manager.apps.help')} </Box>
</Box> )}
)} >
> <Box color="grey">
<ExclamationCircle size={12} /> <IconInfoCircle size={12} />
</Tooltip> </Box>
</span> </Tooltip>
</Box> </Box>
{this.renderList()} {this.renderList()}
</Box> </Box>

14
src/components/ManagerPage/Dashboard.js

@ -2,7 +2,6 @@
import React from 'react' import React from 'react'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import { EXPERIMENTAL_FIRMWARE_UPDATE } from 'config/constants'
import type { T, Device } from 'types/common' import type { T, Device } from 'types/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'
@ -35,15 +34,10 @@ const Dashboard = ({ device, deviceInfo, t }: Props) => (
</Text> </Text>
</Box> </Box>
<Box mt={5}> <Box mt={5}>
{EXPERIMENTAL_FIRMWARE_UPDATE ? ( <FirmwareUpdate
<FirmwareUpdate infos={{ targetId: deviceInfo.targetId, version: deviceInfo.version }}
infos={{ device={device}
targetId: deviceInfo.targetId, />
version: deviceInfo.version,
}}
device={device}
/>
) : null}
</Box> </Box>
<Box mt={5}> <Box mt={5}>
<AppsList device={device} targetId={deviceInfo.targetId} version={deviceInfo.version} /> <AppsList device={device} targetId={deviceInfo.targetId} version={deviceInfo.version} />

4
src/components/ManagerPage/UpdateFirmwareButton.js

@ -4,6 +4,8 @@ import { translate } from 'react-i18next'
import type { T } from 'types/common' import type { T } from 'types/common'
import { EXPERIMENTAL_FIRMWARE_UPDATE } from 'config/constants'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import Text from 'components/base/Text' import Text from 'components/base/Text'
import { getCleanVersion } from 'components/ManagerPage/FirmwareUpdate' import { getCleanVersion } from 'components/ManagerPage/FirmwareUpdate'
@ -25,7 +27,7 @@ const UpdateFirmwareButton = ({ t, firmware, installFirmware }: Props) =>
<Text ff="Open Sans|Regular" fontSize={4} style={{ marginLeft: 'auto', marginRight: 15 }}> <Text ff="Open Sans|Regular" fontSize={4} style={{ marginLeft: 'auto', marginRight: 15 }}>
{t('app:manager.firmware.latest', { version: getCleanVersion(firmware.name) })} {t('app:manager.firmware.latest', { version: getCleanVersion(firmware.name) })}
</Text> </Text>
<Button primary onClick={installFirmware}> <Button primary onClick={installFirmware} disabled={!EXPERIMENTAL_FIRMWARE_UPDATE}>
{t('app:manager.firmware.update')} {t('app:manager.firmware.update')}
</Button> </Button>
</Fragment> </Fragment>

32
src/components/RenderError.js

@ -10,12 +10,10 @@ import hardReset from 'helpers/hardReset'
import type { T } from 'types/common' import type { T } from 'types/common'
import Spoiler from 'components/base/Spoiler'
import ExportLogsBtn from 'components/ExportLogsBtn' import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Space from 'components/base/Space' import Space from 'components/base/Space'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import TranslatedError from './TranslatedError'
type Props = { type Props = {
error: Error, error: Error,
@ -94,17 +92,23 @@ ${error.stack}
{t('app:crash.reset')} {t('app:crash.reset')}
</Button> </Button>
</Box> </Box>
<Space of={20} /> <Box my={6}>
<Spoiler color="wallet" title={t('app:crash.showError')}>
<ErrContainer> <ErrContainer>
<TranslatedError error={error} /> <strong>{String(error)}</strong>
<div>{error.stack || 'no stacktrace'}</div>
</ErrContainer> </ErrContainer>
</Spoiler> </Box>
<Space of={10} /> <pre
<Spoiler color="wallet" title={t('app:crash.showDetails')}> style={{
<ErrContainer>{error.stack}</ErrContainer> position: 'fixed',
</Spoiler> bottom: 8,
<Space of={100} /> left: 8,
opacity: 0.2,
fontSize: 10,
}}
>
{__APP_VERSION__}
</pre>
{children} {children}
</Box> </Box>
) )
@ -114,14 +118,14 @@ ${error.stack}
const ErrContainer = ({ children }: { children: any }) => ( const ErrContainer = ({ children }: { children: any }) => (
<pre <pre
style={{ style={{
marginTop: 10, margin: 'auto',
maxWidth: '80%', maxWidth: '80vw',
overflow: 'auto', overflow: 'auto',
fontSize: 10, fontSize: 10,
fontFamily: 'monospace', fontFamily: 'monospace',
background: 'rgba(0, 0, 0, 0.05)',
cursor: 'text', cursor: 'text',
userSelect: 'text', userSelect: 'text',
opacity: 0.3,
}} }}
> >
{children} {children}

2
src/components/ThrowBlock.js

@ -17,7 +17,7 @@ class ThrowBlock extends PureComponent<Props, State> {
} }
componentDidCatch(error: Error) { componentDidCatch(error: Error) {
logger.error(error) logger.critical(error)
this.setState({ error }) this.setState({ error })
} }

1
src/config/cryptocurrencies.js

@ -26,6 +26,7 @@ const supported: CryptoCurrencyIds[] = [
'viacoin', 'viacoin',
'stealthcoin', 'stealthcoin',
'poswallet', 'poswallet',
'clubcoin',
'bitcoin_testnet', 'bitcoin_testnet',
] ]

20
src/helpers/apps/installApp.js

@ -9,14 +9,26 @@ import type { LedgerScriptParams } from 'helpers/common'
import createCustomErrorClass from '../createCustomErrorClass' import createCustomErrorClass from '../createCustomErrorClass'
const CannotInstall = createCustomErrorClass('CannotInstall') const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpected')
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const ManagerAppAlreadyInstalledError = createCustomErrorClass('ManagerAppAlreadyInstalled')
const ManagerAppRelyOnBTCError = createCustomErrorClass('ManagerAppRelyOnBTC')
function remapError(promise) { function remapError(promise) {
return promise.catch((e: Error) => { return promise.catch((e: Error) => {
if (e.message.endsWith('6982')) { switch (true) {
throw new CannotInstall() case e.message.endsWith('6982'):
throw new ManagerDeviceLockedError()
case e.message.endsWith('6a84') || e.message.endsWith('6a85'):
throw new ManagerNotEnoughSpaceError()
case e.message.endsWith('6a80') || e.message.endsWith('6a81'):
throw new ManagerAppAlreadyInstalledError()
case e.message.endsWith('6a83'):
throw new ManagerAppRelyOnBTCError()
default:
throw new ManagerUnexpectedError(e.message, { msg: e.message })
} }
throw e
}) })
} }

14
src/helpers/apps/uninstallApp.js

@ -8,14 +8,20 @@ import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/common' import type { LedgerScriptParams } from 'helpers/common'
import createCustomErrorClass from '../createCustomErrorClass' import createCustomErrorClass from '../createCustomErrorClass'
const CannotUninstall = createCustomErrorClass('CannotUninstall') const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpectedError')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBTCDep')
function remapError(promise) { function remapError(promise) {
return promise.catch((e: Error) => { return promise.catch((e: Error) => {
if (e.message.endsWith('6a83')) { switch (true) {
throw new CannotUninstall() case e.message.endsWith('6982'):
throw new ManagerDeviceLockedError()
case e.message.endsWith('6a83'):
throw new ManagerUninstallBTCDep()
default:
throw new ManagerUnexpectedError(e.message, { msg: e.message })
} }
throw e
}) })
} }

16
src/logger.js

@ -134,15 +134,31 @@ export default {
console.log(...args) console.log(...args)
addLog('log', ...args) addLog('log', ...args)
}, },
warn: (...args: any) => { warn: (...args: any) => {
console.warn(...args) console.warn(...args)
addLog('warn', ...args) addLog('warn', ...args)
}, },
error: (...args: any) => { error: (...args: any) => {
console.error(...args) console.error(...args)
addLog('error', ...args) addLog('error', ...args)
}, },
critical: (error: Error) => {
addLog('critical', error)
console.error(error)
try {
if (typeof window !== 'undefined') {
require('sentry/browser').captureException(error)
} else {
require('sentry/node').captureException(error)
}
} catch (e) {
console.warn("Can't send to sentry", error, e)
}
},
exportLogs: (): Array<{ type: string, date: Date, args: Array<any> }> => exportLogs: (): Array<{ type: string, date: Date, args: Array<any> }> =>
logs.map(({ type, date, args }) => ({ logs.map(({ type, date, args }) => ({
type, type,

3
src/renderer/init.js

@ -98,7 +98,6 @@ function r(Comp) {
} }
init().catch(e => { init().catch(e => {
// for now we make the app crash instead of pending forever. later we can render the error OR try to recover, but probably this is unrecoverable cases. logger.critical(e)
logger.error(e)
r(<AppError error={e} language="en" />) r(<AppError error={e} language="en" />)
}) })

4
src/sentry/browser.js

@ -7,3 +7,7 @@ import install from './install'
export default (shouldSendCallback: () => boolean) => { export default (shouldSendCallback: () => boolean) => {
install(Raven, shouldSendCallback, user().id) install(Raven, shouldSendCallback, user().id)
} }
export const captureException = (e: Error) => {
Raven.captureException(e)
}

4
src/sentry/node.js

@ -6,3 +6,7 @@ import install from './install'
export default (shouldSendCallback: () => boolean, userId: string) => { export default (shouldSendCallback: () => boolean, userId: string) => {
install(Raven, shouldSendCallback, userId) install(Raven, shouldSendCallback, userId)
} }
export const captureException = (e: Error) => {
Raven.captureException(e)
}

10
static/i18n/en/errors.yml

@ -21,5 +21,11 @@ DeviceSocketNoBulkStatus: Oops, device connection failed. Please try again [bulk
DeviceSocketNoHandler: Oops, device connection failed (handler {{query}}). Please try again. DeviceSocketNoHandler: Oops, device connection failed (handler {{query}}). Please try again.
LatestMCUInstalledError: MCU on device already up to date. LatestMCUInstalledError: MCU on device already up to date.
HardResetFail: Reset failed. Please try again. HardResetFail: Reset failed. Please try again.
CannotUninstall: Cannot uninstall app.
CannotInstall: Not enough room left on your device. Please uninstall some apps and try again. ManagerAPIsFail: Our services are currently unavailable. Please try again later.
ManagerUnexpected: Unexpected error occurred ({{msg}}). Please try again later.
ManagerNotEnoughSpace: Not enough room left on your device. Please uninstall some apps and try again.
ManagerDeviceLocked: Device is locked
ManagerAppAlreadyInstalled: App is already installed
ManagerAppRelyOnBTC: You must install Bitcoin application first
ManagerUninstallBTCDep: You must uninstall other altcoins first

Loading…
Cancel
Save