Browse Source

Make errors reported on init crash

master
Gaëtan Renaudeau 7 years ago
parent
commit
f5a862cb50
  1. 23
      src/components/AppError.js
  2. 97
      src/components/RenderError.js
  3. 85
      src/components/ThrowBlock.js
  4. 2
      src/components/TranslatedError.js
  5. 16
      src/components/TriggerAppReady.js
  6. 5
      src/components/layout/Default.js
  7. 3
      src/renderer/init.js

23
src/components/AppError.js

@ -0,0 +1,23 @@
// @flow
import React from 'react'
import { ThemeProvider } from 'styled-components'
import { I18nextProvider } from 'react-i18next'
import theme from 'styles/theme'
import i18n from 'renderer/i18n/electron'
import TriggerAppReady from './TriggerAppReady'
import RenderError from './RenderError'
// Like App except it just render an error
const App = ({ language, error }: { error: Error, language: string }) => (
<I18nextProvider i18n={i18n} initialLanguage={language}>
<ThemeProvider theme={theme}>
<RenderError disableExport error={error}>
<TriggerAppReady />
</RenderError>
</ThemeProvider>
</I18nextProvider>
)
export default App

97
src/components/RenderError.js

@ -0,0 +1,97 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { shell, remote } from 'electron'
import qs from 'querystring'
import { translate } from 'react-i18next'
import { rgba } from 'styles/helpers'
import db from 'helpers/db'
import type { T } from 'types/common'
import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import TranslatedError from './TranslatedError'
type Props = {
error: Error,
t: T,
disableExport?: boolean,
children?: *,
}
const Container = styled(Box).attrs({
grow: true,
align: 'center',
justify: 'center',
bg: 'lightGraphite',
color: 'alertRed',
ff: 'Museo Sans|Bold',
flow: 2,
})``
const Inner = styled(Box).attrs({
p: 2,
bg: p => rgba(p.theme.colors.alertRed, 0.05),
borderRadius: 1,
})`
border: ${p => `1px solid ${rgba(p.theme.colors.alertRed, 0.1)}`};
`
class RenderError extends PureComponent<Props> {
handleCreateIssue = () => {
const { error } = this.props
if (!error) {
return
}
const q = qs.stringify({
title: `Error: ${error.message}`,
body: `Error was thrown:
\`\`\`
${error.stack}
\`\`\`
`,
})
shell.openExternal(`https://github.com/LedgerHQ/ledger-live-desktop/issues/new?${q}`)
}
handleRestart = () => {
remote.app.relaunch()
remote.app.exit()
}
handleReset = () => {
db.resetAll()
this.handleRestart()
}
render() {
const { error, t, disableExport, children } = this.props
return (
<Container>
<Inner>
<TranslatedError error={error} />
</Inner>
<Box horizontal flow={2}>
<Button primary onClick={this.handleRestart}>
{t('app:crash.restart')}
</Button>
<Button danger onClick={this.handleReset}>
{t('app:crash.reset')}
</Button>
{!disableExport ? <ExportLogsBtn /> : null}
<Button primary onClick={this.handleCreateIssue}>
{t('app:crash.createTicket')}
</Button>
</Box>
{children}
</Container>
)
}
}
export default translate()(RenderError)

85
src/components/ThrowBlock.js

