Skip to content
Closed
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
8 changes: 8 additions & 0 deletions .Jules/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,11 @@
**Notes:**
- Any additional context
-->
### [2026-01-01] - v1.1.0

#### Added
- Pull-to-refresh functionality with haptic feedback to `HomeScreen` and `GroupDetailsScreen` in mobile app.

**Files Modified:**
- `mobile/screens/HomeScreen.js`
- `mobile/screens/GroupDetailsScreen.js`
8 changes: 8 additions & 0 deletions .Jules/knowledge.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,3 +390,11 @@ _Document errors and their solutions here as you encounter them._
- react-native-paper: UI components
- axios: API calls (via api/client.js)
- expo: Platform SDK
### Mobile - Pull-to-Refresh with Haptics
**Date:** 2026-01-01
**Context:** Implemented `RefreshControl` with `expo-haptics` on `HomeScreen` and `GroupDetailsScreen`.
**Solution/Learning:**
- Used `expo-haptics` for tactile feedback during refresh.
- Introduced `isRefreshing` state separate from `isLoading` to ensure the `RefreshControl` spinner is visible during the refresh action. Simply reusing `isLoading` can lead to the spinner disappearing prematurely if `isLoading` is set to `true` during the fetch but the `RefreshControl` expects a dedicated refreshing prop that matches the user interaction lifecycle.
- Used `Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)` for a subtle effect.
**Files Affected:** `mobile/screens/HomeScreen.js`, `mobile/screens/GroupDetailsScreen.js`
8 changes: 3 additions & 5 deletions .Jules/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@

### Mobile

- [ ] **[ux]** Pull-to-refresh with haptic feedback on all list screens
- Files: `mobile/screens/HomeScreen.js`, `mobile/screens/GroupDetailsScreen.js`
- Context: Add RefreshControl + Expo Haptics to main lists
- [x] **[ux]** Pull-to-refresh with haptic feedback on all list screens
- Completed: 2026-01-01
- Files modified: `mobile/screens/HomeScreen.js`, `mobile/screens/GroupDetailsScreen.js`
- Impact: Native feel, users can easily refresh data
- Size: ~45 lines
- Added: 2026-01-01

- [ ] **[ux]** Complete skeleton loading for HomeScreen groups
- File: `mobile/screens/HomeScreen.js`
Expand Down
10 changes: 10 additions & 0 deletions mobile/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@react-navigation/native-stack": "^7.3.23",
"axios": "^1.11.0",
"expo": "^54.0.25",
"expo-haptics": "~15.0.8",
"expo-image-picker": "~17.0.8",
"expo-status-bar": "~3.0.8",
"react": "19.1.0",
Expand Down
16 changes: 14 additions & 2 deletions mobile/screens/GroupDetailsScreen.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useContext, useEffect, useState } from "react";
import { Alert, FlatList, StyleSheet, Text, View } from "react-native";
import { Alert, FlatList, RefreshControl, StyleSheet, Text, View } from "react-native";
import * as Haptics from "expo-haptics";
import {
ActivityIndicator,
Card,
Expand All @@ -22,6 +23,7 @@ const GroupDetailsScreen = ({ route, navigation }) => {
const [expenses, setExpenses] = useState([]);
const [settlements, setSettlements] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [isRefreshing, setIsRefreshing] = useState(false);

// Currency configuration - can be made configurable later
const currency = "₹"; // Default to INR, can be changed to '$' for USD
Expand All @@ -31,7 +33,6 @@ const GroupDetailsScreen = ({ route, navigation }) => {

const fetchData = async () => {
try {
setIsLoading(true);
// Fetch members, expenses, and settlements in parallel
const [membersResponse, expensesResponse, settlementsResponse] =
await Promise.all([
Expand All @@ -47,6 +48,7 @@ const GroupDetailsScreen = ({ route, navigation }) => {
Alert.alert("Error", "Failed to fetch group details.");
} finally {
setIsLoading(false);
setIsRefreshing(false);
}
};

Expand All @@ -61,10 +63,17 @@ const GroupDetailsScreen = ({ route, navigation }) => {
),
});
if (token && groupId) {
setIsLoading(true);
fetchData();
}
}, [token, groupId]);

const onRefresh = async () => {
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setIsRefreshing(true);
await fetchData();
};

const getMemberName = (userId) => {
const member = members.find((m) => m.userId === userId);
return member ? member.user.name : "Unknown";
Expand Down Expand Up @@ -202,6 +211,9 @@ const GroupDetailsScreen = ({ route, navigation }) => {
<Text style={styles.emptyText}>No expenses recorded yet.</Text>
}
contentContainerStyle={{ paddingBottom: 80 }} // To avoid FAB overlap
refreshControl={
<RefreshControl refreshing={isRefreshing} onRefresh={onRefresh} />
}
/>

<FAB
Expand Down
18 changes: 14 additions & 4 deletions mobile/screens/HomeScreen.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useContext, useEffect, useState } from "react";
import { Alert, FlatList, StyleSheet, View } from "react-native";
import { Alert, FlatList, RefreshControl, StyleSheet, View } from "react-native";
import * as Haptics from "expo-haptics";
import {
ActivityIndicator,
Appbar,
Expand All @@ -19,6 +20,7 @@ const HomeScreen = ({ navigation }) => {
const { token, logout, user } = useContext(AuthContext);
const [groups, setGroups] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [isRefreshing, setIsRefreshing] = useState(false);
const [groupSettlements, setGroupSettlements] = useState({}); // Track settlement status for each group

// State for the Create Group modal
Expand Down Expand Up @@ -68,7 +70,6 @@ const HomeScreen = ({ navigation }) => {

const fetchGroups = async () => {
try {
setIsLoading(true);
const response = await getGroups();
const groupsList = response.data.groups;
setGroups(groupsList);
Expand All @@ -92,15 +93,23 @@ const HomeScreen = ({ navigation }) => {
Alert.alert("Error", "Failed to fetch groups.");
} finally {
setIsLoading(false);
setIsRefreshing(false);
}
};

useEffect(() => {
if (token) {
setIsLoading(true);
fetchGroups();
}
}, [token]);

const onRefresh = async () => {
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setIsRefreshing(true);
await fetchGroups();
};

const handleCreateGroup = async () => {
if (!newGroupName) {
Alert.alert("Error", "Please enter a group name.");
Expand Down Expand Up @@ -246,8 +255,9 @@ const HomeScreen = ({ navigation }) => {
No groups found. Create or join one!
</Text>
}
onRefresh={fetchGroups}
refreshing={isLoading}
refreshControl={
<RefreshControl refreshing={isRefreshing} onRefresh={onRefresh} />
}
/>
)}
</View>
Expand Down
Loading