/** * ErrorBoundary — fängt React-Render-Fehler und zeigt eine Error-Box * statt White-Screen-of-Death. Plus: Crash wird zum logger geschickt, * der das ueber RVS an die Bridge weiterleitet. * * Einsatz: kritische Komponenten/Modals damit ein Bug nicht die ganze * App killt. */ import React from 'react'; import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { reportAppError } from '../services/logger'; interface Props { children: React.ReactNode; /** Optional: Bezeichnung der eingegrenzten Section fuer's Log. */ scope?: string; /** Optional: Reset-Callback (z.B. Modal schliessen) — Button ist dann sichtbar. */ onReset?: () => void; } interface State { err: Error | null; info: string; } export class ErrorBoundary extends React.Component { constructor(props: Props) { super(props); this.state = { err: null, info: '' }; } static getDerivedStateFromError(err: Error): Partial { return { err }; } componentDidCatch(err: Error, info: any) { const stack = info?.componentStack || ''; this.setState({ info: stack }); reportAppError({ scope: this.props.scope || 'ErrorBoundary', message: err?.message || String(err), stack: (err?.stack || '') + '\n--- componentStack ---\n' + stack, }); } render() { if (this.state.err) { return ( ⚠️ Etwas ist schiefgegangen {this.props.scope || 'unbekannte Komponente'} {this.state.err.message || String(this.state.err)} {this.state.info ? {this.state.info} : null} {this.props.onReset ? ( { this.setState({err:null,info:''}); this.props.onReset?.(); }}> Schliessen + zurueck ) : ( this.setState({err:null,info:''})}> Erneut versuchen )} Crash wurde an die Bridge gemeldet — sichtbar in der Diagnostic-Web-UI unter /api/app-log ); } return this.props.children; } } const s = StyleSheet.create({ box: { flex:1, padding:16, backgroundColor:'#1A0A0A' }, title: { color:'#FF6B6B', fontWeight:'bold', fontSize:16, marginBottom:6 }, scope: { color:'#FF9500', fontSize:12, marginBottom:10 }, scroll: { flex:1, backgroundColor:'#0D0D1A', borderRadius:6, padding:10, marginBottom:10 }, msg: { color:'#FF6B6B', fontSize:13, marginBottom:8 }, stack: { color:'#8888AA', fontSize:11, fontFamily:'monospace' }, btn: { backgroundColor:'#0096FF', paddingVertical:10, borderRadius:6, alignItems:'center' }, btnText: { color:'#fff', fontWeight:'600' }, hint: { color:'#555570', fontSize:10, marginTop:8, textAlign:'center' }, }); export default ErrorBoundary;