import React, { Component, ReactNode } from 'react'; import { blankScreenPrevention } from '../utils/blankScreenPrevention'; interface Props { children: ReactNode; fallback?: ReactNode; } interface State { hasError: boolean; error?: Error; errorInfo?: React.ErrorInfo; retryCount: number; } export class BlankScreenErrorBoundary extends Component { private maxRetries = 3; private retryTimeout?: NodeJS.Timeout; constructor(props: Props) { super(props); this.state = { hasError: false, retryCount: 0 }; } static getDerivedStateFromError(error: Error): Partial { // Update state so the next render will show the fallback UI return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { console.error('🚨 BlankScreenErrorBoundary caught an error:', error, errorInfo); this.setState({ error, errorInfo }); // Notify blank screen prevention system blankScreenPrevention.getInstance(); // Save error details for debugging try { localStorage.setItem('last-react-error', JSON.stringify({ error: error.message, stack: error.stack, componentStack: errorInfo.componentStack, timestamp: Date.now(), userAgent: navigator.userAgent, url: window.location.href })); } catch (e) { console.error('Failed to save error details:', e); } // Attempt automatic recovery for certain types of errors if (this.shouldAttemptAutoRecovery(error)) { this.attemptAutoRecovery(); } } private shouldAttemptAutoRecovery(error: Error): boolean { // Don't auto-recover on mobile to avoid issues const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); if (isMobile) { return false; } const errorMessage = error.message.toLowerCase(); const recoverableErrors = [ 'chunk load failed', 'loading chunk', 'network error', 'failed to fetch', 'script error' ]; return recoverableErrors.some(pattern => errorMessage.includes(pattern)) && this.state.retryCount < this.maxRetries; } private attemptAutoRecovery = () => { if (this.state.retryCount >= this.maxRetries) { console.error('🚨 Max auto-recovery attempts reached'); return; } console.log(`🔄 Attempting auto-recovery (${this.state.retryCount + 1}/${this.maxRetries})`); // Clear the error state after a delay this.retryTimeout = setTimeout(() => { this.setState(prevState => ({ hasError: false, error: undefined, errorInfo: undefined, retryCount: prevState.retryCount + 1 })); }, 2000); }; private handleManualRetry = () => { console.log('🔄 Manual retry requested'); // Clear any pending auto-retry if (this.retryTimeout) { clearTimeout(this.retryTimeout); } // Reset error state this.setState({ hasError: false, error: undefined, errorInfo: undefined, retryCount: 0 }); }; private handleFullReload = () => { console.log('🔄 Full reload requested'); // Save current state before reload try { localStorage.setItem('error-recovery-reload', JSON.stringify({ timestamp: Date.now(), error: this.state.error?.message, retryCount: this.state.retryCount })); } catch (e) { console.error('Failed to save reload state:', e); } window.location.reload(); }; componentWillUnmount() { if (this.retryTimeout) { clearTimeout(this.retryTimeout); } } render() { if (this.state.hasError) { // Custom fallback UI if (this.props.fallback) { return this.props.fallback; } // Default error UI return (

🎵 Something went wrong

The music app encountered an error. Don't worry - your music and settings are safe.

{this.state.retryCount < this.maxRetries && (

Auto-recovery in progress... ({this.state.retryCount + 1}/{this.maxRetries})

)}
{process.env.NODE_ENV === 'development' && this.state.error && (
Error Details (Development)
                  {this.state.error.stack}
                
)}
); } return this.props.children; } }