Add error boundary component to catch Web UI crashes

This commit is contained in:
Thibaut Girka 2018-11-28 15:01:40 +01:00 committed by ThibG
parent 39c8a71df8
commit 922d05864f
4 changed files with 133 additions and 5 deletions

View file

@ -0,0 +1,92 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
export default class ErrorBoundary extends React.PureComponent {
static propTypes = {
children: PropTypes.node,
};
state = {
hasError: false,
stackTrace: undefined,
componentStack: undefined,
}
componentDidCatch(error, info) {
this.setState({
hasError: true,
stackTrace: error.stack,
componentStack: info && info.componentStack,
});
}
handleReload(e) {
e.preventDefault();
window.location.reload();
}
render() {
const { hasError, stackTrace, componentStack } = this.state;
if (!hasError) return this.props.children;
let debugInfo = '';
if (stackTrace) {
debugInfo += 'Stack trace\n-----------\n\n```\n' + stackTrace.toString() + '\n```';
}
if (componentStack) {
if (debugInfo) {
debugInfo += '\n\n\n';
}
debugInfo += 'React component stack\n---------------------\n\n```\n' + componentStack.toString() + '\n```';
}
return (
<div tabIndex='-1'>
<div className='error-boundary'>
<h1><FormattedMessage id='web_app_crash.title' defaultMessage="We're sorry, but something went wrong with the Mastodon app." /></h1>
<p>
<FormattedMessage id='web_app_crash.content' defaultMessage='You could try any of the following:' />
<ul>
<li>
<FormattedMessage
id='web_app_crash.report_issue'
defaultMessage='Report a bug in the {issuetracker}'
values={{ issuetracker: <a href='https://github.com/glitch-soc/mastodon/issues' rel='noopener' target='_blank'><FormattedMessage id='web_app_crash.issue_tracker' defaultMessage='issue tracker' /></a> }}
/>
{ debugInfo !== '' && (
<details>
<summary><FormattedMessage id='web_app_crash.debug_info' defaultMessage='Debug information' /></summary>
<textarea
className='web_app_crash-stacktrace'
value={debugInfo}
rows='10'
readOnly
/>
</details>
)}
</li>
<li>
<FormattedMessage
id='web_app_crash.reload_page'
defaultMessage='{reload} the current page'
values={{ reload: <a href='#' onClick={this.handleReload}><FormattedMessage id='web_app_crash.reload' defaultMessage='Reload' /></a> }}
/>
</li>
<li>
<FormattedMessage
id='web_app_crash.change_your_settings'
defaultMessage='Change your {settings}'
values={{ settings: <a href='/settings/preferences'><FormattedMessage id='web_app_crash.settings' defaultMessage='settings' /></a> }}
/>
</li>
</ul>
</p>
</div>
</div>
);
}
}

View file

@ -12,6 +12,7 @@ import { connectUserStream } from 'flavours/glitch/actions/streaming';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from 'locales';
import initialState from 'flavours/glitch/util/initial_state';
import ErrorBoundary from 'flavours/glitch/components/error_boundary';
const { localeData, messages } = getLocale();
addLocaleData(localeData);
@ -61,11 +62,13 @@ export default class Mastodon extends React.PureComponent {
return (
<IntlProvider locale={locale} messages={messages}>
<Provider store={store}>
<ErrorBoundary>
<BrowserRouter basename='/web'>
<ScrollContext>
<Route path='/' component={UI} />
</ScrollContext>
</BrowserRouter>
</ErrorBoundary>
</Provider>
</IntlProvider>
);

View file

@ -0,0 +1,32 @@
.error-boundary {
h1 {
font-size: 26px;
line-height: 36px;
font-weight: 400;
margin-bottom: 8px;
}
p {
color: $primary-text-color;
font-size: 15px;
line-height: 20px;
a {
color: $primary-text-color;
text-decoration: underline;
}
ul {
list-style: disc;
margin-left: 0;
padding-left: 1em;
}
textarea.web_app_crash-stacktrace {
width: 100%;
resize: none;
white-space: pre;
font-family: $font-monospace, monospace;
}
}
}

View file

@ -1263,3 +1263,4 @@ noscript {
@import 'lists';
@import 'emoji_picker';
@import 'local_settings';
@import 'error_boundary';