mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-21 22:26:41 +02:00
- Add uload project with apps/web structure
- Reorganize from flat to monorepo structure
- Remove PocketBase binary and local data
- Update to pnpm and @uload/web namespace
- Add picture project to monorepo
- Remove embedded git repository
- Unify all package names to @{project}/{app} schema:
- @maerchenzauber/* (was @storyteller/*)
- @manacore/* (was manacore-*, manacore)
- @manadeck/* (was web, backend, manadeck)
- @memoro/* (was memoro-web, landing, memoro)
- @picture/* (already unified)
- @uload/web
- Add convenient dev scripts for all apps:
- pnpm dev:{project}:web
- pnpm dev:{project}:landing
- pnpm dev:{project}:mobile
- pnpm dev:{project}:backend
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
155 lines
4.4 KiB
TypeScript
155 lines
4.4 KiB
TypeScript
import React, { Component, ReactNode } from 'react';
|
|
import { View, Text, Pressable, ScrollView } from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { Theme, useTheme } from '~/contexts/ThemeContext';
|
|
|
|
interface ErrorBoundaryProps {
|
|
children: ReactNode;
|
|
fallback?: ReactNode;
|
|
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
theme: Theme;
|
|
}
|
|
|
|
interface State {
|
|
hasError: boolean;
|
|
error: Error | null;
|
|
errorInfo: React.ErrorInfo | null;
|
|
}
|
|
|
|
class ErrorBoundaryClass extends Component<ErrorBoundaryProps, State> {
|
|
constructor(props: ErrorBoundaryProps) {
|
|
super(props);
|
|
this.state = {
|
|
hasError: false,
|
|
error: null,
|
|
errorInfo: null,
|
|
};
|
|
}
|
|
|
|
static getDerivedStateFromError(error: Error): State {
|
|
// Update state so the next render will show the fallback UI
|
|
return {
|
|
hasError: true,
|
|
error,
|
|
errorInfo: null,
|
|
};
|
|
}
|
|
|
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
// Log error to console
|
|
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
|
|
|
// Call optional error handler
|
|
this.props.onError?.(error, errorInfo);
|
|
|
|
// Update state with error info
|
|
this.setState({
|
|
error,
|
|
errorInfo,
|
|
});
|
|
}
|
|
|
|
handleReset = () => {
|
|
this.setState({
|
|
hasError: false,
|
|
error: null,
|
|
errorInfo: null,
|
|
});
|
|
};
|
|
|
|
render() {
|
|
if (this.state.hasError) {
|
|
// Use custom fallback if provided
|
|
if (this.props.fallback) {
|
|
return this.props.fallback;
|
|
}
|
|
|
|
const { theme } = this.props;
|
|
|
|
// Default error UI
|
|
return (
|
|
<View style={{ flex: 1, backgroundColor: theme.colors.background, justifyContent: 'center', alignItems: 'center', padding: 24 }}>
|
|
<Ionicons name="alert-circle" size={64} color={theme.colors.error} />
|
|
|
|
<Text style={{ fontSize: 24, fontWeight: 'bold', color: theme.colors.text.primary, marginTop: 24, marginBottom: 8 }}>
|
|
Oops! Etwas ist schiefgelaufen
|
|
</Text>
|
|
|
|
<Text style={{ color: theme.colors.text.tertiary, textAlign: 'center', marginBottom: 24 }}>
|
|
Die App ist auf einen unerwarteten Fehler gestoßen.
|
|
</Text>
|
|
|
|
{this.state.error && (
|
|
<ScrollView
|
|
style={{ width: '100%', maxHeight: 160, backgroundColor: theme.colors.surface, borderRadius: 8, padding: 16, marginBottom: 24 }}
|
|
showsVerticalScrollIndicator={true}
|
|
>
|
|
<Text style={{ color: theme.colors.error, fontSize: 14, fontFamily: 'monospace' }}>
|
|
{this.state.error.toString()}
|
|
</Text>
|
|
{__DEV__ && this.state.errorInfo && (
|
|
<Text style={{ color: theme.colors.text.tertiary, fontSize: 12, fontFamily: 'monospace', marginTop: 8 }}>
|
|
{this.state.errorInfo.componentStack}
|
|
</Text>
|
|
)}
|
|
</ScrollView>
|
|
)}
|
|
|
|
<Pressable
|
|
onPress={this.handleReset}
|
|
style={{ backgroundColor: theme.colors.primary.default, paddingHorizontal: 24, paddingVertical: 12, borderRadius: 8 }}
|
|
>
|
|
<Text style={{ color: '#FFFFFF', fontWeight: '600' }}>
|
|
Erneut versuchen
|
|
</Text>
|
|
</Pressable>
|
|
|
|
{__DEV__ && (
|
|
<Text style={{ color: theme.colors.text.tertiary, fontSize: 12, marginTop: 16 }}>
|
|
Im Development-Modus werden detaillierte Fehlermeldungen angezeigt
|
|
</Text>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return this.props.children;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper component to provide theme to ErrorBoundary
|
|
*/
|
|
export function ErrorBoundary({
|
|
children,
|
|
fallback,
|
|
onError
|
|
}: {
|
|
children: ReactNode;
|
|
fallback?: ReactNode;
|
|
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
}) {
|
|
const { theme } = useTheme();
|
|
return (
|
|
<ErrorBoundaryClass theme={theme} fallback={fallback} onError={onError}>
|
|
{children}
|
|
</ErrorBoundaryClass>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Higher-order component to wrap components with error boundary
|
|
*/
|
|
export function withErrorBoundary<P extends object>(
|
|
Component: React.ComponentType<P>,
|
|
fallback?: ReactNode,
|
|
onError?: (error: Error, errorInfo: React.ErrorInfo) => void
|
|
) {
|
|
return function WithErrorBoundaryWrapper(props: P) {
|
|
return (
|
|
<ErrorBoundary fallback={fallback} onError={onError}>
|
|
<Component {...props} />
|
|
</ErrorBoundary>
|
|
);
|
|
};
|
|
}
|