import { crypto } from '@chat-app/shared'; import { type ReactNode, useEffect, useState } from 'react'; import { createLibsodiumBackend } from '../lib/cryptoBackend'; import { BootError } from './BootError'; import { BootSplash } from './BootSplash'; interface Props { children: ReactNode; } // React Native exposes ErrorUtils on the global. The types ship with RN but // we cast defensively because the renderer used by Vitest does not. interface RNErrorUtils { getGlobalHandler: () => (err: Error, isFatal?: boolean) => void; setGlobalHandler: (handler: (err: Error, isFatal?: boolean) => void) => void; } // Initialises the crypto backend inside a useEffect (not at module-eval) so // any failure surfaces in the React tree. Also installs a global JS error // handler that routes unhandled throws to BootError; this catches errors // thrown during render (e.g. the lazy env proxy reading a missing var) that // would otherwise escape every per-screen ErrorBoundary. export function AppBootstrap({ children }: Props) { const [error, setError] = useState(null); const [ready, setReady] = useState(false); useEffect(() => { try { crypto.setCryptoBackend(createLibsodiumBackend()); setReady(true); } catch (e: unknown) { setError(e instanceof Error ? e : new Error(String(e))); } }, []); useEffect(() => { const eu = (globalThis as unknown as { ErrorUtils?: RNErrorUtils }).ErrorUtils; if (!eu) return; const prev = eu.getGlobalHandler(); eu.setGlobalHandler((err, isFatal) => { prev?.(err, isFatal); setError(err); }); return () => eu.setGlobalHandler(prev); }, []); if (error) return ; if (!ready) return ; return <>{children}; }