Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .Jules/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
7 changes: 7 additions & 0 deletions .Jules/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
5 changes: 4 additions & 1 deletion mobile/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<AuthProvider>
<PaperProvider>
<AppNavigator />
<ErrorBoundary>
<AppNavigator />
</ErrorBoundary>
</PaperProvider>
</AuthProvider>
);
Expand Down
98 changes: 98 additions & 0 deletions mobile/components/ErrorBoundary.js
Original file line number Diff line number Diff line change
@@ -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 (
<View style={[styles.container, { backgroundColor: theme.colors.background }]}>
<Card style={styles.card}>
<Card.Content style={styles.content}>
<Title style={{ color: theme.colors.error, marginBottom: 8 }}>Oops! Something went wrong.</Title>
<Paragraph style={styles.errorText}>
{error?.message || "An unexpected error occurred."}
</Paragraph>
<Paragraph style={styles.suggestionText}>
Please try again or restart the application if the problem persists.
</Paragraph>
</Card.Content>
<Card.Actions style={styles.actions}>
<Button
mode="contained"
onPress={resetError}
buttonColor={theme.colors.error}
textColor={theme.colors.onError}
>
Try Again
</Button>
</Card.Actions>
</Card>
</View>
);
};

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 <ErrorFallback error={this.state.error} resetError={this.resetError} />;
}

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;
Loading