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 (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
);
}