2020-03-27 13:59:38 -07:00
|
|
|
import PropTypes from 'prop-types';
|
2022-01-10 14:17:52 -08:00
|
|
|
import React from 'react';
|
2020-03-27 13:59:38 -07:00
|
|
|
import { FormattedMessage } from 'react-intl';
|
2022-03-21 11:09:01 -07:00
|
|
|
import { connect } from 'react-redux';
|
2022-01-10 14:25:06 -08:00
|
|
|
|
2022-03-21 11:09:01 -07:00
|
|
|
import { NODE_ENV } from 'soapbox/build_config';
|
|
|
|
import { Text, Stack } from 'soapbox/components/ui';
|
2022-01-10 14:17:52 -08:00
|
|
|
import { captureException } from 'soapbox/monitoring';
|
2022-02-23 09:42:04 -08:00
|
|
|
import sourceCode from 'soapbox/utils/code';
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-03-21 11:09:01 -07:00
|
|
|
import { getSoapboxConfig } from '../actions/soapbox';
|
|
|
|
|
|
|
|
const mapStateToProps = (state) => {
|
|
|
|
const soapboxConfig = getSoapboxConfig(state);
|
|
|
|
|
|
|
|
return {
|
2022-03-21 11:33:10 -07:00
|
|
|
siteTitle: state.instance.title,
|
2022-03-21 11:09:01 -07:00
|
|
|
helpLink: soapboxConfig.getIn(['links', 'help']),
|
|
|
|
supportLink: soapboxConfig.getIn(['links', 'support']),
|
|
|
|
statusLink: soapboxConfig.getIn(['links', 'status']),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
@connect(mapStateToProps)
|
|
|
|
class ErrorBoundary extends React.PureComponent {
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
static propTypes = {
|
|
|
|
children: PropTypes.node,
|
2022-03-21 11:33:10 -07:00
|
|
|
siteTitle: PropTypes.string,
|
2022-03-21 11:09:01 -07:00
|
|
|
supportLink: PropTypes.string,
|
|
|
|
helpLink: PropTypes.string,
|
|
|
|
statusLink: PropTypes.string,
|
2020-03-27 13:59:38 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
state = {
|
|
|
|
hasError: false,
|
|
|
|
componentStack: undefined,
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidCatch(error, info) {
|
2021-09-12 10:33:39 -07:00
|
|
|
captureException(error);
|
2021-09-08 13:45:44 -07:00
|
|
|
|
2020-03-27 13:59:38 -07:00
|
|
|
this.setState({
|
|
|
|
hasError: true,
|
2021-07-05 16:08:04 -07:00
|
|
|
error,
|
2020-03-27 13:59:38 -07:00
|
|
|
componentStack: info && info.componentStack,
|
|
|
|
});
|
2021-09-11 16:29:43 -07:00
|
|
|
|
|
|
|
import(/* webpackChunkName: "error" */'bowser')
|
|
|
|
.then(({ default: Bowser }) => {
|
|
|
|
this.setState({
|
|
|
|
browser: Bowser.getParser(window.navigator.userAgent),
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.catch(() => {});
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
|
|
|
|
2021-07-05 16:08:04 -07:00
|
|
|
setTextareaRef = c => {
|
|
|
|
this.textarea = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
handleCopy = e => {
|
|
|
|
if (!this.textarea) return;
|
|
|
|
|
|
|
|
this.textarea.select();
|
|
|
|
this.textarea.setSelectionRange(0, 99999);
|
|
|
|
|
|
|
|
document.execCommand('copy');
|
|
|
|
}
|
|
|
|
|
|
|
|
getErrorText = () => {
|
|
|
|
const { error, componentStack } = this.state;
|
|
|
|
return error + componentStack;
|
|
|
|
}
|
|
|
|
|
2021-04-22 11:57:47 -07:00
|
|
|
clearCookies = e => {
|
|
|
|
localStorage.clear();
|
|
|
|
sessionStorage.clear();
|
|
|
|
}
|
|
|
|
|
2020-03-27 13:59:38 -07:00
|
|
|
render() {
|
2021-09-11 16:29:43 -07:00
|
|
|
const { browser, hasError } = this.state;
|
2022-03-21 11:33:10 -07:00
|
|
|
const { children, siteTitle, helpLink, statusLink, supportLink } = this.props;
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
if (!hasError) {
|
2022-03-21 11:09:01 -07:00
|
|
|
return children;
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
|
|
|
|
2022-03-21 11:09:01 -07:00
|
|
|
const isProduction = NODE_ENV === 'production';
|
|
|
|
|
2021-07-05 16:08:04 -07:00
|
|
|
const errorText = this.getErrorText();
|
|
|
|
|
2020-03-27 13:59:38 -07:00
|
|
|
return (
|
2022-03-21 11:09:01 -07:00
|
|
|
<div className='h-screen pt-16 pb-12 flex flex-col bg-white'>
|
|
|
|
<main className='flex-grow flex flex-col justify-center max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'>
|
|
|
|
<div className='flex-shrink-0 flex justify-center'>
|
|
|
|
<a href='/' className='inline-flex'>
|
2022-03-21 11:33:10 -07:00
|
|
|
<img className='h-12 w-12' src='/instance/images/app-icon.png' alt={siteTitle} />
|
2021-10-10 00:48:35 -07:00
|
|
|
</a>
|
|
|
|
</div>
|
2022-03-21 11:09:01 -07:00
|
|
|
|
|
|
|
<div className='py-8'>
|
|
|
|
<div className='text-center max-w-xl mx-auto space-y-2'>
|
|
|
|
<h1 className='text-3xl font-extrabold text-gray-900 tracking-tight sm:text-4xl'>
|
|
|
|
<FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' />
|
|
|
|
</h1>
|
|
|
|
<p className='text-lg text-gray-500'>
|
|
|
|
We're sorry for the interruption. If the problem persists, please reach out to our support team. You
|
|
|
|
may also try to <a href='/' onClick={this.clearCookies} className='text-gray-700 hover:underline'>
|
2021-04-22 11:57:47 -07:00
|
|
|
<FormattedMessage
|
|
|
|
id='alert.unexpected.clear_cookies'
|
|
|
|
defaultMessage='clear cookies and browser data'
|
|
|
|
/>
|
|
|
|
</a>
|
2022-03-21 11:09:01 -07:00
|
|
|
{' ' }(this will log you out).
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<Text theme='muted'>
|
|
|
|
<Text weight='medium' tag='span' theme='muted'>{sourceCode.displayName}:</Text>
|
|
|
|
|
|
|
|
{' '}{sourceCode.version}
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
<div className='mt-10'>
|
|
|
|
<a href='/' className='text-base font-medium text-primary-600 hover:text-primary-500'>
|
|
|
|
<FormattedMessage id='alert.unexpected.return_home' defaultMessage='Return Home' />
|
|
|
|
<span aria-hidden='true'> →</span>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{!isProduction && (
|
|
|
|
<div className='py-16 max-w-lg mx-auto space-y-4'>
|
|
|
|
{errorText && (
|
|
|
|
<textarea
|
|
|
|
ref={this.setTextareaRef}
|
|
|
|
className='h-48 p-4 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono'
|
|
|
|
value={errorText}
|
|
|
|
onClick={this.handleCopy}
|
|
|
|
readOnly
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{browser && (
|
|
|
|
<Stack>
|
|
|
|
<Text weight='semibold'>Browser</Text>
|
|
|
|
<Text theme='muted'>{browser.getBrowserName()} {browser.getBrowserVersion()}</Text>
|
|
|
|
</Stack>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</main>
|
|
|
|
|
|
|
|
<footer className='flex-shrink-0 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'>
|
|
|
|
<nav className='flex justify-center space-x-4'>
|
|
|
|
{statusLink && (
|
|
|
|
<>
|
|
|
|
<a href={statusLink} className='text-sm font-medium text-gray-500 hover:text-gray-600'>
|
|
|
|
Status
|
|
|
|
</a>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{helpLink && (
|
|
|
|
<>
|
|
|
|
<span className='inline-block border-l border-gray-300' aria-hidden='true' />
|
|
|
|
<a href={helpLink} className='text-sm font-medium text-gray-500 hover:text-gray-600'>
|
|
|
|
Help Center
|
|
|
|
</a>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{supportLink && (
|
|
|
|
<>
|
|
|
|
<span className='inline-block border-l border-gray-300' aria-hidden='true' />
|
|
|
|
<a href={supportLink} className='text-sm font-medium text-gray-500 hover:text-gray-600'>
|
|
|
|
Support
|
|
|
|
</a>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</nav>
|
|
|
|
</footer>
|
2020-03-27 13:59:38 -07:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-03-21 11:09:01 -07:00
|
|
|
|
|
|
|
export default ErrorBoundary;
|