diff --git a/src/components/ManagerPage/AppSearchBar.js b/src/components/ManagerPage/AppSearchBar.js index e728a91a..7b33b49e 100644 --- a/src/components/ManagerPage/AppSearchBar.js +++ b/src/components/ManagerPage/AppSearchBar.js @@ -1,14 +1,12 @@ // @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 Space from 'components/base/Space' +import Input from 'components/base/Input' import Search from 'components/base/Search' import SearchIcon from 'icons/Search' @@ -24,53 +22,13 @@ type State = { 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 { state = { query: '', focused: false, } - handleChange = (e: any) => this.setState({ query: e.target.value }) + handleChange = (query: string) => this.setState({ query }) handleFocus = (bool: boolean) => () => this.setState({ focused: bool }) @@ -88,20 +46,29 @@ class AppSearchBar extends PureComponent { const color = focused ? 'black' : 'grey' return ( - - - - (this.input = c)} - type="text" - value={query} - onChange={this.handleChange} - onFocus={this.handleFocus(true)} - onBlur={this.handleFocus(false)} - placeholder={'Search app'} - /> - {!!query && } - + + (this.input = c)} + type="text" + value={query} + onChange={this.handleChange} + onFocus={this.handleFocus(true)} + onBlur={this.handleFocus(false)} + placeholder={'Search app'} + renderLeft={ + + + + } + renderRight={ + query ? ( + + + + ) : null + } + /> + { items={list} render={items => children(items)} /> - + ) } } diff --git a/src/components/ManagerPage/AppsList.js b/src/components/ManagerPage/AppsList.js index ba343b78..0ba3dea5 100644 --- a/src/components/ManagerPage/AppsList.js +++ b/src/components/ManagerPage/AppsList.js @@ -21,7 +21,7 @@ import Spinner from 'components/base/Spinner' import Button from 'components/base/Button' import TranslatedError from 'components/TranslatedError' -import ExclamationCircle from 'icons/ExclamationCircle' +import IconInfoCircle from 'icons/InfoCircle' import ExclamationCircleThin from 'icons/ExclamationCircleThin' import Update from 'icons/Update' import Trash from 'icons/Trash' @@ -37,8 +37,6 @@ const List = styled(Box).attrs({ flex-wrap: wrap; ` -let CACHED_APPS = null - const ICONS_FALLBACK = { bitcoin_testnet: 'bitcoin', } @@ -85,8 +83,7 @@ class AppsList extends PureComponent { async fetchAppList() { try { const { targetId, version } = this.props - const appsList = CACHED_APPS || (await listApps.send({ targetId, version }).toPromise()) - CACHED_APPS = appsList + const appsList = await listApps.send({ targetId, version }).toPromise() if (!this._unmounted) { this.setState({ appsList, status: 'idle', appsLoaded: true }) } @@ -249,19 +246,19 @@ class AppsList extends PureComponent { return ( - - {t('app:manager.apps.all')} - - ( - - {t('app:manager.apps.help')} - - )} - > - - - + + {t('app:manager.apps.all')} + ( + + {t('app:manager.apps.help')} + + )} + > + + + + {this.renderList()} diff --git a/src/components/ManagerPage/Dashboard.js b/src/components/ManagerPage/Dashboard.js index 5b4eb971..f64df243 100644 --- a/src/components/ManagerPage/Dashboard.js +++ b/src/components/ManagerPage/Dashboard.js @@ -2,7 +2,6 @@ import React from 'react' import { translate } from 'react-i18next' -import { EXPERIMENTAL_FIRMWARE_UPDATE } from 'config/constants' import type { T, Device } from 'types/common' import Box from 'components/base/Box' @@ -35,15 +34,10 @@ const Dashboard = ({ device, deviceInfo, t }: Props) => ( - {EXPERIMENTAL_FIRMWARE_UPDATE ? ( - - ) : null} + diff --git a/src/components/ManagerPage/UpdateFirmwareButton.js b/src/components/ManagerPage/UpdateFirmwareButton.js index 6f9cedcb..ebeb884c 100644 --- a/src/components/ManagerPage/UpdateFirmwareButton.js +++ b/src/components/ManagerPage/UpdateFirmwareButton.js @@ -4,6 +4,8 @@ import { translate } from 'react-i18next' import type { T } from 'types/common' +import { EXPERIMENTAL_FIRMWARE_UPDATE } from 'config/constants' + import Button from 'components/base/Button' import Text from 'components/base/Text' import { getCleanVersion } from 'components/ManagerPage/FirmwareUpdate' @@ -25,7 +27,7 @@ const UpdateFirmwareButton = ({ t, firmware, installFirmware }: Props) => {t('app:manager.firmware.latest', { version: getCleanVersion(firmware.name) })} - diff --git a/src/components/RenderError.js b/src/components/RenderError.js index 1ffa1b80..20f4f366 100644 --- a/src/components/RenderError.js +++ b/src/components/RenderError.js @@ -10,12 +10,10 @@ import hardReset from 'helpers/hardReset' import type { T } from 'types/common' -import Spoiler from 'components/base/Spoiler' import ExportLogsBtn from 'components/ExportLogsBtn' import Box from 'components/base/Box' import Space from 'components/base/Space' import Button from 'components/base/Button' -import TranslatedError from './TranslatedError' type Props = { error: Error, @@ -94,17 +92,23 @@ ${error.stack} {t('app:crash.reset')} - - + - + {String(error)} +
{error.stack || 'no stacktrace'}
-
- - - {error.stack} - - +
+
+          {__APP_VERSION__}
+        
{children} ) @@ -114,14 +118,14 @@ ${error.stack} const ErrContainer = ({ children }: { children: any }) => (
     {children}
diff --git a/src/components/ThrowBlock.js b/src/components/ThrowBlock.js
index 89b0d497..195ee000 100644
--- a/src/components/ThrowBlock.js
+++ b/src/components/ThrowBlock.js
@@ -17,7 +17,7 @@ class ThrowBlock extends PureComponent {
   }
 
   componentDidCatch(error: Error) {
-    logger.error(error)
+    logger.critical(error)
     this.setState({ error })
   }
 
diff --git a/src/config/cryptocurrencies.js b/src/config/cryptocurrencies.js
index 0b097368..894cdeef 100644
--- a/src/config/cryptocurrencies.js
+++ b/src/config/cryptocurrencies.js
@@ -26,6 +26,7 @@ const supported: CryptoCurrencyIds[] = [
   'viacoin',
   'stealthcoin',
   'poswallet',
+  'clubcoin',
   'bitcoin_testnet',
 ]
 
diff --git a/src/helpers/apps/installApp.js b/src/helpers/apps/installApp.js
index 96f1af75..00dfb4fd 100644
--- a/src/helpers/apps/installApp.js
+++ b/src/helpers/apps/installApp.js
@@ -9,14 +9,26 @@ import type { LedgerScriptParams } from 'helpers/common'
 
 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) {
   return promise.catch((e: Error) => {
-    if (e.message.endsWith('6982')) {
-      throw new CannotInstall()
+    switch (true) {
+      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
   })
 }
 
diff --git a/src/helpers/apps/uninstallApp.js b/src/helpers/apps/uninstallApp.js
index 6ead5304..1611a1b4 100644
--- a/src/helpers/apps/uninstallApp.js
+++ b/src/helpers/apps/uninstallApp.js
@@ -8,14 +8,20 @@ import { createDeviceSocket } from 'helpers/socket'
 import type { LedgerScriptParams } from 'helpers/common'
 import createCustomErrorClass from '../createCustomErrorClass'
 
-const CannotUninstall = createCustomErrorClass('CannotUninstall')
+const ManagerUnexpectedError = createCustomErrorClass('ManagerUnexpectedError')
+const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
+const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBTCDep')
 
 function remapError(promise) {
   return promise.catch((e: Error) => {
-    if (e.message.endsWith('6a83')) {
-      throw new CannotUninstall()
+    switch (true) {
+      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
   })
 }
 
diff --git a/src/logger.js b/src/logger.js
index cc6cfc82..0036f0fc 100644
--- a/src/logger.js
+++ b/src/logger.js
@@ -134,15 +134,31 @@ export default {
     console.log(...args)
     addLog('log', ...args)
   },
+
   warn: (...args: any) => {
     console.warn(...args)
     addLog('warn', ...args)
   },
+
   error: (...args: any) => {
     console.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 }> =>
     logs.map(({ type, date, args }) => ({
       type,
diff --git a/src/renderer/init.js b/src/renderer/init.js
index a91a2087..065fde23 100644
--- a/src/renderer/init.js
+++ b/src/renderer/init.js
@@ -98,7 +98,6 @@ function r(Comp) {
 }
 
 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.error(e)
+  logger.critical(e)
   r()
 })
diff --git a/src/sentry/browser.js b/src/sentry/browser.js
index ac5fc55c..8b4b60d9 100644
--- a/src/sentry/browser.js
+++ b/src/sentry/browser.js
@@ -7,3 +7,7 @@ import install from './install'
 export default (shouldSendCallback: () => boolean) => {
   install(Raven, shouldSendCallback, user().id)
 }
+
+export const captureException = (e: Error) => {
+  Raven.captureException(e)
+}
diff --git a/src/sentry/node.js b/src/sentry/node.js
index a617487b..bf19a6e3 100644
--- a/src/sentry/node.js
+++ b/src/sentry/node.js
@@ -6,3 +6,7 @@ import install from './install'
 export default (shouldSendCallback: () => boolean, userId: string) => {
   install(Raven, shouldSendCallback, userId)
 }
+
+export const captureException = (e: Error) => {
+  Raven.captureException(e)
+}
diff --git a/static/i18n/en/errors.yml b/static/i18n/en/errors.yml
index 95ce4465..640ff96d 100644
--- a/static/i18n/en/errors.yml
+++ b/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.
 LatestMCUInstalledError: MCU on device already up to date.
 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