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-01-10 14:25:06 -08:00
2021-10-10 00:48:35 -07:00
import Icon from 'soapbox/components/icon' ;
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
export default class ErrorBoundary extends React . PureComponent {
static propTypes = {
children : PropTypes . node ,
} ;
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 ;
2020-03-27 13:59:38 -07:00
if ( ! hasError ) {
return this . props . children ;
}
2021-07-05 16:08:04 -07:00
const errorText = this . getErrorText ( ) ;
2020-03-27 13:59:38 -07:00
return (
< div className = 'error-boundary' >
< div >
2021-10-10 00:48:35 -07:00
< Icon src = { require ( '@tabler/icons/icons/mood-sad.svg' ) } className = 'sad-face' / >
2020-03-27 13:59:38 -07:00
< FormattedMessage id = 'alert.unexpected.message' defaultMessage = 'An unexpected error occurred.' / >
2021-10-10 00:48:35 -07:00
< div className = 'return-home' >
2022-02-21 00:17:52 -08:00
< a href = '/' >
2021-10-10 00:48:35 -07:00
< Icon src = { require ( '@tabler/icons/icons/arrow-back.svg' ) } / >
< FormattedMessage id = 'alert.unexpected.return_home' defaultMessage = 'Return Home' / >
< / a >
< / d i v >
2021-07-05 16:08:04 -07:00
{ errorText && < textarea
ref = { this . setTextareaRef }
className = 'error-boundary__component-stack'
value = { errorText }
onClick = { this . handleCopy }
readOnly
/ > }
2021-09-11 16:29:43 -07:00
{ browser && < p className = 'error-boundary__browser' >
2021-07-11 16:18:15 -07:00
{ browser . getBrowserName ( ) } { browser . getBrowserVersion ( ) }
2021-09-11 16:29:43 -07:00
< / p > }
2022-02-23 09:42:04 -08:00
< p className = 'error-boundary__version' > { sourceCode . displayName } { sourceCode . version } < / p >
2021-04-22 11:57:47 -07:00
< p className = 'help-text' >
< FormattedMessage
id = 'alert.unexpected.help_text'
defaultMessage = 'If the problem persists, please notify a site admin with a screenshot and information about your web browser. You may also {clear_cookies} (this will log you out).'
values = { { clear _cookies : (
< a href = '/' onClick = { this . clearCookies } >
< FormattedMessage
id = 'alert.unexpected.clear_cookies'
defaultMessage = 'clear cookies and browser data'
/ >
< / a >
) } }
/ >
< / p >
2020-03-27 13:59:38 -07:00
< / d i v >
< / d i v >
) ;
}
}