Skip to content

Conversation

@Devasy
Copy link
Owner

@Devasy Devasy commented Jan 29, 2026

Implemented pull-to-refresh functionality with haptic feedback on HomeScreen, GroupDetailsScreen, and FriendsScreen.

  • Added expo-haptics dependency.
  • Integrated RefreshControl with theme-aware styling.
  • Refactored data fetching logic to support silent refresh (separated isLoading from isRefreshing).
  • Updated internal tracking files.

PR created automatically by Jules for task 14011509928752649065 started by @Devasy23

Summary by CodeRabbit

  • New Features
    • Pull-to-refresh added to Home, Friends, and Group screens with light haptic feedback on trigger.
    • Refresh uses a separate refresh state (no full-screen spinner) for smoother, non-intrusive updates.
    • Refresh indicator now uses the app theme color for visual consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

Co-authored-by: Devasy <110348311+Devasy@users.noreply.github.com>
@Devasy Devasy requested a review from vrajpatelll as a code owner January 29, 2026 19:44
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@netlify
Copy link

netlify bot commented Jan 29, 2026

Deploy Preview for split-but-wiser canceled.

Name Link
🔨 Latest commit 4840434
🔍 Latest deploy log https://app.netlify.com/projects/split-but-wiser/deploys/697bbb7d7d93d10008c77896

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 29, 2026

Walkthrough

Implements mobile pull-to-refresh functionality with haptic feedback across HomeScreen, FriendsScreen, and GroupDetailsScreen. Refactors data fetching with optional loading indicators to enable silent updates. Adds expo-haptics dependency and updates project documentation and tests to reflect completed work.

Changes

Cohort / File(s) Summary
Documentation & Tracking
.Jules/changelog.md, .Jules/todo.md
Updated changelog and todo: recorded the Pull-to-refresh feature as completed, updated file lists and size estimates, and removed/modified prior Confirmation Dialog entry.
Dependencies
mobile/package.json
Added expo-haptics (~15.0.8) to support haptic feedback on refresh.
Screen Components — Refresh Implementation
mobile/screens/HomeScreen.js, mobile/screens/FriendsScreen.js, mobile/screens/GroupDetailsScreen.js
Integrated RefreshControl with isRefreshing state, theme-aware colors via useTheme, haptic feedback via expo-haptics, and refactored fetch functions to accept an optional showLoading flag to allow silent/non-blocking refreshes.
Backend Tests / Optimization Path
backend/tests/expenses/test_expense_service.py
Updated tests to exercise an optimized settlement calculation path: added OptimizedSettlement to exports, patched _recalculate_group_balances / calculate_optimized_settlements across tests, and adjusted assertions and exception expectations to match the two-step optimized flow.

