2018-11-28 23:01:40 +09:00
import PropTypes from 'prop-types' ;
2023-05-28 23:38:10 +09:00
import { PureComponent } from 'react' ;
2018-11-28 23:01:40 +09:00
import { FormattedMessage } from 'react-intl' ;
2023-05-28 23:38:10 +09:00
import { Helmet } from 'react-helmet' ;
import StackTrace from 'stacktrace-js' ;
2023-07-28 02:25:07 +09:00
import { version , source _url } from 'flavours/glitch/initial_state' ;
2018-11-28 23:01:40 +09:00
2023-05-28 21:18:23 +09:00
export default class ErrorBoundary extends PureComponent {
2018-11-28 23:01:40 +09:00
static propTypes = {
children : PropTypes . node ,
} ;
state = {
hasError : false ,
2020-02-20 07:16:29 +09:00
errorMessage : undefined ,
2018-11-28 23:01:40 +09:00
stackTrace : undefined ,
2020-02-20 07:16:29 +09:00
mappedStackTrace : undefined ,
2018-11-28 23:01:40 +09:00
componentStack : undefined ,
2023-02-04 04:52:07 +09:00
} ;
2018-11-28 23:01:40 +09:00
2023-07-28 02:25:07 +09:00
componentDidCatch ( error , info ) {
2018-11-28 23:01:40 +09:00
this . setState ( {
hasError : true ,
2020-02-20 07:16:29 +09:00
errorMessage : error . toString ( ) ,
2018-11-28 23:01:40 +09:00
stackTrace : error . stack ,
componentStack : info && info . componentStack ,
2020-02-20 07:16:29 +09:00
mappedStackTrace : undefined ,
} ) ;
StackTrace . fromError ( error ) . then ( ( stackframes ) => {
this . setState ( {
mappedStackTrace : stackframes . map ( ( sf ) => sf . toString ( ) ) . join ( '\n' ) ,
} ) ;
} ) . catch ( ( ) => {
this . setState ( {
mappedStackTrace : undefined ,
} ) ;
2018-11-28 23:01:40 +09:00
} ) ;
}
2023-07-28 02:25:07 +09:00
handleCopyStackTrace = ( ) => {
const { errorMessage , stackTrace , mappedStackTrace } = this . state ;
const textarea = document . createElement ( 'textarea' ) ;
2018-11-28 23:01:40 +09:00
2023-07-28 02:25:07 +09:00
let contents = [ errorMessage , stackTrace ] ;
if ( mappedStackTrace ) {
contents . push ( mappedStackTrace ) ;
}
2018-11-28 23:01:40 +09:00
2023-07-28 02:25:07 +09:00
textarea . textContent = contents . join ( '\n\n\n' ) ;
textarea . style . position = 'fixed' ;
2018-11-28 23:01:40 +09:00
2023-07-28 02:25:07 +09:00
document . body . appendChild ( textarea ) ;
2020-09-14 22:05:22 +09:00
2023-07-28 02:25:07 +09:00
try {
textarea . select ( ) ;
document . execCommand ( 'copy' ) ;
2024-09-11 22:59:46 +09:00
} catch {
// do nothing
2023-07-28 02:25:07 +09:00
} finally {
document . body . removeChild ( textarea ) ;
2018-11-28 23:01:40 +09:00
}
2023-07-28 02:25:07 +09:00
this . setState ( { copied : true } ) ;
setTimeout ( ( ) => this . setState ( { copied : false } ) , 700 ) ;
} ;
render ( ) {
const { hasError , copied , errorMessage } = this . state ;
if ( ! hasError ) {
return this . props . children ;
2021-09-08 20:47:48 +09:00
}
2023-07-28 02:25:07 +09:00
const likelyBrowserAddonIssue = errorMessage && errorMessage . includes ( 'NotFoundError' ) ;
2018-11-28 23:01:40 +09:00
return (
2023-07-28 02:25:07 +09:00
< div className = 'error-boundary' >
< div >
< p className = 'error-boundary__error' >
{ likelyBrowserAddonIssue ? (
< FormattedMessage id = 'error.unexpected_crash.explanation_addons' defaultMessage = 'This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.' / >
) : (
< FormattedMessage id = 'error.unexpected_crash.explanation' defaultMessage = 'Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.' / >
) }
2019-07-01 20:38:00 +09:00
< / p >
2023-07-28 02:25:07 +09:00
< p >
{ likelyBrowserAddonIssue ? (
< FormattedMessage id = 'error.unexpected_crash.next_steps_addons' defaultMessage = 'Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' / >
) : (
< FormattedMessage id = 'error.unexpected_crash.next_steps' defaultMessage = 'Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' / >
2019-07-01 20:38:00 +09:00
) }
2023-07-28 02:25:07 +09:00
< / p >
< p className = 'error-boundary__footer' > Mastodon v { version } · < a href = { source _url } rel = 'noopener noreferrer' target = '_blank' > < FormattedMessage id = 'errors.unexpected_crash.report_issue' defaultMessage = 'Report issue' / > < / a > · < button onClick = { this . handleCopyStackTrace } className = { copied ? 'copied' : '' } > < FormattedMessage id = 'errors.unexpected_crash.copy_stacktrace' defaultMessage = 'Copy stacktrace to clipboard' / > < / button > < / p >
2018-11-28 23:01:40 +09:00
< / div >
2022-10-20 21:35:29 +09:00
< Helmet >
< meta name = 'robots' content = 'noindex' / >
< / Helmet >
2018-11-28 23:01:40 +09:00
< / div >
) ;
}
}