import { Button } from '@/components/ui/button' import { MessageCircle, RotateCw } from 'lucide-react' import React, { Component, ReactNode } from 'react' import { toast } from 'sonner' import logger from '@/lib/logger' import { isChunkLoadFailureMessage, tryStaleChunkReloadOnce } from '@/lib/stale-chunk-recovery' const ISSUES_URL = 'https://gitrepublic.imwald.eu/repos/npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z/imwald?tab=issues' /** HMR can remount children before parents; context hooks throw. One recovery reload fixes it. */ const CONTEXT_RECOVERY_RELOAD_KEY = 'jumble-context-recovery-reload-at' const CONTEXT_RECOVERY_COOLDOWN_MS = 20_000 function isLikelyBrokenReactContextFromHmr(message: string): boolean { return ( /must be used within (a )?[\w]+/i.test(message) || message.includes('useNostr must be used within') || message.includes('useInterestList must be used within') || (message.includes('useContext') && message.includes('null')) ) } /** Avoid double `reload()` when React StrictMode runs render twice before navigation. */ let contextRecoveryReloadScheduled = false function tryContextRecoveryReload(): boolean { if (typeof window === 'undefined') return false if (contextRecoveryReloadScheduled) return true try { const last = Number(sessionStorage.getItem(CONTEXT_RECOVERY_RELOAD_KEY) || '0') const now = Date.now() if (now - last <= CONTEXT_RECOVERY_COOLDOWN_MS) return false sessionStorage.setItem(CONTEXT_RECOVERY_RELOAD_KEY, String(now)) contextRecoveryReloadScheduled = true window.location.reload() return true } catch { return false } } interface ErrorBoundaryProps { children: ReactNode } interface ErrorBoundaryState { hasError: boolean error?: Error } export class ErrorBoundary extends Component { constructor(props: ErrorBoundaryProps) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error: Error) { return { hasError: true, error } } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { logger.error('ErrorBoundary caught an error', { error, errorInfo }) // Recovery reload runs in render() so navigation starts before the error UI paints. } render() { if (this.state.hasError) { const msg = this.state.error?.message ?? '' if (isChunkLoadFailureMessage(msg) && tryStaleChunkReloadOnce()) { return (
Reloading to pick up the latest app version…
) } if (isLikelyBrokenReactContextFromHmr(msg) && tryContextRecoveryReload()) { return (
Reloading after a dev hot-reload glitch…
) } return (

Oops, something went wrong.

Sorry for the inconvenience. You can help by logging an issue with the error details.

{this.state.error?.message && ( <>
                Error: {this.state.error.message}
              
)}
) } return this.props.children } }