import PropTypes from 'prop-types'; import { PureComponent } from 'react'; import { FormattedMessage } from 'react-intl'; import { Helmet } from 'react-helmet'; import StackTrace from 'stacktrace-js'; import { source_url } from 'flavours/glitch/initial_state'; import { preferencesLink } from 'flavours/glitch/utils/backend_links'; export default class ErrorBoundary extends PureComponent { static propTypes = { children: PropTypes.node, }; state = { hasError: false, errorMessage: undefined, stackTrace: undefined, mappedStackTrace: undefined, componentStack: undefined, }; componentDidCatch(error, info) { this.setState({ hasError: true, errorMessage: error.toString(), stackTrace: error.stack, componentStack: info && info.componentStack, mappedStackTrace: undefined, }); StackTrace.fromError(error).then((stackframes) => { this.setState({ mappedStackTrace: stackframes.map((sf) => sf.toString()).join('\n'), }); }).catch(() => { this.setState({ mappedStackTrace: undefined, }); }); } handleReload(e) { e.preventDefault(); window.location.reload(); } render() { const { hasError, errorMessage, stackTrace, mappedStackTrace, componentStack } = this.state; if (!hasError) return this.props.children; const likelyBrowserAddonIssue = errorMessage && errorMessage.includes('NotFoundError'); let debugInfo = ''; if (stackTrace) { debugInfo += 'Stack trace\n-----------\n\n```\n' + errorMessage + '\n' + stackTrace.toString() + '\n```'; } if (mappedStackTrace) { debugInfo += 'Mapped stack trace\n-----------\n\n```\n' + errorMessage + '\n' + mappedStackTrace.toString() + '\n```'; } if (componentStack) { if (debugInfo) { debugInfo += '\n\n\n'; } debugInfo += 'React component stack\n---------------------\n\n```\n' + componentStack.toString() + '\n```'; } let issueTracker = source_url; if (source_url.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/?$/)) { issueTracker = source_url + '/issues'; } return (