@ -1,49 +1,16 @@
// @flow
import logger from 'logger'
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { shell, remote } from 'electron'
import qs from 'querystring'
import { translate } from 'react-i18next'
import { rgba } from 'styles/helpers'
import db from 'helpers/db'
import type { T } from 'types/common'
import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import TranslatedError from './TranslatedError'
import RenderError from 'components/RenderError'
type Props = {
children: any,
t: T,
}
type State = {
error: ?Error,
}
const Container = styled(Box).attrs({
grow: true,
align: 'center',
justify: 'center',
bg: 'lightGraphite',
color: 'alertRed',
ff: 'Museo Sans|Bold',
flow: 2,
})``
const Inner = styled(Box).attrs({
p: 2,
bg: p => rgba(p.theme.colors.alertRed, 0.05),
borderRadius: 1,
})`
border: ${p => `1px solid ${rgba(p.theme.colors.alertRed, 0.1)}`};
`
class ThrowBlock extends PureComponent<Props, State> {
state = {
error: null,
@ -54,59 +21,13 @@ class ThrowBlock extends PureComponent<Props, State> {
this.setState({ error })
}
handleCreateIssue = () => {
const { error } = this.state
if (!error) {
return
}
const q = qs.stringify({
title: `Error: ${error.message}`,
body: `Error was thrown:
\`\`\`
${error.stack}
\`\`\`
`,
})
shell.openExternal(`https://github.com/LedgerHQ/ledger-live-desktop/issues/new?${q}`)
}
handleRestart = () => {
remote.app.relaunch()
remote.app.exit()
}
handleReset = () => {
db.resetAll()
this.handleRestart()
}
render() {
const { error } = this.state
const { t } = this.props
if (error) {
return (
<Container>
<Inner>
<TranslatedError error={error} />
</Inner>
<Box horizontal flow={2}>
<Button primary onClick={this.handleRestart}>
{t('app:crash.restart')}
</Button>
<Button danger onClick={this.handleReset}>
{t('app:crash.reset')}
</Button>
<ExportLogsBtn />
<Button primary onClick={this.handleCreateIssue}>
{t('app:crash.createTicket')}
</Button>
</Box>
</Container>
)
return <RenderError error={error} />
}
return this.props.children
}
}
export default translate()(ThrowBlock)
export default ThrowBlock

2
src/components/TranslatedError.js

@ -23,7 +23,7 @@ class TranslatedError extends PureComponent<Props> {
if (translation) {
return translation
}
logger.warn('TranslatedError: no transation!', error.name, error)
logger.warn(`TranslatedError: no translation for '${error.name}'`, error)
return error.message || error.name || t('errors:generic')
}
}

16
src/components/TriggerAppReady.js

@ -0,0 +1,16 @@
// @flow
import { PureComponent } from 'react'
export default class TriggerAppReady extends PureComponent<{}> {
componentDidMount() {
window.requestAnimationFrame(() => (this._timeout = setTimeout(() => window.onAppReady(), 300)))
}
componentWillUnmount() {
clearTimeout(this._timeout)
}
_timeout: *
render() {
return null
}
}

5
src/components/layout/Default.js

@ -19,6 +19,7 @@ import ExchangePage from 'components/ExchangePage'
import SettingsPage from 'components/SettingsPage'
import LibcoreBusyIndicator from 'components/LibcoreBusyIndicator'
import DeviceBusyIndicator from 'components/DeviceBusyIndicator'
import TriggerAppReady from 'components/TriggerAppReady'
import AppRegionDrag from 'components/AppRegionDrag'
import IsUnlocked from 'components/IsUnlocked'
@ -41,7 +42,6 @@ type Props = {
class Default extends Component<Props> {
componentDidMount() {
window.requestAnimationFrame(() => (this._timeout = setTimeout(() => window.onAppReady(), 300)))
window.addEventListener('keydown', this.kbShortcut)
}
@ -59,7 +59,6 @@ class Default extends Component<Props> {
}
componentWillUnmount() {
clearTimeout(this._timeout)
window.removeEventListener('keydown', this.kbShortcut) // Prevents adding multiple listeners when hot reloading
}
@ -69,12 +68,12 @@ class Default extends Component<Props> {
}
}
_timeout = undefined
_scrollContainer = null
render() {
return (
<Fragment>
<TriggerAppReady />
{process.platform === 'darwin' && <AppRegionDrag />}
<IsUnlocked>

3
src/renderer/init.js

@ -27,6 +27,7 @@ import hardReset from 'helpers/hardReset'
import sentry from 'sentry/browser'
import App from 'components/App'
import AppError from 'components/AppError'
import 'styles/global'
@ -99,5 +100,5 @@ 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)
process.exit(1)
r(<AppError error={e} language="en" />)
})

Loading…
Cancel
Save