diff --git a/.Jules/changelog.md b/.Jules/changelog.md index 11fc864..3e3bf61 100644 --- a/.Jules/changelog.md +++ b/.Jules/changelog.md @@ -7,6 +7,13 @@ ## [Unreleased] ### Added +- **Mobile Error Boundary System:** Implemented a global React Error Boundary to catch render errors gracefully in the mobile app. + - **Features:** + - Fallback UI using `react-native-paper` components. + - Full theme support natively accessed through the parent `PaperProvider`. + - "Try Again" button to reset the error state and re-render the app. + - **Technical:** Created `mobile/components/ErrorBoundary.js` using a hybrid Class+Functional approach to support React hooks (like `useTheme`) in the fallback UI. Integrated into `mobile/App.js` wrapping the `AppNavigator`. + - **Password Strength Meter:** Added a visual password strength indicator to the signup form. - **Features:** - Real-time strength calculation (Length, Uppercase, Lowercase, Number, Symbol). diff --git a/.Jules/todo.md b/.Jules/todo.md index ebb0c7a..bea6fe5 100644 --- a/.Jules/todo.md +++ b/.Jules/todo.md @@ -50,6 +50,13 @@ ### Mobile +- [x] **[ux]** Error boundary with retry for API failures + - Completed: 2026-04-13 + - Files: Created `mobile/components/ErrorBoundary.js`, wrapped app in `mobile/App.js` + - Context: Catch rendering errors gracefully with a fallback UI and retry button using `react-native-paper` + - Impact: App doesn't crash to a white screen, users can recover + - Size: ~80 lines + - [x] **[ux]** Pull-to-refresh with haptic feedback on all list screens - Completed: 2026-01-21 - Files: `mobile/screens/HomeScreen.js`, `mobile/screens/GroupDetailsScreen.js`, `mobile/screens/FriendsScreen.js` diff --git a/mobile/App.js b/mobile/App.js index f5496ad..03ebc38 100644 --- a/mobile/App.js +++ b/mobile/App.js @@ -2,12 +2,15 @@ import React from 'react'; import AppNavigator from './navigation/AppNavigator'; import { PaperProvider } from 'react-native-paper'; import { AuthProvider } from './context/AuthContext'; +import ErrorBoundary from './components/ErrorBoundary'; export default function App() { return ( - + + + ); diff --git a/mobile/components/ErrorBoundary.js b/mobile/components/ErrorBoundary.js new file mode 100644 index 0000000..4018ee5 --- /dev/null +++ b/mobile/components/ErrorBoundary.js @@ -0,0 +1,98 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { Button, Card, Paragraph, Title, useTheme } from 'react-native-paper'; + +// Functional component to use theme hook +const ErrorFallback = ({ error, resetError }) => { + const theme = useTheme(); + + return ( + + + + Oops! Something went wrong. + + {error?.message || "An unexpected error occurred."} + + + Please try again or restart the application if the problem persists. + + + + + + + + ); +}; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error) { + // Update state so the next render will show the fallback UI. + return { hasError: true, error }; + } + + componentDidCatch(error, errorInfo) { + // You can also log the error to an error reporting service here + console.error("ErrorBoundary caught an error", error, errorInfo); + } + + resetError = () => { + this.setState({ hasError: false, error: null }); + }; + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return ; + } + + return this.props.children; + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 16, + }, + card: { + width: '100%', + maxWidth: 400, + elevation: 4, + }, + content: { + alignItems: 'center', + paddingVertical: 24, + }, + errorText: { + textAlign: 'center', + marginBottom: 16, + opacity: 0.8, + }, + suggestionText: { + textAlign: 'center', + fontSize: 12, + opacity: 0.6, + }, + actions: { + justifyContent: 'center', + paddingBottom: 16, + } +}); + +export default ErrorBoundary;