Suggested reviewers

  • vrajpatelll
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Mobile: Add Pull-to-Refresh with Haptics' accurately summarizes the main changes: pull-to-refresh functionality with haptic feedback across mobile screens.
Docstring Coverage ✅ Passed Docstring coverage is 85.29% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@mobile/screens/GroupDetailsScreen.js`:
- Around line 57-62: The onRefresh handler can get stuck if Haptics.impactAsync
rejects; update the onRefresh function so you call setIsRefreshing(true) then
attempt Haptics.impactAsync but suppress any haptics errors (use .catch or
try/catch) and ensure fetchData(false) is always awaited and
setIsRefreshing(false) is executed in a finally block; reference the onRefresh
function, Haptics.impactAsync, fetchData, and setIsRefreshing when making this
change.

Comment on lines +57 to +62
const onRefresh = async () => {
setIsRefreshing(true);
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
await fetchData(false);
setIsRefreshing(false);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

wc -l mobile/screens/GroupDetailsScreen.js

Repository: Devasy/splitwiser

Length of output: 101


🏁 Script executed:

sed -n '50,70p' mobile/screens/GroupDetailsScreen.js

Repository: Devasy/splitwiser

Length of output: 661


🏁 Script executed:

sed -n '1,30p' mobile/screens/GroupDetailsScreen.js

Repository: Devasy/splitwiser

Length of output: 1025


🏁 Script executed:

rg -n "onRefresh" mobile/screens/GroupDetailsScreen.js

Repository: Devasy/splitwiser

Length of output: 135


🏁 Script executed:

rg -n "Haptics" mobile/screens/ -A 2 -B 2

Repository: Devasy/splitwiser

Length of output: 2227


🏁 Script executed:

sed -n '40,55p' mobile/screens/GroupDetailsScreen.js

Repository: Devasy/splitwiser

Length of output: 671


🏁 Script executed:

web_search
expo-haptics impactAsync error handling rejection behavior

Repository: Devasy/splitwiser

Length of output: 162


🌐 Web query:

expo-haptics impactAsync can it reject throw error

💡 Result:

Yes. expo-haptics’s Haptics.impactAsync() returns a Promise<void> and it can reject (throw) in at least these cases:

  • API unavailable on the current platform/runtime: the JS wrapper explicitly checks for the native method and throws UnavailabilityError if it doesn’t exist, which rejects the promise. (In current source it checks ExpoHaptics.impactAsync and throws if missing.) [2]
  • Native call fails: after the availability check it awaits the native module call, so any native-side error would also reject the promise. [2]

The official docs only state that it “fulfills once native haptics functionality is triggered” and don’t enumerate errors, but it is still a Promise and (per the implementation) can reject as above. [1][2]

Practical usage: wrap it in try/catch if you need to be safe on all platforms/builds. [1][2]

Sources: Expo docs for impactAsync() [1], expo-haptics implementation (Haptics.ts) [2].


Harden refresh flow against haptics failures.

Haptics.impactAsync can reject if haptics is unavailable on the platform or if the native call fails. If it rejects, fetchData is never called and isRefreshing remains true, leaving the UI in a stuck state. Wrap in try/finally and use .catch() to suppress haptics errors so the refresh flow always completes.

✅ Suggested fix
 const onRefresh = async () => {
   setIsRefreshing(true);
-  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
-  await fetchData(false);
-  setIsRefreshing(false);
+  try {
+    await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
+    await fetchData(false);
+  } finally {
+    setIsRefreshing(false);
+  }
 };
🤖 Prompt for AI Agents
In `@mobile/screens/GroupDetailsScreen.js` around lines 57 - 62, The onRefresh
handler can get stuck if Haptics.impactAsync rejects; update the onRefresh
function so you call setIsRefreshing(true) then attempt Haptics.impactAsync but
suppress any haptics errors (use .catch or try/catch) and ensure
fetchData(false) is always awaited and setIsRefreshing(false) is executed in a
finally block; reference the onRefresh function, Haptics.impactAsync, fetchData,
and setIsRefreshing when making this change.

google-labs-jules bot and others added 2 commits January 29, 2026 19:50
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/tests/expenses/test_expense_service.py (1)

743-751: Fix assertion to correctly access the pipeline stage.

This is the cause of the pipeline failure. call_args_list items "are two-tuples of (positional args, keyword args)", so call_args_list[0][0] is the tuple of positional arguments, and call_args_list[0][0][0] is the actual pipeline list. The current assertion checks if "$match" is in the pipeline list (which contains dicts), not in the first pipeline stage dict.

🐛 Proposed fix
             # The FIRST aggregate call is the filtered one
             aggregate_call_args = mock_db.expenses.aggregate.call_args_list[0][0]
-            assert "$match" in aggregate_call_args[0]
-            match_query = aggregate_call_args[0]["$match"]
+            pipeline = aggregate_call_args[0]
+            assert "$match" in pipeline[0]
+            match_query = pipeline[0]["$match"]
             assert "createdAt" in match_query
             assert match_query["createdAt"]["$gte"] == from_date
             assert match_query["createdAt"]["$lte"] == to_date
             assert "tags" in match_query
             assert match_query["tags"]["$in"] == tags
🤖 Fix all issues with AI agents
In `@backend/tests/expenses/test_expense_service.py`:
- Around line 1984-1985: Replace the broad pytest.raises(Exception) in the test
around expense_service.get_friends_balance_summary(user_id_str) with a specific
assertion: either use pytest.raises with the exact exception class the service
raises (e.g., HTTPException) and assert expected attributes (like status_code)
on exc_info.value, or keep pytest.raises but add a match= regex to validate the
error message; update the test to reference
expense_service.get_friends_balance_summary and assert the specific error
characteristics rather than catching all Exceptions.

Comment on lines +1984 to +1985
with pytest.raises(Exception): # The service doesn't catch all exceptions
await expense_service.get_friends_balance_summary(user_id_str)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use a more specific exception or add a match parameter.

Using pytest.raises(Exception) is too broad and can mask unrelated errors. Either catch the specific exception type the service raises or add a match parameter to narrow the scope.

🛡️ Proposed fix
-        with pytest.raises(Exception):  # The service doesn't catch all exceptions
+        with pytest.raises(Exception, match="Calculation failed"):
             await expense_service.get_friends_balance_summary(user_id_str)

Or better, if the service wraps this in a specific exception:

with pytest.raises(HTTPException) as exc_info:
    await expense_service.get_friends_balance_summary(user_id_str)
assert exc_info.value.status_code == 500
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
with pytest.raises(Exception): # The service doesn't catch all exceptions
await expense_service.get_friends_balance_summary(user_id_str)
with pytest.raises(Exception, match="Calculation failed"):
await expense_service.get_friends_balance_summary(user_id_str)
🧰 Tools
🪛 Ruff (0.14.14)

1984-1984: Do not assert blind exception: Exception

(B017)


1984-1984: pytest.raises(Exception) is too broad, set the match parameter or use a more specific exception

(PT011)

🤖 Prompt for AI Agents
In `@backend/tests/expenses/test_expense_service.py` around lines 1984 - 1985,
Replace the broad pytest.raises(Exception) in the test around
expense_service.get_friends_balance_summary(user_id_str) with a specific
assertion: either use pytest.raises with the exact exception class the service
raises (e.g., HTTPException) and assert expected attributes (like status_code)
on exc_info.value, or keep pytest.raises but add a match= regex to validate the
error message; update the test to reference
expense_service.get_friends_balance_summary and assert the specific error
characteristics rather than catching all Exceptions.

google-labs-jules bot and others added 2 commits January 29, 2026 19:55
@vrajpatelll vrajpatelll merged commit 637cbb6 into main Jan 30, 2026
13 checks passed
@vrajpatelll vrajpatelll deleted the jules-mobile-pull-to-refresh-14011509928752649065 branch January 30, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants