import React, { type ErrorInfo, useRef, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { FormattedMessage } from 'react-intl'; import { NODE_ENV } from 'pl-fe/build-config'; import HStack from 'pl-fe/components/ui/hstack'; import Stack from 'pl-fe/components/ui/stack'; import Text from 'pl-fe/components/ui/text'; import Textarea from 'pl-fe/components/ui/textarea'; import { useLogo } from 'pl-fe/hooks/use-logo'; import { usePlFeConfig } from 'pl-fe/hooks/use-pl-fe-config'; import { captureSentryException } from 'pl-fe/sentry'; import KVStore from 'pl-fe/storage/kv-store'; import sourceCode from 'pl-fe/utils/code'; import { unregisterSW } from 'pl-fe/utils/sw'; import SentryFeedbackForm from './sentry-feedback-form'; import SiteLogo from './site-logo'; interface ISiteErrorBoundary { children: React.ReactNode; } /** Application-level error boundary. Fills the whole screen. */ const SiteErrorBoundary: React.FC = ({ children }) => { const { links, sentryDsn } = usePlFeConfig(); const { src: logoSrc } = useLogo(); const textarea = useRef(null); const [error, setError] = useState(); const [componentStack, setComponentStack] = useState(); const [browser, setBrowser] = useState(); const [sentryEventId, setSentryEventId] = useState(); const sentryEnabled = Boolean(sentryDsn); const isProduction = NODE_ENV === 'production'; const errorText = String(error) + componentStack; const clearCookies: React.MouseEventHandler = (e) => { localStorage.clear(); sessionStorage.clear(); KVStore.clear(); if ('serviceWorker' in navigator) { e.preventDefault(); unregisterSW().then(goHome).catch(goHome); } }; const handleCopy: React.MouseEventHandler = () => { if (!textarea.current) return; textarea.current.select(); textarea.current.setSelectionRange(0, 99999); document.execCommand('copy'); }; const handleError = (error: Error, info: ErrorInfo) => { setError(error); setComponentStack(info.componentStack); captureSentryException(error, { tags: { // Allow page crashes to be easily searched in Sentry. ErrorBoundary: 'yes', }, }) .then((eventId) => setSentryEventId(eventId)) .catch(console.error); import('bowser') .then(({ default: Bowser }) => setBrowser(Bowser.getParser(window.navigator.userAgent))) .catch(() => {}); }; const goHome = () => { location.href = '/'; }; const fallback = (
{logoSrc && ( )}

), }} />

{sourceCode.displayName}: {' '}{sourceCode.version}
{(isProduction) ? ( (sentryEnabled && sentryEventId) && ( ) ) : ( <> {errorText && (