diff --git a/app/_layout.tsx b/app/_layout.tsx index 2e56cf7..1892cf6 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,13 +1,55 @@ -import { useEffect } from "react"; +import { useEffect, useState, Component } from "react"; +import type { ReactNode } from "react"; import { Stack, router } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { View, Text, ScrollView } from "react-native"; import "../global.css"; import { Colors } from "@/constants/theme"; import { useSettings } from "@/hooks/useSettings"; +// Catch JS errors in release mode and show them on-screen instead of +// silently crashing to desktop. Remove once the crash is identified. +class CrashBoundary extends Component< + { children: ReactNode }, + { error: Error | null } +> { + state = { error: null }; + static getDerivedStateFromError(e: Error) { + return { error: e }; + } + render() { + const { error } = this.state; + if (error) { + return ( + + + CRASH — copie ce texte + + + + {String(error)}{"\n\n"}{(error as any).stack} + + + + ); + } + return this.props.children; + } +} + export default function RootLayout() { const { settings, ready } = useSettings(); + const [jsError, setJsError] = useState(null); + + useEffect(() => { + const prev = (ErrorUtils as any).getGlobalHandler(); + (ErrorUtils as any).setGlobalHandler((e: Error, isFatal: boolean) => { + if (isFatal) setJsError(`${String(e)}\n\n${(e as any).stack ?? ""}`); + prev?.(e, isFatal); + }); + return () => (ErrorUtils as any).setGlobalHandler(prev); + }, []); useEffect(() => { if (!ready) return; @@ -16,24 +58,41 @@ export default function RootLayout() { } }, [ready, settings.onboardingDone]); + if (jsError) { + return ( + + + FATAL JS ERROR — copie ce texte + + + + {jsError} + + + + ); + } + return ( - - - - - - - - - - + + + + + + + + + + + + ); }